1"""Gathers output from test runs and create an XML file in JUnit format.
2
3The output files from the individual tests have been written in a directory
4structure like:
5
6  $DIR/joblog  (output from "parallel --joblog joblog")
7  $DIR/logs/1/cpp/stdout
8  $DIR/logs/1/cpp/stderr
9  $DIR/logs/1/csharp/stdout
10  $DIR/logs/1/csharp/stderr
11  $DIR/logs/1/java_jdk7/stdout
12  $DIR/logs/1/java_jdk7/stderr
13  etc.
14
15This script bundles them into a single output XML file so Jenkins can show
16detailed test results.  It runs as the last step before the Jenkins build
17finishes.
18"""
19
20import os;
21import sys;
22from yattag import Doc
23from collections import defaultdict
24
25def readtests(basedir):
26  tests = defaultdict(dict)
27
28  # Sample input (note: separators are tabs).
29  #
30  # Seq	Host	Starttime	Runtime	Send	Receive	Exitval	Signal	Command
31  # 1	:	1456263838.313	0.005	0	0	0	0	echo A
32  with open(basedir + "/joblog") as jobs:
33    firstline = next(jobs)
34    for line in jobs:
35      values = line.split("\t")
36
37      name = values[8].split()[-1]
38      test = tests[name]
39      test["name"] = name
40      test["time"] = values[3]
41
42      exitval = values[6]
43      if int(exitval):
44        # We don't have a more specific message.  User should look at stderr.
45        test["failure"] = "TEST FAILURE"
46      else:
47        test["failure"] = False
48
49  for testname in os.listdir(basedir + "/logs/1"):
50    test = tests[testname]
51
52    with open(basedir + "/logs/1/" + testname + "/stdout") as f:
53      test["stdout"] = f.read()
54
55    with open(basedir + "/logs/1/" + testname + "/stderr") as f:
56      test["stderr"] = f.read()
57
58  # The cpp test is special since it doesn't run under parallel so doesn't show
59  # up in the job log.
60  tests["cpp"]["name"] = "cpp"
61
62  with open(basedir + '/logs/1/cpp/build_time', 'r') as f:
63    tests["cpp"]["time"] = f.read().strip()
64  tests["cpp"]["failure"] = False
65
66  ret = tests.values()
67  ret.sort(key=lambda x: x["name"])
68
69  return ret
70
71def genxml(tests):
72  doc, tag, text = Doc().tagtext()
73
74  with tag("testsuites"):
75    with tag("testsuite", name="Protobuf Tests"):
76      for test in tests:
77        with tag("testcase", name=test["name"], classname=test["name"],
78                             time=test["time"]):
79          with tag("system-out"):
80            text(test["stdout"])
81          with tag("system-err"):
82            text(test["stderr"])
83          if test["failure"]:
84            with tag("failure"):
85              text(test["failure"])
86
87  return doc.getvalue()
88
89sys.stderr.write("make_test_output.py: writing XML from directory: " +
90                 sys.argv[1] + "\n");
91print genxml(readtests(sys.argv[1]))
92