def coverage_stop(self, features): """ Stop the coverage measurement and create report """ self.coverage.stop() self.coverage.combine() self.coverage.save() self.coverage.report(file=sys.stdout) if self.cover_html: self.coverage.html_report(directory=self.cover_html) if self.cover_xml: self.coverage.xml_report(outfile=self.cover_xml) if self.cover_min_percentage: report = StringIO() self.coverage.report(file=report) match = re.search(r"^TOTAL\s+(.*)$", report.getvalue(), re.MULTILINE) if not match: raise RadishError( "Failed to find total percentage in coverage report") total_percentage = int(match.groups()[0].split()[-1][:-1]) if total_percentage < int(self.cover_min_percentage): raise RadishError( "Failed to reach minimum expected coverage of {0}% (reached: {1}%)" .format(self.cover_min_percentage, total_percentage))
def parse_matcher_config(matcher_config_path): """Parse the given matcher config file The matcher config file is expected to be a properly formatted YAML file, having the following structure: .. code-block:: yaml - step: <step text> should_match | should_not_match: <step implementation func name> with_arguments: - <args...> The YAML file can contain one or more of these ``step`` blocks. """ with matcher_config_path.open("r", encoding="utf-8") as matcher_config_file: steps_matcher_config = utils.yaml_ordered_load(matcher_config_file) # the matcher config is empty - so we don't have to continue here if not steps_matcher_config: return None # validate the matcher config read from the YAML file for step_matcher_config in steps_matcher_config: # check if the config really has the `step` key if "step" not in step_matcher_config: raise RadishError("All Matcher Config must start with the 'step' key") # check if the config has either the `should_match` or `should_not_match` key if ("should_match" not in step_matcher_config) == ( "should_not_match" not in step_matcher_config ): # noqa raise RadishError( "Each Matcher Config must either contain a 'should_match' or 'should_not_match' key" ) # check if the config has any invalid keys valid_keys = {"step", "should_match", "should_not_match", "with_arguments"} if not set(step_matcher_config.keys()).issubset(valid_keys): raise RadishError( "All Matcher Config only allow the keys: " "{}. The following are not allowed: {}.".format( ", ".join(sorted(valid_keys)), ", ".join( sorted(set(step_matcher_config.keys()).difference(valid_keys)) ), ) ) return steps_matcher_config
def __validate_if_runnable(self): """Validate if this Step is able to run If it's not able to run an Exception is raised. """ if not self.step_impl or not self.step_impl_match: raise RadishError("Unable to run Step '{} {}' because it has " "no Step Implementation assigned to it".format( self.keyword, self.text)) if self.state is not State.UNTESTED: raise RadishError( "Unable to run Step '{} {}' again. A Step can only be run exactly once." .format(self.keyword, self.text))
def __init__( self, cover_packages, cover_append, cover_config_file, cover_branches, cover_erase, cover_min_percentage, cover_html, cover_xml, ): try: from coverage import Coverage # noqa except ImportError: raise RadishError( "if you want to use the code coverage extension you have to " "'pip install radish-bdd[coverage]'") self.cover_packages = cover_packages self.cover_append = cover_append self.cover_config_file = cover_config_file self.cover_branches = cover_branches self.cover_erase = cover_erase self.cover_min_percentage = cover_min_percentage self.cover_html = cover_html self.cover_xml = cover_xml before.all()(self.coverage_start) after.all()(self.coverage_stop) self.coverage = None self.modules_on_init = set(sys.modules.keys())
def skip(self): """Skip this Step This method is supposed to be called from Step Implementations. """ if self.state is not State.RUNNING: raise RadishError("Steps can only be skipped when they run") self.state = State.SKIPPED
def pending(self): """Mark this Step as pending All pending Steps will be reminded about in the Runs summary. This method is supposed to be called from Step Implementations. """ if self.state is not State.RUNNING: raise RadishError("Steps can only be marked as pending when they run") self.state = State.PENDING
def __init__(self, junit_xml_path, tag_expression, scenario_ids): try: from lxml import etree # noqa except ImportError: raise RadishError( "if you want to use the JUnit xml writer you have to " "'pip install lxml'") after.all(order=500)(functools.partial(generate_junit_xml, junit_xml_path, tag_expression, scenario_ids))
def register(self, name, func, pattern=None): """Register a new parse type""" if name in self.types: raise RadishError( "Cannot register custom parse type with name {} because it already exists" .format(name)) if pattern is not None: func.pattern = pattern self.types[name] = func
def test_runner_should_fail_step_when_it_cannot_be_matched( hook_registry, default_config, mocker): """The Runner should fail a Step when it cannot be matched with any Step Implementation""" runner = Runner(default_config, None, hook_registry) step_mock = mocker.MagicMock(name="Step") matcher_mock = mocker.patch("radish.runner.matcher") match_exc = RadishError("buuh!") matcher_mock.match_step.side_effect = match_exc # when runner.run_step(step_mock) # then step_mock.fail.assert_called_once_with(match_exc)
def behave_like(self, step_line): """Run this Step as if it would be the one given in ``step_line`` This function requires ``self._behave_like_runner`` to be set. """ if self._behave_like_runner is None: raise RadishError( "This Step is unable to use the `behave_like`-Feature because no runner is provided" ) state, step = self._behave_like_runner(step_line) if state is State.FAILED: try: raise step.failure_report.exception except RecursionError: raise StepBehaveLikeRecursionError()
def __init__(self, command_line_config): for opt_key, opt_value in command_line_config.items(): setattr(self, opt_key, opt_value) # make some configuration options easily accessible if hasattr(self, "tags") and self.tags: try: self.tag_expression = parse(self.tags) except Exception as exc: raise RadishError( "The given Tag Expression '{}' has Syntax Errors. " "Please consult https://github.com/timofurrer/tag-expressions " "for detailed information about the Tag Expression Syntax". format(self.tags)) from exc else: self.tag_expression = None
def load_modules(locations: List[Path]) -> List[Path]: """Load all Python modules in the given locations All given locations must already be expanded regarding * Environment Variables * User Home Directory """ loaded_modules = [] for location in locations: if not location.exists(): raise RadishError( "Location '{0}' to load modules does not exist".format(location) ) for module_path in location.glob("**/*.py"): load_module(module_path) loaded_modules.append(module_path) return loaded_modules
def generate_cucumber_json(cucumber_json_file, tag_expression, scenario_ids, features): """Generate a cucumber JSON report for the run""" if not features: raise RadishError("No features given to generate cucumber json file") ccjson = [] for feature in features: if not feature.has_to_run(tag_expression, scenario_ids): continue feature_description = "\n".join(feature.description) feature_tags = [] for tag in feature.tags: feature_tags.append({"name": "@" + tag.name, "line": tag.line}) feature_json = { "uri": str(feature.path), "type": "feature", "keyword": "Feature", "id": str(feature.id), "name": feature.short_description, "line": feature.line, "description": feature_description, "tags": feature_tags, "elements": [], } for rule in feature.rules: for scenario in rule.scenarios: if not scenario.has_to_run(tag_expression, scenario_ids): continue if isinstance(scenario, ScenarioOutline): scenario_type = "scenario_outline" elif isinstance(scenario, ScenarioLoop): scenario_type = "scenario_loop" else: scenario_type = "scenario" scenario_tags = [] for tag in scenario.tags: scenario_tags.append({ "name": "@" + tag.name, "line": tag.line }) scenario_json = { "keyword": "Scenario", "type": scenario_type, "id": str(scenario.id), "name": scenario.short_description, "line": scenario.line, "description": "", "steps": [], "tags": scenario_tags, } for step in scenario.steps: step_json = { "keyword": step.keyword, "name": step.text, "line": step.line, "result": { "status": step.state.name.lower(), "duration": int(step.duration().total_seconds() * 1e9), }, } if step.state is State.FAILED: step_json["result"][ "error_message"] = step.failure_report.reason if step.state is State.UNTESTED: if step.starttime is None: step_json["result"][ "status"] = State.PENDING.name.lower() else: step_json["result"][ "status"] = State.SKIPPED.name.lower() if step.embeddings: step_json["embeddings"] = step.embeddings scenario_json["steps"].append(step_json) feature_json["elements"].append(scenario_json) ccjson.append(feature_json) with open(cucumber_json_file, "w+") as f: content = json.dumps(ccjson, indent=4, sort_keys=True) f.write(content)
def raise_exc(): raise RadishError("buuh!")