Split and rename test_helpers module

- Unify XPathToken unit tests into class XPathTokenTest
This commit is contained in:
Davide Brunato 2019-10-07 08:19:06 +02:00
parent 526e9baa9f
commit 9c4b942700
11 changed files with 325 additions and 237 deletions

View File

@ -15,7 +15,7 @@ import decimal
from .compat import PY3, string_base_type
from .exceptions import ElementPathSyntaxError, ElementPathNameError, MissingContextError
from .datatypes import UntypedAtomic, DayTimeDuration, YearMonthDuration, \
NumericTypeProxy, ArithmeticTypeProxy, XSD_BUILTIN_TYPES
NumericTypeProxy, ArithmeticTypeProxy
from .xpath_context import XPathSchemaContext
from .tdop_parser import Parser, MultiLabel
from .namespaces import XML_ID, XML_LANG, XPATH_1_DEFAULT_NAMESPACES, \
@ -252,19 +252,9 @@ def select(self, context=None):
if name[0] != '{' and self.parser.default_namespace:
name = '{%s}%s' % (self.parser.default_namespace, name)
for item in context.iter_children_or_self():
xsd_type = self.match_xsd_type(item, name)
if xsd_type is not None:
primitive_type = self.parser.schema.get_primitive_type(xsd_type)
try:
value = XSD_BUILTIN_TYPES[primitive_type.local_name or 'anyType'].value
except KeyError:
value = XSD_BUILTIN_TYPES['anyType'].value
if isinstance(item, AttributeNode):
yield TypedAttribute(item, value)
else:
yield TypedElement(item, value)
for schema_item in context.iter_children_or_self():
if self.match_xsd_type(schema_item, name) is not None:
yield self.get_typed_node(context, schema_item)
return
if name[0] != '{' and self.parser.default_namespace:
@ -288,53 +278,22 @@ def select(self, context=None):
# Try to match the type using the path
for item in context.iter_children_or_self():
try:
if is_attribute_node(item, name):
path = context.get_path(item)
xsd_attribute = self.parser.schema.find(path, self.parser.namespaces)
if is_attribute_node(item, name) or is_element_node(item, tag):
path = context.get_path(item)
xsd_component = self.parser.schema.find(path, self.parser.namespaces)
if xsd_attribute is not None:
self.xsd_type = xsd_attribute.type
yield TypedAttribute(item, self.xsd_type.decode(item[1]))
else:
self.xsd_type = self.parser.schema
yield item
elif is_element_node(item, tag):
path = context.get_path(item)
xsd_element = self.parser.schema.find(path, self.parser.namespaces)
if xsd_element is not None:
self.xsd_type = xsd_element.type
if isinstance(item, TypedElement):
yield item
elif self.xsd_type.is_simple() or self.xsd_type.has_simple_content():
yield TypedElement(item, self.xsd_type.decode(item.text))
else:
yield item
else:
self.xsd_type = self.parser.schema
yield item
except (TypeError, ValueError):
msg = "Type {!r} does not match sequence type of {!r}"
self.wrong_sequence_type(msg.format(self.xsd_type, item))
# print(path, xsd_component)
if xsd_component is not None:
self.xsd_type = xsd_component.type
else:
self.xsd_type = self.parser.schema
yield self.get_typed_node(context, item)
else:
# XSD typed selection
for item in context.iter_children_or_self():
try:
if is_attribute_node(item, name):
yield TypedAttribute(item, self.xsd_type.decode(item[1]))
elif is_element_node(item, tag):
if isinstance(item, TypedElement):
yield item
elif self.xsd_type.is_simple() or self.xsd_type.has_simple_content():
yield TypedElement(item, self.xsd_type.decode(item.text))
else:
yield item
except (TypeError, ValueError):
msg = "Type {!r} does not match sequence type of {!r}"
self.wrong_sequence_type(msg.format(self.xsd_type, item))
if is_attribute_node(item, name) or is_element_node(item, tag):
yield self.get_typed_node(context, item)
###
@ -411,13 +370,37 @@ def select(self, context=None):
else:
value = '{%s}%s' % (namespace, self[1].value)
if context is not None:
if context is None:
return
elif isinstance(context, XPathSchemaContext):
for schema_item in context.iter_children_or_self():
if self.match_xsd_type(schema_item, value) is not None:
yield self.get_typed_node(context, schema_item)
elif self.xsd_type is self.parser.schema:
for item in context.iter_children_or_self():
if is_attribute_node(item, value):
yield item
elif is_element_node(item, value):
yield item
elif self.xsd_type is None or isinstance(self.xsd_type, AbstractSchemaProxy):
for item in context.iter_children_or_self():
if is_attribute_node(item, value) or is_element_node(item, value):
path = context.get_path(item)
xsd_component = self.parser.schema.find(path, self.parser.namespaces)
if xsd_component is not None:
self.xsd_type = xsd_component.type
else:
self.xsd_type = self.parser.schema
yield self.get_typed_node(context, item)
else:
# XSD typed selection
for item in context.iter_children_or_self():
if is_attribute_node(item, value) or is_element_node(item, value):
yield self.get_typed_node(context, item)
###
# Namespace URI as in ElementPath
@ -725,7 +708,7 @@ def select(self, context=None):
yield result
else:
items = []
left_results = list(self[0].select(context))
left_results = [x for x in self[0].select(context)]
context.size = len(left_results)
for context.position, context.item in enumerate(left_results):
if not is_xpath_node(context.item):
@ -747,26 +730,6 @@ def select(self, context=None):
yield result
@method('/')
def evaluate(self, context=None):
"""
General evaluation method for path operators, that may returns the a single value or None.
"""
if context is not None:
selector = iter(self.select(context))
try:
value = next(selector)
except StopIteration:
return
else:
try:
next(selector)
except StopIteration:
return self.data_value(value)
else:
self.wrong_context_type("atomized operand is a sequence of length greater than one")
@method('//')
def select(self, context=None):
if context is None:
@ -799,7 +762,7 @@ def led(self, left):
def select(self, context=None):
if context is not None:
for position, item in enumerate(self[0].select(context), start=1):
predicate = list(self[1].select(context.copy()))
predicate = [x for x in self[1].select(context.copy())]
if len(predicate) == 1 and isinstance(predicate[0], NumericTypeProxy):
if position == predicate[0]:
yield item
@ -968,7 +931,7 @@ def select(self, context=None):
def select(self, context=None):
if context is not None:
item = context.item
for elem in reversed(list(context.iter_ancestors(axis=self.symbol))):
for elem in reversed([x for x in context.iter_ancestors(axis=self.symbol)]):
context.item = elem
yield elem
yield item
@ -1042,7 +1005,7 @@ def evaluate(self, context=None):
@method(function('count', nargs=1))
def evaluate(self, context=None):
return len(list(self[0].select(context)))
return len([x for x in self[0].select(context)])
@method(function('id', nargs=1))

View File

@ -324,7 +324,7 @@ def evaluate(self, context=None):
# Aggregate functions
@method(function('avg', nargs=1))
def evaluate(self, context=None):
result = list(self[0].select(context))
result = [x for x in self[0].select(context)]
if not result:
return result
elif isinstance(result[0], Duration):
@ -436,7 +436,7 @@ def select(self, context=None):
@method(function('reverse', nargs=1))
def select(self, context=None):
for result in reversed(list(self[0].select(context))):
for result in reversed([x for x in self[0].select(context)]):
yield result
@ -451,7 +451,7 @@ def select(self, context=None):
@method(function('unordered', nargs=1))
def select(self, context=None):
for result in sorted(list(self[0].select(context)), key=lambda x: self.string_value(x)):
for result in sorted([x for x in self[0].select(context)], key=lambda x: self.string_value(x)):
yield result

View File

@ -425,7 +425,7 @@ def evaluate(self, context=None):
@method('if')
def select(self, context=None):
if self.boolean_value(list(self[0].select(context))):
if self.boolean_value([x for x in self[0].select(context)]):
for result in self[1].select(context):
yield result
else:
@ -745,13 +745,13 @@ def evaluate(self, context=None):
def evaluate(self, context=None):
symbol = self.symbol
left = list(self[0].select(context))
left = [x for x in self[0].select(context)]
if not left:
return
elif len(left) > 1 or not is_xpath_node(left[0]):
self[0].wrong_type("left operand of %r must be a single node" % symbol)
right = list(self[1].select(context))
right = [x for x in self[1].select(context)]
if not right:
return
elif len(right) > 1 or not is_xpath_node(right[0]):
@ -783,7 +783,7 @@ def evaluate(self, context=None):
self.wrong_type(str(err))
return
else:
return list(range(start, stop))
return [x for x in range(start, stop)]
@method('to')

View File

@ -103,15 +103,13 @@ class XPathContext(object):
except KeyError:
return
@lru_cache(maxsize=1024)
def get_path(self, item):
"""Cached path resolver for elements and attributes. Returns absolute paths."""
path = []
if isinstance(item, (AttributeNode, TypedAttribute)):
path.append('@%s' % item[0])
item = self._elem
elif isinstance(item, TypedElement):
if isinstance(item, TypedElement):
item = item[0]
while True:

View File

@ -27,12 +27,14 @@ from decimal import Decimal
from .compat import string_base_type, unicode_type
from .exceptions import xpath_error
from .namespaces import XQT_ERRORS_NAMESPACE
from .xpath_nodes import AttributeNode, NamespaceNode, TypedElement, is_etree_element, \
is_attribute_node, elem_iter_strings, is_text_node, is_namespace_node, \
is_comment_node, is_processing_instruction_node, is_element_node, \
is_document_node, is_xpath_node, is_schema_node
from .xpath_nodes import AttributeNode, NamespaceNode, TypedAttribute, TypedElement, \
is_etree_element, is_attribute_node, elem_iter_strings, is_text_node, \
is_namespace_node, is_comment_node, is_processing_instruction_node, \
is_element_node, is_document_node, is_xpath_node, is_schema_node
from .datatypes import UntypedAtomic, Timezone, DayTimeDuration, XSD_BUILTIN_TYPES
from .schema_proxy import AbstractSchemaProxy
from .tdop_parser import Token
from .xpath_context import XPathSchemaContext
def ordinal(n):
@ -61,7 +63,7 @@ class XPathToken(Token):
:param context: The XPath dynamic context.
"""
return list(self.select(context))
return [x for x in self.select(context)]
def select(self, context=None):
"""
@ -207,7 +209,7 @@ class XPathToken(Token):
for item in self.select(context):
value = self.data_value(item)
if value is None:
raise self.error('FOTY0012', "argument node does not have a typed value: {}".format(item))
raise self.error('FOTY0012', "argument node {!r} does not have a typed value".format(item))
else:
yield value
@ -229,6 +231,8 @@ class XPathToken(Token):
except StopIteration:
if isinstance(value, UntypedAtomic):
value = str(value)
if isinstance(context, XPathSchemaContext):
return value
if self.xsd_type is not None and isinstance(value, string_base_type):
try:
value = self.xsd_type.decode(value)
@ -250,10 +254,11 @@ class XPathToken(Token):
:returns: a list of data couples.
"""
if context is None:
operand1, operand2 = list(self[0].select()), list(self[1].select())
operand1 = [x for x in self[0].select()]
operand2 = [x for x in self[1].select()]
else:
operand1 = list(self[0].select(context.copy()))
operand2 = list(self[1].select(context.copy()))
operand1 = [x for x in self[0].select(context.copy())]
operand2 = [x for x in self[1].select(context.copy())]
if self.parser.compatibility_mode:
# Boolean comparison if one of the results is a single boolean value (1.)
@ -284,11 +289,9 @@ class XPathToken(Token):
:param context: the XPath dynamic context.
"""
for result in self.select(context):
if not isinstance(result, tuple):
yield result # not a namedtuple-wrapped result
elif hasattr(result[0], 'type'):
yield result[0] # an XSD schema attribute/element
elif not isinstance(result, NamespaceNode):
if isinstance(result, TypedElement):
yield result[0]
elif isinstance(result, (AttributeNode, TypedAttribute)):
yield result[1]
else:
yield result
@ -419,6 +422,39 @@ class XPathToken(Token):
self.wrong_context_type("Multiple XSD type matching during static analysis")
return xsd_type
def get_typed_node(self, context, item):
"""
Returns a typed node if the token is bound to an XSD type.
:param context: the XPath dynamic context.
:param item: an untyped XPath attribute ot element.
"""
if isinstance(self.xsd_type, (type(None), AbstractSchemaProxy)):
return item
if isinstance(context, XPathSchemaContext):
primitive_type = self.parser.schema.get_primitive_type(self.xsd_type)
try:
value = XSD_BUILTIN_TYPES[primitive_type.local_name or 'anyType'].value
except KeyError:
value = XSD_BUILTIN_TYPES['anyType'].value
if isinstance(item, AttributeNode):
return TypedAttribute(item, value)
else:
return TypedElement(item, value)
else:
try:
if isinstance(item, AttributeNode):
return TypedAttribute(item, self.xsd_type.decode(item[1]))
elif self.xsd_type.is_simple() or self.xsd_type.has_simple_content():
return TypedElement(item, self.xsd_type.decode(item.text))
else:
return item
except (TypeError, ValueError):
msg = "Type {!r} does not match sequence type of {!r}"
self.wrong_sequence_type(msg.format(self.xsd_type, item))
@contextlib.contextmanager
def use_locale(self, collation):
"""A context manager for setting a specific collation for a code block."""

View File

@ -24,8 +24,11 @@ import unittest
if __name__ == '__main__':
try:
from tests.test_helpers import ExceptionHelpersTest, NamespaceHelpersTest, NodeHelpersTest
from tests.test_exceptions import ExceptionsTest
from tests.test_namespaces import NamespacesTest
from tests.test_datatypes import UntypedAtomicTest, DateTimeTypesTest, DurationTypesTest, TimezoneTypeTest
from tests.test_xpath_nodes import XPathNodesTest
from tests.test_xpath_token import XPathTokenTest
from tests.test_xpath_context import XPathContextTest
from tests.test_xpath1_parser import XPath1ParserTest, LxmlXPath1ParserTest
from tests.test_xpath2_parser import XPath2ParserTest, LxmlXPath2ParserTest
@ -34,8 +37,11 @@ if __name__ == '__main__':
from tests.test_package import PackageTest
except ImportError:
# Python 2 fallback
from test_helpers import ExceptionHelpersTest, NamespaceHelpersTest, NodeHelpersTest
from test_exceptions import ExceptionsTest
from test_namespaces import NamespacessTest
from test_datatypes import UntypedAtomicTest, DateTimeTypesTest, DurationTypesTest, TimezoneTypeTest
from test_xpath_nodes import XPathNodesTest
from test_xpath_token import XPathTokenTest
from test_xpath_context import XPathContextTest
from test_xpath1_parser import XPath1ParserTest, LxmlXPath1ParserTest
from test_xpath2_parser import XPath2ParserTest, LxmlXPath2ParserTest

43
tests/test_exceptions.py Normal file
View File

@ -0,0 +1,43 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# Copyright (c), 2018-2019, SISSA (International School for Advanced Studies).
# All rights reserved.
# This file is distributed under the terms of the MIT License.
# See the file 'LICENSE' in the root directory of the present
# distribution, or http://opensource.org/licenses/MIT.
#
# @author Davide Brunato <brunato@sissa.it>
#
from __future__ import unicode_literals
import unittest
from elementpath.exceptions import ElementPathError, xpath_error
from elementpath.namespaces import XSD_NAMESPACE
from elementpath.xpath1_parser import XPath1Parser
class ExceptionsTest(unittest.TestCase):
@classmethod
def setUpClass(cls):
cls.parser = XPath1Parser(namespaces={'xs': XSD_NAMESPACE, 'tst': "http://xpath.test/ns"})
def test_exception_repr(self):
err = ElementPathError("unknown error")
self.assertEqual(str(err), 'unknown error')
err = ElementPathError("unknown error", code='XPST0001')
self.assertEqual(str(err), '[XPST0001] unknown error.')
token = self.parser.symbol_table['true'](self.parser)
err = ElementPathError("unknown error", code='XPST0001', token=token)
self.assertEqual(str(err), "'true' function: [XPST0001] unknown error.")
def test_xpath_error(self):
self.assertEqual(str(xpath_error('XPST0001')), '[err:XPST0001] Parser not bound to a schema.')
self.assertEqual(str(xpath_error('err:XPDY0002', "test message")), '[err:XPDY0002] test message.')
self.assertRaises(ValueError, xpath_error, '')
self.assertRaises(ValueError, xpath_error, 'error:XPDY0002')
if __name__ == '__main__':
unittest.main()

53
tests/test_namespaces.py Normal file
View File

@ -0,0 +1,53 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# Copyright (c), 2018-2019, SISSA (International School for Advanced Studies).
# All rights reserved.
# This file is distributed under the terms of the MIT License.
# See the file 'LICENSE' in the root directory of the present
# distribution, or http://opensource.org/licenses/MIT.
#
# @author Davide Brunato <brunato@sissa.it>
#
from __future__ import unicode_literals
import unittest
from elementpath.namespaces import XSD_NAMESPACE, get_namespace, qname_to_prefixed, \
prefixed_to_qname
class NamespacesTest(unittest.TestCase):
namespaces = {
'xs': XSD_NAMESPACE,
'tst': "http://xpath.test/ns"
}
# namespaces.py module
def test_get_namespace_function(self):
self.assertEqual(get_namespace('A'), '')
self.assertEqual(get_namespace('{ns}foo'), 'ns')
self.assertEqual(get_namespace('{}foo'), '')
self.assertEqual(get_namespace('{A}B{C}'), 'A')
def test_qname_to_prefixed_function(self):
self.assertEqual(qname_to_prefixed('{ns}foo', {'bar': 'ns'}), 'bar:foo')
self.assertEqual(qname_to_prefixed('{ns}foo', {'': 'ns'}), 'foo')
self.assertEqual(qname_to_prefixed('foo', {'': 'ns'}), 'foo')
def test_prefixed_to_qname_function(self):
self.assertEqual(prefixed_to_qname('{ns}foo', {'bar': 'ns'}), '{ns}foo')
self.assertEqual(prefixed_to_qname('bar:foo', {'bar': 'ns'}), '{ns}foo')
self.assertEqual(prefixed_to_qname('foo', {'': 'ns'}), '{ns}foo')
with self.assertRaises(ValueError):
prefixed_to_qname('bar:foo', self.namespaces)
with self.assertRaises(ValueError):
prefixed_to_qname('bar:foo:bar', {'bar': 'ns'})
with self.assertRaises(ValueError):
prefixed_to_qname(':foo', {'': 'ns'})
with self.assertRaises(ValueError):
prefixed_to_qname('foo:', {'': 'ns'})
if __name__ == '__main__':
unittest.main()

View File

@ -211,52 +211,6 @@ class XPath1ParserTest(unittest.TestCase):
else:
self.assertTrue(expected(results))
def test_boolean_value_function(self):
token = self.parser.parse('true()')
elem = ElementTree.Element('A')
with self.assertRaises(TypeError):
token.boolean_value(elem)
self.assertFalse(token.boolean_value([]))
self.assertTrue(token.boolean_value([elem]))
self.assertFalse(token.boolean_value([0]))
self.assertTrue(token.boolean_value([1]))
with self.assertRaises(TypeError):
token.boolean_value([1, 1])
with self.assertRaises(TypeError):
token.boolean_value(elem)
self.assertFalse(token.boolean_value(0))
self.assertTrue(token.boolean_value(1))
def test_data_value_function(self):
token = self.parser.parse('true()')
self.assertIsNone(token.data_value(None))
def test_string_value_function(self):
token = self.parser.parse('true()')
document = ElementTree.parse(io.StringIO(u'<A>123<B1>456</B1><B2>789</B2></A>'))
element = ElementTree.Element('schema')
attribute = AttributeNode('id', '0212349350')
namespace = NamespaceNode('xs', 'http://www.w3.org/2001/XMLSchema')
comment = ElementTree.Comment('nothing important')
pi = ElementTree.ProcessingInstruction('action', 'nothing to do')
text = u'betelgeuse'
self.assertEqual(token.string_value(document), '123456789')
self.assertEqual(token.string_value(element), '')
self.assertEqual(token.string_value(attribute), '0212349350')
self.assertEqual(token.string_value(namespace), 'http://www.w3.org/2001/XMLSchema')
self.assertEqual(token.string_value(comment), 'nothing important')
self.assertEqual(token.string_value(pi), 'action nothing to do')
self.assertEqual(token.string_value(text), 'betelgeuse')
self.assertEqual(token.string_value(None), '')
self.assertEqual(token.string_value(10), '10')
def test_number_value_function(self):
token = self.parser.parse('true()')
self.assertEqual(token.number_value("19"), 19)
self.assertTrue(math.isnan(token.number_value("not a number")))
# Wrong XPath expression checker shortcuts
def wrong_syntax(self, path):
self.assertRaises(SyntaxError, self.parser.parse, path)

View File

@ -14,74 +14,14 @@ import unittest
import io
import xml.etree.ElementTree as ElementTree
from elementpath.exceptions import ElementPathError, xpath_error
from elementpath.namespaces import XSD_NAMESPACE, get_namespace, qname_to_prefixed, \
prefixed_to_qname
from elementpath.xpath_nodes import AttributeNode, NamespaceNode, is_etree_element, \
is_element_node, is_attribute_node, is_comment_node, is_document_node, \
is_namespace_node, is_processing_instruction_node, is_text_node, node_attributes, \
node_base_uri, node_document_uri, node_children, node_is_id, node_is_idrefs, \
node_nilled, node_kind, node_name
from elementpath.xpath_token import ordinal
from elementpath.xpath1_parser import XPath1Parser
class ExceptionHelpersTest(unittest.TestCase):
@classmethod
def setUpClass(cls):
cls.parser = XPath1Parser(namespaces={'xs': XSD_NAMESPACE, 'tst': "http://xpath.test/ns"})
def test_exception_repr(self):
err = ElementPathError("unknown error")
self.assertEqual(str(err), 'unknown error')
err = ElementPathError("unknown error", code='XPST0001')
self.assertEqual(str(err), '[XPST0001] unknown error.')
token = self.parser.symbol_table['true'](self.parser)
err = ElementPathError("unknown error", code='XPST0001', token=token)
self.assertEqual(str(err), "'true' function: [XPST0001] unknown error.")
def test_xpath_error(self):
self.assertEqual(str(xpath_error('XPST0001')), '[err:XPST0001] Parser not bound to a schema.')
self.assertEqual(str(xpath_error('err:XPDY0002', "test message")), '[err:XPDY0002] test message.')
self.assertRaises(ValueError, xpath_error, '')
self.assertRaises(ValueError, xpath_error, 'error:XPDY0002')
class NamespaceHelpersTest(unittest.TestCase):
namespaces = {
'xs': XSD_NAMESPACE,
'tst': "http://xpath.test/ns"
}
# namespaces.py module
def test_get_namespace_function(self):
self.assertEqual(get_namespace('A'), '')
self.assertEqual(get_namespace('{ns}foo'), 'ns')
self.assertEqual(get_namespace('{}foo'), '')
self.assertEqual(get_namespace('{A}B{C}'), 'A')
def test_qname_to_prefixed_function(self):
self.assertEqual(qname_to_prefixed('{ns}foo', {'bar': 'ns'}), 'bar:foo')
self.assertEqual(qname_to_prefixed('{ns}foo', {'': 'ns'}), 'foo')
self.assertEqual(qname_to_prefixed('foo', {'': 'ns'}), 'foo')
def test_prefixed_to_qname_function(self):
self.assertEqual(prefixed_to_qname('{ns}foo', {'bar': 'ns'}), '{ns}foo')
self.assertEqual(prefixed_to_qname('bar:foo', {'bar': 'ns'}), '{ns}foo')
self.assertEqual(prefixed_to_qname('foo', {'': 'ns'}), '{ns}foo')
with self.assertRaises(ValueError):
prefixed_to_qname('bar:foo', self.namespaces)
with self.assertRaises(ValueError):
prefixed_to_qname('bar:foo:bar', {'bar': 'ns'})
with self.assertRaises(ValueError):
prefixed_to_qname(':foo', {'': 'ns'})
with self.assertRaises(ValueError):
prefixed_to_qname('foo:', {'': 'ns'})
class NodeHelpersTest(unittest.TestCase):
class XPathNodesTest(unittest.TestCase):
elem = ElementTree.XML('<node a1="10"/>')
def test_is_etree_element_function(self):
@ -231,28 +171,5 @@ class NodeHelpersTest(unittest.TestCase):
self.assertEqual(node_name(namespace), 'xs')
class XPathTokenHelpersTest(unittest.TestCase):
@classmethod
def setUpClass(cls):
cls.parser = XPath1Parser(namespaces={'xs': XSD_NAMESPACE, 'tst': "http://xpath.test/ns"})
def test_ordinal_function(self):
self.assertEqual(ordinal(1), '1st')
self.assertEqual(ordinal(2), '2nd')
self.assertEqual(ordinal(3), '3rd')
self.assertEqual(ordinal(4), '4th')
self.assertEqual(ordinal(11), '11th')
self.assertEqual(ordinal(23), '23rd')
self.assertEqual(ordinal(34), '34th')
def test_get_argument_method(self):
token = self.parser.symbol_table['true'](self.parser)
self.assertIsNone(token.get_argument(2))
with self.assertRaises(TypeError):
token.get_argument(1, required=True)
if __name__ == '__main__':
unittest.main()

118
tests/test_xpath_token.py Normal file
View File

@ -0,0 +1,118 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# Copyright (c), 2018-2019, SISSA (International School for Advanced Studies).
# All rights reserved.
# This file is distributed under the terms of the MIT License.
# See the file 'LICENSE' in the root directory of the present
# distribution, or http://opensource.org/licenses/MIT.
#
# @author Davide Brunato <brunato@sissa.it>
#
from __future__ import unicode_literals
import unittest
import io
import math
import xml.etree.ElementTree as ElementTree
from elementpath.namespaces import XSD_NAMESPACE
from elementpath.xpath_nodes import AttributeNode, TypedAttribute, TypedElement, NamespaceNode
from elementpath.xpath_token import ordinal
from elementpath.xpath_context import XPathContext
from elementpath.xpath1_parser import XPath1Parser
class XPathTokenTest(unittest.TestCase):
@classmethod
def setUpClass(cls):
cls.parser = XPath1Parser(namespaces={'xs': XSD_NAMESPACE, 'tst': "http://xpath.test/ns"})
def test_ordinal_function(self):
self.assertEqual(ordinal(1), '1st')
self.assertEqual(ordinal(2), '2nd')
self.assertEqual(ordinal(3), '3rd')
self.assertEqual(ordinal(4), '4th')
self.assertEqual(ordinal(11), '11th')
self.assertEqual(ordinal(23), '23rd')
self.assertEqual(ordinal(34), '34th')
def test_get_argument_method(self):
token = self.parser.symbol_table['true'](self.parser)
self.assertIsNone(token.get_argument(2))
with self.assertRaises(TypeError):
token.get_argument(1, required=True)
def test_select_results(self):
token = self.parser.parse('.')
elem = ElementTree.Element('A', attrib={'max': '30'})
elem.text = '10'
context = XPathContext(elem)
self.assertListEqual(list(token.select_results(context)), [elem])
context = XPathContext(elem, item=TypedElement(elem, 10))
self.assertListEqual(list(token.select_results(context)), [elem])
context = XPathContext(elem, item=AttributeNode('max', '30'))
self.assertListEqual(list(token.select_results(context)), ['30'])
context = XPathContext(elem, item=TypedAttribute(AttributeNode('max', '30'), 30))
self.assertListEqual(list(token.select_results(context)), [30])
context = XPathContext(elem, item=10)
self.assertListEqual(list(token.select_results(context)), [10])
context = XPathContext(elem, item='10')
self.assertListEqual(list(token.select_results(context)), ['10'])
def test_boolean_value_function(self):
token = self.parser.parse('true()')
elem = ElementTree.Element('A')
with self.assertRaises(TypeError):
token.boolean_value(elem)
self.assertFalse(token.boolean_value([]))
self.assertTrue(token.boolean_value([elem]))
self.assertFalse(token.boolean_value([0]))
self.assertTrue(token.boolean_value([1]))
with self.assertRaises(TypeError):
token.boolean_value([1, 1])
with self.assertRaises(TypeError):
token.boolean_value(elem)
self.assertFalse(token.boolean_value(0))
self.assertTrue(token.boolean_value(1))
def test_data_value_function(self):
token = self.parser.parse('true()')
self.assertIsNone(token.data_value(None))
def test_string_value_function(self):
token = self.parser.parse('true()')
document = ElementTree.parse(io.StringIO(u'<A>123<B1>456</B1><B2>789</B2></A>'))
element = ElementTree.Element('schema')
attribute = AttributeNode('id', '0212349350')
namespace = NamespaceNode('xs', 'http://www.w3.org/2001/XMLSchema')
comment = ElementTree.Comment('nothing important')
pi = ElementTree.ProcessingInstruction('action', 'nothing to do')
text = u'betelgeuse'
self.assertEqual(token.string_value(document), '123456789')
self.assertEqual(token.string_value(element), '')
self.assertEqual(token.string_value(attribute), '0212349350')
self.assertEqual(token.string_value(namespace), 'http://www.w3.org/2001/XMLSchema')
self.assertEqual(token.string_value(comment), 'nothing important')
self.assertEqual(token.string_value(pi), 'action nothing to do')
self.assertEqual(token.string_value(text), 'betelgeuse')
self.assertEqual(token.string_value(None), '')
self.assertEqual(token.string_value(10), '10')
def test_number_value_function(self):
token = self.parser.parse('true()')
self.assertEqual(token.number_value("19"), 19)
self.assertTrue(math.isnan(token.number_value("not a number")))
if __name__ == '__main__':
unittest.main()