Ejemplo n.º 1
0
 def test_shorten_string(self):
     s = (
         "Do you see any Teletubbies in here?"
         "Do you see a slender plastic tag clipped to my shirt with my name printed on it?"
     )
     assert shorten_string(None, 10) is None
     assert len(shorten_string(s, 20)) == 20
     assert len(shorten_string(s, 20, min_tail_chars=7)) == 20
     assert shorten_string(s, 20) == "Do you see any [...]"
     assert shorten_string(s, 20,
                           min_tail_chars=7) == "Do you s[...] on it?"
Ejemplo n.º 2
0
    def _format_response(cls,
                         resp,
                         short=False,
                         add_headers=False,
                         ruler=True):

        try:
            s = resp.json()
            s = pformat(s)
        except ValueError:
            s = resp.text
            lines = []
            for s in s.split("\n"):
                s = s.rstrip()
                if s:
                    lines.append(s)
            s = "\n".join(lines)
            # s = fill(s)

        res = []
        res.append("")
        res.append("--- Response status: {}, len: {:,} bytes: >>>".format(
            resp.status_code, len(resp.content)))

        for k, v in resp.headers.items():
            res.append("{}: {}".format(k, v))
        res.append("- " * 20)

        if short:
            s = shorten_string(s, 500, 100, place_holder="\n[...]\n")
        res.append(s)
        res.append("<<< ---")
        return "\n".join(res)
Ejemplo n.º 3
0
    def report_activity_error(self, sequence, activity, activity_args, exc):
        """Called session runner when activity `execute()` or assertions raise an error."""
        # self.stats.inc("errors")

        if isinstance(exc, SkippedError):
            logger.warning(yellow("Skipped {}".format(activity)))
            self.pending_activity = None
            return

        # Create a copy of the current context, so we can shorten values
        context = self.context_stack.context.copy()
        context["last_result"] = shorten_string(context.get("last_result"),
                                                500, 100)

        msg = []
        # msg.append("{} {}: {!r}:".format(self.context_stack, activity, exc))
        # msg.append("{!r}:".format(exc))
        msg.append("{!r}:".format(exc))
        if isinstance(exc, ActivityAssertionError):
            msg.append("Failed assertions:")
            for err in exc.assertion_list:
                msg.append("  - {}".format(err))
        msg.append("Execution path: {}".format(self.context_stack))
        msg.append("Activity: {}".format(activity))
        msg.append("Activity args: {}".format(activity_args))
        msg.append("Context: {}".format(context))

        msg = "\n    ".join(msg)
        logger.error(red(msg))
        self.stats.report_error(self, sequence, activity, error=msg)
        return
Ejemplo n.º 4
0
    def __init__(self, config_manager, **activity_args):
        """"""
        super().__init__(config_manager, **activity_args)

        path = activity_args.get("path")
        script = activity_args.get("script")
        # Allow to pass `export: null` to define 'no export wanted'
        # (omitting the argumet is considered 'undefined' and will emit a
        # warning if the script produces variables)
        export = activity_args.get("export", NO_DEFAULT)
        if export in (None, False):
            export = tuple()
        elif export is NO_DEFAULT:
            export = None

        check_arg(path, str, or_none=True)
        check_arg(script, str, or_none=True)
        check_arg(export, (str, list, tuple), or_none=True)

        if path:
            if script:
                raise ActivityCompileError(
                    "`path` and `script` args are mutually exclusive"
                )
            path = config_manager.resolve_path(path)
            with open(path, "rt") as f:
                script = f.read()
            #:
            self.script = compile(script, path, "exec")
        elif script:
            # script = dedent(self.script)
            self.script = compile(script, "<string>", "exec")
        else:
            raise ActivityCompileError("Either `path` or `script` expected")

        #: Store a shortened code snippet for debug output
        self.source = shorten_string(dedent(script), 500, 100)
        # print(self.source)

        if export is None:
            self.export = None
        elif isinstance(export, str):
            self.export = set((export,))
        else:
            self.export = set(export)
        return
Ejemplo n.º 5
0
    def _process_activity_result(self, activity, activity_args, result, elap):
        """Perform common checks.

        Raises:
            ActivityAssertionError
        """
        context = self.context_stack.context
        errors = []
        # warnings = []

        arg = float(activity_args.get("assert_max_time", 0))
        if arg and elap > arg:
            errors.append(
                "Execution time limit of {} seconds exceeded: {:.3} sec.".
                format(arg, elap))

        arg = activity_args.get("assert_match")
        if arg:
            text = str(result)
            # Note: use re.search (not .match)!
            if not re.search(arg, text, re.MULTILINE):
                errors.append("Result does not match `{}`: {!r}".format(
                    arg, shorten_string(text, 500, 100)))

        arg = activity_args.get("store_json")
        if arg:
            for var_name, key_path in arg.items():
                try:
                    val = get_dict_attr(result, key_path)
                    context[var_name] = val
                except Exception:
                    errors.append(
                        "store_json could not find `{}` in activity result {!r}"
                        .format(key_path, result))

        if errors:
            raise ActivityAssertionError(errors)
        return
Ejemplo n.º 6
0
 def _add_error(self, d, error):
     d.setdefault("errors", 0)
     d["errors"] += 1
     d["last_error"] = shorten_string("{}".format(error), 500, 100)
Ejemplo n.º 7
0
    def execute(self, session, **expanded_args):
        """"""
        global_vars = {
            # "foo": 41,
            # "__builtins__": {},
        }
        # local_vars = session.context
        local_vars = session.context.copy()
        assert "result" not in local_vars
        assert "session" not in local_vars
        local_vars["session"] = session.make_helper()

        # prev_local_keys = set(locals())
        prev_global_keys = set(globals())
        prev_context_keys = set(local_vars.keys())

        try:
            exec(self.script, global_vars, local_vars)
        except ConnectionError as e:
            # TODO: more requests-exceptions?
            msg = "Script failed: {!r}: {}".format(e, e)
            raise ScriptActivityError(msg)
        except Exception as e:
            msg = "Script failed: {!r}: {}".format(e, e)
            if session.verbose >= 4:
                logger.exception(msg)
                raise ScriptActivityError(msg) from e
            raise ScriptActivityError(msg)
        finally:
            local_vars.pop("session")

        result = local_vars.pop("result", None)

        context_keys = set(local_vars.keys())
        new_keys = context_keys.difference(prev_context_keys)

        if new_keys:
            if self.export is None:
                logger.info(
                    "Skript activity has no `export` defined. Ignoring new variables: '{}'".format(
                        "', '".join(new_keys)
                    )
                )
            else:
                for k in self.export:
                    v = local_vars.get(k)
                    assert type(v) in (int, float, str, list, dict)
                    session.context[k] = v
                    logger.debug("Set context.{} = {!r}".format(k, v))
                # store_keys = new_keys.intersection(self.export)

        # TODO: this cannot happen?
        new_globals = set(globals().keys()).difference(prev_global_keys)
        if new_globals:
            logger.warning("Script-defined globals: {}".format(new_globals))
            raise ScriptActivityError("Script introduced globals")

        # new_context = context_keys.difference(prev_context_keys)
        # logger.info("Script-defined context-keys: {}".format(new_context))

        # new_locals = set(locals().keys()).difference(prev_local_keys)
        # if new_locals:
        #     logger.info("Script-defined locals: {}".format(new_locals))

        # logger.info("Script locals:\n{}".format(pformat(local_vars)))
        if expanded_args.get("debug") or session.verbose >= 5:
            logger.info(
                "{} {}\n  Context after execute:\n    {}\n  return value: {!r}".format(
                    session.context_stack,
                    self,
                    pformat(session.context, indent=4),
                    result,
                )
            )
        elif session.verbose >= 3 and result is not None:
            logger.info(
                "{} returnd: {!r}".format(
                    session.context_stack,
                    shorten_string(result, 200) if isinstance(result, str) else result,
                )
            )

        return result
Ejemplo n.º 8
0
    def _write_entry(self, fp, entry):
        opts = self.opts
        activity = self.activity_map.get(entry["method"], "HTTPRequest")
        url_list = entry.get("url_list")
        is_bucket = bool(url_list) and len(url_list) > 1
        if is_bucket:
            activity = "StaticRequests"

        lines = []
        if entry.get("comment"):
            lines.append("# {}\n".format(shorten_string(entry["comment"], 75)))
        if is_bucket:
            lines.append("# Auto-collated {:,} GET requests\n".format(
                len(url_list)))
        else:
            lines.append(
                "# Response type: {!r}, size: {:,} bytes, time: {}\n".format(
                    entry.get("resp_type"),
                    entry.get("resp_size", -1),
                    format_elap(entry.get("elap"), high_prec=True),
                ))
        if entry.get("resp_comment"):
            lines.append("# {}\n".format(
                shorten_string(entry["resp_comment"], 75)))

        url = entry["url"]
        expand_url_params = False

        lines.append("- activity: {}\n".format(activity))
        if is_bucket:
            lines.append("  thread_count: {}\n".format(
                opts["collate_thread_count"]))
            lines.append("  url_list:\n")
            for url in url_list:
                lines.append("    - '{}'\n".format(url))
        else:
            if entry.get("query"):
                expand_url_params = True
                url = base_url(url)
            lines.append("  url: '{}'\n".format(url))

        if activity == "HTTPRequest":
            lines.append("  method: {}\n".format(entry["method"]))

        # TODO:
        # We have ?query also as part of the URL
        # if entry.get("query"):
        #     lines.append("  params: {}\n".format(entry["query"]))
        if expand_url_params:
            self._write_args(lines, "params", entry["query"])

        # TODO: set headers = {'Content-type': 'content_type_value'}
        #       if entry.dData.mimeType is defined

        data = entry.get("data")
        if data:
            if isinstance(data, (list, tuple)):
                # Must be a 'params' list of {name: ..., value: ...} objects
                self._write_args(lines, "data", data)
            else:
                assert type(data) is str
                logger.warning(
                    "Expected list, but got text: {!r}".format(data))
                lines.append("  data: {}\n".format(json.dumps(data)))

        lines.append("\n")
        fp.writelines(lines)