Ejemplo n.º 1
0
    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))
Ejemplo n.º 2
0
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
Ejemplo n.º 3
0
    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))
Ejemplo n.º 4
0
    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())
Ejemplo n.º 5
0
    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
Ejemplo n.º 6
0
Archivo: step.py Proyecto: nokia/radish
    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
Ejemplo n.º 7
0
    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))
Ejemplo n.º 8
0
    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
Ejemplo n.º 9
0
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)
Ejemplo n.º 10
0
    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()
Ejemplo n.º 11
0
    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
Ejemplo n.º 12
0
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
Ejemplo n.º 13
0
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)
Ejemplo n.º 14
0
 def raise_exc():
     raise RadishError("buuh!")