Skip to content

Commit 55a8179

Browse files
jenkins-botGerrit Code Review
authored andcommitted
Merge "[IMPR] Improvements for require_version decorator"
2 parents b3c2f88 + 8e9b798 commit 55a8179

File tree

2 files changed

+110
-13
lines changed

2 files changed

+110
-13
lines changed

tests/aspects.py

Lines changed: 26 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -284,7 +284,7 @@ def test_requirement(obj):
284284
return test_requirement
285285

286286

287-
def require_version(version_needed: str, reason: str = ''):
287+
def require_version(version_needed: str, /, reason: str = ''):
288288
"""Require minimum MediaWiki version to be queried.
289289
290290
The version needed for the test; must be given with a preleading rich
@@ -298,9 +298,17 @@ def require_version(version_needed: str, reason: str = ''):
298298
299299
.. versionadded:: 8.0
300300
301+
.. versionchanged:: 10.0
302+
TypeError and ValueError are used for validation fails.
303+
*version_needed* parameter is positional only.
304+
301305
:param version_needed: The version needed
302306
:param reason: A reason for skipping the test.
303-
:raises Exception: Usage validation fails
307+
:raises TypeError: self.site is not a BaseSite or the decorated
308+
method has parameters.
309+
:raises ValueError: The given *version_needed* parameter is invalid
310+
or an operand is given on the left or the version number is
311+
invalid
304312
"""
305313
def test_requirement(method):
306314
"""Test the requirement and return an optionally decorated object."""
@@ -309,26 +317,33 @@ def wrapper(self, *args, **kwargs):
309317
"""Validate environment."""
310318
if not isinstance(self.site, BaseSite) \
311319
or isinstance(self.site, DrySite):
312-
raise Exception( # pragma: no cover
320+
raise TypeError(
313321
f'{type(self).__name__}.site must be a BaseSite not '
314322
f'{type(self.site).__name__}.')
315323

316324
if args or kwargs:
317-
raise Exception( # pragma: no cover
325+
raise TypeError(
318326
f'Test method {method.__name__!r} has parameters which is '
319-
f'not supported with require_version decorator.')
327+
f'not supported with require_version decorator.'
328+
)
320329

321-
_, op, version = re.split('([<>]=?)', version_needed)
322-
if not op: # pragma: no cover
323-
raise Exception(f'There is no valid operator given with '
324-
f'version {version_needed!r}')
330+
try:
331+
site_vers, op, version = re.split('([<>]=?)', version_needed)
332+
except ValueError:
333+
raise ValueError(f'There is no valid operator given with '
334+
f'version {version_needed!r}')
335+
336+
if site_vers:
337+
raise ValueError(
338+
f'first operand {site_vers} should not be set')
325339

326340
skip = not eval(
327341
f'self.site.mw_version {op} MediaWikiVersion(version)')
342+
328343
if not skip:
329-
return method(self, *args, **kwargs)
344+
return method(self)
330345

331-
myreason = ' to ' + reason if reason else '' # pragma: no cover
346+
myreason = ' to ' + reason if reason else ''
332347
raise unittest.SkipTest(
333348
f'MediaWiki {op} v{version} required{myreason}.')
334349

tests/tests_tests.py

Lines changed: 84 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
#!/usr/bin/env python3
22
"""Tests for the tests package."""
33
#
4-
# (C) Pywikibot team, 2014-2023
4+
# (C) Pywikibot team, 2014-2025
55
#
66
# Distributed under the terms of the MIT license.
77
from __future__ import annotations
@@ -10,7 +10,7 @@
1010
from contextlib import suppress
1111

1212
from tests import utils
13-
from tests.aspects import TestCase
13+
from tests.aspects import DefaultSiteTestCase, TestCase, require_version
1414

1515

1616
class HttpServerProblemTestCase(TestCase):
@@ -78,6 +78,88 @@ def test_assert_length_fail(self):
7878
self.assertLength(None, self.seq)
7979

8080

81+
class TestRequireVersionDry(DefaultSiteTestCase):
82+
83+
"""Test require_version decorator."""
84+
85+
dry = True
86+
87+
@require_version('')
88+
def method(self):
89+
"""Test method for decorator."""
90+
91+
def test_require_version(self):
92+
"""Test require_version for DrySite."""
93+
with self.assertRaisesRegex(
94+
TypeError,
95+
f'{type(self).__name__}.site must be a BaseSite not DrySite'):
96+
self.method()
97+
98+
99+
class TestRequireVersion(DefaultSiteTestCase):
100+
101+
"""Test require_version decorator."""
102+
103+
@require_version('')
104+
def method_with_params(self, key):
105+
"""Test method for decorated methods with unsupported arguments."""
106+
107+
def method_failing(self):
108+
"""Test method for decorator with invalid parameter."""
109+
self.assertTrue(False, 'should never happen')
110+
111+
@require_version('>=1.31')
112+
def method_succeed(self):
113+
"""Test that decorator passes."""
114+
self.assertTrue(False, 'intentional fail for method_succeed test')
115+
116+
@require_version('<1.31')
117+
def method_fail(self):
118+
"""Test that decorator skips."""
119+
self.assertTrue(False, 'intentional fail for test')
120+
121+
def test_unsupported_methods(self):
122+
"""Test require_version with unsupported methods."""
123+
with self.assertRaisesRegex(
124+
TypeError, "Test method 'method_with_params' has parameters"):
125+
self.method_with_params('42')
126+
with self.assertRaisesRegex(
127+
TypeError, "Test method 'method_with_params' has parameters"):
128+
self.method_with_params(key='42')
129+
with self.assertRaisesRegex(ValueError,
130+
'There is no valid operator given '):
131+
self.method_with_params()
132+
133+
def test_version_needed(self):
134+
"""Test for invalid decorator parameters."""
135+
with self.assertRaisesRegex(ValueError,
136+
'There is no valid operator given '):
137+
require_version('foo')(self.method_failing)(self)
138+
with self.assertRaisesRegex(ValueError,
139+
'first operand foo should not be set'):
140+
require_version('foo>bar')(self.method_failing)(self)
141+
with self.assertRaisesRegex(ValueError, 'Invalid version number'):
142+
require_version('>bar')(self.method_failing)(self)
143+
with self.assertRaisesRegex(unittest.SkipTest,
144+
r'MediaWiki < v1\.31 required'):
145+
require_version('<1.31')(self.method_failing)(self)
146+
with self.assertRaisesRegex(
147+
unittest.SkipTest,
148+
r'MediaWiki < v1\.31 required to run this test'):
149+
require_version('<1.31',
150+
'run this test')(self.method_failing)(self)
151+
152+
def test_decorator(self):
153+
"""Test that decorator passes or skips."""
154+
with self.assertRaisesRegex(
155+
AssertionError,
156+
'intentional fail for method_succeed test'):
157+
self.method_succeed()
158+
with self.assertRaisesRegex(unittest.SkipTest,
159+
r'MediaWiki < v1\.31 required'):
160+
self.method_fail()
161+
162+
81163
class UtilsTests(TestCase):
82164

83165
"""Tests for tests.utils."""

0 commit comments

Comments
 (0)