From 5ceb71fc369b632eda53d9993999736311884427 Mon Sep 17 00:00:00 2001 From: Davide Brunato Date: Sat, 9 Jan 2021 18:13:32 +0100 Subject: [PATCH] Add check_raise() helper for tests and other tests for XPath 1.0 --- elementpath/xpath1/xpath1_parser.py | 6 ++-- tests/test_xpath1_parser.py | 10 +++++++ tests/test_xpath2_parser.py | 4 +++ tests/xpath_test_class.py | 45 +++++++++++++++++++---------- 4 files changed, 46 insertions(+), 19 deletions(-) diff --git a/elementpath/xpath1/xpath1_parser.py b/elementpath/xpath1/xpath1_parser.py index 3bfb3b42..55b31729 100644 --- a/elementpath/xpath1/xpath1_parser.py +++ b/elementpath/xpath1/xpath1_parser.py @@ -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): diff --git a/tests/test_xpath1_parser.py b/tests/test_xpath1_parser.py index dd68b63c..acf5636c 100644 --- a/tests/test_xpath1_parser.py +++ b/tests/test_xpath1_parser.py @@ -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) @@ -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': @@ -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) @@ -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])) @@ -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('bar') diff --git a/tests/test_xpath2_parser.py b/tests/test_xpath2_parser.py index f985eb50..f00afc84 100644 --- a/tests/test_xpath2_parser.py +++ b/tests/test_xpath2_parser.py @@ -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')) diff --git a/tests/xpath_test_class.py b/tests/xpath_test_class.py index 1fea5c78..d3a2d81c 100644 --- a/tests/xpath_test_class.py +++ b/tests/xpath_test_class.py @@ -21,6 +21,7 @@ # import unittest import math +from copy import copy from contextlib import contextmanager from xml.etree import ElementTree @@ -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__':