root/galaxy-central/eggs/nose-0.11.1-py2.6.egg/nose/plugins/xunit.py @ 3

リビジョン 3, 6.9 KB (コミッタ: kohda, 14 年 前)

Install Unix tools  http://hannonlab.cshl.edu/galaxy_unix_tools/galaxy.html

行番号 
1
2"""This plugin provides test results in the standard XUnit XML format.
3
4It was designed for the `Hudson`_ continuous build system but will
5probably work for anything else that understands an XUnit-formatted XML
6representation of test results.
7
8Add this shell command to your builder ::
9   
10    nosetests --with-xunit
11
12And by default a file named nosetests.xml will be written to the
13working directory. 
14
15In a Hudson builder, tick the box named "Publish JUnit test result report"
16under the Post-build Actions and enter this value for Test report XMLs::
17   
18    **/nosetests.xml
19
20If you need to change the name or location of the file, you can set the
21``--xunit-file`` option.
22
23Here is an abbreviated version of what an XML test report might look like::
24   
25    <?xml version="1.0" encoding="UTF-8"?>
26    <testsuite name="nosetests" tests="1" errors="1" failures="0" skip="0">
27        <testcase classname="path_to_test_suite.TestSomething"
28                  name="path_to_test_suite.TestSomething.test_it" time="0">
29            <error type="exceptions.TypeError">
30            Traceback (most recent call last):
31            ...           
32            TypeError: oops, wrong type
33            </error>
34        </testcase>
35    </testsuite>
36
37.. _Hudson: https://hudson.dev.java.net/
38
39"""
40
41import os
42import traceback
43import re
44import inspect
45from nose.plugins.base import Plugin
46from nose.exc import SkipTest
47from time import time
48import doctest
49
50def xmlsafe(s, encoding="utf-8"):
51    """Used internally to escape XML."""
52    if isinstance(s, unicode):
53        s = s.encode(encoding)
54    s = str(s)
55    for src, rep in [('&', '&amp;', ),
56                     ('<', '&lt;', ),
57                     ('>', '&gt;', ),
58                     ('"', '&quot;', ),
59                     ("'", '&#39;', ),
60                     ]:
61        s = s.replace(src, rep)
62    return s
63
64def nice_classname(obj):
65    """Returns a nice name for class object or class instance.
66   
67        >>> nice_classname(Exception()) # doctest: +ELLIPSIS
68        '...Exception'
69        >>> nice_classname(Exception)
70        'exceptions.Exception'
71   
72    """
73    if inspect.isclass(obj):
74        cls_name = obj.__name__
75    else:
76        cls_name = obj.__class__.__name__
77    mod = inspect.getmodule(obj)
78    if mod:
79        name = mod.__name__
80        # jython
81        if name.startswith('org.python.core.'):
82            name = name[len('org.python.core.'):]
83        return "%s.%s" % (name, cls_name)
84    else:
85        return cls_name
86
87class Xunit(Plugin):
88    """This plugin provides test results in the standard XUnit XML format."""
89    name = 'xunit'
90    score = 2000
91    encoding = 'UTF-8'
92   
93    def _timeTaken(self):
94        if hasattr(self, '_timer'):
95            taken = time() - self._timer
96        else:
97            # test died before it ran (probably error in setup())
98            # or success/failure added before test started probably
99            # due to custom TestResult munging
100            taken = 0.0
101        return taken
102   
103    def _xmlsafe(self, s):
104        return xmlsafe(s, encoding=self.encoding)
105   
106    def options(self, parser, env):
107        """Sets additional command line options."""
108        Plugin.options(self, parser, env)
109        parser.add_option(
110            '--xunit-file', action='store',
111            dest='xunit_file', metavar="FILE",
112            default=env.get('NOSE_XUNIT_FILE', 'nosetests.xml'),
113            help=("Path to xml file to store the xunit report in. "
114                  "Default is nosetests.xml in the working directory "
115                  "[NOSE_XUNIT_FILE]"))
116
117    def configure(self, options, config):
118        """Configures the xunit plugin."""
119        Plugin.configure(self, options, config)
120        self.config = config
121        if self.enabled:
122            self.stats = {'errors': 0,
123                          'failures': 0,
124                          'passes': 0,
125                          'skipped': 0
126                          }
127            self.errorlist = []
128            self.error_report_file = open(options.xunit_file, 'w')
129
130    def report(self, stream):
131        """Writes an Xunit-formatted XML file
132
133        The file includes a report of test errors and failures.
134
135        """
136        self.stats['encoding'] = self.encoding
137        self.stats['total'] = (self.stats['errors'] + self.stats['failures']
138                               + self.stats['passes'] + self.stats['skipped'])
139        self.error_report_file.write(
140            '<?xml version="1.0" encoding="%(encoding)s"?>'
141            '<testsuite name="nosetests" tests="%(total)d" '
142            'errors="%(errors)d" failures="%(failures)d" '
143            'skip="%(skipped)d">' % self.stats)
144        self.error_report_file.write(''.join(self.errorlist))
145        self.error_report_file.write('</testsuite>')
146        self.error_report_file.close()
147        if self.config.verbosity > 1:
148            stream.writeln("-" * 70)
149            stream.writeln("XML: %s" % self.error_report_file.name)
150
151    def startTest(self, test):
152        """Initializes a timer before starting a test."""
153        self._timer = time()
154
155    def addError(self, test, err, capt=None):
156        """Add error output to Xunit report.
157        """
158        taken = self._timeTaken()
159           
160        if issubclass(err[0], SkipTest):
161            self.stats['skipped'] +=1
162            return
163        tb = ''.join(traceback.format_exception(*err))
164        self.stats['errors'] += 1
165        id = test.id()
166        self.errorlist.append(
167            '<testcase classname="%(cls)s" name="%(name)s" time="%(taken)d">'
168            '<error type="%(errtype)s">%(tb)s</error></testcase>' %
169            {'cls': self._xmlsafe('.'.join(id.split('.')[:-1])),
170             'name': self._xmlsafe(id),
171             'errtype': self._xmlsafe(nice_classname(err[0])),
172             'tb': self._xmlsafe(tb),
173             'taken': taken,
174             })
175
176    def addFailure(self, test, err, capt=None, tb_info=None):
177        """Add failure output to Xunit report.
178        """
179        taken = self._timeTaken()
180        tb = ''.join(traceback.format_exception(*err))
181        self.stats['failures'] += 1
182        id = test.id()
183        self.errorlist.append(
184            '<testcase classname="%(cls)s" name="%(name)s" time="%(taken)d">'
185            '<failure type="%(errtype)s">%(tb)s</failure></testcase>' %
186            {'cls': self._xmlsafe('.'.join(id.split('.')[:-1])),
187             'name': self._xmlsafe(id),
188             'errtype': self._xmlsafe(nice_classname(err[0])),
189             'tb': self._xmlsafe(tb),
190             'taken': taken,
191             })
192       
193    def addSuccess(self, test, capt=None):
194        """Add success output to Xunit report.
195        """
196        taken = self._timeTaken()
197           
198        self.stats['passes'] += 1
199        id = test.id()
200        self.errorlist.append(
201            '<testcase classname="%(cls)s" name="%(name)s" '
202            'time="%(taken)d" />' %
203            {'cls': self._xmlsafe('.'.join(id.split('.')[:-1])),
204             'name': self._xmlsafe(id),
205             'taken': taken,
206             })
207
208   
Note: リポジトリブラウザについてのヘルプは TracBrowser を参照してください。