ResultFormats.rst 11.1 KB
Newer Older
1 2
.. _output-plugins:

3
Result Formats
4 5 6
==============

A test runner must provide an assortment of ways to clearly communicate results
7 8
to interested parties, be them humans or machines.

9 10 11
.. note:: There are several optional result plugins, you can find them in
   :ref:`result-plugins`.

12 13 14 15
Results for human beings
------------------------

Avocado has two different result formats that are intended for human beings:
16

17 18 19
* Its default UI, which shows the live test execution results on a command
  line, text based, UI.
* The HTML report, which is generated after the test job finishes running.
20

21 22
Avocado command line UI
~~~~~~~~~~~~~~~~~~~~~~~
23

24 25
A regular run of Avocado will present the test results in a live fashion,
that is, the job and its test(s) results are constantly updated::
26

27
    $ avocado run sleeptest.py failtest.py synctest.py
28 29
    JOB ID    : 5ffe479262ea9025f2e4e84c4e92055b5c79bdc9
    JOB LOG   : $HOME/avocado/job-results/job-2014-08-12T15.57-5ffe4792/job.log
30 31 32
     (1/3) sleeptest.py:SleepTest.test: PASS (1.01 s)
     (2/3) failtest.py:FailTest.test: FAIL (0.00 s)
     (3/3) synctest.py:SyncTest.test: PASS (1.98 s)
33
    RESULTS    : PASS 1 | ERROR 1 | FAIL 1 | SKIP 0 | WARN 0 | INTERRUPT 0
34
    JOB TIME   : 3.27 s
35
    JOB HTML  : $HOME/avocado/job-results/job-2014-08-12T15.57-5ffe4792/html/results.html
36 37

The most important thing is to remember that programs should never need to parse
38 39 40 41 42 43 44 45 46
human output to figure out what happened to a test job run.

Machine readable results
------------------------

Another type of results are those intended to be parsed by other
applications. Several standards exist in the test community, and Avocado can
in theory support pretty much every result standard out there.

47 48 49
Out of the box, Avocado supports a couple of machine readable results. They
are always generated and stored in the results directory in `results.$type`
files, but you can ask for a different location too.
50 51 52

xunit
~~~~~
53

54
The default machine readable output in Avocado is
55 56 57 58 59 60
`xunit <http://help.catchsoftware.com/display/ET/JUnit+Format>`__.

xunit is an XML format that contains test results in a structured form, and
are used by other test automation projects, such as `jenkins
<http://jenkins-ci.org/>`__. If you want to make Avocado to generate xunit
output in the standard output of the runner, simply use::
61

62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86
   $ avocado run sleeptest.py failtest.py synctest.py --xunit -
   <?xml version="1.0" encoding="UTF-8"?>
   <testsuite name="avocado" tests="3" errors="0" failures="1" skipped="0" time="3.5769162178" timestamp="2016-05-04 14:46:52.803365">
           <testcase classname="SleepTest" name="1-sleeptest.py:SleepTest.test" time="1.00204920769"/>
           <testcase classname="FailTest" name="2-failtest.py:FailTest.test" time="0.00120401382446">
                   <failure type="TestFail" message="This test is supposed to fail"><![CDATA[Traceback (most recent call last):
     File "/home/medic/Work/Projekty/avocado/avocado/avocado/core/test.py", line 490, in _run_avocado
       raise test_exception
   TestFail: This test is supposed to fail
   ]]></failure>
                   <system-out><![CDATA[14:46:53 ERROR| 
   14:46:53 ERROR| Reproduced traceback from: /home/medic/Work/Projekty/avocado/avocado/avocado/core/test.py:435
   14:46:53 ERROR| Traceback (most recent call last):
   14:46:53 ERROR|   File "/home/medic/Work/Projekty/avocado/avocado/examples/tests/failtest.py", line 17, in test
   14:46:53 ERROR|     self.fail('This test is supposed to fail')
   14:46:53 ERROR|   File "/home/medic/Work/Projekty/avocado/avocado/avocado/core/test.py", line 585, in fail
   14:46:53 ERROR|     raise exceptions.TestFail(message)
   14:46:53 ERROR| TestFail: This test is supposed to fail
   14:46:53 ERROR| 
   14:46:53 ERROR| FAIL 2-failtest.py:FailTest.test -> TestFail: This test is supposed to fail
   14:46:53 INFO | 
   ]]></system-out>
           </testcase>
           <testcase classname="SyncTest" name="3-synctest.py:SyncTest.test" time="2.57366299629"/>
   </testsuite>
87

88 89 90

.. note:: The dash `-` in the option `--xunit`, it means that the xunit result
          should go to the standard output.
91 92 93 94 95 96
.. note:: In case your tests produce very long outputs, you can limit the
          number of embedded characters by
          `--xunit-max-test-log-chars`. If the output in the log file is
          longer it only attaches up-to max-test-log-chars characters
          one half starting from the beginning of the content, the other
          half from the end of the content.
97

98
JSON
99
~~~~
100 101

`JSON <http://www.json.org/>`__ is a widely used data exchange format. The
102
JSON Avocado plugin outputs job information, similarly to the xunit output
103 104
plugin::

105 106
    $ avocado run sleeptest.py failtest.py synctest.py --json -
    {
L
Lukáš Doktor 已提交
107
        "cancel": 0,
108
        "debuglog": "/home/cleber/avocado/job-results/job-2016-08-09T13.53-10715c4/job.log",
109 110
        "errors": 0,
        "failures": 1,
111
        "job_id": "10715c4645d2d2b57889d7a4317fcd01451b600e",
112 113 114 115
        "pass": 2,
        "skip": 0,
        "tests": [
            {
116
                "end": 1470761623.176954,
117
                "fail_reason": "None",
118 119 120
                "logdir": "/home/cleber/avocado/job-results/job-2016-08-09T13.53-10715c4/test-results/1-sleeptest.py:SleepTest.test",
                "logfile": "/home/cleber/avocado/job-results/job-2016-08-09T13.53-10715c4/test-results/1-sleeptest.py:SleepTest.test/debug.log",
                "start": 1470761622.174918,
121
                "status": "PASS",
122
                "id": "1-sleeptest.py:SleepTest.test",
123
                "time": 1.0020360946655273,
124 125 126
                "whiteboard": ""
            },
            {
127
                "end": 1470761623.193472,
128
                "fail_reason": "This test is supposed to fail",
129 130 131
                "logdir": "/home/cleber/avocado/job-results/job-2016-08-09T13.53-10715c4/test-results/2-failtest.py:FailTest.test",
                "logfile": "/home/cleber/avocado/job-results/job-2016-08-09T13.53-10715c4/test-results/2-failtest.py:FailTest.test/debug.log",
                "start": 1470761623.192334,
132
                "status": "FAIL",
133
                "id": "2-failtest.py:FailTest.test",
134
                "time": 0.0011379718780517578,
135 136 137
                "whiteboard": ""
            },
            {
138
                "end": 1470761625.656061,
139
                "fail_reason": "None",
140 141 142
                "logdir": "/home/cleber/avocado/job-results/job-2016-08-09T13.53-10715c4/test-results/3-synctest.py:SyncTest.test",
                "logfile": "/home/cleber/avocado/job-results/job-2016-08-09T13.53-10715c4/test-results/3-synctest.py:SyncTest.test/debug.log",
                "start": 1470761623.208165,
143
                "status": "PASS",
144
                "id": "3-synctest.py:SyncTest.test",
145
                "time": 2.4478960037231445,
146 147 148
                "whiteboard": ""
            }
        ],
149
        "time": 3.4510700702667236,
150 151
        "total": 3
    }
152

153 154
.. note:: The dash `-` in the option `--json`, it means that the xunit result
          should go to the standard output.
155

156
Bear in mind that there's no documented standard for the Avocado JSON result
157
format. This means that it will probably grow organically to accommodate
158 159 160
newer Avocado features. A reasonable effort will be made to not break
backwards compatibility with applications that parse the current form of its
JSON result.
161

162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177

TAP
~~~

Provides the basic `TAP <http://testanything.org/>`__ (Test Anything Protocol) results, currently in v12. Unlike most existing avocado machine readable outputs this one is streamlined (per test results)::

    $ avocado run sleeptest.py --tap -
    1..1
    # debug.log of sleeptest.py:SleepTest.test:
    #   12:04:38 DEBUG| PARAMS (key=sleep_length, path=*, default=1) => 1
    #   12:04:38 DEBUG| Sleeping for 1.00 seconds
    #   12:04:39 INFO | PASS 1-sleeptest.py:SleepTest.test
    #   12:04:39 INFO |
    ok 1 sleeptest.py:SleepTest.test


178 179
Silent result
~~~~~~~~~~~~~
180

L
Lukáš Doktor 已提交
181 182 183
This result disables all stdout logging (while keeping the error messages
being printed to stderr). One can then use the return code to learn about
the result::
184

185
    $ avocado --silent run failtest.py
186 187 188
    $ echo $?
    1

189 190 191 192 193
In practice, this would usually be used by scripts that will in turn run
Avocado and check its results::

    #!/bin/bash
    ...
194
    $ avocado --silent run /path/to/my/test.py
195 196 197 198
    if [ $? == 0 ]; then
       echo "great success!"
    elif
       ...
199

200 201
more details regarding exit codes in `Exit Codes`_ section.

202 203 204 205 206 207
Multiple results at once
------------------------

You can have multiple results formats at once, as long as only one of them
uses the standard output. For example, it is fine to use the xunit result on
stdout and the JSON result to output to a file::
208

209 210 211 212 213 214
   $ avocado run sleeptest.py synctest.py --xunit - --json /tmp/result.json
   <?xml version="1.0" encoding="UTF-8"?>
   <testsuite name="avocado" tests="2" errors="0" failures="0" skipped="0" time="3.64848303795" timestamp="2016-05-04 17:26:05.645665">
           <testcase classname="SleepTest" name="1-sleeptest.py:SleepTest.test" time="1.00270605087"/>
           <testcase classname="SyncTest" name="2-synctest.py:SyncTest.test" time="2.64577698708"/>
   </testsuite>
215

216
   $ cat /tmp/result.json
217 218 219 220 221
   {
        "debuglog": "/home/cleber/avocado/job-results/job-2016-08-09T13.55-1a94ad6/job.log",
        "errors": 0,
        ...
   }
222

223
But you won't be able to do the same without the --json flag passed to
224 225
the program::

226 227 228
   $ avocado run sleeptest.py synctest.py --xunit - --json -
   Options --json --xunit are trying to use stdout simultaneously
   Please set at least one of them to a file to avoid conflicts
229

230
That's basically the only rule, and a sane one, that you need to follow.
231

232 233 234 235 236
Exit Codes
----------

Avocado exit code tries to represent different things that can happen during
an execution. That means exit codes can be a combination of codes that were
L
Lukáš Doktor 已提交
237
ORed together as a single exit code. The final exit code can be de-bundled so
238 239 240 241 242 243 244 245 246 247 248 249 250 251
users can have a good idea on what happened to the job.

The single individual exit codes are:

* AVOCADO_ALL_OK (0)
* AVOCADO_TESTS_FAIL (1)
* AVOCADO_JOB_FAIL (2)
* AVOCADO_FAIL (4)
* AVOCADO_JOB_INTERRUPTED (8)

If a job finishes with exit code `9`, for example, it means we had at least
one test that failed and also we had at some point a job interruption, probably
due to the job timeout or a `CTRL+C`.

252
Implementing other result formats
253 254
---------------------------------

255
If you are looking to implement a new machine or human readable output
C
Cleber Rosa 已提交
256
format, you can refer to :mod:`avocado.plugins.xunit` and use it as a
257 258
starting point.

259 260 261 262 263 264
If your result is something that is produced at once, based on the
complete job outcome, you should create a new class that inherits from
:class:`avocado.core.plugin_interfaces.Result`  and implements the
:meth:`avocado.core.plugin_interfaces.Result.render` method.

But, if your result implementation is something that outputs
C
Cleber Rosa 已提交
265 266 267 268 269
information live before/during/after tests, then the
:class:`avocado.core.plugin_interfaces.ResultEvents` interface is to
one to look at.  It will require you to implement the methods that
will perform actions (write to a file/stream) for each of the defined
events on a Job and test execution.
270 271 272

You can take a look at :doc:`Plugins` for more information on how to
write a plugin that will activate and execute the new result format.