# -*- coding: utf-8 -*- # # Copyright (C) 2006 Edgewall Software # All rights reserved. # # This software is licensed as described in the file COPYING, which # you should have received as part of this distribution. The terms # are also available at http://genshi.edgewall.org/wiki/License. # # This software consists of voluntary contributions made by many # individuals. For the exact contribution history, see the revision # history and logs, available at http://genshi.edgewall.org/log/. import unittest from genshi.core import Attrs, QName from genshi.input import XML from genshi.path import Path, PathParser, PathSyntaxError, GenericStrategy, \ SingleStepStrategy, SimplePathStrategy from genshi.tests.test_utils import doctest_suite class FakePath(Path): def __init__(self, strategy): self.strategy = strategy def test(self, ignore_context = False): return self.strategy.test(ignore_context) class PathTestCase(unittest.TestCase): strategies = [GenericStrategy, SingleStepStrategy, SimplePathStrategy] def test_error_no_absolute_path(self): self.assertRaises(PathSyntaxError, Path, '/root') def test_error_unsupported_axis(self): self.assertRaises(PathSyntaxError, Path, '..') self.assertRaises(PathSyntaxError, Path, 'parent::ma') def test_1step(self): xml = XML('') self._test_eval( path = 'elem', equiv = '', input = xml, output = '' ) self._test_eval( path = 'elem', equiv = '', input = xml, output = '' ) self._test_eval( path = 'child::elem', equiv = '', input = xml, output = '' ) self._test_eval( path = '//elem', equiv = '', input = xml, output = '' ) self._test_eval( path = 'descendant::elem', equiv = '', input = xml, output = '' ) def test_1step_self(self): xml = XML('') self._test_eval( path = '.', equiv = '', input = xml, output = '' ) self._test_eval( path = 'self::node()', equiv = '', input = xml, output = '' ) def test_1step_wildcard(self): xml = XML('') self._test_eval( path = '*', equiv = '', input = xml, output = '' ) self._test_eval( path = 'child::*', equiv = '', input = xml, output = '' ) self._test_eval( path = 'child::node()', equiv = '', input = xml, output = '' ) self._test_eval( path = '//*', equiv = '', input = xml, output = '' ) def test_1step_attribute(self): self._test_eval( path = '@foo', equiv = '', input = XML(''), output = '' ) xml = XML('') self._test_eval( path = '@foo', equiv = '', input = xml, output = 'bar' ) self._test_eval( path = './@foo', equiv = '', input = xml, output = 'bar' ) def test_1step_text(self): xml = XML('Hey') self._test_eval( path = 'text()', equiv = '', input = xml, output = 'Hey' ) self._test_eval( path = './text()', equiv = '', input = xml, output = 'Hey' ) self._test_eval( path = '//text()', equiv = '', input = xml, output = 'Hey' ) self._test_eval( path = './/text()', equiv = '', input = xml, output = 'Hey' ) def test_2step(self): xml = XML('') self._test_eval('*', input=xml, output='') self._test_eval('bar', input=xml, output='') self._test_eval('baz', input=xml, output='') def test_2step_attribute(self): xml = XML('Hey Joe') self._test_eval('@*', input=xml, output='x') self._test_eval('./@*', input=xml, output='x') self._test_eval('.//@*', input=xml, output='xjoe') self._test_eval('*/@*', input=xml, output='joe') xml = XML('') self._test_eval('@*', input=xml, output='') self._test_eval('foo/@*', input=xml, output='12') def test_2step_complex(self): xml = XML('') self._test_eval( path = 'foo/bar', equiv = '', input = xml, output = '' ) self._test_eval( path = './bar', equiv = '', input = xml, output = '' ) self._test_eval( path = 'foo/*', equiv = '', input = xml, output = '' ) xml = XML('') self._test_eval( path = './bar', equiv = '', input = xml, output = '' ) xml = XML('''
1One
2Two
''') self._test_eval( path = 'tr/td[1]', input = xml, output = '12' ) xml = XML('''
  • item1
    • subitem11
  • item2
    • subitem21
''') self._test_eval( path = 'li[2]/ul', input = xml, output = '
  • subitem21
' ) def test_2step_text(self): xml = XML('Foo') self._test_eval( path = 'item/text()', equiv = '', input = xml, output = 'Foo' ) self._test_eval( path = '*/text()', equiv = '', input = xml, output = 'Foo' ) self._test_eval( path = '//text()', equiv = '', input = xml, output = 'Foo' ) self._test_eval( path = './text()', equiv = '', input = xml, output = '' ) xml = XML('FooBar') self._test_eval( path = 'item/text()', equiv = '', input = xml, output = 'FooBar' ) xml = XML('FooBar') self._test_eval( path = 'item/name/text()', equiv = '', input = xml, output = 'Foo' ) def test_3step(self): xml = XML('') self._test_eval( path = 'foo/*', equiv = '', input = xml, output = '' ) def test_3step_complex(self): self._test_eval( path = '*/bar', equiv = '', input = XML(''), output = '' ) self._test_eval( path = '//bar', equiv = '', input = XML(''), output = '' ) def test_3step_complex_text(self): xml = XML('Some text in here.') self._test_eval( path = 'item/bar/text()', equiv = '', input = xml, output = 'Some text ' ) self._test_eval( path = 'item//bar/text()', equiv = '', input = xml, output = 'Some text in here.' ) def test_node_type_comment(self): xml = XML('') self._test_eval( path = 'comment()', equiv = '', input = xml, output = '' ) def test_node_type_text(self): xml = XML('Some text
in here.
') self._test_eval( path = 'text()', equiv = '', input = xml, output = 'Some text in here.' ) def test_node_type_node(self): xml = XML('Some text
in here.
') self._test_eval( path = 'node()', equiv = '', input = xml, output = 'Some text
in here.' ) def test_node_type_processing_instruction(self): xml = XML('') self._test_eval( path = '//processing-instruction()', equiv = '', input = xml, output = '' ) self._test_eval( path = 'processing-instruction()', equiv = '', input = xml, output = '' ) self._test_eval( path = 'processing-instruction("php")', equiv = '', input = xml, output = '' ) def test_simple_union(self): xml = XML("""1
2
3
""") self._test_eval( path = '*|text()', equiv = '', input = xml, output = '1
2
3
' ) def test_predicate_name(self): xml = XML('') self._test_eval('*[name()="foo"]', input=xml, output='') def test_predicate_localname(self): xml = XML('') self._test_eval('*[local-name()="foo"]', input=xml, output='') def test_predicate_namespace(self): xml = XML('') self._test_eval('*[namespace-uri()="NS"]', input=xml, output='') def test_predicate_not_name(self): xml = XML('') self._test_eval('*[not(name()="foo")]', input=xml, output='') def test_predicate_attr(self): xml = XML('') self._test_eval('item[@important]', input=xml, output='') self._test_eval('item[@important="very"]', input=xml, output='') def test_predicate_attr_equality(self): xml = XML('') self._test_eval('item[@important="very"]', input=xml, output='') self._test_eval('item[@important!="very"]', input=xml, output='') def test_predicate_attr_greater_than(self): xml = XML('') self._test_eval('item[@priority>3]', input=xml, output='') self._test_eval('item[@priority>2]', input=xml, output='') def test_predicate_attr_less_than(self): xml = XML('') self._test_eval('item[@priority<3]', input=xml, output='') self._test_eval('item[@priority<4]', input=xml, output='') def test_predicate_attr_and(self): xml = XML('') self._test_eval('item[@important and @important="very"]', input=xml, output='') self._test_eval('item[@important and @important="notso"]', input=xml, output='') def test_predicate_attr_or(self): xml = XML('') self._test_eval('item[@urgent or @important]', input=xml, output='') self._test_eval('item[@urgent or @notso]', input=xml, output='') def test_predicate_boolean_function(self): xml = XML('bar') self._test_eval('*[boolean("")]', input=xml, output='') self._test_eval('*[boolean("yo")]', input=xml, output='bar') self._test_eval('*[boolean(0)]', input=xml, output='') self._test_eval('*[boolean(42)]', input=xml, output='bar') self._test_eval('*[boolean(false())]', input=xml, output='') self._test_eval('*[boolean(true())]', input=xml, output='bar') def test_predicate_ceil_function(self): xml = XML('bar') self._test_eval('*[ceiling("4.5")=5]', input=xml, output='bar') def test_predicate_concat_function(self): xml = XML('bar') self._test_eval('*[name()=concat("f", "oo")]', input=xml, output='bar') def test_predicate_contains_function(self): xml = XML('bar') self._test_eval('*[contains(name(), "oo")]', input=xml, output='bar') def test_predicate_matches_function(self): xml = XML('barfoo') self._test_eval('*[matches(name(), "foo|bar")]', input=xml, output='barfoo') def test_predicate_false_function(self): xml = XML('bar') self._test_eval('*[false()]', input=xml, output='') def test_predicate_floor_function(self): xml = XML('bar') self._test_eval('*[floor("4.5")=4]', input=xml, output='bar') def test_predicate_normalize_space_function(self): xml = XML('bar') self._test_eval('*[normalize-space(" foo bar ")="foo bar"]', input=xml, output='bar') def test_predicate_number_function(self): xml = XML('bar') self._test_eval('*[number("3.0")=3]', input=xml, output='bar') self._test_eval('*[number("3.0")=3.0]', input=xml, output='bar') self._test_eval('*[number("0.1")=.1]', input=xml, output='bar') def test_predicate_round_function(self): xml = XML('bar') self._test_eval('*[round("4.4")=4]', input=xml, output='bar') self._test_eval('*[round("4.6")=5]', input=xml, output='bar') def test_predicate_starts_with_function(self): xml = XML('bar') self._test_eval('*[starts-with(name(), "f")]', input=xml, output='bar') self._test_eval('*[starts-with(name(), "b")]', input=xml, output='') def test_predicate_string_length_function(self): xml = XML('bar') self._test_eval('*[string-length(name())=3]', input=xml, output='bar') def test_predicate_substring_function(self): xml = XML('bar') self._test_eval('*[substring(name(), 1)="oo"]', input=xml, output='bar') self._test_eval('*[substring(name(), 1, 1)="o"]', input=xml, output='bar') def test_predicate_substring_after_function(self): xml = XML('bar') self._test_eval('*[substring-after(name(), "f")="oo"]', input=xml, output='bar') def test_predicate_substring_before_function(self): xml = XML('bar') self._test_eval('*[substring-before(name(), "oo")="f"]', input=xml, output='bar') def test_predicate_translate_function(self): xml = XML('bar') self._test_eval('*[translate(name(), "fo", "ba")="baa"]', input=xml, output='bar') def test_predicate_true_function(self): xml = XML('bar') self._test_eval('*[true()]', input=xml, output='bar') def test_predicate_variable(self): xml = XML('bar') self._test_eval( path = '*[name()=$bar]', input = xml, output = 'bar', variables = {'bar': 'foo'} ) def test_predicate_position(self): xml = XML('') self._test_eval('*[2]', input=xml, output='') def test_predicate_attr_and_position(self): xml = XML('') self._test_eval('*[@id][2]', input=xml, output='') def test_predicate_position_and_attr(self): xml = XML('') self._test_eval('*[1][@id]', input=xml, output='') self._test_eval('*[2][@id]', input=xml, output='') def test_predicate_advanced_position(self): xml = XML('') self._test_eval( 'descendant-or-self::*/' 'descendant-or-self::*/' 'descendant-or-self::*[2]/' 'self::*/descendant::*[3]', input=xml, output='') def test_predicate_child_position(self): xml = XML('\ 12345') self._test_eval('//a/b[2]', input=xml, output='25') self._test_eval('//a/b[3]', input=xml, output='3') def test_name_with_namespace(self): xml = XML('bar') self._test_eval( path = 'f:foo', equiv = '', input = xml, output = 'bar', namespaces = {'f': 'FOO'} ) def test_wildcard_with_namespace(self): xml = XML('bar') self._test_eval( path = 'f:*', equiv = '', input = xml, output = 'bar', namespaces = {'f': 'FOO'} ) def test_predicate_termination(self): """ Verify that a patch matching the self axis with a predicate doesn't cause an infinite loop. See . """ xml = XML('
  • a
  • b
') self._test_eval('.[@flag="1"]/*', input=xml, output='
  • a
  • b
  • ') xml = XML('
    • a
    • b
    ') self._test_eval('.[@flag="0"]/*', input=xml, output='') def test_attrname_with_namespace(self): xml = XML('') self._test_eval('foo[@f:bar]', input=xml, output='', namespaces={'f': 'FOO'}) def test_attrwildcard_with_namespace(self): xml = XML('') self._test_eval('foo[@f:*]', input=xml, output='', namespaces={'f': 'FOO'}) def test_self_and_descendant(self): xml = XML('') self._test_eval('self::root', input=xml, output='') self._test_eval('self::foo', input=xml, output='') self._test_eval('descendant::root', input=xml, output='') self._test_eval('descendant::foo', input=xml, output='') self._test_eval('descendant-or-self::root', input=xml, output='') self._test_eval('descendant-or-self::foo', input=xml, output='') def test_long_simple_paths(self): xml = XML('!' '') self._test_eval('//a/b/a/b/a/c', input=xml, output='!') self._test_eval('//a/b/a/c', input=xml, output='!') self._test_eval('//a/c', input=xml, output='!') self._test_eval('//c', input=xml, output='!') # Please note that a//b is NOT the same as a/descendant::b # it is a/descendant-or-self::node()/b, which SimplePathStrategy # does NOT support self._test_eval('a/b/descendant::a/c', input=xml, output='!') self._test_eval('a/b/descendant::a/d/descendant::a/c', input=xml, output='!') self._test_eval('a/b/descendant::a/d/a/c', input=xml, output='') self._test_eval('//d/descendant::b/descendant::b/descendant::b' '/descendant::c', input=xml, output='!') self._test_eval('//d/descendant::b/descendant::b/descendant::b' '/descendant::b/descendant::c', input=xml, output='') def test_attr_selection(self): xml = XML('') path = Path('foo/@bar') result = path.select(xml) self.assertEqual(list(result), [ Attrs([(QName('bar'), u'abc')]) ]) def test_attr_selection_with_namespace(self): xml = XML( '' '' '') path = Path('foo/@ns1:bar') result = path.select(xml, namespaces={'ns1': 'http://example.com'}) self.assertEqual(list(result), [ Attrs([(QName('http://example.com}bar'), u'abc')]) ]) def _test_support(self, strategy_class, text): path = PathParser(text, None, -1).parse()[0] return strategy_class.supports(path) def test_simple_strategy_support(self): self.assertTrue(self._test_support(SimplePathStrategy, 'a/b')) self.assertTrue(self._test_support(SimplePathStrategy, 'self::a/b')) self.assertTrue(self._test_support(SimplePathStrategy, 'descendant::a/b')) self.assertTrue(self._test_support(SimplePathStrategy, 'descendant-or-self::a/b')) self.assertTrue(self._test_support(SimplePathStrategy, '//a/b')) self.assertTrue(self._test_support(SimplePathStrategy, 'a/@b')) self.assertTrue(self._test_support(SimplePathStrategy, 'a/text()')) # a//b is a/descendant-or-self::node()/b self.assertTrue(not self._test_support(SimplePathStrategy, 'a//b')) self.assertTrue(not self._test_support(SimplePathStrategy, 'node()/@a')) self.assertTrue(not self._test_support(SimplePathStrategy, '@a')) self.assertTrue(not self._test_support(SimplePathStrategy, 'foo:bar')) self.assertTrue(not self._test_support(SimplePathStrategy, 'a/@foo:bar')) def _test_strategies(self, input, path, output, namespaces=None, variables=None): for strategy in self.strategies: if not strategy.supports(path): continue s = strategy(path) rendered = FakePath(s).select(input, namespaces=namespaces, variables=variables) \ .render(encoding=None) msg = 'Bad render using %s strategy' % str(strategy) msg += '\nExpected:\t%r' % output msg += '\nRendered:\t%r' % rendered self.assertEqual(output, rendered, msg) def _test_eval(self, path, equiv=None, input=None, output='', namespaces=None, variables=None): path = Path(path) if equiv is not None: self.assertEqual(equiv, repr(path)) if input is None: return rendered = path.select(input, namespaces=namespaces, variables=variables).render(encoding=None) msg = 'Bad output using whole path' msg += '\nExpected:\t%r' % output msg += '\nRendered:\t%r' % rendered self.assertEqual(output, rendered, msg) if len(path.paths) == 1: self._test_strategies(input, path.paths[0], output, namespaces=namespaces, variables=variables) def suite(): suite = unittest.TestSuite() suite.addTest(doctest_suite(Path.__module__)) suite.addTest(unittest.makeSuite(PathTestCase, 'test')) return suite if __name__ == '__main__': unittest.main(defaultTest='suite')