Skip to content

Commit e068104

Browse files
committed
Validate result dicts produced by handlers
1 parent e3fcfe5 commit e068104

6 files changed

Lines changed: 84 additions & 14 deletions

File tree

src/oxygen/base_handler.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,10 @@
22

33
from inspect import signature, Parameter
44

5-
from oxygen.errors import MismatchArgumentException
5+
from .errors import MismatchArgumentException
66
from .robot_interface import (RobotInterface, get_keywords_from,
77
set_special_keyword)
8-
8+
from .utils import validate_with_deprecation_warning
99

1010
class BaseHandler(object):
1111
DEFAULT_CLI = {tuple(['result_file']): {}}
@@ -132,6 +132,8 @@ def _build_results(self, keyword, setup_keyword, teardown_keyword):
132132
f'parse_results expects at least {accepted_params_min} '
133133
'arguments but got 1')
134134

135+
self._validate(test_results)
136+
135137
_, result_suite = self._interface.result.build_suite(
136138
100000, test_results)
137139

@@ -149,6 +151,9 @@ def _build_results(self, keyword, setup_keyword, teardown_keyword):
149151

150152
self._inject_suite_report(test, result_suite)
151153

154+
def _validate(self, oxygen_result_dict):
155+
validate_with_deprecation_warning(oxygen_result_dict, self)
156+
152157
def _inject_suite_report(self, test, result_suite):
153158
'''Add the given suite to the parent suite of the test case.
154159

src/oxygen/oxygen.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
InvalidConfigurationException,
1919
ResultFileNotFoundException)
2020
from .robot_interface import RobotInterface
21+
from .utils import validate_with_deprecation_warning
2122
from .version import VERSION
2223

2324

@@ -301,6 +302,7 @@ def convert_to_robot_result(self, args):
301302
output_filename = self.get_output_filename(args.get('result_file'))
302303
parsed_results = args['func'](
303304
**{k: v for (k, v) in args.items() if not callable(v)})
305+
validate_with_deprecation_warning(parsed_results, args['func'])
304306
robot_suite = RobotInterface().running.build_suite(parsed_results)
305307
robot_suite.run(output=output_filename,
306308
log=None,

src/oxygen/utils.py

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,11 @@
44

55
from pathlib import Path
66

7-
from .errors import (SubprocessException,
7+
from .errors import (InvalidOxygenResultException,
88
ResultFileIsNotAFileException,
9-
ResultFileNotFoundException)
10-
9+
ResultFileNotFoundException,
10+
SubprocessException)
11+
from .oxygen_handler_result import validate_oxygen_suite
1112

1213
def run_command_line(command, check_return_code=True, **env):
1314
new_env = os.environ.copy()
@@ -27,7 +28,6 @@ def run_command_line(command, check_return_code=True, **env):
2728
f'code {proc.returncode}:\n"{proc.stdout.decode("utf-8")}"')
2829
return proc.stdout
2930

30-
3131
def validate_path(filepath):
3232
try:
3333
path = Path(filepath)
@@ -39,3 +39,17 @@ def validate_path(filepath):
3939
raise ResultFileIsNotAFileException(f'File "{path}" is not a file, '
4040
'but a directory')
4141
return path
42+
43+
def validate_with_deprecation_warning(oxygen_result_dict, handler):
44+
try:
45+
validate_oxygen_suite(oxygen_result_dict)
46+
except InvalidOxygenResultException as e:
47+
import warnings
48+
# this is not done with triple quotes intentionally
49+
# to get sensible formatting to output
50+
msg = (f'\n{handler.__module__} is producing invalid results:\n'
51+
f'{e}\n\n'
52+
'In Oxygen 1.0, handlers will need to produce valid '
53+
'results.\nSee: '
54+
'https://github.com/eficode/robotframework-oxygen/blob/master/parser_specification.md')
55+
warnings.warn(msg)

tests/utest/helpers.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,11 @@ def example_robot_output():
5959

6060
MINIMAL_KEYWORD_DICT = { 'name': 'someKeyword', 'pass': True }
6161
MINIMAL_TC_DICT = { 'name': 'Minimal TC', 'keywords': [MINIMAL_KEYWORD_DICT] }
62+
MINIMAL_SUITE_DICT = {'name': 'Minimal Suite',
63+
'suites': [{
64+
'name': 'Minimal Subsuite',
65+
'tests': [ MINIMAL_TC_DICT ]}]}
66+
6267

6368
class _ListSubclass(list):
6469
'''Used in test cases'''

tests/utest/oxygen_handler_result/test_OxygenSuiteDict.py

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,10 @@
33
from oxygen.errors import InvalidOxygenResultException
44
from oxygen.oxygen_handler_result import OxygenSuiteDict, validate_oxygen_suite
55

6-
from ..helpers import MINIMAL_TC_DICT, _ListSubclass, _TCSubclass
6+
from ..helpers import (MINIMAL_TC_DICT,
7+
MINIMAL_SUITE_DICT,
8+
_ListSubclass,
9+
_TCSubclass)
710
from .shared_tests import (SharedTestsForName,
811
SharedTestsForKeywordField,
912
SharedTestsForTags)
@@ -13,13 +16,7 @@ class TestOxygenSuiteDict(TestCase,
1316
SharedTestsForKeywordField,
1417
SharedTestsForTags):
1518
def setUp(self):
16-
self.minimal = {
17-
'name': 'Minimal Suite',
18-
'suites': [{
19-
'name': 'Minimal Subsuite',
20-
'tests': [ MINIMAL_TC_DICT ]
21-
}]
22-
}
19+
self.minimal = MINIMAL_SUITE_DICT
2320

2421
def test_validate_oxygen_suite_validates_correctly(self):
2522
with self.assertRaises(InvalidOxygenResultException):
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
'''
2+
Currently, the introduction of pydantic[1] to validate result dictionaries that
3+
handlers return is planned to just raise a deprecation warning.
4+
5+
After 1.0 -- ie. a backwards-incompatible release -- we should turn deprecation
6+
warning to actually start failing. See issue [2].
7+
8+
[1| https://github.com/eficode/robotframework-oxygen/issues/43
9+
[2] https://github.com/eficode/robotframework-oxygen/issues/45
10+
'''
11+
from unittest import TestCase
12+
from unittest.mock import patch
13+
from oxygen.base_handler import BaseHandler
14+
from oxygen.oxygen import OxygenCLI
15+
16+
from ..helpers import get_config, MINIMAL_SUITE_DICT
17+
18+
class TestDeprecationWarningWhenValidating(TestCase):
19+
def setUp(self):
20+
self.cli = OxygenCLI()
21+
22+
def test_warning_about_invalid_result(self):
23+
handler = BaseHandler(get_config()['oxygen.junit'])
24+
25+
with self.assertWarns(UserWarning) as warning:
26+
handler._validate({})
27+
28+
warning_message = str(warning.warning)
29+
self.assertIn('oxygen.base_handler', warning_message)
30+
self.assertIn('validation error for typed-dict', warning_message)
31+
self.assertIn('In Oxygen 1.0, handlers will'
32+
' need to produce valid results.', warning_message)
33+
34+
@patch('oxygen.oxygen.RobotInterface')
35+
def test_warning_about_invalid_result_in_CLI(self, mock_iface):
36+
with self.assertWarns(UserWarning) as warning:
37+
self.cli.convert_to_robot_result({
38+
'result_file': 'doesentmatter',
39+
'func': lambda **_: {**MINIMAL_SUITE_DICT, 'setup': []}
40+
})
41+
42+
mock_iface.assert_any_call()
43+
44+
def test_deprecation_was_removed(self):
45+
'''Remove this test once deprecation warning has been removed'''
46+
if self.cli.__version__.startswith('1'):
47+
self.fail('Deprecation warning should have been removed in 1.0')

0 commit comments

Comments
 (0)