def from_file(cls, session, msg_file_name, **fargs): # Process Yaml file from arjuna import C, Yaml file_path = os.path.join(C("httpauto.message.dir"), msg_file_name + ".yaml") f = open(file_path, "r") msg_yaml = f.read() f.close() from arjuna.core.fmt import arj_format_str, arj_convert # Convert Arjuna custom objects to raw Python objects before formatting. fargs = {k: arj_convert(v) for k, v in fargs.items()} msg_yaml = arj_format_str(msg_yaml, tuple(), fargs) msg_yaml = Yaml.from_str(msg_yaml, allow_any=True) if msg_yaml is None: return cls.root(session) if "request" in msg_yaml: req_repr = HttpRequestYamlRepr( session, CIStringDict(msg_yaml["request"].as_map())) del msg_yaml["request"] else: req_repr = HttpRequestYamlRepr(session, CIStringDict()) resp_proc = HttpResponseYamlRepr(session, CIStringDict(msg_yaml.as_map())) return HttpMessage(session, req_repr, resp_proc)
def __init__(self, mdict=None): from arjuna import log_debug 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) log_debug("Meta dictionary is: {}".format(repr_dict(self.__mdict)))
def __init__(self, session, resp_yaml): self.__xproc = None self.__unexproc = None if "unexpected" in resp_yaml: self.__unexproc = HttpUnexpectedResProcessor( session, CIStringDict(resp_yaml["unexpected"])) del resp_yaml["unexpected"] self.__xproc = HttpExpectedResProcessor(session, CIStringDict(resp_yaml))
def __init__(self, action, resp_yaml): self.__xproc = None self.__unexproc = None if "unexpected" in resp_yaml: self.__unexproc = HttpUnexpectedResProcessor( action._endpoint.service, CIStringDict(resp_yaml["unexpected"])) del resp_yaml["unexpected"] self.__xproc = HttpExpectedResProcessor(action._endpoint.service, CIStringDict(resp_yaml))
def __init__(self, mdict=None): self.__mdict = not mdict and CIStringDict() or CIStringDict(mdict) from arjuna.core.constant import GuiWidgetType if "type" in self.__mdict: try: widget_type = self.__mdict["type"] if not isinstance(widget_type, GuiWidgetType): self.__mdict["type"] = GuiWidgetType[widget_type.upper()] except Exception as e: raise Exception( "{} is not a valid Gui widget type.".format(widget_type)) else: self.__mdict["type"] = GuiWidgetType.ELEMENT self.__mdict["settings"] = InteractionConfig( self.__mdict) # Interconfig keys are removed
def __init__(self, xdict={}): self.__xdict = CIStringDict() for k,v in xdict.items(): try: self.__xdict[Validator.name(k)] = {"wtype" : xdict[k]["wtype"].strip().upper(), "wvalue" : xdict[k]["wvalue"]} except Exception as e: raise Exception(f"Invalid WithX entry for name >>{k}<<.")
def __init__(self, xdict={}): def process_value(wtype, wvalue): if wtype in {'ATTR', 'FATTR', 'BATTR', 'EATTR'}: #:, 'NODE', 'BNODE', 'FNODE'}: if len(wvalue) > 1: raise Exception( "attr/fattr/battr/eattr specification should contain only a single key value pair for attribute name and value. Wrong withx definition found wtype: {} with value {}" .format(wtype, wvalue)) name = list(wvalue.keys())[0] value = list(wvalue.values())[0] return {'name': name, 'value': value} else: return wvalue self.__xdict = CIStringDict() for k, v in xdict.items(): try: wname = Validator.name(k) wtype = xdict[k]["wtype"].strip().upper() wvalue = xdict[k]["wvalue"] self.__xdict[wname] = { "wtype": wtype, "wvalue": process_value(wtype, wvalue) } except Exception as e: raise Exception(f"Invalid WithX entry for name >>{k}<<. {e}")
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"} }) log_debug("Meta dictionary is: {}".format(repr_dict(self.__mdict)))
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 _simple_dec(func, test_meta_data): __process_test_meta_data(func, test_meta_data) from arjuna import Arjuna Arjuna.register_test_meta_data(test_meta_data['info']['qual_name'], CIStringDict(test_meta_data)) func.__name__ = "check_" + func.__name__ @functools.wraps(func) def wrapper(request, *args, **kwargs): request_wrapper = My(test_meta_data) request_wrapper.set_req_obj(request) _call_func(func, request_wrapper, *args, **kwargs) return wrapper
def _load(self, **sfargs): from arjuna import C, Yaml margs = {} margs.update(self.__fargs) margs.update(sfargs) from arjuna.core.fmt import arj_convert margs = {k: arj_convert(v) for k, v in margs.items()} msg_yaml = Text(self.__msg_yaml).format(**margs) msg_yaml = Yaml.from_str(msg_yaml, allow_any=True) if msg_yaml is None: req_repr = HttpRequestYamlRepr(self._action, CIStringDict(), label="Root") resp_proc = HttpResponseYamlRepr(self._action, CIStringDict()) return self.name, req_repr, resp_proc if "label" in msg_yaml: label = msg_yaml["label"] del msg_yaml["label"] else: label = self.name if "request" in msg_yaml: req_repr = HttpRequestYamlRepr(self._action, CIStringDict( msg_yaml["request"].as_map()), label=label) del msg_yaml["request"] else: req_repr = HttpRequestYamlRepr(self._action, CIStringDict(), label=label) resp_proc = HttpResponseYamlRepr(self._action, CIStringDict(msg_yaml.as_map())) return label, req_repr, resp_proc
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 __init__(self, *, name, endpoint, **fargs): super().__init__(name=name, endpoint=endpoint) check_data_arg_type(fargs) self.__fargs = fargs # Process Yaml file from arjuna import C, Yaml # Check Built-In Security Action my_dir = os.path.dirname(os.path.realpath(__file__)) self.__action_file_path = os.path.abspath(os.path.join(my_dir, "..", "..","..", "res", "security", "http", "action", name + ".yaml")) try: f = open(self.__action_file_path, "r") except FileNotFoundError: self.__action_file_path = os.path.join(endpoint.root_dir, "action", name + ".yaml") try: f = open(self.__action_file_path, "r") except FileNotFoundError: raise SEAMfulActionFileError(self, msg=f"Action file not found at location: {self.__action_file_path}") self.__msg_yaml = f.read() f.close() self.__store = CIStringDict()
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
def __init__(self, pydict): CIStringDict.__init__(self, pydict) YamlElement.__init__(self, self.orig_dict) self.__sections = tuple(self.keys()) self.__iter = None
def __init__(self, node): self.__node = node self.__attrs = CIStringDict(self.node.attrib)
def __init__(self, *, name, pydict, file_path=None): self.__name = name self.__ydict = pydict is not None and pydict or dict() self.__sections = tuple(self.__ydict.keys()) self.__ydict = CIStringDict(self.__ydict) self.__file_path = file_path
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 __init__(self): vars(self)['_store'] = CIStringDict()
def _load(self, **sfargs): from arjuna import C, Yaml margs = {} margs.update(self.__fargs) margs.update(sfargs) from arjuna.core.fmt import arj_convert margs = {k:arj_convert(v) for k,v in margs.items()} action_yaml = Text(self.__msg_yaml).format(**margs) action_yaml = Yaml.from_str(action_yaml, allow_any=True) if action_yaml is None: return CIStringDict(), margs, list() def call_generator(in_dict): d = {} d.update(in_dict) gen = d["generator"] del d["generator"] return getattr(Random, gen)(**d) def gen_or_value(value): if isinstance(value, YamlDict): if "generator" in value: return call_generator(value) else: return value else: return value if 'data' not in self.store: self.store['data'] = CIStringDict() if "data" in action_yaml: for name, instruction in action_yaml["data"].items(): if type(instruction) is dict or isinstance(instruction, YamlDict): if "generator" in instruction: self.store['data'][name] = call_generator(instruction) else: self.store['data'][name] = instruction if "entity" in action_yaml: from arjuna.core.importer import import_arj_entity for name, instruction in action_yaml["entity"].items(): if name == "data": raise SEAMfulActionFileError(self, msg=f"Entity name can not be >>data<< as it is reserved space for action.store.data. Validate action file: {self.__action_file_path}") if type(instruction) is str: entity = instruction kwargs = {} elif type(instruction) is dict or isinstance(instruction, YamlDict): entity = instruction['type'] kwargs = {} kwargs.update(instruction.items()) del kwargs['type'] else: raise SEAMfulActionFileError(self, msg=f"Entity >>{entity}<< can not be loaded. Its value can either be the Entity Class Name or dictionary has 'type' key set to Entity Class Name and rest of them are keyword arguments. Validate action file: {self.__action_file_path}") try: entity_callable = import_arj_entity(entity) except Exception as e: raise SEAMfulActionFileError(self, msg=f"Entity >>{entity}<< can not be loaded. Error: {e}. Validate action file: {self.__action_file_path}") else: entity_kwarg_dict = {k:gen_or_value(v) for k,v in kwargs.items()} self.store[name] = entity_callable(**entity_kwarg_dict) if "alias" in action_yaml: for k, v in action_yaml["alias"].items(): if v in self.store: self.store[k] = self.store[v] elif v in self.store['data']: self.store[k] = self.store['data'][v] elif v.split(".")[0] in self.store: ename, attr = v.split(".", 1) if isinstance(self.store[ename], _DataEntity): self.store[k] = getattr(self.store[ename], attr) else: self.store[k] = v if 'data' in margs: if isinstance(margs['data'], _DataEntity): margs['data'] = margs['data'].as_dict() else: margs['data'] = dict() margs['data'].update(self.store['data']) margs.update({k:v for k,v in self.store.items() if k != "data"}) mproc = None if "messages" in action_yaml: mproc = _HttpActionSteps(self, action_yaml['messages']) del action_yaml["messages"] else: mproc = _HttpActionSteps(self) return CIStringDict(), margs, mproc
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)
def root(cls, session): req_repr = HttpRequestYamlRepr(session, CIStringDict()) resp_proc = HttpResponseYamlRepr(session, CIStringDict()) return HttpMessage(session, req_repr, resp_proc)
def __init__(self, *, options_dict, creation_context, validate=True): self.__options = CIStringDict() self.__validate = validate if options_dict: self.update_all(options_dict)
def __init__(self, path): self.__path = path self.__name = _get_file_name(path) self.__map = CIStringDict() self._populate() self.__iter = None
class Yaml: def __init__(self, *, name, pydict, file_path=None): self.__name = name self.__ydict = pydict is not None and pydict or dict() self.__sections = tuple(self.__ydict.keys()) self.__ydict = CIStringDict(self.__ydict) self.__file_path = file_path @property def name(self): return self.__name @property def file_path(self): return self.__file_path def is_empty(self): return not self.__ydict def get_section(self, name, *, strict=True): val = self.get_value(name, strict=strict) if val is not None and type(val) is not dict: raise YamlError( f"Section content must be a dictionary. Found content >>{val}<< in {name} section." ) return Yaml(name=name, pydict=val, file_path=self.file_path) def get_value(self, name, *, strict=True, as_yaml_str=False): if self.has_section(name): if as_yaml_str: return yaml.dump(self.__ydict[name]) else: return self.__ydict[name] else: if strict: raise YamlUndefinedSectionError( f"Yaml object does not have a section with the name: {name}" ) else: return None def as_map(self): return self.__ydict def has_section(self, name): return name in self.__ydict @property def section_names(self): return self.__ydict.keys() def validate_sections_present(*section_names, atleast_one=False): absent_sections = [] present_section_names = self.section_names for section_name in section_names: if section_name not in present_section_names: absent_sections.append(section_name) if len(absent_sections) == section_names or ( len(absent_sections) < len(section_names) and not atleast_one): raise YamlUndefinedSectionError( f"Yaml object does not contains mandatory sections: {absent_sections}" ) @classmethod def from_file(cls, *, file_path): yaml_name = os.path.basename(file_path).split(".yaml")[0] f = open(file_path, "r") ydict = yaml.load(f, Loader=yaml.SafeLoader) f.close() return Yaml(name=yaml_name, pydict=ydict, file_path=file_path) @classmethod def from_str(cls, *, name, contents): return Yaml(name=name, pydict=yaml.safe_load(contents)) def as_str(self): return yaml.dump(self.__ydict)
def __init__(self, session, response): super().__init__(response) self.__session = session self.__resp = response self.__store = CIStringDict()
class ContextualDataReference(metaclass=abc.ABCMeta): ''' Base class for autoloaded Contextual data references in Arjuna. Arguments: path: Path of the contextual data reference file. Note: It behaves like a Python dictionary. So, you get items by names/keys and loop over it. ''' def __init__(self, path): self.__path = path self.__name = _get_file_name(path) self.__map = CIStringDict() self._populate() self.__iter = None def __iter__(self): self.__iter = iter(self.__map) return self def __next__(self): return next(self.__iter) def __len__(self): return len(self.__map) def __getitem__(self, context): return self._record_for(context) def keys(self): ''' Names of contexts/keys in this reference. ''' return self.__map.keys() def items(self): ''' Items iterator for this reference. ''' return self.__map.items() @property def _map(self): return self.__map @property def path(self): ''' Path of this reference file. ''' return self.__path @property def name(self): ''' Name of this reference. ''' return self.__name def _update(self, data_reference): for context, record in data_reference.map.items(): if context not in self.map: self.__map[context] = CIStringDict() self.__map[context].update(record.named_values) def _update_from_dict(self, context, map): if context not in self.map: self.__map[context] = dict() self.__map[context].update(map) def _add_record(self, context, record): self.__map[context] = DataRecord(context="Ref-{}[{}]".format( self.name, context), **record) def _record_for(self, context): try: return self.__map[context] except: raise Exception("{} at {} does not contain {} context key.".format( self.__class__.__name__, self.path, context)) def __str__(self): return str({k: str(v) for k, v in self.__map.items()}) def enumerate(self): ''' Print all items in this data reference. ''' for k, v in self.__map.items(): print(k, "::", type(v), str(v)) @abc.abstractmethod def _populate(self): pass def as_json(self): from arjuna import Json, Http return Json.from_str(Http.content.json(self.named_values).content)
def _load(self, **sfargs): req_repr = HttpRequestYamlRepr(self._action, CIStringDict(), label="Root") resp_proc = HttpResponseYamlRepr(self._action, CIStringDict()) return "Root", req_repr, resp_proc
def _update(self, data_reference): for context, record in data_reference.map.items(): if context not in self.map: self.__map[context] = CIStringDict() self.__map[context].update(record.named_values)
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