class Meta: _ALLOWED_FILTERS = { GuiWidgetType.ELEMENT: {'pos'}, GuiWidgetType.MULTI_ELEMENT: {'pos'}, GuiWidgetType.DROPDOWN: {'pos'}, GuiWidgetType.RADIO_GROUP: set(), } _POS = {"first", "last", "random", "odd", "even"} @track("trace") def __init__(self, mdict=None): from arjuna import log_debug log_debug("Input Meta Dict for Meta creation: {}".format( repr_dict(mdict))) temp_dict = not mdict and CIStringDict() or CIStringDict(mdict) self.__mdict = CIStringDict() self.__process_type(temp_dict) self.__process_relations(temp_dict) self.__process_filters(temp_dict) self.__process_settings(temp_dict) self.__mdict.update({ k: v for k, v in temp_dict.items() if k.lower() not in {"type", "relations", "settings", "filters"} }) print(self.__mdict) log_debug("Meta dictionary is: {}".format(repr_dict(self.__mdict))) def __process_type(self, temp_dict): from arjuna import log_debug from arjuna.core.constant import GuiWidgetType if "type" in temp_dict: log_debug("Copying provided type from meta dict: {}".format( temp_dict["type"])) try: widget_type = temp_dict["type"] if not isinstance(widget_type, GuiWidgetType): self.__mdict["type"] = GuiWidgetType[widget_type.upper()] else: self.__mdict["type"] = temp_dict["type"] except Exception as e: raise Exception( "{} is not a valid Gui widget type.".format(widget_type)) else: self.__mdict["type"] = GuiWidgetType.ELEMENT def __process_relations(self, temp_dict): from arjuna import log_debug self.__mdict["relations"] = CIStringDict() to_remove = list() for k, v in temp_dict.items(): if k.lower() in {'above', 'below', 'left_of', 'right_of', 'near'}: to_remove.append(k) for k in to_remove: del temp_dict[k] if "relations" in temp_dict: self.__mdict["relations"].update(temp_dict["relations"]) del temp_dict["relations"] from arjuna.tpi.guiauto.base.single_widget import SingleGuiWidget from arjuna.tpi.error import GuiWidgetDefinitionError for k, v in self.__mdict["relations"].items(): self.__set_relation(k, v) def __process_filters(self, temp_dict): from arjuna import log_debug self.__mdict["filters"] = CIStringDict() to_remove = list() for k, v in temp_dict.items(): if k.lower() in {'pos'}: self.__set_filter(k, v) to_remove.append(k) for k in to_remove: del temp_dict[k] if "filters" in temp_dict: self.__mdict["filters"].update(temp_dict["filters"]) del temp_dict["filters"] def __process_settings(self, temp_dict): self.__mdict["settings"] = InteractionConfig( temp_dict) # Interconfig keys are removed def update_settings(self, source_wmd): self.settings.update(source_wmd.meta.settings) def items(self): return self.__mdict.items() def has(self, name): return name.lower() in self.__mdict @track("trace") def __getattr__(self, name): return self[name] def __getitem__(self, name): from arjuna import log_debug if self.has(name): return self.__mdict[name] else: log_debug(f"Meta dict does not have {name}. Returning None.") return None def __set_relation(self, name, value): from arjuna.tpi.guiauto.base.single_widget import SingleGuiWidget if isinstance(value, SingleGuiWidget): value = value.dispatcher.driver_element self.__mdict["relations"][name] = value def __format_pos(self, pos): from arjuna.tpi.helper.extract import pos as pos_factory from arjuna.tpi.helper.extract import Extractor if self.__mdict["type"] == GuiWidgetType.RADIO_GROUP: raise Exception( ">>pos<< filter is not supported for Gui Widget Type RADIO_GROUP." ) if type(pos) is str: fpos = pos.lower().strip() if fpos in self._POS: return pos_factory._create_extractor(fpos) else: raise Exception( "The only string liternals support for defining position are first/last/random" ) elif type(pos) in {list, tuple}: return pos_factory.at(*[int(str(i).strip()) for i in pos]) elif type(pos) is dict: if len(pos) > 1: raise Exception( "Extractor specification dictionary can take only one root key. Found entry: {}" .format(pos)) extractor_name = list(pos.keys())[0].lower().strip() extractor_args = list(pos.values())[0] if type(extractor_args) in {list, tuple}: return pos_factory._create_extractor(extractor_name, *extractor_args) elif type(extractor_args) is dict: return pos_factory._create_extractor(extractor_name, **extractor_args) else: return pos_factory._create_extractor(extractor_name, extractor_args) elif isinstance(pos, Extractor): return pos else: try: return pos_factory.at(int(str(pos).lower().strip())) except: raise Exception( "Value of pos is not of any allowed type. It can be an int, a list of ints, an extractor specification dictionary or an Extractor object." ) def __set_filter(self, name, value): allowed_filters = self._ALLOWED_FILTERS[self.__mdict['type']] if name not in allowed_filters: raise Exception( "{} is not allowed filter meta data for GuiWidget of type: {}. Allowed: {}" .format(k, self.__mdict['type'], allowed_filters)) if name == "pos": self.__mdict["filters"][name] = self.__format_pos(value) else: self.__mdict["filters"][name] = value def __setitem__(self, name, value): if InteractionConfig.is_a_setting(name): self["settings"][name] = value elif name.lower() in {"above", "below", "near", "right_of", "left_of"}: self.__set_relation(name, value) elif name.lower() in {"pos", "slice"}: self.__set_filter(name, value) else: self.__mdict[name] = value def __str__(self): return repr_dict(self.__mdict)
class Options(metaclass=abc.ABCMeta): def __init__(self, *, options_dict, creation_context, validate=True): self.__options = CIStringDict() self.__validate = validate if options_dict: self.update_all(options_dict) def update(self, option_name, option_value): option_name = self._process_option_name(option_name) try: is_not_set = False try: is_not_set = option_value.lower() == "not_set" except: pass finally: option_key = self.__get_option_key(option_name) if is_not_set: self.__options[option_key] = "not_set" else: validator_name, validator = self._get_validator_for( option_name) if self.__validate: self.__options[option_key] = validator(option_value) else: if validator_name.lower() not in { 'absolute_dir_path', 'absolute_file_path' }: self.__options[option_key] = validator( option_value) else: self.__options[option_key] = option_value except Exception as e: raise Exception( "Config option value <{}>(type:{}) for <{}> option did not pass the validation check: [{}]" .format(option_value, type(option_value), option_name, validator_name)) def __get_option_key(self, option_name): return isinstance(option_name, Enum) and option_name.name or option_name @abc.abstractmethod def _process_option_name(self, option_name): pass @abc.abstractmethod def _get_validator_for(self, option_name): pass def value(self, option_name): return self.__options[self.__get_option_key( self._process_option_name(option_name))] def update_all(self, options): if isinstance(options, Options): self.__options.update(options.as_dict()) else: if options: for k, v in options.items(): self.update(k, v) def as_dict(self): return self.__options def is_not_set(self, option_name): self._process_option_name(option_name) try: return self.value(option_name).upper() == "NOT_SET" except: return False
def test(f: Callable = None, *, id: str = None, resources: ListOrTuple = None, drive_with: 'DataSource' = None, exclude_if: 'Relation' = None, xfail: "boolOrXFail" = False, skip: "boolOrSkip" = False, priority: int = 5, author: str = None, idea: str = None, component: str = None, app_version: str = '0.0.0', level: str = None, reviewed: bool = False, unstable: bool = False, tags: set = set(), bugs: set = set(), envs: set = set(), **test_attrs): ''' Decorator for marking a function as a test function. Args: func: A Function with signature **f(request)**. The name request is mandatory and enforced. Keyword Arguments: id: Alnum string representing an ID which you want to associate with the test. resources: Fixtures/Resources that you want to associate this test with. Wraps pytest.mark.usefixtures. Instead of using this, you can also pass the names as direct arguments in the function signature. drive_with: Used for data driven testing. Argument can be Arjuna Data Source. Wraps **pytest.mark.parametrize**. If you use this argument, the test function signature must include a **data** argument e.g. .. code-block:: python @test(drive_with=<DS>) def check_sample(request, data): pass exclude_if: Define exclusion condition. Argument can be an Arjuna Relation. Wraps **pytest.mark.dependency**. xfail: Mark this test as a expected fail by setting to True. You can also use helper `xfail()` to create an advanced xfail construct. Wraps **pytest.mark.xfail**. skip: Mark this test as a expected skipped by setting to True. You can also use helper `skip()` to create an advanced skip construct. Wraps **pytest.mark.skip** and Wraps **pytest.mark.skipif**. priority: An integer value 1-5 depicting priority of this test, 1 being highest, 5 being lowest. author: Author of this test idea: The idea describing this test component: Primary software component that this test targets. app_version: Version of SUT that this test targets level: Level of this test. reviewed: Has this test been reviewed? unstable: Is this test unstable? tags: Set of tags for this test bugs: Set of bugs associated with this test envs: Set of Environment names on which this test is supposed to run. **test_attrs: Arbitrary name-value pairs to provide further test attributes. Note: The test function name must start with the prefix **check_** The test function must have the minimum signature as **check_<some_name>(request)** with **request** as the first argument. ''' info_dict = CIStringDict() info_dict.update({ 'id': id, 'priority': priority, 'author': author, 'idea': idea, 'component': component, 'app_version': app_version, 'level': level, 'reviewed': reviewed, 'unstable': unstable, }) info_dict.update({k.lower(): v for k, v in test_attrs.items()}) test_meta_data = { 'info': info_dict, 'tags': tags, 'bugs': bugs, 'envs': envs, } # Check if @test is provided without arguments if f is not None: return _simple_dec(f, test_meta_data) if resources: if type(resources) is str: resources = (resources) elif type(resources) is list: resources = tuple(resources) else: raise Exception( "resources value must be a string or list/tuple of strings") def format_test_func(func): __process_test_meta_data(func, test_meta_data) func = __process_func_for_xfail(func, test_meta_data, xfail) func = __process_func_for_skip(func, test_meta_data, skip) from arjuna import Arjuna Arjuna.register_test_meta_data(test_meta_data['info']['qual_name'], CIStringDict(test_meta_data)) orig_func = func if exclude_if: func = pytest.mark.dependency(name=id, depends=exclude_if())(func) else: func = pytest.mark.dependency(name=id)(func) if resources: func = pytest.mark.usefixtures(*resources)(func) if drive_with: records = drive_with.build().all_records func = pytest.mark.parametrize('data', records, ids=_repr_record)(func) my = My(test_meta_data) @functools.wraps(orig_func) def wrapper_without_data(request, *args, **kwargs): my.set_req_obj(request) _call_func(func, my, *args, **kwargs) @functools.wraps(orig_func) def wrapper_with_data(request, data, *args, **kwargs): my.set_req_obj(request) _call_func(func, my, data, *args, **kwargs) if drive_with: return wrapper_with_data else: return wrapper_without_data return format_test_func