##// 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 import threading
57 import threading
58 import killdaemons as killmod
58 import killdaemons as killmod
59 import Queue as queue
59 import Queue as queue
60 from xml.dom import minidom
60 import unittest
61 import unittest
61
62
62 processlock = threading.Lock()
63 processlock = threading.Lock()
@@ -190,6 +191,8 b' def getparser():'
190 " (implies --keep-tmpdir)")
191 " (implies --keep-tmpdir)")
191 parser.add_option("-v", "--verbose", action="store_true",
192 parser.add_option("-v", "--verbose", action="store_true",
192 help="output verbose messages")
193 help="output verbose messages")
194 parser.add_option("--xunit", type="string",
195 help="record xunit results at specified path")
193 parser.add_option("--view", type="string",
196 parser.add_option("--view", type="string",
194 help="external diff viewer")
197 help="external diff viewer")
195 parser.add_option("--with-hg", type="string",
198 parser.add_option("--with-hg", type="string",
@@ -304,6 +307,20 b' def vlog(*msg):'
304
307
305 return log(*msg)
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 def log(*msg):
324 def log(*msg):
308 """Log something to stdout.
325 """Log something to stdout.
309
326
@@ -1085,6 +1102,9 b' class TestResult(unittest._TextTestResul'
1085 self.times = []
1102 self.times = []
1086 self._started = {}
1103 self._started = {}
1087 self._stopped = {}
1104 self._stopped = {}
1105 # Data stored for the benefit of generating xunit reports.
1106 self.successes = []
1107 self.faildata = {}
1088
1108
1089 def addFailure(self, test, reason):
1109 def addFailure(self, test, reason):
1090 self.failures.append((test, reason))
1110 self.failures.append((test, reason))
@@ -1099,9 +1119,12 b' class TestResult(unittest._TextTestResul'
1099 self.stream.write('!')
1119 self.stream.write('!')
1100 iolock.release()
1120 iolock.release()
1101
1121
1102 def addError(self, *args, **kwargs):
1122 def addSuccess(self, test):
1103 super(TestResult, self).addError(*args, **kwargs)
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 if self._options.first:
1128 if self._options.first:
1106 self.stop()
1129 self.stop()
1107
1130
@@ -1141,6 +1164,8 b' class TestResult(unittest._TextTestResul'
1141 """Record a mismatch in test output for a particular test."""
1164 """Record a mismatch in test output for a particular test."""
1142
1165
1143 accepted = False
1166 accepted = False
1167 failed = False
1168 lines = []
1144
1169
1145 iolock.acquire()
1170 iolock.acquire()
1146 if self._options.nodiff:
1171 if self._options.nodiff:
@@ -1169,7 +1194,8 b' class TestResult(unittest._TextTestResul'
1169 else:
1194 else:
1170 rename(test.errpath, '%s.out' % test.path)
1195 rename(test.errpath, '%s.out' % test.path)
1171 accepted = True
1196 accepted = True
1172
1197 if not accepted and not failed:
1198 self.faildata[test.name] = ''.join(lines)
1173 iolock.release()
1199 iolock.release()
1174
1200
1175 return accepted
1201 return accepted
@@ -1344,6 +1370,35 b' class TextTestRunner(unittest.TextTestRu'
1344 for test, msg in result.errors:
1370 for test, msg in result.errors:
1345 self.stream.writeln('Errored %s: %s' % (test.name, msg))
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 self._runner._checkhglib('Tested')
1402 self._runner._checkhglib('Tested')
1348
1403
1349 self.stream.writeln('# Ran %d tests, %d skipped, %d warned, %d failed.'
1404 self.stream.writeln('# Ran %d tests, %d skipped, %d warned, %d failed.'
@@ -48,6 +48,39 b' failing test'
48 # Ran 2 tests, 0 skipped, 0 warned, 1 failed.
48 # Ran 2 tests, 0 skipped, 0 warned, 1 failed.
49 python hash seed: * (glob)
49 python hash seed: * (glob)
50 [1]
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 test for --retest
85 test for --retest
53 ====================
86 ====================
@@ -291,6 +324,18 b' Skips'
291 Skipped test-skip.t: irrelevant
324 Skipped test-skip.t: irrelevant
292 # Ran 1 tests, 2 skipped, 0 warned, 0 failed.
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 Missing skips or blacklisted skips don't count as executed:
339 Missing skips or blacklisted skips don't count as executed:
295 $ echo test-failure.t > blacklist
340 $ echo test-failure.t > blacklist
296 $ $TESTDIR/run-tests.py --with-hg=`which hg` --blacklist=blacklist \
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