[Yt-svn] yt: Adding xunit plugin from nose, modified not to use nose, as ...
hg at spacepope.org
hg at spacepope.org
Sat Sep 11 01:12:23 PDT 2010
hg Repository: yt
details: yt/rev/07a945f94009
changeset: 3395:07a945f94009
user: Matthew Turk <matthewturk at gmail.com>
date:
Sat Sep 11 01:12:12 2010 -0700
description:
Adding xunit plugin from nose, modified not to use nose, as well as removing
check_for_setup. -n to enzo_test will now output nosetests.xml.
diffstat:
check_for_setup.py | 4 -
yt/utilities/answer_testing/runner.py | 24 +++-
yt/utilities/answer_testing/xunit.py | 222 +++++++++++++++++++++++++++++++++++++
3 files changed, 242 insertions(+), 8 deletions(-)
diffs (truncated from 306 to 300 lines):
diff -r eab5391f1f10 -r 07a945f94009 check_for_setup.py
--- a/check_for_setup.py Wed Sep 08 19:19:17 2010 -0700
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,4 +0,0 @@
-import os
-
-for dirpath, dirnames, filenames in os.walk("yt"):
- if "setup.py" not in filenames: print dirpath
diff -r eab5391f1f10 -r 07a945f94009 yt/utilities/answer_testing/runner.py
--- a/yt/utilities/answer_testing/runner.py Wed Sep 08 19:19:17 2010 -0700
+++ b/yt/utilities/answer_testing/runner.py Sat Sep 11 01:12:12 2010 -0700
@@ -2,6 +2,7 @@
import os, shelve, cPickle, sys
from yt.config import ytcfg; ytcfg["yt","serialize"] = "False"
import yt.utilities.cmdln as cmdln
+from .xunit import Xunit
from output_tests import test_registry, MultipleOutputTest, \
RegressionTestException
@@ -73,26 +74,34 @@
test_instance = test(self.io_log)
self._run(test_instance)
+ watcher = None
def _run(self, test):
+ if self.watcher is not None:
+ self.watcher.start()
print self.id, "Running", test.name,
test.setup()
test.run()
self.plot_list[test.name] = test.plot()
self.results[test.name] = test.result
- success = self._compare(test)
+ success, msg = self._compare(test)
if success == True: print "SUCCEEDED"
else: print "FAILED"
self.passed_tests[test.name] = success
+ if self.watcher is not None:
+ if success == True:
+ self.watcher.addSuccess(test.name)
+ else:
+ self.watcher.addFailure(test.name, msg)
def _compare(self, test):
if self.old_results is None:
- return True
+ return (True, "New Test")
old_result = self.old_results[test.name]
try:
test.compare(old_result)
except RegressionTestException, exc:
- return str(exc)
- return True
+ return (False, str(exc))
+ return (True, "Pass")
def run_tests_from_file(self, filename):
for line in open(filename):
@@ -125,6 +134,9 @@
@cmdln.option("-o", "--output", action="store",
help="output results to file",
dest="outputfile", default=None)
+ @cmdln.option("-n", "--nose", action="store_true",
+ help="run through nose with xUnit testing",
+ dest="run_nose", default=False)
def do_compare(self, subcmd, opts, reference, comparison, *test_modules):
"""
${cmd_name}: Compare a reference dataset against a new dataset. The
@@ -139,7 +151,11 @@
print "Loading module %s" % (fn)
__import__(fn)
test_runner = RegressionTestRunner(comparison, reference)
+ if opts.run_nose:
+ test_runner.watcher = Xunit()
results = test_runner.run_all_tests()
+ if opts.run_nose:
+ test_runner.watcher.report()
if opts.outputfile is not None:
f = open(str(opts.outputfile), "w")
for testname, success in sorted(results.items()):
diff -r eab5391f1f10 -r 07a945f94009 yt/utilities/answer_testing/xunit.py
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/yt/utilities/answer_testing/xunit.py Sat Sep 11 01:12:12 2010 -0700
@@ -0,0 +1,222 @@
+"""
+This code was modified from the nose plugins/xunit.py module, licensed under
+the GNU Lesser General Public License and copyrighted by Jason Pellerin.
+
+For more information, see the nose homepage at:
+
+ http://python-nose.googlecode.com/
+
+--
+This plugin provides test results in the standard XUnit XML format.
+
+It was designed for the `Hudson`_ continuous build system but will
+probably work for anything else that understands an XUnit-formatted XML
+representation of test results.
+
+Add this shell command to your builder ::
+
+ nosetests --with-xunit
+
+And by default a file named nosetests.xml will be written to the
+working directory.
+
+In a Hudson builder, tick the box named "Publish JUnit test result report"
+under the Post-build Actions and enter this value for Test report XMLs::
+
+ **/nosetests.xml
+
+If you need to change the name or location of the file, you can set the
+``--xunit-file`` option.
+
+Here is an abbreviated version of what an XML test report might look like::
+
+ <?xml version="1.0" encoding="UTF-8"?>
+ <testsuite name="nosetests" tests="1" errors="1" failures="0" skip="0">
+ <testcase classname="path_to_test_suite.TestSomething"
+ name="test_it" time="0">
+ <error type="exceptions.TypeError" message="oops, wrong type">
+ Traceback (most recent call last):
+ ...
+ TypeError: oops, wrong type
+ </error>
+ </testcase>
+ </testsuite>
+
+.. _Hudson: https://hudson.dev.java.net/
+
+"""
+
+import doctest
+import os
+import traceback
+import re
+import inspect
+from time import time
+from xml.sax import saxutils
+
+# Invalid XML characters, control characters 0-31 sans \t, \n and \r
+CONTROL_CHARACTERS = re.compile(r"[\000-\010\013\014\016-\037]")
+
+def xml_safe(value):
+ """Replaces invalid XML characters with '?'."""
+ return CONTROL_CHARACTERS.sub('?', value)
+
+def escape_cdata(cdata):
+ """Escape a string for an XML CDATA section."""
+ return xml_safe(cdata).replace(']]>', ']]>]]><![CDATA[')
+
+def nice_classname(obj):
+ """Returns a nice name for class object or class instance.
+
+ >>> nice_classname(Exception()) # doctest: +ELLIPSIS
+ '...Exception'
+ >>> nice_classname(Exception)
+ 'exceptions.Exception'
+
+ """
+ if inspect.isclass(obj):
+ cls_name = obj.__name__
+ else:
+ cls_name = obj.__class__.__name__
+ mod = inspect.getmodule(obj)
+ if mod:
+ name = mod.__name__
+ # jython
+ if name.startswith('org.python.core.'):
+ name = name[len('org.python.core.'):]
+ return "%s.%s" % (name, cls_name)
+ else:
+ return cls_name
+
+def exc_message(exc_info):
+ """Return the exception's message."""
+ exc = exc_info[1]
+ if exc is None:
+ # str exception
+ result = exc_info[0]
+ else:
+ try:
+ result = str(exc)
+ except UnicodeEncodeError:
+ try:
+ result = unicode(exc)
+ except UnicodeError:
+ # Fallback to args as neither str nor
+ # unicode(Exception(u'\xe6')) work in Python < 2.6
+ result = exc.args[0]
+ return xml_safe(result)
+
+class Xunit(object):
+ """This plugin provides test results in the standard XUnit XML format."""
+ name = 'xunit'
+ xunit_file = "nosetests.xml"
+ score = 2000
+ encoding = 'UTF-8'
+ error_report_file = None
+
+ def _timeTaken(self):
+ if hasattr(self, '_timer'):
+ taken = time() - self._timer
+ else:
+ # test died before it ran (probably error in setup())
+ # or success/failure added before test started probably
+ # due to custom TestResult munging
+ taken = 0.0
+ return taken
+
+ def _quoteattr(self, attr):
+ """Escape an XML attribute. Value can be unicode."""
+ attr = xml_safe(attr)
+ if isinstance(attr, unicode):
+ attr = attr.encode(self.encoding)
+ return saxutils.quoteattr(attr)
+
+ def __init__(self):
+ """Configures the xunit plugin."""
+ self.stats = {'errors': 0,
+ 'failures': 0,
+ 'passes': 0,
+ 'skipped': 0
+ }
+ self.errorlist = []
+ self.error_report_file = open(self.xunit_file, 'w')
+
+ def report(self):
+ """Writes an Xunit-formatted XML file
+
+ The file includes a report of test errors and failures.
+
+ """
+ self.stats['encoding'] = self.encoding
+ self.stats['total'] = (self.stats['errors'] + self.stats['failures']
+ + self.stats['passes'] + self.stats['skipped'])
+ self.error_report_file.write(
+ '<?xml version="1.0" encoding="%(encoding)s"?>'
+ '<testsuite name="nosetests" tests="%(total)d" '
+ 'errors="%(errors)d" failures="%(failures)d" '
+ 'skip="%(skipped)d">' % self.stats)
+ self.error_report_file.write(''.join(self.errorlist))
+ self.error_report_file.write('</testsuite>')
+ self.error_report_file.close()
+
+ def addError(self, test, err, capt=None):
+ """Add error output to Xunit report.
+ """
+ taken = self._timeTaken()
+
+ if issubclass(err[0], SkipTest):
+ type = 'skipped'
+ self.stats['skipped'] += 1
+ else:
+ type = 'error'
+ self.stats['errors'] += 1
+ tb = ''.join(traceback.format_exception(*err))
+ id = test.id()
+ self.errorlist.append(
+ '<testcase classname=%(cls)s name=%(name)s time="%(taken)d">'
+ '<%(type)s type=%(errtype)s message=%(message)s><![CDATA[%(tb)s]]>'
+ '</%(type)s></testcase>' %
+ {'cls': self._quoteattr('.'.join(id.split('.')[:-1])),
+ 'name': self._quoteattr(id.split('.')[-1]),
+ 'taken': taken,
+ 'type': type,
+ 'errtype': self._quoteattr(nice_classname(err[0])),
+ 'message': self._quoteattr(exc_message(err)),
+ 'tb': escape_cdata(tb),
+ })
+
+ def addFailure(self, test, err, capt=None, tb_info=None):
+ """Add failure output to Xunit report.
+ """
+ taken = self._timeTaken()
+ tb = ''.join(traceback.format_exception(*err))
+ self.stats['failures'] += 1
+ id = test
+ self.errorlist.append(
+ '<testcase classname=%(cls)s name=%(name)s time="%(taken)d">'
+ '<failure type=%(errtype)s message=%(message)s><![CDATA[%(tb)s]]>'
+ '</failure></testcase>' %
+ {'cls': self._quoteattr('.'.join(id.split('.')[:-1])),
+ 'name': self._quoteattr(id.split('.')[-1]),
+ 'taken': taken,
+ 'errtype': self._quoteattr(nice_classname(err[0])),
+ 'message': self._quoteattr(exc_message(err)),
+ 'tb': escape_cdata(tb),
+ })
+
+ def addSuccess(self, test, capt=None):
+ """Add success output to Xunit report.
+ """
+ taken = self._timeTaken()
+ self.stats['passes'] += 1
+ id = test
+ self.errorlist.append(
+ '<testcase classname=%(cls)s name=%(name)s '
+ 'time="%(taken)d" />' %
+ {'cls': self._quoteattr('.'.join(id.split('.')[:-1])),
More information about the yt-svn
mailing list