Skip to content

Commit

Permalink
Refactor TestDefauls.
Browse files Browse the repository at this point in the history
Store tags as a tuple, not as `Tags`, and setup/teardown as a dict,
not as `Keyword`. The main motivation is avoiding possible issues
if same `Keyword` instance is used as setup/teardown with multiple
tests and modified in one place.
  • Loading branch information
pekkaklarck committed Apr 27, 2023
1 parent 38daf17 commit dd50beb
Show file tree
Hide file tree
Showing 3 changed files with 49 additions and 59 deletions.
10 changes: 5 additions & 5 deletions doc/userguide/src/ExtendingRobotFramework/ParserInterface.rst
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ actually parse anything.
def parse(source):
suite = TestSuite(name='Example', source=source)
test = suite.tests.create(name='Test')
test.body.create_keyword('Log', args=['Hello!'])
test.body.create_keyword(name='Log', args=['Hello!'])
return suite

Parser implemented as class
Expand All @@ -131,7 +131,7 @@ from each line it contains.
suite = TestSuite(TestSuite.name_from_source(source), source=source)
for line in source.read_text().splitlines():
test = suite.tests.create(name=line)
test.body.create_keyword('Log', args=['Hello!'])
test.body.create_keyword(name='Log', args=['Hello!'])
return suite

Parser extending optional base class
Expand All @@ -154,8 +154,8 @@ initialization files, uses TestDefaults_ and registers multiple extensions.
"""Create a suite and set possible defaults from init files to tests."""
suite = TestSuite(TestSuite.name_from_source(source), source=source)
for line in source.read_text().splitlines():
test = suite.tests.create(name=line)
test.body.create_keyword('Log', args=['Hello!'])
test = suite.tests.create(name=line, doc='Example')
test.body.create_keyword(name='Log', args=['Hello!'])
defaults.set_to(test)
return suite

Expand All @@ -165,7 +165,7 @@ initialization files, uses TestDefaults_ and registers multiple extensions.
This method is called only if there is an initialization file with
a supported extension.
"""
defaults.tags = ['tag from init']
defaults.tags = ('tags', 'from init')
defaults.setup = {'name': 'Log', 'args': ['Hello from init!']}
return TestSuite(TestSuite.name_from_source(source.parent), doc='Example',
source=source, metadata={'Example': 'Value'})
Expand Down
87 changes: 40 additions & 47 deletions src/robot/running/builder/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,21 +22,18 @@
except ImportError:
TypedDict = dict

from robot.model import Tags
from ..model import TestCase

from ..model import Keyword, TestCase

class FixtureDict(TypedDict):
"""Dictionary containing setup or teardown info.
class KeywordDict(TypedDict):
"""Dictionary to create setup or teardown from.
:attr:`args` and :attr:`lineno` are optional.
:attr:`args` is optional.
"""
# `args` and `lineno` are not marked optional, because that would be hard
# until we require Python 3.8 and ugly until Python 3.11.
# `args` is not marked optional because that would be hard until we require
# Python 3.8 and ugly until Python 3.11.
name: str
args: 'Sequence[str]'
lineno: int


class TestDefaults:
Expand All @@ -55,8 +52,8 @@ class TestDefaults:
"""

def __init__(self, parent: 'TestDefaults|None' = None,
setup: 'Keyword|KeywordDict|None' = None,
teardown: 'Keyword|KeywordDict|None' = None,
setup: 'FixtureDict|None' = None,
teardown: 'FixtureDict|None' = None,
tags: 'Sequence[str]' = (), timeout: 'str|None' = None):
self.parent = parent
self.setup = setup
Expand All @@ -65,7 +62,7 @@ def __init__(self, parent: 'TestDefaults|None' = None,
self.timeout = timeout

@property
def setup(self) -> 'Keyword|None':
def setup(self) -> 'FixtureDict|None':
"""Default setup as a ``Keyword`` object or ``None`` when not set.
Can be set also using a dictionary.
Expand All @@ -77,13 +74,11 @@ def setup(self) -> 'Keyword|None':
return None

@setup.setter
def setup(self, setup: 'Keyword|KeywordDict|None'):
if isinstance(setup, dict):
setup = Keyword.from_dict(setup)
def setup(self, setup: 'FixtureDict|None'):
self._setup = setup

@property
def teardown(self) -> 'Keyword|None':
def teardown(self) -> 'FixtureDict|None':
"""Default teardown as a ``Keyword`` object or ``None`` when not set.
Can be set also using a dictionary.
Expand All @@ -95,19 +90,17 @@ def teardown(self) -> 'Keyword|None':
return None

@teardown.setter
def teardown(self, teardown: 'Keyword|KeywordDict|None'):
if isinstance(teardown, dict):
teardown = Keyword.from_dict(teardown)
def teardown(self, teardown: 'FixtureDict|None'):
self._teardown = teardown

@property
def tags(self) -> Tags:
def tags(self) -> 'tuple[str]':
"""Default tags. Can be set also as a sequence."""
return self._tags + self.parent.tags if self.parent else self._tags

@tags.setter
def tags(self, tags: 'Sequence[str]'):
self._tags = Tags(tags)
self._tags = tuple(tags)

@property
def timeout(self) -> 'str|None':
Expand All @@ -131,9 +124,9 @@ def set_to(self, test: TestCase):
if self.tags:
test.tags += self.tags
if self.setup and not test.has_setup:
test.setup = self.setup
test.setup.config(**self.setup)
if self.teardown and not test.has_teardown:
test.teardown = self.teardown
test.teardown.config(**self.teardown)
if self.timeout and not test.timeout:
test.timeout = self.timeout

Expand All @@ -142,85 +135,85 @@ class FileSettings:

def __init__(self, test_defaults: 'TestDefaults|None' = None):
self.test_defaults = test_defaults or TestDefaults()
self._test_setup = None
self._test_teardown = None
self._test_tags = Tags()
self._test_timeout = None
self._test_template = None
self._default_tags = Tags()
self._keyword_tags = Tags()
self.test_setup = None
self.test_teardown = None
self.test_tags = ()
self.test_timeout = None
self.test_template = None
self.default_tags = ()
self.keyword_tags = ()

@property
def test_setup(self) -> 'Keyword|None':
def test_setup(self) -> 'FixtureDict|None':
return self._test_setup or self.test_defaults.setup

@test_setup.setter
def test_setup(self, setup: KeywordDict):
self._test_setup = Keyword.from_dict(setup)
def test_setup(self, setup: 'FixtureDict|None'):
self._test_setup = setup

@property
def test_teardown(self) -> 'Keyword|None':
def test_teardown(self) -> 'FixtureDict|None':
return self._test_teardown or self.test_defaults.teardown

@test_teardown.setter
def test_teardown(self, teardown: KeywordDict):
self._test_teardown = Keyword.from_dict(teardown)
def test_teardown(self, teardown: 'FixtureDict|None'):
self._test_teardown = teardown

@property
def test_tags(self) -> Tags:
def test_tags(self) -> 'tuple[str]':
return self._test_tags + self.test_defaults.tags

@test_tags.setter
def test_tags(self, tags: 'Sequence[str]'):
self._test_tags = Tags(tags)
self._test_tags = tuple(tags)

@property
def test_timeout(self) -> 'str|None':
return self._test_timeout or self.test_defaults.timeout

@test_timeout.setter
def test_timeout(self, timeout: str):
def test_timeout(self, timeout: 'str|None'):
self._test_timeout = timeout

@property
def test_template(self) -> 'str|None':
return self._test_template

@test_template.setter
def test_template(self, template: str):
def test_template(self, template: 'str|None'):
self._test_template = template

@property
def default_tags(self) -> Tags:
def default_tags(self) -> 'tuple[str]':
return self._default_tags

@default_tags.setter
def default_tags(self, tags: 'Sequence[str]'):
self._default_tags = Tags(tags)
self._default_tags = tuple(tags)

@property
def keyword_tags(self) -> Tags:
def keyword_tags(self) -> 'tuple[str]':
return self._keyword_tags

@keyword_tags.setter
def keyword_tags(self, tags: 'Sequence[str]'):
self._keyword_tags = Tags(tags)
self._keyword_tags = tuple(tags)


class InitFileSettings(FileSettings):

@FileSettings.test_setup.setter
def test_setup(self, setup: KeywordDict):
def test_setup(self, setup: 'FixtureDict|None'):
self.test_defaults.setup = setup

@FileSettings.test_teardown.setter
def test_teardown(self, teardown: KeywordDict):
def test_teardown(self, teardown: 'FixtureDict|None'):
self.test_defaults.teardown = teardown

@FileSettings.test_tags.setter
def test_tags(self, tags: 'Sequence[str]'):
self.test_defaults.tags = tags

@FileSettings.test_timeout.setter
def test_timeout(self, timeout: str):
def test_timeout(self, timeout: 'str|None'):
self.test_defaults.timeout = timeout
11 changes: 4 additions & 7 deletions src/robot/running/builder/transformers.py
Original file line number Diff line number Diff line change
Expand Up @@ -179,16 +179,13 @@ def visit_TestCase(self, node):
template=settings.test_template,
error=error)
if settings.test_setup:
self.test.setup.config(name=settings.test_setup.name,
args=settings.test_setup.args,
lineno=settings.test_setup.lineno)
self.test.setup.config(**settings.test_setup)
if settings.test_teardown:
self.test.teardown.config(name=settings.test_teardown.name,
args=settings.test_teardown.args,
lineno=settings.test_teardown.lineno)
self.test.teardown.config(**settings.test_teardown)
self.generic_visit(node)
tags = self.tags if self.tags is not None else settings.default_tags
self.test.tags.add(tags)
if tags:
self.test.tags.add(tags)
if self.test.template:
self._set_template(self.test, self.test.template)

Expand Down

0 comments on commit dd50beb

Please sign in to comment.