diff options
author | Benoit Foucher <benoit@zeroc.com> | 2017-07-11 10:21:45 +0200 |
---|---|---|
committer | Benoit Foucher <benoit@zeroc.com> | 2017-07-11 10:21:45 +0200 |
commit | 2d8b44932779f887ce47c43e25dbda72b817e45d (patch) | |
tree | 515a11b5b2be7cc5f5ba265f9d1b3ee00d8882b5 | |
parent | Fix for ICE-8260 and ICE-8168 - .NET connection closure hang (diff) | |
download | ice-2d8b44932779f887ce47c43e25dbda72b817e45d.tar.bz2 ice-2d8b44932779f887ce47c43e25dbda72b817e45d.tar.xz ice-2d8b44932779f887ce47c43e25dbda72b817e45d.zip |
Added JUnit XML reports to allTests.py
-rw-r--r-- | scripts/LocalDriver.py | 37 | ||||
-rw-r--r-- | scripts/Util.py | 61 |
2 files changed, 88 insertions, 10 deletions
diff --git a/scripts/LocalDriver.py b/scripts/LocalDriver.py index af6efbd2739..5a839622c3f 100644 --- a/scripts/LocalDriver.py +++ b/scripts/LocalDriver.py @@ -301,6 +301,23 @@ class RemoteTestCaseRunner(TestCaseRunner): current.config.cprops, current.config.sprops) +class XmlExporter: + + def __init__(self, results, duration, failures): + self.results = results + self.duration = duration + self.failures = failures + + def save(self, filename, hostname): + with open(filename, "w") as out: + out.write('<?xml version="1.1" encoding="UTF-8"?>\n') + out.write('<testsuites tests="{0}" failures="{1}" time="{2}">\n'.format(len(self.results), + self.duration, + len(self.failures))) + for r in self.results: + r.writeAsXml(out, hostname) + out.write('</testsuites>\n') + class LocalDriver(Driver): class Current(Driver.Current): @@ -313,7 +330,7 @@ class LocalDriver(Driver): @classmethod def getSupportedArgs(self): return ("", ["cross=", "workers=", "continue", "loop", "start=", "all", "all-cross", "host=", - "client=", "server=", "show-durations"]) + "client=", "server=", "show-durations", "export-xml="]) @classmethod def usage(self): @@ -329,6 +346,7 @@ class LocalDriver(Driver): print("--client=<proxy> The endpoint of the controller to run the client side.") print("--server=<proxy> The endpoint of the controller to run the server side.") print("--show-durations Print out the duration of each tests.") + print("--export-xml=<file> Export JUnit XML test report.") def __init__(self, options, *args, **kargs): Driver.__init__(self, options, *args, **kargs) @@ -341,6 +359,7 @@ class LocalDriver(Driver): self.start = 0 self.all = False self.showDurations = False + self.exportToXml = "" self.clientCtlPrx = "" self.serverCtlPrx = "" @@ -350,7 +369,8 @@ class LocalDriver(Driver): "all-cross" : "allCross", "client" : "clientCtlPrx", "server" : "serverCtlPrx", - "show-durations" : "showDurations" }) + "show-durations" : "showDurations", + "export-xml" : "exportToXml" }) if self.cross: self.cross = Mapping.getByName(self.cross) @@ -409,7 +429,12 @@ class LocalDriver(Driver): Expect.cleanup() # Cleanup processes which might still be around failures = [r for r in results if not r.isSuccess()] - m, s = divmod(time.time() - now, 60) + duration = time.time() - now + + if self.exportToXml: + XmlExporter(results, duration, failures).save(self.exportToXml, os.getenv("NODE_NAME", "")) + + m, s = divmod(duration, 60) print("") if m > 0: print("Ran {0} tests in {1} minutes {2:02.2f} seconds".format(len(results), m, s)) @@ -505,7 +530,7 @@ class LocalDriver(Driver): if cross and server.getMapping() != cross: if not self.allCross: - current.writeln("skipped, no server available for `{0}' mapping".format(cross)) + current.result.skipped(self.testcase, "no server available for `{0}' mapping".format(cross)) continue current.writeln("[ running {0} test - {1} ]".format(current.testcase, time.strftime("%x %X"))) @@ -517,7 +542,7 @@ class LocalDriver(Driver): if cross: current.writeln("- Mappings: {0},{1}".format(client.getMapping(), server.getMapping())) if not current.config.canRun(current) or not current.testcase.canRun(current): - current.writeln("skipped, not supported with this configuration") + current.result.skipped(self.testcase, "not supported with this configuration") return success = False @@ -538,7 +563,7 @@ class LocalDriver(Driver): if confStr: current.writeln("- Config: {0}".format(confStr)) if not current.config.canRun(current) or not current.testcase.canRun(current): - current.writeln("skipped, not supported with this configuration") + current.result.skipped(self.testcase, "not supported with this configuration") return current.testcase._runClientSide(current) diff --git a/scripts/Util.py b/scripts/Util.py index 7d83362842d..c792a187257 100644 --- a/scripts/Util.py +++ b/scripts/Util.py @@ -9,6 +9,8 @@ import os, sys, runpy, getopt, traceback, types, threading, time, datetime, re, itertools, random, subprocess, shutil, copy, inspect +import xml.sax.saxutils + isPython2 = sys.version_info[0] == 2 if isPython2: import Queue as queue @@ -48,6 +50,13 @@ def val(v, escapeQuotes=False, quoteValue=True): else: return str(v) +def escape(s): + # Remove backspace characters from the output (they aren't accepted by Jenkins XML parser) + if isPython2: + return xml.sax.saxutils.escape("".join(ch for ch in unicode(s) if ch != u"\u0008").encode("utf-8")) + else: + return xml.sax.saxutils.escape("".join(ch for ch in s if ch != u"\u0008")) + def getIceSoVersion(): config = open(os.path.join(toplevel, "cpp", "include", "IceUtil", "Config.h"), "r") intVersion = int(re.search("ICE_INT_VERSION ([0-9]*)", config.read()).group(1)) @@ -1547,13 +1556,14 @@ class Result: def __init__(self, testsuite, writeToStdout): self.testsuite = testsuite - self._skipped = [] self._failed = {} self._succeeded = [] + self._skipped = [] self._stdout = StringIO() self._writeToStdout = writeToStdout self._testcases = {} self._duration = 0 + self._testCaseDuration = 0; def start(self): self._duration = time.time() @@ -1562,11 +1572,13 @@ class Result: self._duration = time.time() - self._duration def started(self, testcase): + self._testCaseDuration = time.time(); self._start = self._stdout.tell() def failed(self, testcase, exception): + self._testCaseDuration = time.time() - self._testCaseDuration; self.writeln("\ntest in {0} failed:\n{1}".format(self.testsuite, exception)) - self._testcases[testcase] = (self._start, self._stdout.tell()) + self._testcases[testcase] = (self._start, self._stdout.tell(), self._testCaseDuration) self._failed[testcase] = exception output = self.getOutput(testcase) for s in ["EADDRINUSE", "Address already in use"]: @@ -1578,9 +1590,16 @@ class Result: self.writeln(run("lsof -n -P -i; ps ax")) def succeeded(self, testcase): - self._testcases[testcase] = (self._start, self._stdout.tell()) + self._testCaseDuration = time.time() - self._testCaseDuration; + self._testcases[testcase] = (self._start, self._stdout.tell(), self._testCaseDuration) self._succeeded.append(testcase) + def skipped(self, testcase, reason): + self._start = self._stdout.tell() + self.writeln("skipped, " + reason) + self._testcases[testcase] = (self._start, self._stdout.tell(), 0) + self._skipped[testcase] = reason + def isSuccess(self): return len(self._failed) == 0 @@ -1593,7 +1612,7 @@ class Result: def getOutput(self, testcase=None): if testcase: if testcase in self._testcases: - (start, end) = self._testcases[testcase] + (start, end, duration) = self._testcases[testcase] self._stdout.seek(start) try: return self._stdout.read(end - start) @@ -1632,6 +1651,40 @@ class Result: self._stdout.write(msg) self._stdout.write("\n") + def writeAsXml(self, out, hostname=""): + out.write(' <testsuite tests="{0}" failures="{1}" skipped="{2}" time="{3}" name="{4}">\n' + .format(len(self._testcases) - 2, + len(self._failed), + len(self._skipped), + self._duration, + self.testsuite)) + + for (tc, v) in self._testcases.items(): + if isinstance(tc, TestCase): + (s, e, d) = v + out.write(' <testcase name="{0}" time="{1}" classname="{2}.{3}">\n' + .format(tc, + d, + self.testsuite.getMapping(), + self.testsuite.getId().replace("/", "."))) + if tc in self._failed: + last = self._failed[tc].strip().split('\n') + if len(last) > 0: + last = last[len(last) - 1] + if hostname: + last = "Failed on {0}\n{1}".format(hostname, last) + out.write(' <failure message="{1}">{0}</failure>\n'.format(escape(self._failed[tc]), last)) + elif tc in self._skipped: + out.write(' <skipped message="{0}"/>\n'.format(escape(self._skipped[tc]))) + out.write(' <system-out>\n') + if hostname: + out.write('Running on {0}\n'.format(hostname)) + out.write(escape(self.getOutput(tc))) + out.write(' </system-out>\n') + out.write(' </testcase>\n') + + out.write( '</testsuite>\n') + class TestSuite: def __init__(self, path, testcases=None, options=None, libDirs=None, runOnMainThread=False, chdir=False, |