diff --git a/src/_balder/controllers/scenario_controller.py b/src/_balder/controllers/scenario_controller.py index b9cdfe98..98d5ebbe 100644 --- a/src/_balder/controllers/scenario_controller.py +++ b/src/_balder/controllers/scenario_controller.py @@ -37,6 +37,9 @@ def __init__(self, related_cls, _priv_instantiate_key): # describes if the current controller is for setups or for scenarios (has to be set in child controller) self._related_type = Scenario + # holds covered-by configuration + self._covered_by = {} + # this helps to make this constructor only possible inside the controller object if _priv_instantiate_key != ScenarioController.__priv_instantiate_key: raise RuntimeError('it is not allowed to instantiate a controller manually -> use the static method ' @@ -127,6 +130,73 @@ def get_parametrization_for( ordered_dict[cur_arg] = params[cur_arg] return ordered_dict + def register_covered_by_for(self, meth: Union[str, None], covered_by: Union[Scenario, Callable, None]) -> None: + """ + This method registers a covered-by statement for this Scenario. If `meth` is provided, the statement is for the + specific test method of the scenario, otherwise it is for the whole setup. The item provided in `covered_by` + describes the test object that covers this scenario (method). + + :param meth: if provided this attribute describes the test method that should be registered, otherwise the whole + scenario will be registered + :param covered_by: describes the test object that covers this scenario (method) + """ + if not (meth is None or isinstance(meth, str)): + raise TypeError('meth needs to be None or a string') + if meth is not None: + if not meth.startswith('test_'): + raise TypeError( + f"the use of the `@covered_by` decorator is only allowed for `Scenario` objects and test methods " + f"of `Scenario` objects - the method `{self.related_cls.__name__}.{meth}` does not start with " + f"`test_` and is not a valid test method") + if not hasattr(self.related_cls, meth): + raise ValueError( + f"the provided test method `{meth}` does not exist in scenario `{self.related_cls.__name__}`" + ) + + if meth not in self._covered_by.keys(): + self._covered_by[meth] = [] + if covered_by is None: + # reset it + # todo what if there are more than one decorator in one class + del self._covered_by[meth] + else: + self._covered_by[meth].append(covered_by) + + def get_raw_covered_by_dict(self) -> Dict[Union[str, None], List[Union[Scenario, Callable]]]: + """ + :return: returns the internal covered-by dictionary + """ + return self._covered_by.copy() + + def get_abs_covered_by_dict(self) -> Dict[Union[str, None], List[Union[Scenario, Callable]]]: + """ + This method resolves the covered-by statements over all inheritance levels. It automatically + cleans up every inheritance of the covered_by decorators for every parent class of this scenario. + """ + parent_classes = [p for p in self.related_cls.__bases__ if issubclass(p, Scenario) and p != Scenario] + if len(parent_classes) > 1: + raise MultiInheritanceError( + f'can not resolve classes for `{self.related_cls}` because there are more than one Scenario based ' + f'parent classes' + ) + # no more parent classes -> raw is absolute + if len(parent_classes) == 0: + return self.get_raw_covered_by_dict() + parent_controller = self.__class__.get_for(parent_classes[0]) + self_raw_covered_by_dict = self.get_raw_covered_by_dict() + + #: first fill result with data from parent controller + result = { + k if k is None else getattr(self.related_cls, k.__name__): v + for k, v in parent_controller.get_abs_covered_by_dict().items() + } + for cur_callable, cur_coveredby in self_raw_covered_by_dict.items(): + if cur_callable in result.keys(): + result[cur_callable].extend(cur_coveredby) + else: + result[cur_callable] = cur_coveredby + return result + def check_for_parameter_loop_in_dynamic_parametrization(self, cur_fn: Callable): """ This method checks for a parameter loop in all dynamic parametrization for a specific test method. If it detects diff --git a/src/_balder/decorator_covered_by.py b/src/_balder/decorator_covered_by.py index 505e2a81..ecd99758 100644 --- a/src/_balder/decorator_covered_by.py +++ b/src/_balder/decorator_covered_by.py @@ -2,11 +2,12 @@ from typing import Type, Union import inspect + +from _balder.controllers import ScenarioController from _balder.scenario import Scenario -from _balder.utils import get_class_that_defines_method -def covered_by(item: Union[Type[Scenario], callable, None]): +def covered_by(item: Union[Type[Scenario], str, callable, None]): """ This decorator defines that there exists another Scenario class or test method item that has a similar implementation like the decorated :class:`Scenario` class or the decorated test method. @@ -16,14 +17,15 @@ def covered_by(item: Union[Type[Scenario], callable, None]): if item is None: pass - elif callable(item) and inspect.isfunction(item) and item.__name__.startswith("test_") and \ - issubclass(get_class_that_defines_method(item), Scenario): + elif isinstance(item, str) and item.startswith("test_"): pass - elif isinstance(item, type) and issubclass(item, Scenario): + elif callable(item) and inspect.isfunction(item) and item.__name__.startswith("test_"): pass + elif isinstance(item, type) and issubclass(item, Scenario): + raise NotImplementedError('The covered-by other scenario classes is not supported yet') else: - raise TypeError("the given element for `item` must be a `Scenario` (or a subclass thereof) or a test method of " - "a scenario class (has to start with `test_`)") + raise TypeError("the given element for `item` must be a test method of a scenario class (has to start with " + "`test_`)") class CoveredByDecorator: """decorator class for `@covered_by` decorator""" @@ -36,46 +38,36 @@ def __init__(self, func): raise TypeError(f"The decorator `@covered_by` may only be used for `Scenario` objects or for test " f"methods of one `Scenario` object. This is not possible for the applied class " f"`{func.__name__}`.") - if not hasattr(func, '_covered_by'): - func._covered_by = {} - if func not in func._covered_by.keys(): - func._covered_by[func] = [] - if item is None: - # reset it - func._covered_by[func] = [] - elif item not in func._covered_by[func]: - func._covered_by[func].append(item) - elif inspect.isfunction(func): + raise NotImplementedError('The covered-by decoration of other scenario classes is not supported yet') + # scenario_controller = ScenarioController.get_for(func) + # register for the whole class + # scenario_controller.register_covered_by_for(meth=None, covered_by=item) + if inspect.isfunction(func): # work will done in `__set_name__` pass else: raise TypeError(f"The use of the `@covered_by` decorator is not allowed for the `{str(func)}` element. " - f"You should only use this decorator for `Scenario` elements or test method elements " - f"of a `Scenario` object") + f"You should only use this decorator for test method elements of a `Scenario` object") def __set_name__(self, owner, name): if issubclass(owner, Scenario): if not inspect.isfunction(self.func): - raise TypeError("the use of the `@covered_by` decorator is only allowed for `Scenario` objects and " - "test methods of `Scenario` objects") + raise TypeError("the use of the `@covered_by` decorator is only allowed for test methods of " + "`Scenario` objects") if not name.startswith('test_'): - raise TypeError(f"the use of the `@covered_by` decorator is only allowed for `Scenario` objects " - f"and test methods of `Scenario` objects - the method `{owner.__name__}.{name}` " - f"does not start with `test_` and is not a valid test method") - - if not hasattr(owner, '_covered_by'): - owner._covered_by = {} - if self.func not in owner._covered_by.keys(): - owner._covered_by[self.func] = [] - if item is None: - # reset it - owner._covered_by[self.func] = [] - elif item not in owner._covered_by[self.func]: - owner._covered_by[self.func].append(item) + raise TypeError(f"the use of the `@covered_by` decorator is only allowed for test methods of " + f"`Scenario` objects - the method `{owner.__name__}.{name}` does not start with " + f"`test_` and is not a valid test method") + # if item is a string - resolve method + cleared_item = item + if isinstance(item, str): + cleared_item = getattr(owner, item) + scenario_controller = ScenarioController.get_for(owner) + scenario_controller.register_covered_by_for(meth=name, covered_by=cleared_item) else: raise TypeError(f"The use of the `@covered_by` decorator is not allowed for methods of a " - f"`{owner.__name__}`. You should only use this decorator for `Scenario` objects or " - f"valid test methods of a `Scenario` object") + f"`{owner.__name__}`. You should only use this decorator for valid test methods of a " + f"`Scenario` object") setattr(owner, name, self.func) diff --git a/src/_balder/executor/scenario_executor.py b/src/_balder/executor/scenario_executor.py index a0b53963..213217b5 100644 --- a/src/_balder/executor/scenario_executor.py +++ b/src/_balder/executor/scenario_executor.py @@ -1,9 +1,8 @@ from __future__ import annotations -from typing import Type, Union, List, Dict, TYPE_CHECKING +from typing import Type, Union, List, TYPE_CHECKING from _balder.fixture_execution_level import FixtureExecutionLevel from _balder.testresult import ResultState, BranchBodyResult -from _balder.utils import get_class_that_defines_method from _balder.executor.basic_executable_executor import BasicExecutableExecutor from _balder.executor.variation_executor import VariationExecutor from _balder.previous_executor_mark import PreviousExecutorMark @@ -137,71 +136,12 @@ def cleanup_empty_executor_branches(self, consider_discarded=False): for cur_variation_executor in to_remove_executor: self._variation_executors.remove(cur_variation_executor) - def get_covered_by_dict(self) -> Dict[Union[Type[Scenario], callable], List[Union[Type[Scenario], callable]]]: - """ - This method returns the complete resolved ``@covered_by`` dictionary for this scenario. It automatically - cleans up every inheritance of the covered_by decorators for every parent class of our scenario. - """ - def determine_most_inherited_class(class_list): - for cur_candidate in class_list: - candidate_is_valid = True - for cur_other_candidate in class_list: - if cur_candidate == cur_other_candidate: - pass - if not issubclass(cur_candidate, cur_other_candidate): - candidate_is_valid = False - break - if candidate_is_valid: - return cur_candidate - return None - - # all data will be inherited while ``@covered_by`` overwrites elements only if there is a new decorator at the - # overwritten method - # -> we have to filter the dictionary and only return the value given for highest overwritten method - relative_covered_by_dict = {} - if hasattr(self.base_scenario_class, '_covered_by'): - function_name_mapping = {} - classes = [] - for cur_key in self.base_scenario_class._covered_by.keys(): - if issubclass(cur_key, Scenario): - # this is a covered_by definition for the whole class - classes.append(cur_key) - else: - # this is a covered_by definition for one test method - if cur_key.__name__ in function_name_mapping.keys(): - function_name_mapping[cur_key.__name__] = [cur_key] - else: - function_name_mapping[cur_key.__name__].append(cur_key) - - # determine the highest definition for class statement (only if necessary) - if len(classes) > 0: - most_inherited_class = determine_most_inherited_class(classes) - # this is the most inherited child -> add this definition - relative_covered_by_dict[most_inherited_class] = \ - self.base_scenario_class._covered_by[most_inherited_class] - - # determine the highest definition for every test method - for cur_function_name, cur_possible_candidates in function_name_mapping.items(): - classes = [get_class_that_defines_method(meth) for meth in cur_possible_candidates] - most_inherited_class = determine_most_inherited_class(classes) - most_inherited_test_method = cur_possible_candidates[classes.index(most_inherited_class)] - # this is the most inherited test method -> add the definition of this one and replace the method with - # this Scenario's one - relative_covered_by_dict[getattr(self.base_scenario_class, cur_function_name)] = \ - self.base_scenario_class._covered_by[most_inherited_test_method] - else: - pass - return relative_covered_by_dict - def get_covered_by_element(self) -> List[Union[Scenario, callable]]: """ This method returns a list of elements where the whole scenario is covered from. This means, that the whole test methods in this scenario are already be covered from one of the elements in the list. """ - covered_by_dict_resolved = self.get_covered_by_dict() - if self in covered_by_dict_resolved.keys(): - return covered_by_dict_resolved[self] - return [] + return self.base_scenario_controller.get_abs_covered_by_dict().get(None, []) def add_variation_executor(self, variation_executor: VariationExecutor): """ diff --git a/src/_balder/executor/testcase_executor.py b/src/_balder/executor/testcase_executor.py index 73c8114f..61308e6f 100644 --- a/src/_balder/executor/testcase_executor.py +++ b/src/_balder/executor/testcase_executor.py @@ -109,6 +109,9 @@ def _body_execution(self, show_discarded): if self.should_be_ignored(): self.body_result.set_result(ResultState.NOT_RUN) return + if self.is_covered_by(): + self.body_result.set_result(ResultState.COVERED_BY) + return start_time = time.perf_counter() try: @@ -156,6 +159,10 @@ def should_be_ignored(self): return True return False + def is_covered_by(self): + """returns true if the testcase is covered-by""" + return self.prev_mark == PreviousExecutorMark.COVERED_BY + def has_skipped_tests(self) -> bool: return self.prev_mark == PreviousExecutorMark.SKIP @@ -175,14 +182,12 @@ def get_covered_by_element(self) -> List[Union[Scenario, callable]]: This method returns a list of elements where the whole scenario is covered from. This means, that the whole test methods in this scenario are already be covered from every single element in the list. """ - all_covered_by_data = [] scenario_executor = self.parent_executor.parent_executor - scenario_class = scenario_executor.base_scenario_class - covered_by_dict_resolved = scenario_executor.get_covered_by_dict() - if self.base_testcase_callable in covered_by_dict_resolved.keys(): - all_covered_by_data += covered_by_dict_resolved[self.base_testcase_callable] - if scenario_class in covered_by_dict_resolved.keys(): - all_covered_by_data += covered_by_dict_resolved[scenario_class] + + covered_by_dict = scenario_executor.base_scenario_controller.get_abs_covered_by_dict() + all_covered_by_data = covered_by_dict.get(self.base_testcase_callable.__name__, []) + # also add all scenario specified covered-by elements + all_covered_by_data.extend(covered_by_dict.get(None, [])) return all_covered_by_data def get_all_test_method_args(self): diff --git a/src/_balder/executor/unresolved_parametrized_testcase_executor.py b/src/_balder/executor/unresolved_parametrized_testcase_executor.py index 2366d3cf..b6827138 100644 --- a/src/_balder/executor/unresolved_parametrized_testcase_executor.py +++ b/src/_balder/executor/unresolved_parametrized_testcase_executor.py @@ -109,14 +109,12 @@ def get_covered_by_element(self) -> List[Scenario | callable]: This method returns a list of elements where the whole scenario is covered from. This means, that the whole test methods in this scenario are already be covered from every single element in the list. """ - all_covered_by_data = [] scenario_executor = self.parent_executor.parent_executor - scenario_class = scenario_executor.base_scenario_class - covered_by_dict_resolved = scenario_executor.get_covered_by_dict() - if self.base_testcase_callable in covered_by_dict_resolved.keys(): - all_covered_by_data += covered_by_dict_resolved[self.base_testcase_callable] - if scenario_class in covered_by_dict_resolved.keys(): - all_covered_by_data += covered_by_dict_resolved[scenario_class] + + covered_by_dict = scenario_executor.base_scenario_controller.get_abs_covered_by_dict() + all_covered_by_data = covered_by_dict.get(self.base_testcase_callable.__name__, []) + # also add all scenario specified covered-by elements + all_covered_by_data.extend(covered_by_dict.get(None, [])) return all_covered_by_data def get_testcase_executors(self) -> List[ParametrizedTestcaseExecutor | UnresolvedParametrizedTestcaseExecutor]: diff --git a/src/_balder/executor/variation_executor.py b/src/_balder/executor/variation_executor.py index 665335f4..832d9774 100644 --- a/src/_balder/executor/variation_executor.py +++ b/src/_balder/executor/variation_executor.py @@ -179,7 +179,9 @@ def _body_execution(self, show_discarded): return for cur_testcase_executor in self.get_testcase_executors(): - if cur_testcase_executor.has_runnable_tests() or cur_testcase_executor.has_skipped_tests(): + if (cur_testcase_executor.has_runnable_tests() + or cur_testcase_executor.has_skipped_tests() + or cur_testcase_executor.has_covered_by_tests()): cur_testcase_executor.execute() else: cur_testcase_executor.set_result_for_whole_branch(ResultState.NOT_RUN) diff --git a/tests/covered_by/__init__.py b/tests/covered_by/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/covered_by/test_0_basic_covered_by_other_meth/__init__.py b/tests/covered_by/test_0_basic_covered_by_other_meth/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/covered_by/test_0_basic_covered_by_other_meth/env/__init__.py b/tests/covered_by/test_0_basic_covered_by_other_meth/env/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/covered_by/test_0_basic_covered_by_other_meth/env/balderglob.py b/tests/covered_by/test_0_basic_covered_by_other_meth/env/balderglob.py new file mode 100644 index 00000000..232be40a --- /dev/null +++ b/tests/covered_by/test_0_basic_covered_by_other_meth/env/balderglob.py @@ -0,0 +1,150 @@ +from typing import Literal, Union, List +from datetime import datetime + +import pathlib +import balder +import argparse +import logging +from multiprocessing import Queue +from .lib.utils import FixtureReturn + +logger = logging.getLogger(__file__) + + +class MyTestException(Exception): + pass + + +class RuntimeObserver: + """This is a helper object, that will be used from this test environment to observe the execution order""" + queue: Union[Queue, None] = None + + error_throwing = {} + + @staticmethod + def add_entry(file: Union[str, pathlib.Path], cls: object, meth: callable, msg: str, + category: Literal["fixture", "testcase", "feature"] = None, + part: Literal["construction", "teardown"] = None): + """ + adds a new entry and sends it over the queue + + :param file: the full filepath where the log will be generated + + :param cls: the class object, the entry is generated in + + :param meth: the method name, the entry is generated in + + :param msg: the message that should be inserted into the entry + + :param category: optional string of the category the entry is from + + :param part: optional string of the sub part the entry is from + """ + if hasattr(meth, 'fn'): + meth = meth.fn + new_dataset = { + "timestamp": datetime.now(), "file": file, "cls": "" if cls is None else cls.__name__, + "meth": meth.__name__, "msg": msg, "category": category, "part": part + } + logger.info("{:22} | {:20} | {:30} | {:12} | {:15} | {}".format( + pathlib.Path(file).parts[-1], "" if cls is None else cls.__name__, "" if meth is None else meth.__name__, + "" if category is None else category, "" if part is None else part, "" if msg is None else msg)) + + RuntimeObserver.queue.put(new_dataset) + # check if we have to throw the error + error_throwing_required = len(RuntimeObserver.error_throwing) > 0 + for cur_key in RuntimeObserver.error_throwing.keys(): + new_dataset_val = new_dataset[cur_key] + if callable(new_dataset_val): + new_dataset_val = new_dataset_val.__name__ + if new_dataset_val != RuntimeObserver.error_throwing[cur_key]: + error_throwing_required = False + break + if error_throwing_required: + raise MyTestException(f'raise test triggered exception for `{str(RuntimeObserver.error_throwing)}`') + + +class MyErrorThrowingPlugin(balder.BalderPlugin): + """ + This is a plugin that reads the values from console arguments and sets these values into the + :class:`RuntimeObserver`. The static method `RuntimeObserver.add_entry` will automatically throw an exception on the + given position. + """ + + def addoption(self, argument_parser: argparse.ArgumentParser): + argument_parser.add_argument('--test-error-file', help='the file id, the error should be thrown in') + argument_parser.add_argument('--test-error-cls', help='the class id, the error should be thrown in') + argument_parser.add_argument('--test-error-meth', help='the meth id, the error should be thrown in') + argument_parser.add_argument('--test-error-part', help='the part (`construct` or `teardown`), the error should ' + 'be thrown in - only for fixtures') + + def modify_collected_pyfiles(self, pyfiles: List[pathlib.Path]) -> List[pathlib.Path]: + # use this method to set the values + RuntimeObserver.error_throwing = {} + if self.balder_session.parsed_args.test_error_file: + path = pathlib.Path(self.balder_session.parsed_args.test_error_file) + if not path.is_absolute(): + path = str(self.balder_session.working_dir.joinpath(path)) + RuntimeObserver.error_throwing['file'] = path + if self.balder_session.parsed_args.test_error_cls: + RuntimeObserver.error_throwing['cls'] = self.balder_session.parsed_args.test_error_cls + if self.balder_session.parsed_args.test_error_meth: + RuntimeObserver.error_throwing['meth'] = self.balder_session.parsed_args.test_error_meth + if self.balder_session.parsed_args.test_error_part: + RuntimeObserver.error_throwing['part'] = self.balder_session.parsed_args.test_error_part + return pyfiles + + +@balder.fixture(level="session") +def balderglob_fixture_session(): + RuntimeObserver.add_entry(__file__, None, balderglob_fixture_session, "begin execution CONSTRUCTION of fixture", + category="fixture", part="construction") + + yield FixtureReturn.BALDERGLOB_SESSION + + RuntimeObserver.add_entry(__file__, None, balderglob_fixture_session, "begin execution TEARDOWN of fixture", + category="fixture", part="teardown") + + +@balder.fixture(level="setup") +def balderglob_fixture_setup(): + RuntimeObserver.add_entry(__file__, None, balderglob_fixture_setup, "begin execution CONSTRUCTION of fixture", + category="fixture", part="construction") + + yield FixtureReturn.BALDERGLOB_SETUP + + RuntimeObserver.add_entry(__file__, None, balderglob_fixture_setup, "begin execution TEARDOWN of fixture", + category="fixture", part="teardown") + + +@balder.fixture(level="scenario") +def balderglob_fixture_scenario(): + RuntimeObserver.add_entry(__file__, None, balderglob_fixture_scenario, "begin execution CONSTRUCTION of fixture", + category="fixture", part="construction") + + yield FixtureReturn.BALDERGLOB_SCENARIO + + RuntimeObserver.add_entry(__file__, None, balderglob_fixture_scenario, "begin execution TEARDOWN of fixture", + category="fixture", part="teardown") + + +@balder.fixture(level="variation") +def balderglob_fixture_variation(): + RuntimeObserver.add_entry(__file__, None, balderglob_fixture_variation, "begin execution CONSTRUCTION of fixture", + category="fixture", part="construction") + + yield FixtureReturn.BALDERGLOB_VARIATION + + RuntimeObserver.add_entry(__file__, None, balderglob_fixture_variation, "begin execution TEARDOWN of fixture", + category="fixture", part="teardown") + + +@balder.fixture(level="testcase") +def balderglob_fixture_testcase(): + RuntimeObserver.add_entry(__file__, None, balderglob_fixture_testcase, "begin execution CONSTRUCTION of fixture", + category="fixture", part="construction") + + yield FixtureReturn.BALDERGLOB_TESTCASE + + RuntimeObserver.add_entry(__file__, None, balderglob_fixture_testcase, "begin execution TEARDOWN of fixture", + category="fixture", part="teardown") diff --git a/tests/covered_by/test_0_basic_covered_by_other_meth/env/lib/__init__.py b/tests/covered_by/test_0_basic_covered_by_other_meth/env/lib/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/covered_by/test_0_basic_covered_by_other_meth/env/lib/connections.py b/tests/covered_by/test_0_basic_covered_by_other_meth/env/lib/connections.py new file mode 100644 index 00000000..cf1e8b61 --- /dev/null +++ b/tests/covered_by/test_0_basic_covered_by_other_meth/env/lib/connections.py @@ -0,0 +1,11 @@ +import balder + + +@balder.insert_into_tree() +class AConnection(balder.Connection): + pass + + +@balder.insert_into_tree() +class BConnection(balder.Connection): + pass diff --git a/tests/covered_by/test_0_basic_covered_by_other_meth/env/lib/features.py b/tests/covered_by/test_0_basic_covered_by_other_meth/env/lib/features.py new file mode 100644 index 00000000..fae8ae25 --- /dev/null +++ b/tests/covered_by/test_0_basic_covered_by_other_meth/env/lib/features.py @@ -0,0 +1,30 @@ +import balder +from ..balderglob import RuntimeObserver + + +class FeatureI(balder.Feature): + + def do_something(self): + RuntimeObserver.add_entry( + __file__, FeatureI, FeatureI.do_something, "enter `FeatureI.do_something`", category="feature") + + +class FeatureII(balder.Feature): + + def do_something(self): + RuntimeObserver.add_entry( + __file__, FeatureII, FeatureII.do_something, "enter `FeatureII.do_something`", category="feature") + + +class FeatureIII(balder.Feature): + + def do_something(self): + RuntimeObserver.add_entry( + __file__, FeatureIII, FeatureIII.do_something, "enter `FeatureIII.do_something`", category="feature") + + +class FeatureIV(balder.Feature): + + def do_something(self): + RuntimeObserver.add_entry( + __file__, FeatureIV, FeatureIV.do_something, "enter `FeatureIV.do_something`", category="feature") diff --git a/tests/covered_by/test_0_basic_covered_by_other_meth/env/lib/utils.py b/tests/covered_by/test_0_basic_covered_by_other_meth/env/lib/utils.py new file mode 100644 index 00000000..fe4559af --- /dev/null +++ b/tests/covered_by/test_0_basic_covered_by_other_meth/env/lib/utils.py @@ -0,0 +1,22 @@ + + +class FixtureReturn: + """helper const class for return values""" + BALDERGLOB_SESSION = "balderglob_session_fixt" + BALDERGLOB_SETUP = "balderglob_setup_fixt" + BALDERGLOB_SCENARIO = "balderglob_scenario_fixt" + BALDERGLOB_VARIATION = "balderglob_variation_fixt" + BALDERGLOB_TESTCASE = "balderglob_testcase_fixt" + + SETUP_SESSION = "setup_session_fixt" + SETUP_SETUP = "setup_setup_fixt" + SETUP_SCENARIO = "setup_scenario_fixt" + SETUP_VARIATION = "setup_variation_fixt" + SETUP_TESTCASE = "setup_testcase_fixt" + + SCENARIO_SESSION = "scenario_session_fixt" + SCENARIO_SETUP = "scenario_setup_fixt" + SCENARIO_SCENARIO = "scenario_scenario_fixt" + SCENARIO_VARIATION = "scenario_variation_fixt" + SCENARIO_TESTCASE = "scenario_testcase_fixt" + diff --git a/tests/covered_by/test_0_basic_covered_by_other_meth/env/scenarios/__init__.py b/tests/covered_by/test_0_basic_covered_by_other_meth/env/scenarios/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/covered_by/test_0_basic_covered_by_other_meth/env/scenarios/scenario_a.py b/tests/covered_by/test_0_basic_covered_by_other_meth/env/scenarios/scenario_a.py new file mode 100644 index 00000000..cc773615 --- /dev/null +++ b/tests/covered_by/test_0_basic_covered_by_other_meth/env/scenarios/scenario_a.py @@ -0,0 +1,109 @@ +import balder +import logging +from ..lib.features import FeatureI, FeatureII +from ..lib.connections import AConnection +from ..balderglob import RuntimeObserver + +logger = logging.getLogger(__file__) + + +class ScenarioA(balder.Scenario): + """This is the scenario of category A""" + + class ScenarioDevice1(balder.Device): + i = FeatureI() + + @balder.connect(ScenarioDevice1, over_connection=AConnection) + class ScenarioDevice2(balder.Device): + ii = FeatureII() + + def test_a_1(self): + RuntimeObserver.add_entry(__file__, ScenarioA, ScenarioA.test_a_1, category="testcase", + msg=f"execute Test `{ScenarioA.test_a_1.__qualname__}`") + self.ScenarioDevice1.i.do_something() + self.ScenarioDevice2.ii.do_something() + + @balder.covered_by(test_a_1) + def test_a_2(self): + RuntimeObserver.add_entry(__file__, ScenarioA, ScenarioA.test_a_2, category="testcase", + msg=f"execute Test `{ScenarioA.test_a_2.__qualname__}`") + self.ScenarioDevice1.i.do_something() + self.ScenarioDevice2.ii.do_something() + + @balder.fixture(level="session") + def fixture_session(self): + RuntimeObserver.add_entry(__file__, ScenarioA, ScenarioA.fixture_session, category="fixture", + part="construction", msg="begin execution CONSTRUCTION of fixture") + + self.ScenarioDevice1.i.do_something() + self.ScenarioDevice2.ii.do_something() + + yield + + RuntimeObserver.add_entry(__file__, ScenarioA, ScenarioA.fixture_session, category="fixture", part="teardown", + msg="begin execution TEARDOWN of fixture") + + self.ScenarioDevice1.i.do_something() + self.ScenarioDevice2.ii.do_something() + + @balder.fixture(level="setup") + def fixture_setup(self): + RuntimeObserver.add_entry(__file__, ScenarioA, ScenarioA.fixture_setup, category="fixture", part="construction", + msg="begin execution CONSTRUCTION of fixture") + self.ScenarioDevice1.i.do_something() + self.ScenarioDevice2.ii.do_something() + + yield + + RuntimeObserver.add_entry(__file__, ScenarioA, ScenarioA.fixture_setup, category="fixture", part="teardown", + msg="begin execution TEARDOWN of fixture") + + self.ScenarioDevice1.i.do_something() + self.ScenarioDevice2.ii.do_something() + + @balder.fixture(level="scenario") + def fixture_scenario(self): + RuntimeObserver.add_entry(__file__, ScenarioA, ScenarioA.fixture_scenario, category="fixture", + part="construction", msg="begin execution CONSTRUCTION of fixture") + + self.ScenarioDevice1.i.do_something() + self.ScenarioDevice2.ii.do_something() + + yield + + RuntimeObserver.add_entry(__file__, ScenarioA, ScenarioA.fixture_scenario, category="fixture", part="teardown", + msg="begin execution TEARDOWN of fixture") + + self.ScenarioDevice1.i.do_something() + self.ScenarioDevice2.ii.do_something() + + @balder.fixture(level="variation") + def fixture_variation(self): + RuntimeObserver.add_entry(__file__, ScenarioA, ScenarioA.fixture_variation, category="fixture", + part="construction", msg="begin execution CONSTRUCTION of fixture") + self.ScenarioDevice1.i.do_something() + self.ScenarioDevice2.ii.do_something() + + yield + + RuntimeObserver.add_entry(__file__, ScenarioA, ScenarioA.fixture_variation, category="fixture", part="teardown", + msg="begin execution TEARDOWN of fixture") + + self.ScenarioDevice1.i.do_something() + self.ScenarioDevice2.ii.do_something() + + @balder.fixture(level="testcase") + def fixture_testcase(self): + RuntimeObserver.add_entry(__file__, ScenarioA, ScenarioA.fixture_testcase, category="fixture", + part="construction", msg="begin execution CONSTRUCTION of fixture") + + self.ScenarioDevice1.i.do_something() + self.ScenarioDevice2.ii.do_something() + + yield + + RuntimeObserver.add_entry(__file__, ScenarioA, ScenarioA.fixture_testcase, category="fixture", part="teardown", + msg="begin execution TEARDOWN of fixture") + + self.ScenarioDevice1.i.do_something() + self.ScenarioDevice2.ii.do_something() diff --git a/tests/covered_by/test_0_basic_covered_by_other_meth/env/scenarios/scenario_b.py b/tests/covered_by/test_0_basic_covered_by_other_meth/env/scenarios/scenario_b.py new file mode 100644 index 00000000..577613ac --- /dev/null +++ b/tests/covered_by/test_0_basic_covered_by_other_meth/env/scenarios/scenario_b.py @@ -0,0 +1,110 @@ +import balder +import logging +from ..lib.features import FeatureIII, FeatureIV +from ..lib.connections import BConnection +from ..balderglob import RuntimeObserver + +logger = logging.getLogger(__name__) + + +class ScenarioB(balder.Scenario): + """This is the scenario of category B""" + + class ScenarioDevice3(balder.Device): + iii = FeatureIII() + + @balder.connect(ScenarioDevice3, over_connection=BConnection) + class ScenarioDevice4(balder.Device): + iv = FeatureIV() + + @balder.covered_by('test_b_2') + def test_b_1(self): + RuntimeObserver.add_entry(__file__, ScenarioB, ScenarioB.test_b_1, category="testcase", + msg=f"execute Test `{ScenarioB.test_b_1.__qualname__}`") + self.ScenarioDevice3.iii.do_something() + self.ScenarioDevice4.iv.do_something() + + def test_b_2(self): + RuntimeObserver.add_entry(__file__, ScenarioB, ScenarioB.test_b_2, category="testcase", + msg=f"execute Test `{ScenarioB.test_b_2.__qualname__}`") + self.ScenarioDevice3.iii.do_something() + self.ScenarioDevice4.iv.do_something() + + @balder.fixture(level="session") + def fixture_session(self): + RuntimeObserver.add_entry(__file__, ScenarioB, ScenarioB.fixture_session, category="fixture", + part="construction", msg="begin execution CONSTRUCTION of fixture") + self.ScenarioDevice3.iii.do_something() + self.ScenarioDevice4.iv.do_something() + + yield + + RuntimeObserver.add_entry(__file__, ScenarioB, ScenarioB.fixture_session, category="fixture", part="teardown", + msg="begin execution TEARDOWN of fixture") + + self.ScenarioDevice3.iii.do_something() + self.ScenarioDevice4.iv.do_something() + + @balder.fixture(level="setup") + def fixture_setup(self): + RuntimeObserver.add_entry(__file__, ScenarioB, ScenarioB.fixture_setup, category="fixture", part="construction", + msg="begin execution CONSTRUCTION of fixture") + + self.ScenarioDevice3.iii.do_something() + self.ScenarioDevice4.iv.do_something() + + yield + + RuntimeObserver.add_entry(__file__, ScenarioB, ScenarioB.fixture_setup, category="fixture", part="teardown", + msg="begin execution TEARDOWN of fixture") + + self.ScenarioDevice3.iii.do_something() + self.ScenarioDevice4.iv.do_something() + + @balder.fixture(level="scenario") + def fixture_scenario(self): + RuntimeObserver.add_entry(__file__, ScenarioB, ScenarioB.fixture_scenario, category="fixture", + part="construction", msg="begin execution CONSTRUCTION of fixture") + + self.ScenarioDevice3.iii.do_something() + self.ScenarioDevice4.iv.do_something() + + yield + + RuntimeObserver.add_entry(__file__, ScenarioB, ScenarioB.fixture_scenario, category="fixture", part="teardown", + msg="begin execution TEARDOWN of fixture") + + self.ScenarioDevice3.iii.do_something() + self.ScenarioDevice4.iv.do_something() + + @balder.fixture(level="variation") + def fixture_variation(self): + RuntimeObserver.add_entry(__file__, ScenarioB, ScenarioB.fixture_variation, category="fixture", + part="construction", msg="begin execution CONSTRUCTION of fixture") + + self.ScenarioDevice3.iii.do_something() + self.ScenarioDevice4.iv.do_something() + + yield + + RuntimeObserver.add_entry(__file__, ScenarioB, ScenarioB.fixture_variation, category="fixture", part="teardown", + msg="begin execution TEARDOWN of fixture") + + self.ScenarioDevice3.iii.do_something() + self.ScenarioDevice4.iv.do_something() + + @balder.fixture(level="testcase") + def fixture_testcase(self): + RuntimeObserver.add_entry(__file__, ScenarioB, ScenarioB.fixture_testcase, category="fixture", + part="construction", msg="begin execution CONSTRUCTION of fixture") + + self.ScenarioDevice3.iii.do_something() + self.ScenarioDevice4.iv.do_something() + + yield + + RuntimeObserver.add_entry(__file__, ScenarioB, ScenarioB.fixture_testcase, category="fixture", part="teardown", + msg="begin execution TEARDOWN of fixture") + + self.ScenarioDevice3.iii.do_something() + self.ScenarioDevice4.iv.do_something() diff --git a/tests/covered_by/test_0_basic_covered_by_other_meth/env/setups/__init__.py b/tests/covered_by/test_0_basic_covered_by_other_meth/env/setups/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/covered_by/test_0_basic_covered_by_other_meth/env/setups/setup_a.py b/tests/covered_by/test_0_basic_covered_by_other_meth/env/setups/setup_a.py new file mode 100644 index 00000000..5f8b4699 --- /dev/null +++ b/tests/covered_by/test_0_basic_covered_by_other_meth/env/setups/setup_a.py @@ -0,0 +1,98 @@ +import balder +import logging +from .setup_features import SetupFeatureI, SetupFeatureII +from ..lib.connections import AConnection +from ..balderglob import RuntimeObserver + +logger = logging.getLogger(__name__) + + +class SetupA(balder.Setup): + """This is a setup of category A (exactly the same as scenario A)""" + + class SetupDevice1(balder.Device): + s_i = SetupFeatureI() + + @balder.connect(SetupDevice1, over_connection=AConnection) + class SetupDevice2(balder.Device): + s_ii = SetupFeatureII() + + @balder.fixture(level="session") + def fixture_session(self): + RuntimeObserver.add_entry(__file__, SetupA, SetupA.fixture_session, category="fixture", part="construction", + msg="begin execution CONSTRUCTION of fixture") + + self.SetupDevice1.s_i.do_something() + self.SetupDevice2.s_ii.do_something() + + yield + + RuntimeObserver.add_entry(__file__, SetupA, SetupA.fixture_session, category="fixture", part="teardown", + msg="begin execution TEARDOWN of fixture") + + self.SetupDevice1.s_i.do_something() + self.SetupDevice2.s_ii.do_something() + + @balder.fixture(level="setup") + def fixture_setup(self): + RuntimeObserver.add_entry(__file__, SetupA, SetupA.fixture_setup, category="fixture", part="construction", + msg="begin execution CONSTRUCTION of fixture") + + self.SetupDevice1.s_i.do_something() + self.SetupDevice2.s_ii.do_something() + + yield + + RuntimeObserver.add_entry(__file__, SetupA, SetupA.fixture_setup, category="fixture", part="teardown", + msg="begin execution TEARDOWN of fixture") + + self.SetupDevice1.s_i.do_something() + self.SetupDevice2.s_ii.do_something() + + @balder.fixture(level="scenario") + def fixture_scenario(self): + RuntimeObserver.add_entry(__file__, SetupA, SetupA.fixture_scenario, category="fixture", part="construction", + msg="begin execution CONSTRUCTION of fixture") + + self.SetupDevice1.s_i.do_something() + self.SetupDevice2.s_ii.do_something() + + yield + + RuntimeObserver.add_entry(__file__, SetupA, SetupA.fixture_scenario, category="fixture", part="teardown", + msg="begin execution TEARDOWN of fixture") + + self.SetupDevice1.s_i.do_something() + self.SetupDevice2.s_ii.do_something() + + @balder.fixture(level="variation") + def fixture_variation(self): + RuntimeObserver.add_entry(__file__, SetupA, SetupA.fixture_variation, category="fixture", part="construction", + msg="begin execution CONSTRUCTION of fixture") + + self.SetupDevice1.s_i.do_something() + self.SetupDevice2.s_ii.do_something() + + yield + + RuntimeObserver.add_entry(__file__, SetupA, SetupA.fixture_variation, category="fixture", part="teardown", + msg="begin execution TEARDOWN of fixture") + + self.SetupDevice1.s_i.do_something() + self.SetupDevice2.s_ii.do_something() + + @balder.fixture(level="testcase") + def fixture_testcase(self): + RuntimeObserver.add_entry(__file__, SetupA, SetupA.fixture_testcase, category="fixture", part="construction", + msg="begin execution CONSTRUCTION of fixture") + + self.SetupDevice1.s_i.do_something() + self.SetupDevice2.s_ii.do_something() + + yield + + RuntimeObserver.add_entry(__file__, SetupA, SetupA.fixture_testcase, category="fixture", part="teardown", + msg="begin execution TEARDOWN of fixture") + + self.SetupDevice1.s_i.do_something() + self.SetupDevice2.s_ii.do_something() diff --git a/tests/covered_by/test_0_basic_covered_by_other_meth/env/setups/setup_b.py b/tests/covered_by/test_0_basic_covered_by_other_meth/env/setups/setup_b.py new file mode 100644 index 00000000..d9641b4b --- /dev/null +++ b/tests/covered_by/test_0_basic_covered_by_other_meth/env/setups/setup_b.py @@ -0,0 +1,98 @@ +import balder +import logging +from .setup_features import SetupFeatureIII, SetupFeatureIV +from ..lib.connections import BConnection +from ..balderglob import RuntimeObserver + +logger = logging.getLogger(__name__) + + +class SetupB(balder.Setup): + """This is a setup of category B (exactly the same as scenario B)""" + + class SetupDevice3(balder.Device): + s_iii = SetupFeatureIII() + + @balder.connect(SetupDevice3, over_connection=BConnection) + class SetupDevice4(balder.Device): + s_iv = SetupFeatureIV() + + @balder.fixture(level="session") + def fixture_session(self): + RuntimeObserver.add_entry(__file__, SetupB, SetupB.fixture_session, category="fixture", part="construction", + msg="begin execution CONSTRUCTION of fixture") + + self.SetupDevice3.s_iii.do_something() + self.SetupDevice4.s_iv.do_something() + + yield + + RuntimeObserver.add_entry(__file__, SetupB, SetupB.fixture_session, category="fixture", part="teardown", + msg="begin execution TEARDOWN of fixture") + + self.SetupDevice3.s_iii.do_something() + self.SetupDevice4.s_iv.do_something() + + @balder.fixture(level="setup") + def fixture_setup(self): + RuntimeObserver.add_entry(__file__, SetupB, SetupB.fixture_setup, category="fixture", part="construction", + msg="begin execution CONSTRUCTION of fixture") + + self.SetupDevice3.s_iii.do_something() + self.SetupDevice4.s_iv.do_something() + + yield + + RuntimeObserver.add_entry(__file__, SetupB, SetupB.fixture_setup, category="fixture", part="teardown", + msg="begin execution TEARDOWN of fixture") + + self.SetupDevice3.s_iii.do_something() + self.SetupDevice4.s_iv.do_something() + + @balder.fixture(level="scenario") + def fixture_scenario(self): + RuntimeObserver.add_entry(__file__, SetupB, SetupB.fixture_scenario, category="fixture", part="construction", + msg="begin execution CONSTRUCTION of fixture") + + self.SetupDevice3.s_iii.do_something() + self.SetupDevice4.s_iv.do_something() + + yield + + RuntimeObserver.add_entry(__file__, SetupB, SetupB.fixture_scenario, category="fixture", part="teardown", + msg="begin execution TEARDOWN of fixture") + + self.SetupDevice3.s_iii.do_something() + self.SetupDevice4.s_iv.do_something() + + @balder.fixture(level="variation") + def fixture_variation(self): + RuntimeObserver.add_entry(__file__, SetupB, SetupB.fixture_variation, category="fixture", part="construction", + msg="begin execution CONSTRUCTION of fixture") + + self.SetupDevice3.s_iii.do_something() + self.SetupDevice4.s_iv.do_something() + + yield + + RuntimeObserver.add_entry(__file__, SetupB, SetupB.fixture_variation, category="fixture", part="teardown", + msg="begin execution TEARDOWN of fixture") + + self.SetupDevice3.s_iii.do_something() + self.SetupDevice4.s_iv.do_something() + + @balder.fixture(level="testcase") + def fixture_testcase(self): + RuntimeObserver.add_entry(__file__, SetupB, SetupB.fixture_testcase, category="fixture", part="construction", + msg="begin execution CONSTRUCTION of fixture") + + self.SetupDevice3.s_iii.do_something() + self.SetupDevice4.s_iv.do_something() + + yield + + RuntimeObserver.add_entry(__file__, SetupB, SetupB.fixture_testcase, category="fixture", part="teardown", + msg="begin execution TEARDOWN of fixture") + + self.SetupDevice3.s_iii.do_something() + self.SetupDevice4.s_iv.do_something() diff --git a/tests/covered_by/test_0_basic_covered_by_other_meth/env/setups/setup_features/__init__.py b/tests/covered_by/test_0_basic_covered_by_other_meth/env/setups/setup_features/__init__.py new file mode 100644 index 00000000..0158f44f --- /dev/null +++ b/tests/covered_by/test_0_basic_covered_by_other_meth/env/setups/setup_features/__init__.py @@ -0,0 +1,11 @@ +from .feature_i import SetupFeatureI +from .feature_ii import SetupFeatureII +from .feature_iii import SetupFeatureIII +from .feature_iv import SetupFeatureIV + +__all__ = [ + "SetupFeatureI", + "SetupFeatureII", + "SetupFeatureIII", + "SetupFeatureIV" +] diff --git a/tests/covered_by/test_0_basic_covered_by_other_meth/env/setups/setup_features/feature_i.py b/tests/covered_by/test_0_basic_covered_by_other_meth/env/setups/setup_features/feature_i.py new file mode 100644 index 00000000..8f4cd2e5 --- /dev/null +++ b/tests/covered_by/test_0_basic_covered_by_other_meth/env/setups/setup_features/feature_i.py @@ -0,0 +1,10 @@ +from ...lib.features import FeatureI +from ...balderglob import RuntimeObserver + + +class SetupFeatureI(FeatureI): + + def do_something(self): + RuntimeObserver.add_entry( + __file__, SetupFeatureI, SetupFeatureI.do_something, "enter `SetupFeatureI.do_something`", + category="feature") diff --git a/tests/covered_by/test_0_basic_covered_by_other_meth/env/setups/setup_features/feature_ii.py b/tests/covered_by/test_0_basic_covered_by_other_meth/env/setups/setup_features/feature_ii.py new file mode 100644 index 00000000..a38e27c7 --- /dev/null +++ b/tests/covered_by/test_0_basic_covered_by_other_meth/env/setups/setup_features/feature_ii.py @@ -0,0 +1,10 @@ +from ...lib.features import FeatureII +from ...balderglob import RuntimeObserver + + +class SetupFeatureII(FeatureII): + + def do_something(self): + RuntimeObserver.add_entry( + __file__, SetupFeatureII, SetupFeatureII.do_something, "enter `SetupFeatureII.do_something`", + category="feature") diff --git a/tests/covered_by/test_0_basic_covered_by_other_meth/env/setups/setup_features/feature_iii.py b/tests/covered_by/test_0_basic_covered_by_other_meth/env/setups/setup_features/feature_iii.py new file mode 100644 index 00000000..53bffb63 --- /dev/null +++ b/tests/covered_by/test_0_basic_covered_by_other_meth/env/setups/setup_features/feature_iii.py @@ -0,0 +1,10 @@ +from ...lib.features import FeatureIII +from ...balderglob import RuntimeObserver + + +class SetupFeatureIII(FeatureIII): + + def do_something(self): + RuntimeObserver.add_entry( + __file__, SetupFeatureIII, SetupFeatureIII.do_something, "enter `SetupFeatureIII.do_something`", + category="feature") diff --git a/tests/covered_by/test_0_basic_covered_by_other_meth/env/setups/setup_features/feature_iv.py b/tests/covered_by/test_0_basic_covered_by_other_meth/env/setups/setup_features/feature_iv.py new file mode 100644 index 00000000..630d4fbf --- /dev/null +++ b/tests/covered_by/test_0_basic_covered_by_other_meth/env/setups/setup_features/feature_iv.py @@ -0,0 +1,10 @@ +from ...lib.features import FeatureIV +from ...balderglob import RuntimeObserver + + +class SetupFeatureIV(FeatureIV): + + def do_something(self): + RuntimeObserver.add_entry( + __file__, SetupFeatureIV, SetupFeatureIV.do_something, "enter `SetupFeatureIV.do_something`", + category="feature") diff --git a/tests/covered_by/test_0_basic_covered_by_other_meth/test_0_basic_covered_by_other_meth.py b/tests/covered_by/test_0_basic_covered_by_other_meth/test_0_basic_covered_by_other_meth.py new file mode 100644 index 00000000..0544ca75 --- /dev/null +++ b/tests/covered_by/test_0_basic_covered_by_other_meth/test_0_basic_covered_by_other_meth.py @@ -0,0 +1,301 @@ +from _balder.testresult import ResultState +from _balder.balder_session import BalderSession +from ...test_utilities.base_0_envtester_class import Base0EnvtesterClass + + +class Test0Envtester(Base0EnvtesterClass): + """ + This testcase executes the basic example and checks if the tree ends with the result SUCCESS + """ + + @property + def expected_data(self): + return ( + # FIXTURE-CONSTRUCTION: balderglob_fixture_session + {"file": "balderglob.py", "meth": "balderglob_fixture_session", "part": "construction"}, + [ + ( + # FIXTURE-CONSTRUCTION: SetupA.fixture_session + {"cls": "SetupA", "meth": "fixture_session", "part": "construction"}, + {"cls": "SetupFeatureI", "meth": "do_something", "category": "feature"}, + {"cls": "SetupFeatureII", "meth": "do_something", "category": "feature"}, + ), + ( + # FIXTURE-CONSTRUCTION: SetupB.fixture_session + {"cls": "SetupB", "meth": "fixture_session", "part": "construction"}, + {"cls": "SetupFeatureIII", "meth": "do_something", "category": "feature"}, + {"cls": "SetupFeatureIV", "meth": "do_something", "category": "feature"}, + ) + ], + [ + ( + # FIXTURE-CONSTRUCTION: ScenarioA.fixture_session + {"cls": "ScenarioA", "meth": "fixture_session", "part": "construction"}, + {"cls": "FeatureI", "meth": "do_something", "category": "feature"}, + {"cls": "FeatureII", "meth": "do_something", "category": "feature"}, + ), + ( + # FIXTURE-CONSTRUCTION: ScenarioB.fixture_session + {"cls": "ScenarioB", "meth": "fixture_session", "part": "construction"}, + {"cls": "FeatureIII", "meth": "do_something", "category": "feature"}, + {"cls": "FeatureIV", "meth": "do_something", "category": "feature"}, + ) + ], + [ + ( + # FIXTURE-CONSTRUCTION: balderglob_fixture_setup + {"file": "balderglob.py", "meth": "balderglob_fixture_setup", "part": "construction"}, + # FIXTURE-CONSTRUCTION: SetupA.fixture_setup + {"cls": "SetupA", "meth": "fixture_setup", "part": "construction"}, + {"cls": "SetupFeatureI", "meth": "do_something", "category": "feature"}, + {"cls": "SetupFeatureII", "meth": "do_something", "category": "feature"}, + # FIXTURE-CONSTRUCTION: ScenarioA.fixture_setup + {"cls": "ScenarioA", "meth": "fixture_setup", "part": "construction"}, + {"cls": "FeatureI", "meth": "do_something", "category": "feature"}, + {"cls": "FeatureII", "meth": "do_something", "category": "feature"}, + # FIXTURE-CONSTRUCTION: balderglob_fixture_scenario + {"file": "balderglob.py", "meth": "balderglob_fixture_scenario", "part": "construction"}, + # FIXTURE-CONSTRUCTION: SetupA.fixture_scenario + {"cls": "SetupA", "meth": "fixture_scenario", "part": "construction"}, + {"cls": "SetupFeatureI", "meth": "do_something", "category": "feature"}, + {"cls": "SetupFeatureII", "meth": "do_something", "category": "feature"}, + # FIXTURE-CONSTRUCTION: ScenarioA.fixture_scenario + {"cls": "ScenarioA", "meth": "fixture_scenario", "part": "construction"}, + {"cls": "FeatureI", "meth": "do_something", "category": "feature"}, + {"cls": "FeatureII", "meth": "do_something", "category": "feature"}, + # FIXTURE-CONSTRUCTION: balderglob_fixture_variation + {"file": "balderglob.py", "meth": "balderglob_fixture_variation", "part": "construction"}, + # FIXTURE-CONSTRUCTION: SetupA.fixture_variation + {"cls": "SetupA", "meth": "fixture_variation", "part": "construction"}, + {"cls": "SetupFeatureI", "meth": "do_something", "category": "feature"}, + {"cls": "SetupFeatureII", "meth": "do_something", "category": "feature"}, + # FIXTURE-CONSTRUCTION: ScenarioA.fixture_variation + {"cls": "ScenarioA", "meth": "fixture_variation", "part": "construction"}, + {"cls": "SetupFeatureI", "meth": "do_something", "category": "feature"}, + {"cls": "SetupFeatureII", "meth": "do_something", "category": "feature"}, + # FIXTURE-CONSTRUCTION: balderglob_fixture_testcase + {"file": "balderglob.py", "meth": "balderglob_fixture_testcase", "part": "construction"}, + # FIXTURE-CONSTRUCTION: SetupA.fixture_testcase + {"cls": "SetupA", "meth": "fixture_testcase", "part": "construction"}, + {"cls": "SetupFeatureI", "meth": "do_something", "category": "feature"}, + {"cls": "SetupFeatureII", "meth": "do_something", "category": "feature"}, + # FIXTURE-CONSTRUCTION: ScenarioA.fixture_testcase + {"cls": "ScenarioA", "meth": "fixture_testcase", "part": "construction"}, + {"cls": "SetupFeatureI", "meth": "do_something", "category": "feature"}, + {"cls": "SetupFeatureII", "meth": "do_something", "category": "feature"}, + # TESTCASE: ScenarioA.test_a_1 + {"cls": "ScenarioA", "meth": "test_a_1"}, + {"cls": "SetupFeatureI", "meth": "do_something", "category": "feature"}, + {"cls": "SetupFeatureII", "meth": "do_something", "category": "feature"}, + # FIXTURE-TEARDOWN: ScenarioA.fixture_testcase + {"cls": "ScenarioA", "meth": "fixture_testcase", "part": "teardown"}, + {"cls": "SetupFeatureI", "meth": "do_something", "category": "feature"}, + {"cls": "SetupFeatureII", "meth": "do_something", "category": "feature"}, + # FIXTURE-TEARDOWN: SetupA.fixture_testcase + {"cls": "SetupA", "meth": "fixture_testcase", "part": "teardown"}, + {"cls": "SetupFeatureI", "meth": "do_something", "category": "feature"}, + {"cls": "SetupFeatureII", "meth": "do_something", "category": "feature"}, + # FIXTURE-TEARDOWN: balderglob_fixture_testcase + {"file": "balderglob.py", "meth": "balderglob_fixture_testcase", "part": "teardown"}, + # FIXTURE-TEARDOWN: ScenarioA.fixture_variation + {"cls": "ScenarioA", "meth": "fixture_variation", "part": "teardown"}, + {"cls": "SetupFeatureI", "meth": "do_something", "category": "feature"}, + {"cls": "SetupFeatureII", "meth": "do_something", "category": "feature"}, + # FIXTURE-TEARDOWN: SetupA.fixture_variation + {"cls": "SetupA", "meth": "fixture_variation", "part": "teardown"}, + {"cls": "SetupFeatureI", "meth": "do_something", "category": "feature"}, + {"cls": "SetupFeatureII", "meth": "do_something", "category": "feature"}, + # FIXTURE-TEARDOWN: balderglob_fixture_variation + {"file": "balderglob.py", "meth": "balderglob_fixture_variation", "part": "teardown"}, + # FIXTURE-TEARDOWN: ScenarioA.fixture_scenario + {"cls": "ScenarioA", "meth": "fixture_scenario", "part": "teardown"}, + {"cls": "FeatureI", "meth": "do_something", "category": "feature"}, + {"cls": "FeatureII", "meth": "do_something", "category": "feature"}, + # FIXTURE-TEARDOWN: SetupA.fixture_scenario + {"cls": "SetupA", "meth": "fixture_scenario", "part": "teardown"}, + {"cls": "SetupFeatureI", "meth": "do_something", "category": "feature"}, + {"cls": "SetupFeatureII", "meth": "do_something", "category": "feature"}, + # FIXTURE-TEARDOWN: balderglob_fixture_scenario + {"file": "balderglob.py", "meth": "balderglob_fixture_scenario", "part": "teardown"}, + # FIXTURE-TEARDOWN: ScenarioA.fixture_setup + {"cls": "ScenarioA", "meth": "fixture_setup", "part": "teardown"}, + {"cls": "FeatureI", "meth": "do_something", "category": "feature"}, + {"cls": "FeatureII", "meth": "do_something", "category": "feature"}, + # FIXTURE-TEARDOWN: SetupA.fixture_setup + {"cls": "SetupA", "meth": "fixture_setup", "part": "teardown"}, + {"cls": "SetupFeatureI", "meth": "do_something", "category": "feature"}, + {"cls": "SetupFeatureII", "meth": "do_something", "category": "feature"}, + # FIXTURE-TEARDOWN: balderglob_fixture_setup + {"file": "balderglob.py", "meth": "balderglob_fixture_setup", "part": "teardown"}, + ), + ( + # FIXTURE-CONSTRUCTION: balderglob_fixture_setup + {"file": "balderglob.py", "meth": "balderglob_fixture_setup", "part": "construction"}, + # FIXTURE-CONSTRUCTION: SetupB.fixture_setup + {"cls": "SetupB", "meth": "fixture_setup", "part": "construction"}, + {"cls": "SetupFeatureIII", "meth": "do_something", "category": "feature"}, + {"cls": "SetupFeatureIV", "meth": "do_something", "category": "feature"}, + # FIXTURE-CONSTRUCTION: ScenarioB.fixture_setup + {"cls": "ScenarioB", "meth": "fixture_setup", "part": "construction"}, + {"cls": "FeatureIII", "meth": "do_something", "category": "feature"}, + {"cls": "FeatureIV", "meth": "do_something", "category": "feature"}, + # FIXTURE-CONSTRUCTION: balderglob_fixture_scenario + {"file": "balderglob.py", "meth": "balderglob_fixture_scenario", "part": "construction"}, + # FIXTURE-CONSTRUCTION: SetupB.fixture_scenario + {"cls": "SetupB", "meth": "fixture_scenario", "part": "construction"}, + {"cls": "SetupFeatureIII", "meth": "do_something", "category": "feature"}, + {"cls": "SetupFeatureIV", "meth": "do_something", "category": "feature"}, + # FIXTURE-CONSTRUCTION: ScenarioB.fixture_scenario + {"cls": "ScenarioB", "meth": "fixture_scenario", "part": "construction"}, + {"cls": "FeatureIII", "meth": "do_something", "category": "feature"}, + {"cls": "FeatureIV", "meth": "do_something", "category": "feature"}, + # FIXTURE-CONSTRUCTION: balderglob_fixture_variation + {"file": "balderglob.py", "meth": "balderglob_fixture_variation", "part": "construction"}, + # FIXTURE-CONSTRUCTION: SetupB.fixture_variation + {"cls": "SetupB", "meth": "fixture_variation", "part": "construction"}, + {"cls": "SetupFeatureIII", "meth": "do_something", "category": "feature"}, + {"cls": "SetupFeatureIV", "meth": "do_something", "category": "feature"}, + # FIXTURE-CONSTRUCTION: ScenarioB.fixture_variation + {"cls": "ScenarioB", "meth": "fixture_variation", "part": "construction"}, + {"cls": "SetupFeatureIII", "meth": "do_something", "category": "feature"}, + {"cls": "SetupFeatureIV", "meth": "do_something", "category": "feature"}, + # FIXTURE-CONSTRUCTION: balderglob_fixture_testcase + {"file": "balderglob.py", "meth": "balderglob_fixture_testcase", "part": "construction"}, + # FIXTURE-CONSTRUCTION: SetupB.fixture_testcase + {"cls": "SetupB", "meth": "fixture_testcase", "part": "construction"}, + {"cls": "SetupFeatureIII", "meth": "do_something", "category": "feature"}, + {"cls": "SetupFeatureIV", "meth": "do_something", "category": "feature"}, + # FIXTURE-CONSTRUCTION: ScenarioB.fixture_testcase + {"cls": "ScenarioB", "meth": "fixture_testcase", "part": "construction"}, + {"cls": "SetupFeatureIII", "meth": "do_something", "category": "feature"}, + {"cls": "SetupFeatureIV", "meth": "do_something", "category": "feature"}, + # TESTCASE: ScenarioA.test_b_2 + {"cls": "ScenarioB", "meth": "test_b_2"}, + {"cls": "SetupFeatureIII", "meth": "do_something", "category": "feature"}, + {"cls": "SetupFeatureIV", "meth": "do_something", "category": "feature"}, + # FIXTURE-TEARDOWN: ScenarioB.fixture_testcase + {"cls": "ScenarioB", "meth": "fixture_testcase", "part": "teardown"}, + {"cls": "SetupFeatureIII", "meth": "do_something", "category": "feature"}, + {"cls": "SetupFeatureIV", "meth": "do_something", "category": "feature"}, + # FIXTURE-TEARDOWN: SetupB.fixture_testcase + {"cls": "SetupB", "meth": "fixture_testcase", "part": "teardown"}, + {"cls": "SetupFeatureIII", "meth": "do_something", "category": "feature"}, + {"cls": "SetupFeatureIV", "meth": "do_something", "category": "feature"}, + # FIXTURE-TEARDOWN: balderglob_fixture_testcase + {"file": "balderglob.py", "meth": "balderglob_fixture_testcase", "part": "teardown"}, + # FIXTURE-TEARDOWN: ScenarioB.fixture_variation + {"cls": "ScenarioB", "meth": "fixture_variation", "part": "teardown"}, + {"cls": "SetupFeatureIII", "meth": "do_something", "category": "feature"}, + {"cls": "SetupFeatureIV", "meth": "do_something", "category": "feature"}, + # FIXTURE-TEARDOWN: SetupB.fixture_variation + {"cls": "SetupB", "meth": "fixture_variation", "part": "teardown"}, + {"cls": "SetupFeatureIII", "meth": "do_something", "category": "feature"}, + {"cls": "SetupFeatureIV", "meth": "do_something", "category": "feature"}, + # FIXTURE-TEARDOWN: balderglob_fixture_variation + {"file": "balderglob.py", "meth": "balderglob_fixture_variation", "part": "teardown"}, + # FIXTURE-TEARDOWN: ScenarioB.fixture_scenario + {"cls": "ScenarioB", "meth": "fixture_scenario", "part": "teardown"}, + {"cls": "FeatureIII", "meth": "do_something", "category": "feature"}, + {"cls": "FeatureIV", "meth": "do_something", "category": "feature"}, + # FIXTURE-TEARDOWN: SetupB.fixture_scenario + {"cls": "SetupB", "meth": "fixture_scenario", "part": "teardown"}, + {"cls": "SetupFeatureIII", "meth": "do_something", "category": "feature"}, + {"cls": "SetupFeatureIV", "meth": "do_something", "category": "feature"}, + # FIXTURE-TEARDOWN: balderglob_fixture_scenario + {"file": "balderglob.py", "meth": "balderglob_fixture_scenario", "part": "teardown"}, + # FIXTURE-TEARDOWN: ScenarioB.fixture_setup + {"cls": "ScenarioB", "meth": "fixture_setup", "part": "teardown"}, + {"cls": "FeatureIII", "meth": "do_something", "category": "feature"}, + {"cls": "FeatureIV", "meth": "do_something", "category": "feature"}, + # FIXTURE-TEARDOWN: SetupB.fixture_setup + {"cls": "SetupB", "meth": "fixture_setup", "part": "teardown"}, + {"cls": "SetupFeatureIII", "meth": "do_something", "category": "feature"}, + {"cls": "SetupFeatureIV", "meth": "do_something", "category": "feature"}, + # FIXTURE-TEARDOWN: balderglob_fixture_setup + {"file": "balderglob.py", "meth": "balderglob_fixture_setup", "part": "teardown"}, + ), + ], + [ + ( + # FIXTURE-TEARDOWN: ScenarioA.fixture_session + {"cls": "ScenarioA", "meth": "fixture_session", "part": "teardown"}, + {"cls": "FeatureI", "meth": "do_something", "category": "feature"}, + {"cls": "FeatureII", "meth": "do_something", "category": "feature"}, + ), + ( + # FIXTURE-TEARDOWN: ScenarioB.fixture_session + {"cls": "ScenarioB", "meth": "fixture_session", "part": "teardown"}, + {"cls": "FeatureIII", "meth": "do_something", "category": "feature"}, + {"cls": "FeatureIV", "meth": "do_something", "category": "feature"}, + ) + ], + [ + ( + # FIXTURE-TEARDOWN: SetupA.fixture_session + {"cls": "SetupA", "meth": "fixture_session", "part": "teardown"}, + {"cls": "SetupFeatureI", "meth": "do_something", "category": "feature"}, + {"cls": "SetupFeatureII", "meth": "do_something", "category": "feature"}, + ), + ( + # FIXTURE-TEARDOWN: SetupA.fixture_session + {"cls": "SetupB", "meth": "fixture_session", "part": "teardown"}, + {"cls": "SetupFeatureIII", "meth": "do_something", "category": "feature"}, + {"cls": "SetupFeatureIV", "meth": "do_something", "category": "feature"}, + ) + ], + # FIXTURE-TEARDOWN: balderglob_fixture_session + {"file": "balderglob.py", "meth": "balderglob_fixture_session", "part": "teardown"}, + ) + + @staticmethod + def validate_finished_session(session: BalderSession): + # check result states everywhere (have to be SUCCESS everywhere + assert session.executor_tree.executor_result == ResultState.SUCCESS, \ + "test session does not terminates with success" + + assert session.executor_tree.construct_result.result == ResultState.SUCCESS, \ + "global executor tree construct part does not set ResultState.SUCCESS" + assert session.executor_tree.body_result.result == ResultState.SUCCESS, \ + "global executor tree body part does not set ResultState.SUCCESS" + assert session.executor_tree.teardown_result.result == ResultState.SUCCESS, \ + "global executor tree teardown part does not set ResultState.SUCCESS" + for cur_setup_executor in session.executor_tree.get_setup_executors(): + assert cur_setup_executor.executor_result == ResultState.SUCCESS, \ + "the setup executor does not have result SUCCESS" + + assert cur_setup_executor.construct_result.result == ResultState.SUCCESS + assert cur_setup_executor.body_result.result == ResultState.SUCCESS + assert cur_setup_executor.teardown_result.result == ResultState.SUCCESS + + for cur_scenario_executor in cur_setup_executor.get_scenario_executors(): + assert cur_scenario_executor.executor_result == ResultState.SUCCESS, \ + "the scenario executor does not have result SUCCESS" + + assert cur_scenario_executor.construct_result.result == ResultState.SUCCESS + assert cur_scenario_executor.body_result.result == ResultState.SUCCESS + assert cur_scenario_executor.teardown_result.result == ResultState.SUCCESS + + for cur_variation_executor in cur_scenario_executor.get_variation_executors(): + assert cur_variation_executor.executor_result == ResultState.SUCCESS, \ + "the variation executor does not have result SUCCESS" + + assert cur_variation_executor.construct_result.result == ResultState.SUCCESS + assert cur_variation_executor.body_result.result == ResultState.SUCCESS + assert cur_variation_executor.teardown_result.result == ResultState.SUCCESS + + for cur_testcase_executor in cur_variation_executor.get_testcase_executors(): + if cur_testcase_executor.base_testcase_callable.__name__ in ['test_a_2', 'test_b_1']: + assert cur_testcase_executor.executor_result == ResultState.COVERED_BY, \ + "the testcase executor does not have result COVERED_BY" + + assert cur_testcase_executor.construct_result.result == ResultState.NOT_RUN + assert cur_testcase_executor.body_result.result == ResultState.COVERED_BY + assert cur_testcase_executor.teardown_result.result == ResultState.NOT_RUN + else: + + assert cur_testcase_executor.executor_result == ResultState.SUCCESS, \ + "the testcase executor does not have result SUCCESS" + + assert cur_testcase_executor.construct_result.result == ResultState.SUCCESS + assert cur_testcase_executor.body_result.result == ResultState.SUCCESS + assert cur_testcase_executor.teardown_result.result == ResultState.SUCCESS diff --git a/tests/covered_by/test_1_covered_by_non_testmethod/__init__.py b/tests/covered_by/test_1_covered_by_non_testmethod/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/covered_by/test_1_covered_by_non_testmethod/env/lib/__init__.py b/tests/covered_by/test_1_covered_by_non_testmethod/env/lib/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/covered_by/test_1_covered_by_non_testmethod/env/lib/connections.py b/tests/covered_by/test_1_covered_by_non_testmethod/env/lib/connections.py new file mode 100644 index 00000000..10f95659 --- /dev/null +++ b/tests/covered_by/test_1_covered_by_non_testmethod/env/lib/connections.py @@ -0,0 +1,6 @@ +import balder + + +class MySimplySharedMemoryConnection(balder.Connection): + """This is a simply shared memory connection""" + pass diff --git a/tests/covered_by/test_1_covered_by_non_testmethod/env/lib/features.py b/tests/covered_by/test_1_covered_by_non_testmethod/env/lib/features.py new file mode 100644 index 00000000..60c58ed3 --- /dev/null +++ b/tests/covered_by/test_1_covered_by_non_testmethod/env/lib/features.py @@ -0,0 +1,31 @@ +import balder + + +class ProvidesANumberFeature(balder.Feature): + + the_number = 0 + + def set_number(self, number): + """user method that allows to set the number""" + self.the_number = number + + def get_number(self): + """returns the set number""" + raise NotImplementedError("has to be implemented in subclass") + + def sends_the_number(self): + """sends the set number""" + raise NotImplementedError("has to be implemented in subclass") + + +class AddCalculateFeature(balder.Feature): + + all_numbers = [] + + def get_numbers(self): + """this method get all the single numbers that should be calculated""" + raise NotImplementedError("has to be implemented in subclass") + + def add_numbers(self): + """this method adds all the numbers""" + raise NotImplementedError("has to be implemented in subclass") diff --git a/tests/covered_by/test_1_covered_by_non_testmethod/env/lib/utils.py b/tests/covered_by/test_1_covered_by_non_testmethod/env/lib/utils.py new file mode 100644 index 00000000..5f8968b9 --- /dev/null +++ b/tests/covered_by/test_1_covered_by_non_testmethod/env/lib/utils.py @@ -0,0 +1,6 @@ + + +class SharedObj: + """this class simulates a basic shared memory""" + + shared_mem_list = [] diff --git a/tests/covered_by/test_1_covered_by_non_testmethod/env/scenarios/__init__.py b/tests/covered_by/test_1_covered_by_non_testmethod/env/scenarios/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/covered_by/test_1_covered_by_non_testmethod/env/scenarios/scenario_adding.py b/tests/covered_by/test_1_covered_by_non_testmethod/env/scenarios/scenario_adding.py new file mode 100644 index 00000000..c082db47 --- /dev/null +++ b/tests/covered_by/test_1_covered_by_non_testmethod/env/scenarios/scenario_adding.py @@ -0,0 +1,32 @@ +import balder +from ..lib.features import AddCalculateFeature, ProvidesANumberFeature + + +class ScenarioAdding(balder.Scenario): + + class Calculator(balder.Device): + # we could add vDevices here later too -> f.e. `NumberOne="NumberOneDevice", NumberTwo="NumberTwoDevice"` + adds = AddCalculateFeature() + + @balder.connect(Calculator, over_connection=balder.Connection) + class NumberOneDevice(balder.Device): + number = ProvidesANumberFeature() + + @balder.connect(Calculator, over_connection=balder.Connection) + class NumberTwoDevice(balder.Device): + number = ProvidesANumberFeature() + + def normal_method(self): + pass + + @balder.covered_by('normal_method') + def test_add_two_numbers(self): + self.NumberOneDevice.number.set_number(3) + self.NumberTwoDevice.number.set_number(4) + + self.NumberOneDevice.number.sends_the_number() + self.NumberTwoDevice.number.sends_the_number() + + self.Calculator.adds.get_numbers() + result = self.Calculator.adds.add_numbers() + assert result == 7, "result is not as expected" diff --git a/tests/covered_by/test_1_covered_by_non_testmethod/env/setups/__init__.py b/tests/covered_by/test_1_covered_by_non_testmethod/env/setups/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/covered_by/test_1_covered_by_non_testmethod/env/setups/features_setup.py b/tests/covered_by/test_1_covered_by_non_testmethod/env/setups/features_setup.py new file mode 100644 index 00000000..f72a2e4d --- /dev/null +++ b/tests/covered_by/test_1_covered_by_non_testmethod/env/setups/features_setup.py @@ -0,0 +1,23 @@ +from ..lib.features import AddCalculateFeature, ProvidesANumberFeature +from ..lib.utils import SharedObj + + +class PyAddProvideANumber(ProvidesANumberFeature): + + def get_number(self): + return self.the_number + + def sends_the_number(self): + SharedObj.shared_mem_list.append(self.the_number) + + +class PyAddCalculate(AddCalculateFeature): + + def get_numbers(self): + self.all_numbers = SharedObj.shared_mem_list + + def add_numbers(self): + result = 0 + for cur_number in self.all_numbers: + result += cur_number + return result diff --git a/tests/covered_by/test_1_covered_by_non_testmethod/env/setups/setup_pythonadd.py b/tests/covered_by/test_1_covered_by_non_testmethod/env/setups/setup_pythonadd.py new file mode 100644 index 00000000..1450b41c --- /dev/null +++ b/tests/covered_by/test_1_covered_by_non_testmethod/env/setups/setup_pythonadd.py @@ -0,0 +1,22 @@ +import balder +from ..lib.connections import MySimplySharedMemoryConnection +from ..lib.utils import SharedObj +from .features_setup import PyAddCalculate, PyAddProvideANumber + + +class SetupPythonAdd(balder.Setup): + + class Calculator(balder.Device): + calc = PyAddCalculate() + + @balder.connect(Calculator, over_connection=MySimplySharedMemoryConnection) + class NumberProvider1(balder.Device): + n = PyAddProvideANumber() + + @balder.connect(Calculator, over_connection=MySimplySharedMemoryConnection) + class NumberProvider2(balder.Device): + n = PyAddProvideANumber() + + @balder.fixture(level="testcase") + def cleanup_memory(self): + SharedObj.shared_mem_list = [] diff --git a/tests/covered_by/test_1_covered_by_non_testmethod/test_1_covered_by_non_testmethod.py b/tests/covered_by/test_1_covered_by_non_testmethod/test_1_covered_by_non_testmethod.py new file mode 100644 index 00000000..da125177 --- /dev/null +++ b/tests/covered_by/test_1_covered_by_non_testmethod/test_1_covered_by_non_testmethod.py @@ -0,0 +1,27 @@ +from multiprocessing import Process + +from _balder.balder_session import BalderSession + + +def test_1_covered_by_non_testmethod(balder_working_dir): + """ + This testcase executes the basic example but uses a `covered_by` decorator with a non-testmethod. + """ + proc = Process(target=processed, args=(balder_working_dir, )) + proc.start() + proc.join() + assert proc.exitcode == 0, "the process terminates with an error" + + +def processed(env_dir): + print("\n", flush=True) + session = BalderSession(cmd_args=[], working_dir=env_dir) + try: + session.run() + print("\n") + assert False, "test session terminates without an error" + except TypeError as exc: + assert exc.args[0] == ("the given element for `item` must be a test method of a scenario class (has to start " + "with `test_`)") + + assert session.executor_tree is None, "test session does not terminates before collector work was done" diff --git a/tests/covered_by/test_1_illegal_non_testmethod_decorated/__init__.py b/tests/covered_by/test_1_illegal_non_testmethod_decorated/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/covered_by/test_1_illegal_non_testmethod_decorated/env/lib/__init__.py b/tests/covered_by/test_1_illegal_non_testmethod_decorated/env/lib/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/covered_by/test_1_illegal_non_testmethod_decorated/env/lib/connections.py b/tests/covered_by/test_1_illegal_non_testmethod_decorated/env/lib/connections.py new file mode 100644 index 00000000..10f95659 --- /dev/null +++ b/tests/covered_by/test_1_illegal_non_testmethod_decorated/env/lib/connections.py @@ -0,0 +1,6 @@ +import balder + + +class MySimplySharedMemoryConnection(balder.Connection): + """This is a simply shared memory connection""" + pass diff --git a/tests/covered_by/test_1_illegal_non_testmethod_decorated/env/lib/features.py b/tests/covered_by/test_1_illegal_non_testmethod_decorated/env/lib/features.py new file mode 100644 index 00000000..60c58ed3 --- /dev/null +++ b/tests/covered_by/test_1_illegal_non_testmethod_decorated/env/lib/features.py @@ -0,0 +1,31 @@ +import balder + + +class ProvidesANumberFeature(balder.Feature): + + the_number = 0 + + def set_number(self, number): + """user method that allows to set the number""" + self.the_number = number + + def get_number(self): + """returns the set number""" + raise NotImplementedError("has to be implemented in subclass") + + def sends_the_number(self): + """sends the set number""" + raise NotImplementedError("has to be implemented in subclass") + + +class AddCalculateFeature(balder.Feature): + + all_numbers = [] + + def get_numbers(self): + """this method get all the single numbers that should be calculated""" + raise NotImplementedError("has to be implemented in subclass") + + def add_numbers(self): + """this method adds all the numbers""" + raise NotImplementedError("has to be implemented in subclass") diff --git a/tests/covered_by/test_1_illegal_non_testmethod_decorated/env/lib/utils.py b/tests/covered_by/test_1_illegal_non_testmethod_decorated/env/lib/utils.py new file mode 100644 index 00000000..5f8968b9 --- /dev/null +++ b/tests/covered_by/test_1_illegal_non_testmethod_decorated/env/lib/utils.py @@ -0,0 +1,6 @@ + + +class SharedObj: + """this class simulates a basic shared memory""" + + shared_mem_list = [] diff --git a/tests/covered_by/test_1_illegal_non_testmethod_decorated/env/scenarios/__init__.py b/tests/covered_by/test_1_illegal_non_testmethod_decorated/env/scenarios/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/covered_by/test_1_illegal_non_testmethod_decorated/env/scenarios/scenario_adding.py b/tests/covered_by/test_1_illegal_non_testmethod_decorated/env/scenarios/scenario_adding.py new file mode 100644 index 00000000..d7f54fea --- /dev/null +++ b/tests/covered_by/test_1_illegal_non_testmethod_decorated/env/scenarios/scenario_adding.py @@ -0,0 +1,32 @@ +import balder +from ..lib.features import AddCalculateFeature, ProvidesANumberFeature + + +class ScenarioAdding(balder.Scenario): + + class Calculator(balder.Device): + # we could add vDevices here later too -> f.e. `NumberOne="NumberOneDevice", NumberTwo="NumberTwoDevice"` + adds = AddCalculateFeature() + + @balder.connect(Calculator, over_connection=balder.Connection) + class NumberOneDevice(balder.Device): + number = ProvidesANumberFeature() + + @balder.connect(Calculator, over_connection=balder.Connection) + class NumberTwoDevice(balder.Device): + number = ProvidesANumberFeature() + + def test_add_two_numbers(self): + self.NumberOneDevice.number.set_number(3) + self.NumberTwoDevice.number.set_number(4) + + self.NumberOneDevice.number.sends_the_number() + self.NumberTwoDevice.number.sends_the_number() + + self.Calculator.adds.get_numbers() + result = self.Calculator.adds.add_numbers() + assert result == 7, "result is not as expected" + + @balder.covered_by('test_add_two_numbers') + def normal_method(self): + pass diff --git a/tests/covered_by/test_1_illegal_non_testmethod_decorated/env/setups/__init__.py b/tests/covered_by/test_1_illegal_non_testmethod_decorated/env/setups/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/covered_by/test_1_illegal_non_testmethod_decorated/env/setups/features_setup.py b/tests/covered_by/test_1_illegal_non_testmethod_decorated/env/setups/features_setup.py new file mode 100644 index 00000000..f72a2e4d --- /dev/null +++ b/tests/covered_by/test_1_illegal_non_testmethod_decorated/env/setups/features_setup.py @@ -0,0 +1,23 @@ +from ..lib.features import AddCalculateFeature, ProvidesANumberFeature +from ..lib.utils import SharedObj + + +class PyAddProvideANumber(ProvidesANumberFeature): + + def get_number(self): + return self.the_number + + def sends_the_number(self): + SharedObj.shared_mem_list.append(self.the_number) + + +class PyAddCalculate(AddCalculateFeature): + + def get_numbers(self): + self.all_numbers = SharedObj.shared_mem_list + + def add_numbers(self): + result = 0 + for cur_number in self.all_numbers: + result += cur_number + return result diff --git a/tests/covered_by/test_1_illegal_non_testmethod_decorated/env/setups/setup_pythonadd.py b/tests/covered_by/test_1_illegal_non_testmethod_decorated/env/setups/setup_pythonadd.py new file mode 100644 index 00000000..1450b41c --- /dev/null +++ b/tests/covered_by/test_1_illegal_non_testmethod_decorated/env/setups/setup_pythonadd.py @@ -0,0 +1,22 @@ +import balder +from ..lib.connections import MySimplySharedMemoryConnection +from ..lib.utils import SharedObj +from .features_setup import PyAddCalculate, PyAddProvideANumber + + +class SetupPythonAdd(balder.Setup): + + class Calculator(balder.Device): + calc = PyAddCalculate() + + @balder.connect(Calculator, over_connection=MySimplySharedMemoryConnection) + class NumberProvider1(balder.Device): + n = PyAddProvideANumber() + + @balder.connect(Calculator, over_connection=MySimplySharedMemoryConnection) + class NumberProvider2(balder.Device): + n = PyAddProvideANumber() + + @balder.fixture(level="testcase") + def cleanup_memory(self): + SharedObj.shared_mem_list = [] diff --git a/tests/covered_by/test_1_illegal_non_testmethod_decorated/test_1_illegal_non_testmethod_decorated.py b/tests/covered_by/test_1_illegal_non_testmethod_decorated/test_1_illegal_non_testmethod_decorated.py new file mode 100644 index 00000000..4b227b22 --- /dev/null +++ b/tests/covered_by/test_1_illegal_non_testmethod_decorated/test_1_illegal_non_testmethod_decorated.py @@ -0,0 +1,38 @@ +from multiprocessing import Process + +from _balder.testresult import ResultState +from _balder.balder_session import BalderSession + + +def test_1_illegal_non_testmethod_decorated(balder_working_dir): + """ + This testcase executes the basic example but decorated a non-testmethod with the `covered_by` decorator. The test + expects an error message. + """ + proc = Process(target=processed, args=(balder_working_dir, )) + proc.start() + proc.join() + assert proc.exitcode == 0, "the process terminates with an error" + + +def check_error_of_exc(exc: BaseException): + assert isinstance(exc, TypeError) + assert exc.args[0] == ("the use of the `@covered_by` decorator is only allowed for test methods of " + "`Scenario` objects - the method `ScenarioAdding.normal_method` does not " + "start with `test_` and is not a valid test method") + +def processed(env_dir): + print("\n", flush=True) + session = BalderSession(cmd_args=[], working_dir=env_dir) + try: + session.run() + print("\n") + assert False, "test session terminates without an error" + except RuntimeError as exc: + assert exc.args[0] == ("Error calling __set_name__ on 'CoveredByDecorator' instance 'normal_method' in " + "'ScenarioAdding'") + check_error_of_exc(exc.__cause__) + except TypeError as exc: + check_error_of_exc(exc) + + assert session.executor_tree is None, "test session does not terminates before collector work was done" diff --git a/tests/covered_by/test_1_unsupported_class_decorator/__init__.py b/tests/covered_by/test_1_unsupported_class_decorator/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/covered_by/test_1_unsupported_class_decorator/env/lib/__init__.py b/tests/covered_by/test_1_unsupported_class_decorator/env/lib/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/covered_by/test_1_unsupported_class_decorator/env/lib/connections.py b/tests/covered_by/test_1_unsupported_class_decorator/env/lib/connections.py new file mode 100644 index 00000000..10f95659 --- /dev/null +++ b/tests/covered_by/test_1_unsupported_class_decorator/env/lib/connections.py @@ -0,0 +1,6 @@ +import balder + + +class MySimplySharedMemoryConnection(balder.Connection): + """This is a simply shared memory connection""" + pass diff --git a/tests/covered_by/test_1_unsupported_class_decorator/env/lib/features.py b/tests/covered_by/test_1_unsupported_class_decorator/env/lib/features.py new file mode 100644 index 00000000..60c58ed3 --- /dev/null +++ b/tests/covered_by/test_1_unsupported_class_decorator/env/lib/features.py @@ -0,0 +1,31 @@ +import balder + + +class ProvidesANumberFeature(balder.Feature): + + the_number = 0 + + def set_number(self, number): + """user method that allows to set the number""" + self.the_number = number + + def get_number(self): + """returns the set number""" + raise NotImplementedError("has to be implemented in subclass") + + def sends_the_number(self): + """sends the set number""" + raise NotImplementedError("has to be implemented in subclass") + + +class AddCalculateFeature(balder.Feature): + + all_numbers = [] + + def get_numbers(self): + """this method get all the single numbers that should be calculated""" + raise NotImplementedError("has to be implemented in subclass") + + def add_numbers(self): + """this method adds all the numbers""" + raise NotImplementedError("has to be implemented in subclass") diff --git a/tests/covered_by/test_1_unsupported_class_decorator/env/lib/utils.py b/tests/covered_by/test_1_unsupported_class_decorator/env/lib/utils.py new file mode 100644 index 00000000..5f8968b9 --- /dev/null +++ b/tests/covered_by/test_1_unsupported_class_decorator/env/lib/utils.py @@ -0,0 +1,6 @@ + + +class SharedObj: + """this class simulates a basic shared memory""" + + shared_mem_list = [] diff --git a/tests/covered_by/test_1_unsupported_class_decorator/env/scenarios/__init__.py b/tests/covered_by/test_1_unsupported_class_decorator/env/scenarios/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/covered_by/test_1_unsupported_class_decorator/env/scenarios/scenario_adding.py b/tests/covered_by/test_1_unsupported_class_decorator/env/scenarios/scenario_adding.py new file mode 100644 index 00000000..be6654f9 --- /dev/null +++ b/tests/covered_by/test_1_unsupported_class_decorator/env/scenarios/scenario_adding.py @@ -0,0 +1,28 @@ +import balder +from ..lib.features import AddCalculateFeature, ProvidesANumberFeature + + +class ScenarioAdding(balder.Scenario): + + class Calculator(balder.Device): + # we could add vDevices here later too -> f.e. `NumberOne="NumberOneDevice", NumberTwo="NumberTwoDevice"` + adds = AddCalculateFeature() + + @balder.connect(Calculator, over_connection=balder.Connection) + class NumberOneDevice(balder.Device): + number = ProvidesANumberFeature() + + @balder.connect(Calculator, over_connection=balder.Connection) + class NumberTwoDevice(balder.Device): + number = ProvidesANumberFeature() + + def test_add_two_numbers(self): + self.NumberOneDevice.number.set_number(3) + self.NumberTwoDevice.number.set_number(4) + + self.NumberOneDevice.number.sends_the_number() + self.NumberTwoDevice.number.sends_the_number() + + self.Calculator.adds.get_numbers() + result = self.Calculator.adds.add_numbers() + assert result == 7, "result is not as expected" diff --git a/tests/covered_by/test_1_unsupported_class_decorator/env/scenarios/scenario_adding2.py b/tests/covered_by/test_1_unsupported_class_decorator/env/scenarios/scenario_adding2.py new file mode 100644 index 00000000..d2eaa907 --- /dev/null +++ b/tests/covered_by/test_1_unsupported_class_decorator/env/scenarios/scenario_adding2.py @@ -0,0 +1,30 @@ +import balder +from .scenario_adding import ScenarioAdding +from ..lib.features import AddCalculateFeature, ProvidesANumberFeature + + +@balder.covered_by(ScenarioAdding.test_add_two_numbers) # currently unsupported +class ScenarioAdding2(balder.Scenario): + + class Calculator(balder.Device): + # we could add vDevices here later too -> f.e. `NumberOne="NumberOneDevice", NumberTwo="NumberTwoDevice"` + adds = AddCalculateFeature() + + @balder.connect(Calculator, over_connection=balder.Connection) + class NumberOneDevice(balder.Device): + number = ProvidesANumberFeature() + + @balder.connect(Calculator, over_connection=balder.Connection) + class NumberTwoDevice(balder.Device): + number = ProvidesANumberFeature() + + def test_add_two_numbers(self): + self.NumberOneDevice.number.set_number(3) + self.NumberTwoDevice.number.set_number(4) + + self.NumberOneDevice.number.sends_the_number() + self.NumberTwoDevice.number.sends_the_number() + + self.Calculator.adds.get_numbers() + result = self.Calculator.adds.add_numbers() + assert result == 7, "result is not as expected" diff --git a/tests/covered_by/test_1_unsupported_class_decorator/env/setups/__init__.py b/tests/covered_by/test_1_unsupported_class_decorator/env/setups/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/covered_by/test_1_unsupported_class_decorator/env/setups/features_setup.py b/tests/covered_by/test_1_unsupported_class_decorator/env/setups/features_setup.py new file mode 100644 index 00000000..f72a2e4d --- /dev/null +++ b/tests/covered_by/test_1_unsupported_class_decorator/env/setups/features_setup.py @@ -0,0 +1,23 @@ +from ..lib.features import AddCalculateFeature, ProvidesANumberFeature +from ..lib.utils import SharedObj + + +class PyAddProvideANumber(ProvidesANumberFeature): + + def get_number(self): + return self.the_number + + def sends_the_number(self): + SharedObj.shared_mem_list.append(self.the_number) + + +class PyAddCalculate(AddCalculateFeature): + + def get_numbers(self): + self.all_numbers = SharedObj.shared_mem_list + + def add_numbers(self): + result = 0 + for cur_number in self.all_numbers: + result += cur_number + return result diff --git a/tests/covered_by/test_1_unsupported_class_decorator/env/setups/setup_pythonadd.py b/tests/covered_by/test_1_unsupported_class_decorator/env/setups/setup_pythonadd.py new file mode 100644 index 00000000..1450b41c --- /dev/null +++ b/tests/covered_by/test_1_unsupported_class_decorator/env/setups/setup_pythonadd.py @@ -0,0 +1,22 @@ +import balder +from ..lib.connections import MySimplySharedMemoryConnection +from ..lib.utils import SharedObj +from .features_setup import PyAddCalculate, PyAddProvideANumber + + +class SetupPythonAdd(balder.Setup): + + class Calculator(balder.Device): + calc = PyAddCalculate() + + @balder.connect(Calculator, over_connection=MySimplySharedMemoryConnection) + class NumberProvider1(balder.Device): + n = PyAddProvideANumber() + + @balder.connect(Calculator, over_connection=MySimplySharedMemoryConnection) + class NumberProvider2(balder.Device): + n = PyAddProvideANumber() + + @balder.fixture(level="testcase") + def cleanup_memory(self): + SharedObj.shared_mem_list = [] diff --git a/tests/covered_by/test_1_unsupported_class_decorator/test_1_unsupported_class_decorator.py b/tests/covered_by/test_1_unsupported_class_decorator/test_1_unsupported_class_decorator.py new file mode 100644 index 00000000..3211a82d --- /dev/null +++ b/tests/covered_by/test_1_unsupported_class_decorator/test_1_unsupported_class_decorator.py @@ -0,0 +1,28 @@ +from multiprocessing import Process + +from _balder.testresult import ResultState +from _balder.balder_session import BalderSession + + +def test_1_calculator(balder_working_dir): + """ + This testcase executes the basic example by using the `covered_by()` decorator on scenario class. This is currently + not supported, the test checks that an expected error message is thrown. + """ + proc = Process(target=processed, args=(balder_working_dir, )) + proc.start() + proc.join() + assert proc.exitcode == 0, "the process terminates with an error" + + +def processed(env_dir): + print("\n", flush=True) + session = BalderSession(cmd_args=[], working_dir=env_dir) + try: + session.run() + print("\n") + assert False, "test session terminates without an error" + except NotImplementedError as exc: + assert exc.args[0] == "The covered-by decoration of other scenario classes is not supported yet" + + assert session.executor_tree is None, "test session does not terminates before collector work was done" diff --git a/tests/covered_by/test_1_unsupported_decorated_with_class/__init__.py b/tests/covered_by/test_1_unsupported_decorated_with_class/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/covered_by/test_1_unsupported_decorated_with_class/env/lib/__init__.py b/tests/covered_by/test_1_unsupported_decorated_with_class/env/lib/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/covered_by/test_1_unsupported_decorated_with_class/env/lib/connections.py b/tests/covered_by/test_1_unsupported_decorated_with_class/env/lib/connections.py new file mode 100644 index 00000000..10f95659 --- /dev/null +++ b/tests/covered_by/test_1_unsupported_decorated_with_class/env/lib/connections.py @@ -0,0 +1,6 @@ +import balder + + +class MySimplySharedMemoryConnection(balder.Connection): + """This is a simply shared memory connection""" + pass diff --git a/tests/covered_by/test_1_unsupported_decorated_with_class/env/lib/features.py b/tests/covered_by/test_1_unsupported_decorated_with_class/env/lib/features.py new file mode 100644 index 00000000..60c58ed3 --- /dev/null +++ b/tests/covered_by/test_1_unsupported_decorated_with_class/env/lib/features.py @@ -0,0 +1,31 @@ +import balder + + +class ProvidesANumberFeature(balder.Feature): + + the_number = 0 + + def set_number(self, number): + """user method that allows to set the number""" + self.the_number = number + + def get_number(self): + """returns the set number""" + raise NotImplementedError("has to be implemented in subclass") + + def sends_the_number(self): + """sends the set number""" + raise NotImplementedError("has to be implemented in subclass") + + +class AddCalculateFeature(balder.Feature): + + all_numbers = [] + + def get_numbers(self): + """this method get all the single numbers that should be calculated""" + raise NotImplementedError("has to be implemented in subclass") + + def add_numbers(self): + """this method adds all the numbers""" + raise NotImplementedError("has to be implemented in subclass") diff --git a/tests/covered_by/test_1_unsupported_decorated_with_class/env/lib/utils.py b/tests/covered_by/test_1_unsupported_decorated_with_class/env/lib/utils.py new file mode 100644 index 00000000..5f8968b9 --- /dev/null +++ b/tests/covered_by/test_1_unsupported_decorated_with_class/env/lib/utils.py @@ -0,0 +1,6 @@ + + +class SharedObj: + """this class simulates a basic shared memory""" + + shared_mem_list = [] diff --git a/tests/covered_by/test_1_unsupported_decorated_with_class/env/scenarios/__init__.py b/tests/covered_by/test_1_unsupported_decorated_with_class/env/scenarios/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/covered_by/test_1_unsupported_decorated_with_class/env/scenarios/scenario_adding.py b/tests/covered_by/test_1_unsupported_decorated_with_class/env/scenarios/scenario_adding.py new file mode 100644 index 00000000..be6654f9 --- /dev/null +++ b/tests/covered_by/test_1_unsupported_decorated_with_class/env/scenarios/scenario_adding.py @@ -0,0 +1,28 @@ +import balder +from ..lib.features import AddCalculateFeature, ProvidesANumberFeature + + +class ScenarioAdding(balder.Scenario): + + class Calculator(balder.Device): + # we could add vDevices here later too -> f.e. `NumberOne="NumberOneDevice", NumberTwo="NumberTwoDevice"` + adds = AddCalculateFeature() + + @balder.connect(Calculator, over_connection=balder.Connection) + class NumberOneDevice(balder.Device): + number = ProvidesANumberFeature() + + @balder.connect(Calculator, over_connection=balder.Connection) + class NumberTwoDevice(balder.Device): + number = ProvidesANumberFeature() + + def test_add_two_numbers(self): + self.NumberOneDevice.number.set_number(3) + self.NumberTwoDevice.number.set_number(4) + + self.NumberOneDevice.number.sends_the_number() + self.NumberTwoDevice.number.sends_the_number() + + self.Calculator.adds.get_numbers() + result = self.Calculator.adds.add_numbers() + assert result == 7, "result is not as expected" diff --git a/tests/covered_by/test_1_unsupported_decorated_with_class/env/scenarios/scenario_adding2.py b/tests/covered_by/test_1_unsupported_decorated_with_class/env/scenarios/scenario_adding2.py new file mode 100644 index 00000000..0582aec1 --- /dev/null +++ b/tests/covered_by/test_1_unsupported_decorated_with_class/env/scenarios/scenario_adding2.py @@ -0,0 +1,30 @@ +import balder +from .scenario_adding import ScenarioAdding +from ..lib.features import AddCalculateFeature, ProvidesANumberFeature + + +class ScenarioAdding2(balder.Scenario): + + class Calculator(balder.Device): + # we could add vDevices here later too -> f.e. `NumberOne="NumberOneDevice", NumberTwo="NumberTwoDevice"` + adds = AddCalculateFeature() + + @balder.connect(Calculator, over_connection=balder.Connection) + class NumberOneDevice(balder.Device): + number = ProvidesANumberFeature() + + @balder.connect(Calculator, over_connection=balder.Connection) + class NumberTwoDevice(balder.Device): + number = ProvidesANumberFeature() + + @balder.covered_by(ScenarioAdding) # currently unsupported + def test_add_two_numbers(self): + self.NumberOneDevice.number.set_number(3) + self.NumberTwoDevice.number.set_number(4) + + self.NumberOneDevice.number.sends_the_number() + self.NumberTwoDevice.number.sends_the_number() + + self.Calculator.adds.get_numbers() + result = self.Calculator.adds.add_numbers() + assert result == 7, "result is not as expected" diff --git a/tests/covered_by/test_1_unsupported_decorated_with_class/env/setups/__init__.py b/tests/covered_by/test_1_unsupported_decorated_with_class/env/setups/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/covered_by/test_1_unsupported_decorated_with_class/env/setups/features_setup.py b/tests/covered_by/test_1_unsupported_decorated_with_class/env/setups/features_setup.py new file mode 100644 index 00000000..f72a2e4d --- /dev/null +++ b/tests/covered_by/test_1_unsupported_decorated_with_class/env/setups/features_setup.py @@ -0,0 +1,23 @@ +from ..lib.features import AddCalculateFeature, ProvidesANumberFeature +from ..lib.utils import SharedObj + + +class PyAddProvideANumber(ProvidesANumberFeature): + + def get_number(self): + return self.the_number + + def sends_the_number(self): + SharedObj.shared_mem_list.append(self.the_number) + + +class PyAddCalculate(AddCalculateFeature): + + def get_numbers(self): + self.all_numbers = SharedObj.shared_mem_list + + def add_numbers(self): + result = 0 + for cur_number in self.all_numbers: + result += cur_number + return result diff --git a/tests/covered_by/test_1_unsupported_decorated_with_class/env/setups/setup_pythonadd.py b/tests/covered_by/test_1_unsupported_decorated_with_class/env/setups/setup_pythonadd.py new file mode 100644 index 00000000..1450b41c --- /dev/null +++ b/tests/covered_by/test_1_unsupported_decorated_with_class/env/setups/setup_pythonadd.py @@ -0,0 +1,22 @@ +import balder +from ..lib.connections import MySimplySharedMemoryConnection +from ..lib.utils import SharedObj +from .features_setup import PyAddCalculate, PyAddProvideANumber + + +class SetupPythonAdd(balder.Setup): + + class Calculator(balder.Device): + calc = PyAddCalculate() + + @balder.connect(Calculator, over_connection=MySimplySharedMemoryConnection) + class NumberProvider1(balder.Device): + n = PyAddProvideANumber() + + @balder.connect(Calculator, over_connection=MySimplySharedMemoryConnection) + class NumberProvider2(balder.Device): + n = PyAddProvideANumber() + + @balder.fixture(level="testcase") + def cleanup_memory(self): + SharedObj.shared_mem_list = [] diff --git a/tests/covered_by/test_1_unsupported_decorated_with_class/test_1_unsupported_decorated_with_class.py b/tests/covered_by/test_1_unsupported_decorated_with_class/test_1_unsupported_decorated_with_class.py new file mode 100644 index 00000000..d19ef1fb --- /dev/null +++ b/tests/covered_by/test_1_unsupported_decorated_with_class/test_1_unsupported_decorated_with_class.py @@ -0,0 +1,28 @@ +from multiprocessing import Process + +from _balder.testresult import ResultState +from _balder.balder_session import BalderSession + + +def test_1_calculator(balder_working_dir): + """ + This testcase executes the basic example by using the `covered_by()` decorator with a scenario class. This is + currently not supported, the test checks that an expected error message is thrown. + """ + proc = Process(target=processed, args=(balder_working_dir, )) + proc.start() + proc.join() + assert proc.exitcode == 0, "the process terminates with an error" + + +def processed(env_dir): + print("\n", flush=True) + session = BalderSession(cmd_args=[], working_dir=env_dir) + try: + session.run() + print("\n") + assert False, "test session terminates without an error" + except NotImplementedError as exc: + assert exc.args[0] == "The covered-by other scenario classes is not supported yet" + + assert session.executor_tree is None, "test session does not terminates before collector work was done"