Giter Club home page Giter Club logo

statuschecker's Introduction

StatusChecker

Introduction

StatusChecker is a tool for validating that executed Robot Framework test cases have expected statuses and log messages. It is mainly useful for Robot Framework test library developers who want to use Robot Framework to also test their libraries. StatusChecker 1.3 and newer are compatible both with Python 2 and Python 3.

StatusChecker project is hosted at GitHub and downloads are at PyPI.

Installation instructions

The easiest way to install StatusChecker is by using pip:

pip install robotstatuschecker

Alternatively you can get the code by cloning the project from GitHub or downloading the source distribution from PyPI and extracting it. After that you can install the tool with:

python setup.py install

Usage

From the command line:

python -m robotstatuschecker infile [outfile]

Programmatically:

python

from robotstatuschecker import process_output

process_output('infile.xml', 'outfile.xml')

If an output file is not given, the input file is edited in place.

Defining expected test status

By default, all test cases are expected to PASS and have no message. Changing the expected status to FAIL is done by having the word FAIL (in uppercase) somewhere in the test case documentation. The expected error message must then follow the FAIL marker.

For robotframework version 4 you can also change the expected status to SKIP by adding the word SKIP in the test case documentation. Like Fail, the expected skip message must follow the word SKIP. If a test documentation contains the words FAIL and SKIP, SKIP will be ignored and the expected status will be FAIL.

If a test is expected to PASS with a certain message, the word PASS must be added to its documentation explicitly and the expected message given after that.

If a message check should happen in test setup or teardown, that check must be prefixed with SETUP or TEARDOWN word.

The expected message can also be specified as a regular expression by prefixing it with REGEXP:. The specified regular expression must match the error message fully. Having spaces between the status, the message and the possible regular expression prefix is optional.

An alternative to using regular expressions is using glob patterns where * matches anything (including newline) and ? matches any single character. This is can be accomplished by starting the expected message with GLOB:.

Finally, it is possible to test that the message starts with something by prefixing the expected message with STARTS:.

The following examples illustrate different ways to define test statuses and messages:

robotframework

* Test Cases* Simple failure [Documentation] FAIL Expected error message Steps

Check in test setup is done by SETUP marker

[Documentation] LOG SETUP This first log message in test setup [Setup] Test specific setup Steps

Exclude documentation before marker

[Documentation] This text is ignored FAIL Expected error message Steps

Regexp example

[Documentation] FAIL REGEXP: (IOError|OSError): .* Steps

Glob example

[Documentation] FAIL GLOB: ??Error: * Steps

Start example

[Documentation] FAIL STARTS: IOError: Steps

Passing without message

Steps

Passing with message

[Documentation] PASS Expected message Steps

Defining expected log messages

The expected keyword log messages can also be defined in the test case documentation using a syntax such as:

LOG x.y:z LEVEL Actual message

The part before the colon specifies the keyword to check. For example, 1 means first keyword, 1.2 is the second child keyword of the first keyword, and so on.

The part after the colon species the message. For example, 1:2 means the second message of the first keyword and 1.2:3 is the third message of the second child keyword of the first keyword. The message index is optional and defaults to 1. The message index also supports wildcard *. For example 1:* matches any message of the first keyword.

Message level is specified before the actual message, and it can be any of the valid log levels in capital letters. If the level is not given it defaults to INFO. Starting from 1.4 release also ERROR level is supported. The message level also supports wildcard ANY which will match all log levels.

Possible leading and trailing whitespace is ignored both in the expected and in the actual log message.

This syntax can be used multiple times to test multiple messages. It also works together with specifying the expected error message with FAIL, but it that case FAIL and the expected error must be first.

It is also possible to give the message as a regular expression or glob pattern or to give just the start of the message. This is accomplished by prefixing the message with REGEXP:, GLOB: or STARTS:, respectively, exactly like when defining expected test status.

Finally, to check that a keyword does not have a certain message, it is possible to use NONE in the place of the message.

robotframework

* Test cases* Simple example [Documentation] LOG 1 Hello, world! Steps

Nested keywords

[Documentation] LOG 2.1 1st child of 2nd kw Steps

Message index

[Documentation] LOG 2:2 2nd msg of 2nd kw Steps

Nested and index

[Documentation] LOG 3.1:2 2nd msg of 3rd kw's 1st child Steps

Log levels

[Documentation] LOG 2 DEBUG Debug-level message ... LOG 1.2:3 WARN Warning Steps

Multiple messages

[Documentation] LOG 1 First tested message ... LOG 1.2 Second tested message ... LOG 2.2.1 DEBUG Third tested message Steps

Status and log

[Documentation] FAIL Expected error message ... LOG 1.2 Expected log message Steps

Regexp message

[Documentation] LOG 1 REGEXP: (Hello|Hi) world! Steps

Glob message

[Documentation] LOG 1 GLOB: * world! Steps

Start of the message

[Documentation] LOG 1 STARTS: Hello w Steps

No message

[Documentation] LOG 1:1 Test that we have only 1 msg ... LOG 1:2 NONE Steps

Count Messages

[Documentation] LOG 4 COUNT: 2 # Fourth keyword should have excatly 2 messages. Steps

statuschecker's People

Contributors

aaltat avatar ericbjones avatar japiiron avatar pekkaklarck avatar spooning avatar tattoo avatar yahman72 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

statuschecker's Issues

Can't install with Ubuntu - NameError: name 'sys_platform' is not defined

OS Ubuntu 14.04 x32

python setup.py install Traceback (most recent call last): File "setup.py", line 42, in <module> install_requires = ['robotframework'] File "/usr/lib/python2.7/distutils/core.py", line 111, in setup _setup_distribution = dist = klass(attrs) File "/usr/local/lib/python2.7/dist-packages/distribute-0.6.49-py2.7.egg/setuptools/dist.py", line 225, in __init__ _Distribution.__init__(self,attrs) File "/usr/lib/python2.7/distutils/dist.py", line 287, in __init__ self.finalize_options() File "/usr/local/lib/python2.7/dist-packages/distribute-0.6.49-py2.7.egg/setuptools/dist.py", line 257, in finalize_options ep.require(installer=self.fetch_build_egg) File "/usr/local/lib/python2.7/dist-packages/distribute-0.6.49-py2.7.egg/pkg_resources.py", line 2099, in require working_set.resolve(self.dist.requires(self.extras),env,installer)) File "/usr/local/lib/python2.7/dist-packages/distribute-0.6.49-py2.7.egg/pkg_resources.py", line 2309, in requires dm = self._dep_map File "/usr/local/lib/python2.7/dist-packages/distribute-0.6.49-py2.7.egg/pkg_resources.py", line 2538, in _dep_map self.__dep_map = self._compute_dependencies() File "/usr/local/lib/python2.7/dist-packages/distribute-0.6.49-py2.7.egg/pkg_resources.py", line 2571, in _compute_dependencies common = frozenset(reqs_for_extra(None)) File "/usr/local/lib/python2.7/dist-packages/distribute-0.6.49-py2.7.egg/pkg_resources.py", line 2568, in reqs_for_extra if req.marker_fn(override={'extra':extra}): File "/usr/local/lib/python2.7/dist-packages/distribute-0.6.49-py2.7.egg/_markerlib/markers.py", line 109, in marker_fn return eval(compiled_marker, environment) File "<environment marker>", line 1, in <module> NameError: name 'sys_platform' is not defined

RF 6.1 support

When test looks like this:

Test
    [Documentation]    LOG 1:1    KALA
    Log    KALA

prior RF 6.1 doc was returned like: LOG 1:1 KALA, with single pace between 1.1 and KALA. But in RF 6.1 doc is returned as is. This causes problem in validation message and being backwards compatible.

Robot logger.error(''Message") is not supported by statuschecker

If I have library which does this:

except Exception as error:
            logger.error('Received exception: %s' % error)

And when I have test that has this:

[Documentation]
    ...    LOG 3:9    ERROR Received exception: 'NoneType' object has no attribute 'quit'

Then the correct level is not matched, instead log says it defaults to INFO level:

Keyword 'SeleniumLibrary.Close All Browsers' (index 3) message 9 has wrong level. 

Expected: INFO 
Actual: ERROR

By looking the code, the bug could be in here: https://github.com/robotframework/statuschecker/blob/master/robotstatuschecker.py#L136 Would it be sufficient to add ERROR in the list and write a test for it?

"FAIL GLOB" fails if patternt contains "-"

Pattern:

FAIL GLOB:
Several failures occurred:

  1. Page should have contained element '//span[contains(text(),'test\-part\-unit (EDB)')]' but did not.

  2. Page should have contained element '//span[contains(text(),'PNC1-0000015')]' but did not.


Run:
$ python3 -m robotstatuschecker ~/nasc/exec_logs/main.xml

Result:
Traceback (most recent call last):
File "/usr/lib/python3.8/runpy.py", line 194, in _run_module_as_main
return _run_code(code, main_globals, None,
File "/usr/lib/python3.8/runpy.py", line 87, in _run_code
exec(code, run_globals)
File "/home/xander/.local/lib/python3.8/site-packages/robotstatuschecker.py", line 324, in
rc = process_output(*sys.argv[1:])
File "/home/xander/.local/lib/python3.8/site-packages/robotstatuschecker.py", line 68, in process_output
result = StatusChecker().process_output(inpath, outpath)
File "/home/xander/.local/lib/python3.8/site-packages/robotstatuschecker.py", line 77, in process_output
result.suite.visit(self)
File "/home/xander/.local/lib/python3.8/site-packages/robot/model/testsuite.py", line 226, in visit
visitor.visit_suite(self)
File "/home/xander/.local/lib/python3.8/site-packages/robot/model/visitor.py", line 87, in visit_suite
suite.suites.visit(self)
File "/home/xander/.local/lib/python3.8/site-packages/robot/model/itemlist.py", line 77, in visit
item.visit(visitor)
File "/home/xander/.local/lib/python3.8/site-packages/robot/model/testsuite.py", line 226, in visit
visitor.visit_suite(self)
File "/home/xander/.local/lib/python3.8/site-packages/robot/model/visitor.py", line 87, in visit_suite
suite.suites.visit(self)
File "/home/xander/.local/lib/python3.8/site-packages/robot/model/itemlist.py", line 77, in visit
item.visit(visitor)
File "/home/xander/.local/lib/python3.8/site-packages/robot/model/testsuite.py", line 226, in visit
visitor.visit_suite(self)
File "/home/xander/.local/lib/python3.8/site-packages/robot/model/visitor.py", line 88, in visit_suite
suite.tests.visit(self)
File "/home/xander/.local/lib/python3.8/site-packages/robot/model/itemlist.py", line 77, in visit
item.visit(visitor)
File "/home/xander/.local/lib/python3.8/site-packages/robot/model/testcase.py", line 130, in visit
visitor.visit_test(self)
File "/home/xander/.local/lib/python3.8/site-packages/robotstatuschecker.py", line 83, in visit_test
if TestStatusChecker(expected).check(test):
File "/home/xander/.local/lib/python3.8/site-packages/robotstatuschecker.py", line 206, in check
return self._check_message(test)
File "/home/xander/.local/lib/python3.8/site-packages/robotstatuschecker.py", line 214, in _check_message
if not self._message_matches(test.message, self.message):
File "/home/xander/.local/lib/python3.8/site-packages/robotstatuschecker.py", line 167, in _message_matches
matcher = Matcher(pattern, caseless=False, spaceless=False)
File "/home/xander/.local/lib/python3.8/site-packages/robot/utils/match.py", line 42, in init
self._regexp = self._compile(self._normalize(pattern), regexp=regexp)
File "/home/xander/.local/lib/python3.8/site-packages/robot/utils/match.py", line 50, in _compile
return re.compile(pattern, re.DOTALL)
File "/usr/lib/python3.8/re.py", line 252, in compile
return _compile(pattern, flags)
File "/usr/lib/python3.8/re.py", line 304, in _compile
p = sre_compile.compile(pattern, flags)
File "/usr/lib/python3.8/sre_compile.py", line 764, in compile
p = sre_parse.parse(p, flags)
File "/usr/lib/python3.8/sre_parse.py", line 948, in parse
p = _parse_sub(source, state, flags & SRE_FLAG_VERBOSE, 0)
File "/usr/lib/python3.8/sre_parse.py", line 443, in _parse_sub
itemsappend(_parse(source, state, verbose, nested + 1,
File "/usr/lib/python3.8/sre_parse.py", line 834, in _parse
p = _parse_sub(source, state, sub_verbose, nested + 1)
File "/usr/lib/python3.8/sre_parse.py", line 443, in _parse_sub
itemsappend(_parse(source, state, verbose, nested + 1,
File "/usr/lib/python3.8/sre_parse.py", line 598, in _parse
raise source.error(msg, len(this) + 1 + len(that))
re.error: bad character range 1-0 at position 232 (line 5, column 74)


Escaping with "\" helps only if no digits near "-"

Tests fail with robotframework 7.0

They produce errors like

Expected message to be "Keyword 'BuiltIn.Log' (index 2) does not have message 2." but it was "Keyword 'Log' (index 2) does not have message 2.".

Replacing Builtin.Log with Log in test/tests.robot fixes that.

Allow for user controlled or raw regular expression

The current implementation of the REGEX: match automatically prepend with ^ and postpend $ matching the entire message. This default is good because it reads without needing these symbols. But in some case when we want to use an inline modifier or flag then this throws an exception under Python 3.11 or greater. (Might be an earlier version but my search show this to have changed from a warning to an error with 3.11). Reason is that inline modifiers must be at the beginning of the expression. But with the behind the scenes addition of ^. this prevents this from happening with REGEX:.

To resolve while still keeping the existing usage I was proposing a new match called RAWRE (or RAWREGX or RAWREGEX or the like), for "raw regular expression", which will not include the prepended ^ and postpended $.

Attached is a sample test demonstrating the problem and a proposed solution. In addition to removing the prepend and postpended flags I also removed the re.DOTALL flag allowing the user to use rawre to use whichever flags they choose.

leading_global_flag_error.patch

diff --git a/test/tests.robot b/test/tests.robot
index 35b3fa1..3587a03 100644
--- a/test/tests.robot
+++ b/test/tests.robot
@@ -271,6 +271,7 @@ Expected PASS and log messages with COUNT
     ...    PASS Told ya!!
     ...    LOG 4 COUNT: 2
     ...    LOG 4:2 NONE
+    ...    LOG 3 INFO REGEXP: (?i)Any.*now\.\.\.
     Status    PASS    Told ya!!
     Log    Passing soon!
     Log    Any time now...

raw_re_solution.patch

diff --git a/robotstatuschecker.py b/robotstatuschecker.py
index 2f6acde..5091f9b 100755
--- a/robotstatuschecker.py
+++ b/robotstatuschecker.py
@@ -167,6 +167,10 @@ class BaseChecker:
             pattern = f"^{expected.replace('REGEXP:', '', 1).strip()}$"
             if re.match(pattern, actual, re.DOTALL):
                 return True
+        if expected.startswith("RAWRE:"):
+            pattern = f"{expected.replace('RAWRE:', '', 1).strip()}"
+            if re.match(pattern, actual):
+                return True
         if expected.startswith("GLOB:"):
             pattern = expected.replace("GLOB:", "", 1).strip()
             matcher = Matcher(pattern, caseless=False, spaceless=False)

test_solution.patch

diff --git a/test/tests.robot b/test/tests.robot
index 35b3fa1..21ff448 100644
--- a/test/tests.robot
+++ b/test/tests.robot
@@ -271,6 +271,7 @@ Expected PASS and log messages with COUNT
     ...    PASS Told ya!!
     ...    LOG 4 COUNT: 2
     ...    LOG 4:2 NONE
+    ...    LOG 3 INFO RAWRE: (?si)^Any.*now\.\.\.$
     Status    PASS    Told ya!!
     Log    Passing soon!
     Log    Any time now...

Broken with RF4

Process /Users/mkorpela/workspace/robotframework-browser/atest/output/output.xml
/Users/mkorpela/workspace/robotframework-browser/.venv/lib/python3.9/site-packages/robot/model/keyword.py:97: UserWarning: 'keywords' attribute is read-only and deprecated since Robot Framework 4.0. Use 'body', 'setup' or 'teardown' instead.
  warnings.warn(self.deprecation_message, UserWarning)

Wildcard support for the log verification

Any thoughts about supporting wildcards in the log verification?
i.e. in LOG x.y:z LEVEL Actual message

My use cases for this would be:

  • Running tests with log level (i.e. -L argument) will cause the log index check (:z ) to fail --> e.g. use :*
  • Sometimes I want to verify that a message there, but I don't care about the log level --> e.g. use ANY as log level

IMHO the above should not cause too much hassle as it would be backwards compatible

Use of robotstatuschecker with --prerebotmodifier option

Hello,

I discovered a nice hidden feature in statuschecker. Since the class StatusChecker is implemented as a ResultVisitor, you can use it as follows:

robot --prerebotmodifier robotstatuschecker.StatusChecker

or:

rebot --prerebotmodifier robotstatuschecker.StatusChecker

`
This will not alter output.xml, but it modifies test results before generating log.html and report.html.

This is now my preferred way of using robotstatuschecker.

Could you add this to the documentation?

Best regards,

Henk van den Akker

Python 3 compatibility

Tasks:

  • Fix code and tests to be Python 3 compatible. There's already PR #1 about this.
  • Update classifiers in setup.py to include Python 2 and 3. Can use same classifiers as RF itself.
  • Mention Python 3 support in README.rst.

Validating message with FAIL level fails if test itself passes

I'm trying to update the test case Page Should Contain with

Page Should Contain
    [Documentation]    Default log level does not have html output.
    ...    LOG 2:7 Current page contains text 'needle'.
    ...    LOG 4.1:14 FAIL REGEXP: .*
    Page Should Contain    needle
    Page Should Contain    This is the haystack
    Run Keyword And Expect Error
    ...    Page should have contained text 'non existing text' but did not.
    ...    Page Should Contain    non existing text

Basically, my code is to make it so that keyword does not log the DOM at INFO and that test case runs at INFO level and should check that the DOM is indeed not logged.

To check that the DOM is not logged, it needs to check line 14 of step 4.1.
So I need to check that that line has status FAIL.

LOG 4.1:14 FAIL REGEXP: .* indeed checks that that line has status FAIL (LogMessageChecker._check_msg_level(...) passes), but also uses that information to check that the test case has status FAIL (TestStatusChecker._check_status(...) fails).

Looks like TestStatusChecker._check_status(...) should not use the values from LOG to check for test case status.

image

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.