##// END OF EJS Templates
run-tests: add support for xunit test reports...
Augie Fackler -
r22044:a06172e8 default
parent child Browse files
Show More
@@ -57,6 +57,7 b' import re'
57 57 import threading
58 58 import killdaemons as killmod
59 59 import Queue as queue
60 from xml.dom import minidom
60 61 import unittest
61 62
62 63 processlock = threading.Lock()
@@ -190,6 +191,8 b' def getparser():'
190 191 " (implies --keep-tmpdir)")
191 192 parser.add_option("-v", "--verbose", action="store_true",
192 193 help="output verbose messages")
194 parser.add_option("--xunit", type="string",
195 help="record xunit results at specified path")
193 196 parser.add_option("--view", type="string",
194 197 help="external diff viewer")
195 198 parser.add_option("--with-hg", type="string",
@@ -304,6 +307,20 b' def vlog(*msg):'
304 307
305 308 return log(*msg)
306 309
310 # Bytes that break XML even in a CDATA block: control characters 0-31
311 # sans \t, \n and \r
312 CDATA_EVIL = re.compile(r"[\000-\010\013\014\016-\037]")
313
314 def cdatasafe(data):
315 """Make a string safe to include in a CDATA block.
316
317 Certain control characters are illegal in a CDATA block, and
318 there's no way to include a ]]> in a CDATA either. This function
319 replaces illegal bytes with ? and adds a space between the ]] so
320 that it won't break the CDATA block.
321 """
322 return CDATA_EVIL.sub('?', data).replace(']]>', '] ]>')
323
307 324 def log(*msg):
308 325 """Log something to stdout.
309 326
@@ -1085,6 +1102,9 b' class TestResult(unittest._TextTestResul'
1085 1102 self.times = []
1086 1103 self._started = {}
1087 1104 self._stopped = {}
1105 # Data stored for the benefit of generating xunit reports.
1106 self.successes = []
1107 self.faildata = {}
1088 1108
1089 1109 def addFailure(self, test, reason):
1090 1110 self.failures.append((test, reason))
@@ -1099,9 +1119,12 b' class TestResult(unittest._TextTestResul'
1099 1119 self.stream.write('!')
1100 1120 iolock.release()
1101 1121
1102 def addError(self, *args, **kwargs):
1103 super(TestResult, self).addError(*args, **kwargs)
1122 def addSuccess(self, test):
1123 super(TestResult, self).addSuccess(test)
1124 self.successes.append(test)
1104 1125
1126 def addError(self, test, err):
1127 super(TestResult, self).addError(test, err)
1105 1128 if self._options.first:
1106 1129 self.stop()
1107 1130
@@ -1141,6 +1164,8 b' class TestResult(unittest._TextTestResul'
1141 1164 """Record a mismatch in test output for a particular test."""
1142 1165
1143 1166 accepted = False
1167 failed = False
1168 lines = []
1144 1169
1145 1170 iolock.acquire()
1146 1171 if self._options.nodiff:
@@ -1169,7 +1194,8 b' class TestResult(unittest._TextTestResul'
1169 1194 else:
1170 1195 rename(test.errpath, '%s.out' % test.path)
1171 1196 accepted = True
1172
1197 if not accepted and not failed:
1198 self.faildata[test.name] = ''.join(lines)
1173 1199 iolock.release()
1174 1200
1175 1201 return accepted
@@ -1344,6 +1370,35 b' class TextTestRunner(unittest.TextTestRu'
1344 1370 for test, msg in result.errors:
1345 1371 self.stream.writeln('Errored %s: %s' % (test.name, msg))
1346 1372
1373 if self._runner.options.xunit:
1374 xuf = open(self._runner.options.xunit, 'wb')
1375 try:
1376 timesd = dict(
1377 (test, real) for test, cuser, csys, real in result.times)
1378 doc = minidom.Document()
1379 s = doc.createElement('testsuite')
1380 s.setAttribute('name', 'run-tests')
1381 s.setAttribute('tests', str(result.testsRun))
1382 s.setAttribute('errors', "0") # TODO
1383 s.setAttribute('failures', str(failed))
1384 s.setAttribute('skipped', str(skipped + ignored))
1385 doc.appendChild(s)
1386 for tc in result.successes:
1387 t = doc.createElement('testcase')
1388 t.setAttribute('name', tc.name)
1389 t.setAttribute('time', '%.3f' % timesd[tc.name])
1390 s.appendChild(t)
1391 for tc, err in sorted(result.faildata.iteritems()):
1392 t = doc.createElement('testcase')
1393 t.setAttribute('name', tc)
1394 t.setAttribute('time', '%.3f' % timesd[tc])
1395 cd = doc.createCDATASection(cdatasafe(err))
1396 t.appendChild(cd)
1397 s.appendChild(t)
1398 xuf.write(doc.toprettyxml(indent=' ', encoding='utf-8'))
1399 finally:
1400 xuf.close()
1401
1347 1402 self._runner._checkhglib('Tested')
1348 1403
1349 1404 self.stream.writeln('# Ran %d tests, %d skipped, %d warned, %d failed.'
@@ -48,6 +48,39 b' failing test'
48 48 # Ran 2 tests, 0 skipped, 0 warned, 1 failed.
49 49 python hash seed: * (glob)
50 50 [1]
51 test --xunit support
52 $ $TESTDIR/run-tests.py --with-hg=`which hg` --xunit=xunit.xml
53
54 --- $TESTTMP/test-failure.t
55 +++ $TESTTMP/test-failure.t.err
56 @@ -1,4 +1,4 @@
57 $ echo babar
58 - rataxes
59 + babar
60 This is a noop statement so that
61 this test is still more bytes than success.
62
63 ERROR: test-failure.t output changed
64 !.
65 Failed test-failure.t: output changed
66 # Ran 2 tests, 0 skipped, 0 warned, 1 failed.
67 python hash seed: * (glob)
68 [1]
69 $ cat xunit.xml
70 <?xml version="1.0" encoding="utf-8"?>
71 <testsuite errors="0" failures="1" name="run-tests" skipped="0" tests="2">
72 <testcase name="test-success.t" time="*"/> (glob)
73 <testcase name="test-failure.t" time="*"> (glob)
74 <![CDATA[--- $TESTTMP/test-failure.t
75 +++ $TESTTMP/test-failure.t.err
76 @@ -1,4 +1,4 @@
77 $ echo babar
78 - rataxes
79 + babar
80 This is a noop statement so that
81 this test is still more bytes than success.
82 ]]> </testcase>
83 </testsuite>
51 84
52 85 test for --retest
53 86 ====================
@@ -291,6 +324,18 b' Skips'
291 324 Skipped test-skip.t: irrelevant
292 325 # Ran 1 tests, 2 skipped, 0 warned, 0 failed.
293 326
327 Skips with xml
328 $ $TESTDIR/run-tests.py --with-hg=`which hg` --keyword xyzzy \
329 > --xunit=xunit.xml
330 i.s
331 Skipped test-skip.t: irrelevant
332 # Ran 1 tests, 2 skipped, 0 warned, 0 failed.
333 $ cat xunit.xml
334 <?xml version="1.0" encoding="utf-8"?>
335 <testsuite errors="0" failures="0" name="run-tests" skipped="2" tests="1">
336 <testcase name="test-success.t" time="*"/> (glob)
337 </testsuite>
338
294 339 Missing skips or blacklisted skips don't count as executed:
295 340 $ echo test-failure.t > blacklist
296 341 $ $TESTDIR/run-tests.py --with-hg=`which hg` --blacklist=blacklist \
General Comments 0
You need to be logged in to leave comments. Login now