Skip to content

Commit

Permalink
Add check_raise() helper for tests and other tests for XPath 1.0
Browse files Browse the repository at this point in the history
  • Loading branch information
brunato committed Jan 9, 2021
1 parent a929150 commit 5ceb71f
Show file tree
Hide file tree
Showing 4 changed files with 46 additions and 19 deletions.
6 changes: 3 additions & 3 deletions elementpath/xpath1/xpath1_parser.py
Expand Up @@ -1052,9 +1052,9 @@ def select(self, context=None):
raise self.missing_context()

for _ in context.inner_focus_select(self[0]):
if self[1].label in ('axis', 'kind test') or self[1].symbol == '..':
if not is_xpath_node(context.item):
raise self.error('XPTY0020')
if (self[1].label in ('axis', 'kind test') or self[1].symbol == '..') \
and not is_xpath_node(context.item):
raise self.error('XPTY0020')

predicate = [x for x in self[1].select(context.copy())]
if len(predicate) == 1 and isinstance(predicate[0], NumericProxy):
Expand Down
10 changes: 10 additions & 0 deletions tests/test_xpath1_parser.py
Expand Up @@ -983,6 +983,7 @@ def test_div_operator(self):
self.check_value("10div 3", SyntaxError) # TODO: accepted syntax in XPath 1.0
else:
self.check_value("() div 2")
self.check_raise('1 div 0.0', ZeroDivisionError, 'FOAR0001', 'Division by zero')

def test_numerical_add_operator(self):
self.check_value("3 + 8", 11)
Expand Down Expand Up @@ -1036,6 +1037,8 @@ def test_numerical_mod_operator(self):
self.check_value("11 mod 3", 2)
self.check_value("4.5 mod 1.2", Decimal('0.9'))
self.check_value("1.23E2 mod 0.6E1", 3.0E0)
self.check_value("10 mod 0e1", math.isnan)
self.check_raise('3 mod 0', ZeroDivisionError, 'FOAR0001')

root = self.etree.XML(XML_DATA_TEST)
if self.parser.version == '1.0':
Expand All @@ -1044,6 +1047,8 @@ def test_numerical_mod_operator(self):
else:
self.check_selector("/values/a mod 2", root, TypeError)
self.check_value("/values/b mod 2", TypeError, context=XPathContext(root))
self.check_value("() mod 2e1")
self.check_value("2 mod xs:float('INF')", 2)

self.check_selector("/values/d mod 3", root, 2)

Expand Down Expand Up @@ -1454,6 +1459,9 @@ def test_predicate(self):
self.check_value("a[not(b)]", [], context=XPathContext(root, item=root[0]))
self.check_value("a[not(b)]", [root[1][0]], context=XPathContext(root, item=root[1]))

self.check_raise('88[..]', TypeError, 'XPTY0020', 'Context item is not a node',
context=XPathContext(root))

self.check_tree("preceding::a[not(b)]", '([ (preceding (a)) (not (b)))')

self.check_value("a[preceding::a[not(b)]]", [], context=XPathContext(root, item=root[0]))
Expand All @@ -1473,6 +1481,8 @@ def test_union(self):
self.check_selector('/A/B2 | /A/*', root, root[:])
self.check_selector('/A/B2 | /A/* | /A/B1', root, root[:])
self.check_selector('/A/@min | /A/@max', root, {'1', '10'})
self.check_raise('1|2|3', TypeError, 'XPTY0004', 'only XPath nodes are allowed',
context=XPathContext(root))

def test_default_namespace(self):
root = self.etree.XML('<foo>bar</foo>')
Expand Down
4 changes: 4 additions & 0 deletions tests/test_xpath2_parser.py
Expand Up @@ -626,6 +626,10 @@ def test_year_month_duration_operators(self):
self.check_value('xs:yearMonthDuration("P1Y") * xs:double("INF")', OverflowError)
self.wrong_value('xs:yearMonthDuration("P3Y") div xs:double("NaN")', 'FOCA0005')

self.check_raise('xs:yearMonthDuration("P3Y") div xs:yearMonthDuration("P0Y")',
ZeroDivisionError, 'FOAR0001', 'Division by zero')
self.check_raise('xs:yearMonthDuration("P3Y36M") div 0', OverflowError, 'FODT0002')

def test_day_time_duration_operators(self):
self.check_value('xs:dayTimeDuration("P2DT12H5M") + xs:dayTimeDuration("P5DT12H")',
DayTimeDuration.fromstring('P8DT5M'))
Expand Down
45 changes: 29 additions & 16 deletions tests/xpath_test_class.py
Expand Up @@ -21,6 +21,7 @@
#
import unittest
import math
from copy import copy
from contextlib import contextmanager
from xml.etree import ElementTree

Expand Down Expand Up @@ -222,33 +223,45 @@ def schema_bound_parser(self, schema_proxy):
self.parser.schema = None

# Wrong XPath expression checker shortcuts
def wrong_syntax(self, path, *message_parts):
with self.assertRaises(SyntaxError) as err:
self.parser.parse(path)
def check_raise(self, path, exception_class, *message_parts, context=None):
with self.assertRaises(exception_class) as error_context:
root_token = self.parser.parse(path)
root_token.evaluate(copy(context))

for part in message_parts:
self.assertIn(part, str(error_context.exception))

def wrong_syntax(self, path, *message_parts, context=None):
with self.assertRaises(SyntaxError) as error_context:
root_token = self.parser.parse(path)
root_token.evaluate(copy(context))

for part in message_parts:
self.assertIn(part, str(err.exception))
self.assertIn(part, str(error_context.exception))

def wrong_value(self, path, *message_parts):
with self.assertRaises(ValueError) as err:
self.parser.parse(path)
def wrong_value(self, path, *message_parts, context=None):
with self.assertRaises(ValueError) as error_context:
root_token = self.parser.parse(path)
root_token.evaluate(copy(context))

for part in message_parts:
self.assertIn(part, str(err.exception))
self.assertIn(part, str(error_context.exception))

def wrong_type(self, path, *message_parts):
with self.assertRaises(TypeError) as err:
self.parser.parse(path)
def wrong_type(self, path, *message_parts, context=None):
with self.assertRaises(TypeError) as error_context:
root_token = self.parser.parse(path)
root_token.evaluate(copy(context))

for part in message_parts:
self.assertIn(part, str(err.exception))
self.assertIn(part, str(error_context.exception))

def wrong_name(self, path, *message_parts):
with self.assertRaises(NameError) as err:
self.parser.parse(path)
def wrong_name(self, path, *message_parts, context=None):
with self.assertRaises(NameError) as error_context:
root_token = self.parser.parse(path)
root_token.evaluate(copy(context))

for part in message_parts:
self.assertIn(part, str(err.exception))
self.assertIn(part, str(error_context.exception))


if __name__ == '__main__':
Expand Down

0 comments on commit 5ceb71f

Please sign in to comment.