예제 #1
0
    def run_test_set(self, test_set):
        """Run all ``.webtest`` files in the given `TestSet`.
        """
        for filename in test_set.filenames:
            if WebtestRunner.verbosity != 'error':
                log("==== Executing: %s ==========" % filename)

            self._run_webtest_file(filename)

        # Sleep between scenarios
        grinder.sleep(WebtestRunner.scenario_think_time)
예제 #2
0
    def run_test_set(self, test_set):
        """Run all ``.webtest`` files in the given `TestSet`.
        """
        for filename in test_set.filenames:
            if WebtestRunner.verbosity != 'error':
                log("==== Executing: %s ==========" % filename)

            self._run_webtest_file(filename)

        # Sleep between scenarios
        grinder.sleep(WebtestRunner.scenario_think_time)
예제 #3
0
    def execute(self, test, wrapper, request):
        """Execute a Grinder `Test` instance, wrapped in ``wrapper``, that
        sends a `~webtest.parser.Request`.
        """
        if WebtestRunner.verbosity != 'error':
            log("------ Test %d: %s" % (test.getNumber(), request))

        # Evaluate any expressions in the request URL
        url = self.eval_expressions(request.url)

        # Evaluate expressions in parameters and headers, and
        # convert them to NVPairs
        parameters = self.evaluated_nvpairs(request.parameters)
        headers = self.evaluated_nvpairs(request.headers)

        # Send a POST or GET to the wrapped HTTPRequest
        if request.method == 'POST':
            # If the request has a body, use that
            if request.body:
                body = self.eval_expressions(request.body)
                response = wrapper.POST(url, body, headers)
            # Otherwise, pass the form parameters
            else:
                response = wrapper.POST(url, parameters, headers)

        elif request.method == 'GET':
            response = wrapper.GET(url, parameters, headers)

        else:
            message = "Unknown HTTP method: '%s'" % request.method
            message += " in request defined on line %d" % request.line_number
            raise BadRequestMethod(message)

        if WebtestRunner.verbosity == 'debug':
            log("------ Response from %s: ------" % request)
            self.log_response(response)

        # If request has a 'Capture' attribute, parse it
        if request.capture:
            self.eval_capture(request, response)

        return response
예제 #4
0
    def execute(self, test, wrapper, request):
        """Execute a Grinder `Test` instance, wrapped in ``wrapper``, that
        sends a `~webtest.parser.Request`.
        """
        if WebtestRunner.verbosity != 'error':
            log("------ Test %d: %s" % (test.getNumber(), request))

        # Evaluate any expressions in the request URL
        url = self.eval_expressions(request.url)

        # Evaluate expressions in parameters and headers, and
        # convert them to NVPairs
        parameters = self.evaluated_nvpairs(request.parameters)
        headers = self.evaluated_nvpairs(request.headers)

        # Send a POST or GET to the wrapped HTTPRequest
        if request.method == 'POST':
            # If the request has a body, use that
            if request.body:
                body = self.eval_expressions(request.body)
                response = wrapper.POST(url, body, headers)
            # Otherwise, pass the form parameters
            else:
                response = wrapper.POST(url, parameters, headers)

        elif request.method == 'GET':
            response = wrapper.GET(url, parameters, headers)

        else:
            message = "Unknown HTTP method: '%s'" % request.method
            message += " in request defined on line %d" % request.line_number
            raise BadRequestMethod(message)

        if WebtestRunner.verbosity == 'debug':
            log("------ Response from %s: ------" % request)
            self.log_response(response)

        # If request has a 'Capture' attribute, parse it
        if request.capture:
            self.eval_capture(request, response)

        return response
예제 #5
0
    def log_response(self, response):
        """Output full response information to the log file.
        """
        log("HEADERS:")
        for name in response.listHeaders():
            value = response.getHeader(name)
            log("  %s: %s" % (name, value))

        log("BODY:")
        body = response.getText()
        # Prettify xml
        if body.startswith('<?xml'):
            # This helps with making the XML more readable, but gets UTF-8
            # errors from time to time. If they occur, ignore them and just
            # skip prettification
            import xml.dom.minidom
            try:
                body = body.replace('\n', '').replace('\r', '').replace('\t', '')
                body = xml.dom.minidom.parseString(body).toprettyxml()
            except:
                pass
        log(body)
예제 #6
0
    def log_response(self, response):
        """Output full response information to the log file.
        """
        log("HEADERS:")
        for name in response.listHeaders():
            value = response.getHeader(name)
            log("  %s: %s" % (name, value))

        log("BODY:")
        body = response.getText()
        # Prettify xml
        if body.startswith('<?xml'):
            # This helps with making the XML more readable, but gets UTF-8
            # errors from time to time. If they occur, ignore them and just
            # skip prettification
            import xml.dom.minidom
            try:
                body = body.replace('\n', '').replace('\r',
                                                      '').replace('\t', '')
                body = xml.dom.minidom.parseString(body).toprettyxml()
            except:
                pass
        log(body)
예제 #7
0
    def eval_capture(self, request, response):
        """Evaluate any ``Capture`` expressions in the given request, and set
        variables to matching text in the given response. Return the number of
        capture expressions that were successfully evaluated.

        In order for this to work, you should include a ``Capture`` element
        inside the ``Request`` element whose response you want to capture. Each
        expression inside ``Capture`` should follow this format::

            {VAR_NAME = regexp}

        Where ``regexp`` is a regular expression with parentheses around the
        part you want to capture (leave out the parentheses to capture the
        entire match). For example, if your response contains::

            ... <a href="http://python.org"> ...

        And you want to store the URL into a variable called ``HREF``, do::

            {HREF = <a href="([^"]+)">}

        If any capture expression is not found in the response, a `CaptureFailed`
        is raised. This makes them useful for verification too--if you want to
        ensure that a response contains expected text, just include a capture
        expression that looks for it. In this case, you can leave out the
        parentheses, since you don't need to capture any particular part, and
        if you don't need to keep the value for later, you can just assign it
        to a dummy variable.

        For example, to verify that the response includes a form with a
        "submit" button::

            {VERIFY = <form.*>.*<input type="submit".*>.*</form>}

        You can include multiple ``{VAR_NAME = regexp}`` expressions in the same
        ``Capture`` element, to capture several variables from the same response,
        or to do several verifications.

        Since regular expressions often contain characters that are precious to
        XML, such as ``< > &`` and so on, you can enclose your capture expressions
        in a ``CDATA`` block to prevent them from being interpreted as XML::

            <Request ...>
                ...
                <Capture>
                    <![CDATA[
                        {VAR_A = <a href="([^"]+)">}
                        {VAR_B = <b>([^<]+)</b>)}
                    ]]>
                </Capture>
            </Request>

        You can include ``{VAR_NAME}`` references in the right-hand side of the
        expression; for example, if you have previously assigned a value to
        ``ORDER_NUMBER``, and you want to capture the contents of a ``div``
        having that order number as its ``id``, you could do::

            {ORDER_DIV = <div id="{ORDER_NUMBER}">(.*)</div>}

        If ``ORDER_NUMBER`` was previously set to ``12345``, then the above
        will be expanded to::

            {ORDER_DIV = <div id="12345">(.*)</div>}

        before matching in the response body.
        """
        # Number of successful captures
        captured = 0

        # If capture expression is empty, there's nothing to do
        if not request.captures():
            return captured

        # Import re here to avoid threading problems
        # See: http://osdir.com/ml/java.grinder.user/2003-07/msg00030.html
        import re

        # Match a {VAR_NAME = <regular expression>}
        re_capture = re.compile('^{([_A-Z0-9]+) ?= ?(.+)}$')

        # Get response body
        body = str(response.getText())
        # Evaluate each {...} expression found in the list of request.captures()
        for expression in request.captures():
            # Error if this expression doesn't look like a capture
            match = re_capture.search(expression)
            if not match:
                message = "Syntax error in capture expression '%s'" % expression
                message += " in request defined on line %d" % request.line_number
                raise SyntaxError(message)

            # Get the two parts of the capture expression
            name, value = match.groups()
            # Expand any {VAR} expressions before evaluating the regexp
            regexp = self.eval_expressions(value)

            if WebtestRunner.verbosity in ('debug', 'info'):
                log("Looking in response for match to regexp: %s" % regexp)

            # Error if the regexp doesn't match part of the response body
            match = re.search(regexp, body)
            if not match:
                log("!!!!!! No match for %s" % regexp)
                log("!!!!!! In request defined on line %d" % request.line_number)
                log("!!!!!! Response body:")
                log(body)
                raise CaptureFailed("No match for %s" % regexp)

            # Set the given variable name to the first parenthesized expression
            if match.groups():
                value = match.group(1)
            # or the entire match if there was no parenthesized expression
            else:
                value = match.group(0)
            captured += 1
            if WebtestRunner.verbosity in ('debug', 'info'):
                log("Captured %s = %s" % (name, value))
            self.variables[name] = value

        return captured
예제 #8
0
    def eval_expressions(self, value):
        """Parse the given string for variables or macros, and do any necessary
        variable assignment. Return the string with all expressions expanded.

        Allowed expressions:

            ``{MY_VAR}``
                Expand to the current value of ``MY_VAR``
            ``{MY_VAR = literal}``
                Assign ``MY_VAR`` a literal value, and expand to that value
            ``{macro_name(args)}``
                Expand to the result of ``macro_name(args)``
            ``{MY_VAR = macro_name(args)}``
                Assign ``MY_VAR`` the result of calling ``macro_name(args)``,
                and also expand to the resulting value. See the `webtest.macro`
                module for more information.

        Any given value that does not match any of these forms is simply
        returned as-is. If you need to use literal ``{`` or ``}`` characters in
        a string, precede them with a backslash, like ``\\{`` or ``\\}``.

        The given value may contain multiple ``{...}`` expressions, and may have
        additional text before or after any expression. For example, if you
        have previously assigned two variables ``FOO`` and ``BAR``:

            >>> eval_expressions('{FOO = Hello}')
            'Hello'
            >>> eval_expressions('{BAR = world}')
            'world'

        you can combine them in an expression like this:

            >>> eval_expressions('{FOO} {BAR}!')
            'Hello world!'

        The only caveat is that a ``{...}`` expression may not contain another
        ``{...}`` expression inside it.
        """
        # Regular expressions to match in eval_expressions
        # Import re here to avoid threading problems
        # See: http://osdir.com/ml/java.grinder.user/2003-07/msg00030.html
        import re
        # Match the first {...} expression
        re_expansion = re.compile(r'((?:[^{\\]|\\.)*){((?:[^}\\]|\\.)*)}(.*)')
        # VAR_NAME=macro(args)
        re_var_macro = re.compile('([_A-Z0-0-99]+) ?= ?([_a-z]+)\(([^)]*)\)')
        # VAR_NAME=literal
        re_var_literal = re.compile('([_A-Z0-9]+) ?= ?([^}]+)')
        # macro(args)
        re_macro = re.compile('([_a-z]+)\(([^)]*)\)')
        # VAR_NAME
        re_var = re.compile('^([_A-Z0-9]+)$')

        macro = WebtestRunner.macro_class()

        # Match and replace until no {...} expressions are found
        to_expand = re_expansion.match(value)
        while to_expand:
            before, expression, after = to_expand.groups()

            # VAR_NAME=macro(args)
            if re_var_macro.match(expression):
                name, macro_name, args = re_var_macro.match(expression).groups()
                expanded = self.variables[name] = macro.invoke(macro_name, args)

            # VAR_NAME=literal
            elif re_var_literal.match(expression):
                name, literal = re_var_literal.match(expression).groups()
                self.variables[name] = literal
                expanded = literal

            # macro(args)
            elif re_macro.match(expression):
                macro_name, args = re_macro.match(expression).groups()
                expanded = macro.invoke(macro_name, args)

            # VAR_NAME
            elif re_var.match(expression):
                name = re_var.match(expression).groups()[0]
                if name in self.variables:
                    expanded = self.variables[name]
                else:
                    raise NameError("Variable '%s' is not initialized!" % name)

            # Invalid expression
            else:
                raise SyntaxError(
                  "Syntax error '%s' in value '%s'" % (expression, value))

            if WebtestRunner.verbosity in ('debug', 'info'):
                log("%s => '%s'" % (expression, expanded))

            # Assemble the expanded value and check for another match
            value = before + expanded + after
            to_expand = re_expansion.match(value)

        return value
예제 #9
0
    def eval_capture(self, request, response):
        """Evaluate any ``Capture`` expressions in the given request, and set
        variables to matching text in the given response. Return the number of
        capture expressions that were successfully evaluated.

        In order for this to work, you should include a ``Capture`` element
        inside the ``Request`` element whose response you want to capture. Each
        expression inside ``Capture`` should follow this format::

            {VAR_NAME = regexp}

        Where ``regexp`` is a regular expression with parentheses around the
        part you want to capture (leave out the parentheses to capture the
        entire match). For example, if your response contains::

            ... <a href="http://python.org"> ...

        And you want to store the URL into a variable called ``HREF``, do::

            {HREF = <a href="([^"]+)">}

        If any capture expression is not found in the response, a `CaptureFailed`
        is raised. This makes them useful for verification too--if you want to
        ensure that a response contains expected text, just include a capture
        expression that looks for it. In this case, you can leave out the
        parentheses, since you don't need to capture any particular part, and
        if you don't need to keep the value for later, you can just assign it
        to a dummy variable.

        For example, to verify that the response includes a form with a
        "submit" button::

            {VERIFY = <form.*>.*<input type="submit".*>.*</form>}

        You can include multiple ``{VAR_NAME = regexp}`` expressions in the same
        ``Capture`` element, to capture several variables from the same response,
        or to do several verifications.

        Since regular expressions often contain characters that are precious to
        XML, such as ``< > &`` and so on, you can enclose your capture expressions
        in a ``CDATA`` block to prevent them from being interpreted as XML::

            <Request ...>
                ...
                <Capture>
                    <![CDATA[
                        {VAR_A = <a href="([^"]+)">}
                        {VAR_B = <b>([^<]+)</b>)}
                    ]]>
                </Capture>
            </Request>

        You can include ``{VAR_NAME}`` references in the right-hand side of the
        expression; for example, if you have previously assigned a value to
        ``ORDER_NUMBER``, and you want to capture the contents of a ``div``
        having that order number as its ``id``, you could do::

            {ORDER_DIV = <div id="{ORDER_NUMBER}">(.*)</div>}

        If ``ORDER_NUMBER`` was previously set to ``12345``, then the above
        will be expanded to::

            {ORDER_DIV = <div id="12345">(.*)</div>}

        before matching in the response body.
        """
        # Number of successful captures
        captured = 0

        # If capture expression is empty, there's nothing to do
        if not request.captures():
            return captured

        # Import re here to avoid threading problems
        # See: http://osdir.com/ml/java.grinder.user/2003-07/msg00030.html
        import re

        # Match a {VAR_NAME = <regular expression>}
        re_capture = re.compile('^{([_A-Z0-9]+) ?= ?(.+)}$')

        # Get response body
        body = str(response.getText())
        # Evaluate each {...} expression found in the list of request.captures()
        for expression in request.captures():
            # Error if this expression doesn't look like a capture
            match = re_capture.search(expression)
            if not match:
                message = "Syntax error in capture expression '%s'" % expression
                message += " in request defined on line %d" % request.line_number
                raise SyntaxError(message)

            # Get the two parts of the capture expression
            name, value = match.groups()
            # Expand any {VAR} expressions before evaluating the regexp
            regexp = self.eval_expressions(value)

            if WebtestRunner.verbosity in ('debug', 'info'):
                log("Looking in response for match to regexp: %s" % regexp)

            # Error if the regexp doesn't match part of the response body
            match = re.search(regexp, body)
            if not match:
                log("!!!!!! No match for %s" % regexp)
                log("!!!!!! In request defined on line %d" %
                    request.line_number)
                log("!!!!!! Response body:")
                log(body)
                raise CaptureFailed("No match for %s" % regexp)

            # Set the given variable name to the first parenthesized expression
            if match.groups():
                value = match.group(1)
            # or the entire match if there was no parenthesized expression
            else:
                value = match.group(0)
            captured += 1
            if WebtestRunner.verbosity in ('debug', 'info'):
                log("Captured %s = %s" % (name, value))
            self.variables[name] = value

        return captured
예제 #10
0
    def eval_expressions(self, value):
        """Parse the given string for variables or macros, and do any necessary
        variable assignment. Return the string with all expressions expanded.

        Allowed expressions:

            ``{MY_VAR}``
                Expand to the current value of ``MY_VAR``
            ``{MY_VAR = literal}``
                Assign ``MY_VAR`` a literal value, and expand to that value
            ``{macro_name(args)}``
                Expand to the result of ``macro_name(args)``
            ``{MY_VAR = macro_name(args)}``
                Assign ``MY_VAR`` the result of calling ``macro_name(args)``,
                and also expand to the resulting value. See the `webtest.macro`
                module for more information.

        Any given value that does not match any of these forms is simply
        returned as-is. If you need to use literal ``{`` or ``}`` characters in
        a string, precede them with a backslash, like ``\\{`` or ``\\}``.

        The given value may contain multiple ``{...}`` expressions, and may have
        additional text before or after any expression. For example, if you
        have previously assigned two variables ``FOO`` and ``BAR``:

            >>> eval_expressions('{FOO = Hello}')
            'Hello'
            >>> eval_expressions('{BAR = world}')
            'world'

        you can combine them in an expression like this:

            >>> eval_expressions('{FOO} {BAR}!')
            'Hello world!'

        The only caveat is that a ``{...}`` expression may not contain another
        ``{...}`` expression inside it.
        """
        # Regular expressions to match in eval_expressions
        # Import re here to avoid threading problems
        # See: http://osdir.com/ml/java.grinder.user/2003-07/msg00030.html
        import re
        # Match the first {...} expression
        re_expansion = re.compile(r'((?:[^{\\]|\\.)*){((?:[^}\\]|\\.)*)}(.*)')
        # VAR_NAME=macro(args)
        re_var_macro = re.compile('([_A-Z0-0-99]+) ?= ?([_a-z]+)\(([^)]*)\)')
        # VAR_NAME=literal
        re_var_literal = re.compile('([_A-Z0-9]+) ?= ?([^}]+)')
        # macro(args)
        re_macro = re.compile('([_a-z]+)\(([^)]*)\)')
        # VAR_NAME
        re_var = re.compile('^([_A-Z0-9]+)$')

        macro = WebtestRunner.macro_class()

        # Match and replace until no {...} expressions are found
        to_expand = re_expansion.match(value)
        while to_expand:
            before, expression, after = to_expand.groups()

            # VAR_NAME=macro(args)
            if re_var_macro.match(expression):
                name, macro_name, args = re_var_macro.match(
                    expression).groups()
                expanded = self.variables[name] = macro.invoke(
                    macro_name, args)

            # VAR_NAME=literal
            elif re_var_literal.match(expression):
                name, literal = re_var_literal.match(expression).groups()
                self.variables[name] = literal
                expanded = literal

            # macro(args)
            elif re_macro.match(expression):
                macro_name, args = re_macro.match(expression).groups()
                expanded = macro.invoke(macro_name, args)

            # VAR_NAME
            elif re_var.match(expression):
                name = re_var.match(expression).groups()[0]
                if name in self.variables:
                    expanded = self.variables[name]
                else:
                    raise NameError("Variable '%s' is not initialized!" % name)

            # Invalid expression
            else:
                raise SyntaxError("Syntax error '%s' in value '%s'" %
                                  (expression, value))

            if WebtestRunner.verbosity in ('debug', 'info'):
                log("%s => '%s'" % (expression, expanded))

            # Assemble the expanded value and check for another match
            value = before + expanded + after
            to_expand = re_expansion.match(value)

        return value