1823b8294SYaroslav Brustinov# -*- coding: utf-8 -*-
2823b8294SYaroslav Brustinovimport xml.etree.ElementTree as ET
33f747bcfSYaroslav Brustinovimport outer_packages
4823b8294SYaroslav Brustinovimport argparse
5823b8294SYaroslav Brustinovimport glob
6823b8294SYaroslav Brustinovfrom pprint import pprint
7823b8294SYaroslav Brustinovimport sys, os
8823b8294SYaroslav Brustinovfrom collections import OrderedDict
9823b8294SYaroslav Brustinovimport copy
10823b8294SYaroslav Brustinovimport datetime, time
112766ec01SYaroslav Brustinovimport traceback
122766ec01SYaroslav Brustinovimport yaml
13823b8294SYaroslav Brustinovimport subprocess, shlex
143f747bcfSYaroslav Brustinovfrom ansi2html import Ansi2HTMLConverter
153f747bcfSYaroslav Brustinov
163f747bcfSYaroslav Brustinovconverter = Ansi2HTMLConverter(inline = True)
173f747bcfSYaroslav Brustinovconvert = converter.convert
183f747bcfSYaroslav Brustinov
193f747bcfSYaroslav Brustinovdef ansi2html(text):
203f747bcfSYaroslav Brustinov    return convert(text, full = False)
21823b8294SYaroslav Brustinov
22823b8294SYaroslav BrustinovFUNCTIONAL_CATEGORY = 'Functional' # how to display those categories
23823b8294SYaroslav BrustinovERROR_CATEGORY = 'Error'
24823b8294SYaroslav Brustinov
25823b8294SYaroslav Brustinov
262766ec01SYaroslav Brustinovdef try_write(file, text):
272766ec01SYaroslav Brustinov    try:
282766ec01SYaroslav Brustinov        file.write(text)
292766ec01SYaroslav Brustinov    except:
302766ec01SYaroslav Brustinov        try:
312766ec01SYaroslav Brustinov            file.write(text.encode('utf-8'))
322766ec01SYaroslav Brustinov        except:
332766ec01SYaroslav Brustinov            file.write(text.decode('utf-8'))
342766ec01SYaroslav Brustinov
35823b8294SYaroslav Brustinovdef pad_tag(text, tag):
36823b8294SYaroslav Brustinov    return '<%s>%s</%s>' % (tag, text, tag)
37823b8294SYaroslav Brustinov
3851f3f97dSYaroslav Brustinovdef mark_string(text, color, condition):
3951f3f97dSYaroslav Brustinov    if condition:
4051f3f97dSYaroslav Brustinov        return '<font color=%s><b>%s</b></font>' % (color, text)
4151f3f97dSYaroslav Brustinov    return text
42d4b2ae7dSYaroslav Brustinov
43d4b2ae7dSYaroslav Brustinov
44823b8294SYaroslav Brustinovdef is_functional_test_name(testname):
45d7ea8e1eSYaroslav Brustinov    #if testname.startswith(('platform_', 'misc_methods_', 'vm_', 'payload_gen_', 'pkt_builder_')):
46d7ea8e1eSYaroslav Brustinov    #    return True
47d7ea8e1eSYaroslav Brustinov    #return False
48afefddfaSYaroslav Brustinov    if testname.startswith('functional_tests.'):
49afefddfaSYaroslav Brustinov        return True
50afefddfaSYaroslav Brustinov    return False
51823b8294SYaroslav Brustinov
52823b8294SYaroslav Brustinovdef is_good_status(text):
53823b8294SYaroslav Brustinov    return text in ('Successful', 'Fixed', 'Passed', 'True', 'Pass')
54823b8294SYaroslav Brustinov
55823b8294SYaroslav Brustinov# input: xml element with test result
56823b8294SYaroslav Brustinov# output string: 'error', 'failure', 'skipped', 'passed'
57823b8294SYaroslav Brustinovdef get_test_result(test):
58823b8294SYaroslav Brustinov    for child in test.getchildren():
59823b8294SYaroslav Brustinov        if child.tag in ('error', 'failure', 'skipped'):
60823b8294SYaroslav Brustinov            return child.tag
61823b8294SYaroslav Brustinov    return 'passed'
62823b8294SYaroslav Brustinov
63823b8294SYaroslav Brustinov# returns row of table with <th> and <td> columns - key: value
64823b8294SYaroslav Brustinovdef add_th_td(key, value):
65823b8294SYaroslav Brustinov    return '<tr><th>%s</th><td>%s</td></tr>\n' % (key, value)
66823b8294SYaroslav Brustinov
67823b8294SYaroslav Brustinov# returns row of table with <td> and <td> columns - key: value
68823b8294SYaroslav Brustinovdef add_td_td(key, value):
69823b8294SYaroslav Brustinov    return '<tr><td>%s</td><td>%s</td></tr>\n' % (key, value)
70823b8294SYaroslav Brustinov
71823b8294SYaroslav Brustinov# returns row of table with <th> and <th> columns - key: value
72823b8294SYaroslav Brustinovdef add_th_th(key, value):
73823b8294SYaroslav Brustinov    return '<tr><th>%s</th><th>%s</th></tr>\n' % (key, value)
74823b8294SYaroslav Brustinov
75823b8294SYaroslav Brustinov# returns <div> with table of tests under given category.
76823b8294SYaroslav Brustinov# category - string with name of category
77823b8294SYaroslav Brustinov# tests - list of tests, derived from aggregated xml report, changed a little to get easily stdout etc.
78afefddfaSYaroslav Brustinov# tests_type - stateful or stateless
79823b8294SYaroslav Brustinov# category_info_dir - folder to search for category info file
80823b8294SYaroslav Brustinov# expanded - bool, false = outputs (stdout etc.) of tests are hidden by CSS
81823b8294SYaroslav Brustinov# brief - bool, true = cut some part of tests outputs (useful for errors section with expanded flag)
82afefddfaSYaroslav Brustinovdef add_category_of_tests(category, tests, tests_type = None, category_info_dir = None, expanded = False, brief = False):
83823b8294SYaroslav Brustinov    is_actual_category = category not in (FUNCTIONAL_CATEGORY, ERROR_CATEGORY)
84afefddfaSYaroslav Brustinov    category_id = '_'.join([category, tests_type]) if tests_type else category
85afefddfaSYaroslav Brustinov    category_name = ' '.join([category, tests_type.capitalize()]) if tests_type else category
86afefddfaSYaroslav Brustinov    html_output = ''
87823b8294SYaroslav Brustinov    if is_actual_category:
88823b8294SYaroslav Brustinov        html_output += '<br><table class="reference">\n'
89823b8294SYaroslav Brustinov
903f747bcfSYaroslav Brustinov        if category_info_dir:
91823b8294SYaroslav Brustinov            category_info_file = '%s/report_%s.info' % (category_info_dir, category)
92823b8294SYaroslav Brustinov            if os.path.exists(category_info_file):
93823b8294SYaroslav Brustinov                with open(category_info_file) as f:
94823b8294SYaroslav Brustinov                    for info_line in f.readlines():
95823b8294SYaroslav Brustinov                        key_value = info_line.split(':', 1)
96501fb3b4SYaroslav Brustinov                        if key_value[0].strip() in list(trex_info_dict.keys()) + ['User']: # always 'hhaim', no need to show
97823b8294SYaroslav Brustinov                            continue
98823b8294SYaroslav Brustinov                        html_output += add_th_td('%s:' % key_value[0], key_value[1])
99823b8294SYaroslav Brustinov            else:
100823b8294SYaroslav Brustinov                html_output += add_th_td('Info:', 'No info')
101501fb3b4SYaroslav Brustinov                print('add_category_of_tests: no category info %s' % category_info_file)
102afefddfaSYaroslav Brustinov        if tests_type:
103afefddfaSYaroslav Brustinov            html_output += add_th_td('Tests type:', tests_type.capitalize())
104823b8294SYaroslav Brustinov        if len(tests):
105823b8294SYaroslav Brustinov            total_duration = 0.0
106823b8294SYaroslav Brustinov            for test in tests:
107823b8294SYaroslav Brustinov                total_duration += float(test.attrib['time'])
108823b8294SYaroslav Brustinov            html_output += add_th_td('Tests duration:', datetime.timedelta(seconds = int(total_duration)))
109823b8294SYaroslav Brustinov        html_output += '</table>\n'
110823b8294SYaroslav Brustinov
111823b8294SYaroslav Brustinov    if not len(tests):
1123f747bcfSYaroslav Brustinov        return html_output + pad_tag('<br><font color=red>No tests!</font>', 'b')
113148fd251SYaroslav Brustinov    html_output += '<br>\n<table class="reference" width="100%">\n<tr><th align="left">'
114823b8294SYaroslav Brustinov
115823b8294SYaroslav Brustinov    if category == ERROR_CATEGORY:
116823b8294SYaroslav Brustinov        html_output += 'Setup</th><th align="left">Failed tests:'
117823b8294SYaroslav Brustinov    else:
118afefddfaSYaroslav Brustinov        html_output += '%s tests:' % category_name
119823b8294SYaroslav Brustinov    html_output += '</th><th align="center">Final Result</th>\n<th align="center">Time (s)</th>\n</tr>\n'
120823b8294SYaroslav Brustinov    for test in tests:
121823b8294SYaroslav Brustinov        functional_test = is_functional_test_name(test.attrib['name'])
122823b8294SYaroslav Brustinov        if functional_test and is_actual_category:
123823b8294SYaroslav Brustinov            continue
124823b8294SYaroslav Brustinov        if category == ERROR_CATEGORY:
125823b8294SYaroslav Brustinov            test_id = ('err_' + test.attrib['classname'] + test.attrib['name']).replace('.', '_')
126823b8294SYaroslav Brustinov        else:
127afefddfaSYaroslav Brustinov            test_id = (category_id + test.attrib['name']).replace('.', '_')
128823b8294SYaroslav Brustinov        if expanded:
129823b8294SYaroslav Brustinov            html_output += '<tr>\n<th>'
130823b8294SYaroslav Brustinov        else:
131823b8294SYaroslav Brustinov            html_output += '<tr onclick=tgl_test("%s") class=linktr>\n<td class=linktext>' % test_id
132823b8294SYaroslav Brustinov        if category == ERROR_CATEGORY:
133823b8294SYaroslav Brustinov            html_output += FUNCTIONAL_CATEGORY if functional_test else test.attrib['classname']
134823b8294SYaroslav Brustinov            if expanded:
135823b8294SYaroslav Brustinov                html_output += '</th><td>'
136823b8294SYaroslav Brustinov            else:
137823b8294SYaroslav Brustinov                html_output += '</td><td class=linktext>'
138823b8294SYaroslav Brustinov        html_output += '%s</td>\n<td align="center">' % test.attrib['name']
139823b8294SYaroslav Brustinov        test_result = get_test_result(test)
140823b8294SYaroslav Brustinov        if test_result == 'error':
141823b8294SYaroslav Brustinov            html_output += '<font color="red"><b>ERROR</b></font></td>'
142823b8294SYaroslav Brustinov        elif test_result == 'failure':
143823b8294SYaroslav Brustinov            html_output += '<font color="red"><b>FAILED</b></font></td>'
144823b8294SYaroslav Brustinov        elif test_result == 'skipped':
145823b8294SYaroslav Brustinov            html_output += '<font color="blue"><b>SKIPPED</b></font></td>'
146823b8294SYaroslav Brustinov        else:
147823b8294SYaroslav Brustinov            html_output += '<font color="green"><b>PASSED</b></font></td>'
148148fd251SYaroslav Brustinov        html_output += '<td align="center"> '+ test.attrib['time'] + '</td></tr>'
149823b8294SYaroslav Brustinov
150823b8294SYaroslav Brustinov        result, result_text = test.attrib.get('result', ('', ''))
151823b8294SYaroslav Brustinov        if result_text:
152afefddfaSYaroslav Brustinov            start_index_errors_stl = result_text.find('STLError: \n******')
153afefddfaSYaroslav Brustinov            if start_index_errors_stl > 0:
154afefddfaSYaroslav Brustinov                result_text = result_text[start_index_errors_stl:].strip() # cut traceback
155148fd251SYaroslav Brustinov            start_index_errors = result_text.find('Exception: The test is failed, reasons:')
156148fd251SYaroslav Brustinov            if start_index_errors > 0:
157148fd251SYaroslav Brustinov                result_text = result_text[start_index_errors + 10:].strip() # cut traceback
1583f747bcfSYaroslav Brustinov            result_text = ansi2html(result_text)
159823b8294SYaroslav Brustinov            result_text = '<b style="color:000080;">%s:</b><br>%s<br><br>' % (result.capitalize(), result_text.replace('\n', '<br>'))
160823b8294SYaroslav Brustinov        stderr = '' if brief and result_text else test.get('stderr', '')
161823b8294SYaroslav Brustinov        if stderr:
1623f747bcfSYaroslav Brustinov            stderr = ansi2html(stderr)
163823b8294SYaroslav Brustinov            stderr = '<b style="color:000080;"><text color=000080>Stderr</text>:</b><br>%s<br><br>\n' % stderr.replace('\n', '<br>')
164823b8294SYaroslav Brustinov        stdout = '' if brief and result_text else test.get('stdout', '')
165823b8294SYaroslav Brustinov        if stdout:
1663f747bcfSYaroslav Brustinov            stdout = ansi2html(stdout)
167823b8294SYaroslav Brustinov            if brief: # cut off server logs
168823b8294SYaroslav Brustinov                stdout = stdout.split('>>>>>>>>>>>>>>>', 1)[0]
169823b8294SYaroslav Brustinov            stdout = '<b style="color:000080;">Stdout:</b><br>%s<br><br>\n' % stdout.replace('\n', '<br>')
170823b8294SYaroslav Brustinov
171823b8294SYaroslav Brustinov        html_output += '<tr style="%scolor:603000;" id="%s"><td colspan=%s>' % ('' if expanded else 'display:none;', test_id, 4 if category == ERROR_CATEGORY else 3)
172823b8294SYaroslav Brustinov        if result_text or stderr or stdout:
173823b8294SYaroslav Brustinov            html_output += '%s%s%s</td></tr>' % (result_text, stderr, stdout)
174823b8294SYaroslav Brustinov        else:
175823b8294SYaroslav Brustinov            html_output += '<b style="color:000080;">No output</b></td></tr>'
176823b8294SYaroslav Brustinov
177afefddfaSYaroslav Brustinov    html_output += '\n</table>'
178823b8294SYaroslav Brustinov    return html_output
179823b8294SYaroslav Brustinov
180e1e895a4SYaroslav Brustinovstyle_css = """
181e1e895a4SYaroslav Brustinovhtml {overflow-y:scroll;}
182e1e895a4SYaroslav Brustinov
183e1e895a4SYaroslav Brustinovbody {
184e1e895a4SYaroslav Brustinov    font-size:12px;
185e1e895a4SYaroslav Brustinov    color:#000000;
186e1e895a4SYaroslav Brustinov    background-color:#ffffff;
187e1e895a4SYaroslav Brustinov    margin:0px;
188e1e895a4SYaroslav Brustinov    font-family:verdana,helvetica,arial,sans-serif;
189e1e895a4SYaroslav Brustinov}
190e1e895a4SYaroslav Brustinov
191e1e895a4SYaroslav Brustinovdiv {width:100%;}
192e1e895a4SYaroslav Brustinov
193e1e895a4SYaroslav Brustinovtable,th,td,input,textarea {
194e1e895a4SYaroslav Brustinov    font-size:100%;
195e1e895a4SYaroslav Brustinov}
196e1e895a4SYaroslav Brustinov
197e1e895a4SYaroslav Brustinovtable.reference, table.reference_fail {
198e1e895a4SYaroslav Brustinov    background-color:#ffffff;
199e1e895a4SYaroslav Brustinov    border:1px solid #c3c3c3;
200e1e895a4SYaroslav Brustinov    border-collapse:collapse;
201e1e895a4SYaroslav Brustinov    vertical-align:middle;
202e1e895a4SYaroslav Brustinov}
203e1e895a4SYaroslav Brustinov
204e1e895a4SYaroslav Brustinovtable.reference th {
205e1e895a4SYaroslav Brustinov    background-color:#e5eecc;
206e1e895a4SYaroslav Brustinov    border:1px solid #c3c3c3;
207e1e895a4SYaroslav Brustinov    padding:3px;
208e1e895a4SYaroslav Brustinov}
209e1e895a4SYaroslav Brustinov
210e1e895a4SYaroslav Brustinovtable.reference_fail th {
211e1e895a4SYaroslav Brustinov    background-color:#ffcccc;
212e1e895a4SYaroslav Brustinov    border:1px solid #c3c3c3;
213e1e895a4SYaroslav Brustinov    padding:3px;
214e1e895a4SYaroslav Brustinov}
215e1e895a4SYaroslav Brustinov
216e1e895a4SYaroslav Brustinov
217e1e895a4SYaroslav Brustinovtable.reference td, table.reference_fail td {
218e1e895a4SYaroslav Brustinov    border:1px solid #c3c3c3;
219e1e895a4SYaroslav Brustinov    padding:3px;
220e1e895a4SYaroslav Brustinov}
221e1e895a4SYaroslav Brustinov
222e1e895a4SYaroslav Brustinova.example {font-weight:bold}
223e1e895a4SYaroslav Brustinov
224e1e895a4SYaroslav Brustinov#a:link,a:visited {color:#900B09; background-color:transparent}
225e1e895a4SYaroslav Brustinov#a:hover,a:active {color:#FF0000; background-color:transparent}
226e1e895a4SYaroslav Brustinov
227e1e895a4SYaroslav Brustinov.linktr {
228e1e895a4SYaroslav Brustinov    cursor: pointer;
229e1e895a4SYaroslav Brustinov}
230e1e895a4SYaroslav Brustinov
231e1e895a4SYaroslav Brustinov.linktext {
232e1e895a4SYaroslav Brustinov    color:#0000FF;
233e1e895a4SYaroslav Brustinov    text-decoration: underline;
234e1e895a4SYaroslav Brustinov}
235e1e895a4SYaroslav Brustinov"""
236e1e895a4SYaroslav Brustinov
237e1e895a4SYaroslav Brustinov
238823b8294SYaroslav Brustinov# main
239823b8294SYaroslav Brustinovif __name__ == '__main__':
240823b8294SYaroslav Brustinov
241823b8294SYaroslav Brustinov    # deal with input args
242823b8294SYaroslav Brustinov    argparser = argparse.ArgumentParser(description='Aggregate test results of from ./reports dir, produces xml, html, mail report.')
243823b8294SYaroslav Brustinov    argparser.add_argument('--input_dir', default='./reports',
244823b8294SYaroslav Brustinov                   help='Directory with xmls/setups info. Filenames: report_<setup name>.xml/report_<setup name>.info')
245823b8294SYaroslav Brustinov    argparser.add_argument('--output_xml', default='./reports/aggregated_tests.xml',
246823b8294SYaroslav Brustinov                   dest = 'output_xmlfile', help='Name of output xml file with aggregated results.')
247823b8294SYaroslav Brustinov    argparser.add_argument('--output_html', default='./reports/aggregated_tests.html',
248823b8294SYaroslav Brustinov                   dest = 'output_htmlfile', help='Name of output html file with aggregated results.')
249823b8294SYaroslav Brustinov    argparser.add_argument('--output_mail', default='./reports/aggregated_tests_mail.html',
250823b8294SYaroslav Brustinov                   dest = 'output_mailfile', help='Name of output html file with aggregated results for mail.')
251823b8294SYaroslav Brustinov    argparser.add_argument('--output_title', default='./reports/aggregated_tests_title.txt',
252823b8294SYaroslav Brustinov                   dest = 'output_titlefile', help='Name of output file to contain title of mail.')
253823b8294SYaroslav Brustinov    argparser.add_argument('--build_status_file', default='./reports/build_status',
254823b8294SYaroslav Brustinov                   dest = 'build_status_file', help='Name of output file to save scenaries build results (should not be wiped).')
255148fd251SYaroslav Brustinov    argparser.add_argument('--last_passed_commit', default='./reports/last_passed_commit',
256148fd251SYaroslav Brustinov                   dest = 'last_passed_commit', help='Name of output file to save last passed commit (should not be wiped).')
257823b8294SYaroslav Brustinov    args = argparser.parse_args()
258823b8294SYaroslav Brustinov
259823b8294SYaroslav Brustinov
260823b8294SYaroslav Brustinov##### get input variables/TRex commit info
261823b8294SYaroslav Brustinov
262823b8294SYaroslav Brustinov    scenario                = os.environ.get('SCENARIO')
263823b8294SYaroslav Brustinov    build_url               = os.environ.get('BUILD_URL')
264e562c918SYaroslav Brustinov    build_id                = os.environ.get('BUILD_NUM')
265504b0f99SYaroslav Brustinov    trex_repo               = os.environ.get('TREX_CORE_REPO')
266081f56e6SYaroslav Brustinov    last_commit_info_file   = os.environ.get('LAST_COMMIT_INFO')
2678ed3bb72SYaroslav Brustinov    last_commit_branch_file = os.environ.get('LAST_COMMIT_BRANCH')
268501fb3b4SYaroslav Brustinov    python_ver              = os.environ.get('PYTHON_VER')
269823b8294SYaroslav Brustinov    if not scenario:
270501fb3b4SYaroslav Brustinov        print('Warning: no environment variable SCENARIO, using default')
271823b8294SYaroslav Brustinov        scenario = 'TRex regression'
272823b8294SYaroslav Brustinov    if not build_url:
273501fb3b4SYaroslav Brustinov        print('Warning: no environment variable BUILD_URL')
274823b8294SYaroslav Brustinov    if not build_id:
275e562c918SYaroslav Brustinov        print('Warning: no environment variable BUILD_NUM')
276501fb3b4SYaroslav Brustinov    if not python_ver:
277501fb3b4SYaroslav Brustinov        print('Warning: no environment variable PYTHON_VER')
278148fd251SYaroslav Brustinov
279148fd251SYaroslav Brustinov    trex_info_dict = OrderedDict()
280148fd251SYaroslav Brustinov    for file in glob.glob('%s/report_*.info' % args.input_dir):
281148fd251SYaroslav Brustinov        with open(file) as f:
282148fd251SYaroslav Brustinov            file_lines = f.readlines()
283148fd251SYaroslav Brustinov            if not len(file_lines):
284148fd251SYaroslav Brustinov                continue # to next file
285148fd251SYaroslav Brustinov            for info_line in file_lines:
286148fd251SYaroslav Brustinov                key_value = info_line.split(':', 1)
287148fd251SYaroslav Brustinov                not_trex_keys = ['Server', 'Router', 'User']
288148fd251SYaroslav Brustinov                if key_value[0].strip() in not_trex_keys:
289148fd251SYaroslav Brustinov                    continue # to next parameters
290148fd251SYaroslav Brustinov                trex_info_dict[key_value[0].strip()] = key_value[1].strip()
291148fd251SYaroslav Brustinov            break
292148fd251SYaroslav Brustinov
2938ed3bb72SYaroslav Brustinov    branch_name = ''
2948ed3bb72SYaroslav Brustinov    if last_commit_branch_file and os.path.exists(last_commit_branch_file):
2958ed3bb72SYaroslav Brustinov        with open(last_commit_branch_file) as f:
2968ed3bb72SYaroslav Brustinov            branch_name = f.read().strip()
2978ed3bb72SYaroslav Brustinov
298823b8294SYaroslav Brustinov    trex_last_commit_info = ''
299148fd251SYaroslav Brustinov    trex_last_commit_hash = trex_info_dict.get('Git SHA')
300081f56e6SYaroslav Brustinov    if last_commit_info_file and os.path.exists(last_commit_info_file):
301081f56e6SYaroslav Brustinov        with open(last_commit_info_file) as f:
302081f56e6SYaroslav Brustinov            trex_last_commit_info = f.read().strip().replace('\n', '<br>\n')
303504b0f99SYaroslav Brustinov    elif trex_last_commit_hash and trex_repo:
304823b8294SYaroslav Brustinov        try:
305081f56e6SYaroslav Brustinov            command = 'git show %s -s' % trex_last_commit_hash
306501fb3b4SYaroslav Brustinov            print('Executing: %s' % command)
307504b0f99SYaroslav Brustinov            proc = subprocess.Popen(shlex.split(command), stdout = subprocess.PIPE, stderr = subprocess.STDOUT, cwd = trex_repo)
308081f56e6SYaroslav Brustinov            (stdout, stderr) = proc.communicate()
3092766ec01SYaroslav Brustinov            stdout = stdout.decode('utf-8', errors = 'replace')
310081f56e6SYaroslav Brustinov            print('Stdout:\n\t' + stdout.replace('\n', '\n\t'))
311081f56e6SYaroslav Brustinov            if stderr or proc.returncode:
312081f56e6SYaroslav Brustinov                print('Return code: %s' % proc.returncode)
313081f56e6SYaroslav Brustinov            trex_last_commit_info = stdout.replace('\n', '<br>\n')
314823b8294SYaroslav Brustinov        except Exception as e:
3152766ec01SYaroslav Brustinov            traceback.print_exc()
316501fb3b4SYaroslav Brustinov            print('Error getting last commit: %s' % e)
317081f56e6SYaroslav Brustinov    else:
318081f56e6SYaroslav Brustinov        print('Could not find info about commit!')
319823b8294SYaroslav Brustinov
320823b8294SYaroslav Brustinov##### get xmls: report_<setup name>.xml
321823b8294SYaroslav Brustinov
322823b8294SYaroslav Brustinov    err = []
323823b8294SYaroslav Brustinov    jobs_list = []
324823b8294SYaroslav Brustinov    jobs_file = '%s/jobs_list.info' % args.input_dir
325823b8294SYaroslav Brustinov    if os.path.exists(jobs_file):
326823b8294SYaroslav Brustinov        with open('%s/jobs_list.info' % args.input_dir) as f:
327823b8294SYaroslav Brustinov            for line in f.readlines():
328823b8294SYaroslav Brustinov                line = line.strip()
329823b8294SYaroslav Brustinov                if line:
330823b8294SYaroslav Brustinov                    jobs_list.append(line)
331823b8294SYaroslav Brustinov    else:
332823b8294SYaroslav Brustinov        message = '%s does not exist!' % jobs_file
333501fb3b4SYaroslav Brustinov        print(message)
334823b8294SYaroslav Brustinov        err.append(message)
335823b8294SYaroslav Brustinov
336823b8294SYaroslav Brustinov##### aggregate results to 1 single tree
337823b8294SYaroslav Brustinov    aggregated_root = ET.Element('testsuite')
338afefddfaSYaroslav Brustinov    test_types = ('functional', 'stateful', 'stateless')
339823b8294SYaroslav Brustinov    setups = {}
340823b8294SYaroslav Brustinov    for job in jobs_list:
341afefddfaSYaroslav Brustinov        setups[job] = {}
342afefddfaSYaroslav Brustinov        for test_type in test_types:
343afefddfaSYaroslav Brustinov            xml_file = '%s/report_%s_%s.xml' % (args.input_dir, job, test_type)
344afefddfaSYaroslav Brustinov            if not os.path.exists(xml_file):
345afefddfaSYaroslav Brustinov                continue
346afefddfaSYaroslav Brustinov            if os.path.basename(xml_file) == os.path.basename(args.output_xmlfile):
347afefddfaSYaroslav Brustinov                continue
348afefddfaSYaroslav Brustinov            setups[job][test_type] = []
349afefddfaSYaroslav Brustinov            print('Processing report: %s.%s' % (job, test_type))
350afefddfaSYaroslav Brustinov            tree = ET.parse(xml_file)
351afefddfaSYaroslav Brustinov            root = tree.getroot()
352afefddfaSYaroslav Brustinov            for key, value in root.attrib.items():
353afefddfaSYaroslav Brustinov                if key in aggregated_root.attrib and value.isdigit(): # sum total number of failed tests etc.
354afefddfaSYaroslav Brustinov                    aggregated_root.attrib[key] = str(int(value) + int(aggregated_root.attrib[key]))
355afefddfaSYaroslav Brustinov                else:
356afefddfaSYaroslav Brustinov                    aggregated_root.attrib[key] = value
357afefddfaSYaroslav Brustinov            tests = root.getchildren()
358afefddfaSYaroslav Brustinov            if not len(tests): # there should be tests:
359afefddfaSYaroslav Brustinov                message = 'No tests in xml %s' % xml_file
360501fb3b4SYaroslav Brustinov                print(message)
361afefddfaSYaroslav Brustinov                #err.append(message)
362afefddfaSYaroslav Brustinov            for test in tests:
363afefddfaSYaroslav Brustinov                setups[job][test_type].append(test)
364afefddfaSYaroslav Brustinov                test.attrib['name'] = test.attrib['classname'] + '.' + test.attrib['name']
365afefddfaSYaroslav Brustinov                test.attrib['classname'] = job
366afefddfaSYaroslav Brustinov                aggregated_root.append(test)
367afefddfaSYaroslav Brustinov        if not sum([len(x) for x in setups[job].values()]):
368afefddfaSYaroslav Brustinov            message = 'No reports from setup %s!' % job
369501fb3b4SYaroslav Brustinov            print(message)
370823b8294SYaroslav Brustinov            err.append(message)
371823b8294SYaroslav Brustinov            continue
372823b8294SYaroslav Brustinov
373d4b2ae7dSYaroslav Brustinov    total_tests_count   = int(aggregated_root.attrib.get('tests', 0))
374d4b2ae7dSYaroslav Brustinov    error_tests_count   = int(aggregated_root.attrib.get('errors', 0))
375d4b2ae7dSYaroslav Brustinov    failure_tests_count = int(aggregated_root.attrib.get('failures', 0))
376d4b2ae7dSYaroslav Brustinov    skipped_tests_count = int(aggregated_root.attrib.get('skip', 0))
377d4b2ae7dSYaroslav Brustinov    passed_tests_count  = total_tests_count - error_tests_count - failure_tests_count - skipped_tests_count
37851f3f97dSYaroslav Brustinov
37951f3f97dSYaroslav Brustinov    tests_count_string = mark_string('Total: %s' % total_tests_count, 'red', total_tests_count == 0) + ', '
38051f3f97dSYaroslav Brustinov    tests_count_string += mark_string('Passed: %s' % passed_tests_count, 'red', error_tests_count + failure_tests_count > 0) + ', '
38151f3f97dSYaroslav Brustinov    tests_count_string += mark_string('Error: %s' % error_tests_count, 'red', error_tests_count > 0) + ', '
38251f3f97dSYaroslav Brustinov    tests_count_string += mark_string('Failure: %s' % failure_tests_count, 'red', failure_tests_count > 0) + ', '
38351f3f97dSYaroslav Brustinov    tests_count_string += 'Skipped: %s' % skipped_tests_count
384d4b2ae7dSYaroslav Brustinov
385823b8294SYaroslav Brustinov##### save output xml
386823b8294SYaroslav Brustinov
387823b8294SYaroslav Brustinov    print('Writing output file: %s' % args.output_xmlfile)
388823b8294SYaroslav Brustinov    ET.ElementTree(aggregated_root).write(args.output_xmlfile)
389823b8294SYaroslav Brustinov
390823b8294SYaroslav Brustinov
391823b8294SYaroslav Brustinov##### build output html
392823b8294SYaroslav Brustinov    error_tests = []
393823b8294SYaroslav Brustinov    functional_tests = OrderedDict()
394823b8294SYaroslav Brustinov    # categorize and get output of each test
395823b8294SYaroslav Brustinov    for test in aggregated_root.getchildren(): # each test in xml
396823b8294SYaroslav Brustinov        if is_functional_test_name(test.attrib['name']):
397823b8294SYaroslav Brustinov            functional_tests[test.attrib['name']] = test
398823b8294SYaroslav Brustinov        result_tuple = None
399823b8294SYaroslav Brustinov        for child in test.getchildren():        # <system-out>, <system-err>  (<failure>, <error>, <skipped> other: passed)
400823b8294SYaroslav Brustinov#            if child.tag in ('failure', 'error'):
401823b8294SYaroslav Brustinov                #temp = copy.deepcopy(test)
402823b8294SYaroslav Brustinov                #print temp._children
403823b8294SYaroslav Brustinov                #print test._children
404823b8294SYaroslav Brustinov#                error_tests.append(test)
405823b8294SYaroslav Brustinov            if child.tag == 'failure':
406823b8294SYaroslav Brustinov                error_tests.append(test)
407823b8294SYaroslav Brustinov                result_tuple = ('failure', child.text)
408823b8294SYaroslav Brustinov            elif child.tag == 'error':
409823b8294SYaroslav Brustinov                error_tests.append(test)
410823b8294SYaroslav Brustinov                result_tuple = ('error', child.text)
411823b8294SYaroslav Brustinov            elif child.tag == 'skipped':
412823b8294SYaroslav Brustinov                result_tuple = ('skipped', child.text)
413823b8294SYaroslav Brustinov            elif child.tag == 'system-out':
414823b8294SYaroslav Brustinov                test.attrib['stdout'] = child.text
415823b8294SYaroslav Brustinov            elif child.tag == 'system-err':
416823b8294SYaroslav Brustinov                test.attrib['stderr'] = child.text
417823b8294SYaroslav Brustinov        if result_tuple:
418823b8294SYaroslav Brustinov            test.attrib['result'] = result_tuple
419823b8294SYaroslav Brustinov
420823b8294SYaroslav Brustinov    html_output = '''\
421823b8294SYaroslav Brustinov<html>
422823b8294SYaroslav Brustinov<head>
423823b8294SYaroslav Brustinov<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
424823b8294SYaroslav Brustinov<style type="text/css">
425823b8294SYaroslav Brustinov'''
426823b8294SYaroslav Brustinov    html_output += style_css
427823b8294SYaroslav Brustinov    html_output +='''
428823b8294SYaroslav Brustinov</style>
429823b8294SYaroslav Brustinov</head>
430823b8294SYaroslav Brustinov
431823b8294SYaroslav Brustinov<body>
432823b8294SYaroslav Brustinov<table class="reference">
433823b8294SYaroslav Brustinov'''
434501fb3b4SYaroslav Brustinov    if scenario:
435501fb3b4SYaroslav Brustinov        html_output += add_th_td('Scenario:', scenario.capitalize())
436501fb3b4SYaroslav Brustinov    if python_ver:
437501fb3b4SYaroslav Brustinov        html_output += add_th_td('Python:', python_ver)
438823b8294SYaroslav Brustinov    start_time_file = '%s/start_time.info' % args.input_dir
439823b8294SYaroslav Brustinov    if os.path.exists(start_time_file):
440823b8294SYaroslav Brustinov        with open(start_time_file) as f:
441823b8294SYaroslav Brustinov            start_time = int(f.read())
442823b8294SYaroslav Brustinov        total_time = int(time.time()) - start_time
4431a935f29SYaroslav Brustinov        html_output += add_th_td('Regression start:', datetime.datetime.fromtimestamp(start_time).strftime('%d/%m/%Y %H:%M'))
444148fd251SYaroslav Brustinov        html_output += add_th_td('Regression duration:', datetime.timedelta(seconds = total_time))
445d4b2ae7dSYaroslav Brustinov    html_output += add_th_td('Tests count:', tests_count_string)
446148fd251SYaroslav Brustinov    for key in trex_info_dict:
447148fd251SYaroslav Brustinov        if key == 'Git SHA':
448148fd251SYaroslav Brustinov            continue
449148fd251SYaroslav Brustinov        html_output += add_th_td(key, trex_info_dict[key])
450823b8294SYaroslav Brustinov    if trex_last_commit_info:
451823b8294SYaroslav Brustinov        html_output += add_th_td('Last commit:', trex_last_commit_info)
452823b8294SYaroslav Brustinov    html_output += '</table><br>\n'
453823b8294SYaroslav Brustinov    if err:
454823b8294SYaroslav Brustinov        html_output += '<font color=red>%s<font><br><br>\n' % '\n<br>'.join(err)
455823b8294SYaroslav Brustinov
456823b8294SYaroslav Brustinov#<table style="width:100%;">
457823b8294SYaroslav Brustinov#    <tr>
458823b8294SYaroslav Brustinov#        <td>Summary:</td>\
459823b8294SYaroslav Brustinov#'''
460823b8294SYaroslav Brustinov    #passed_quantity = len(result_types['passed'])
461823b8294SYaroslav Brustinov    #failed_quantity = len(result_types['failed'])
462823b8294SYaroslav Brustinov    #error_quantity = len(result_types['error'])
463823b8294SYaroslav Brustinov    #skipped_quantity = len(result_types['skipped'])
464823b8294SYaroslav Brustinov
465823b8294SYaroslav Brustinov    #html_output += '<td>Passed: %s</td>' % passed_quantity
466823b8294SYaroslav Brustinov    #html_output += '<td>Failed: %s</td>' % (pad_tag(failed_quantity, 'b') if failed_quantity else '0')
467823b8294SYaroslav Brustinov    #html_output += '<td>Error: %s</td>' % (pad_tag(error_quantity, 'b') if error_quantity else '0')
468823b8294SYaroslav Brustinov    #html_output += '<td>Skipped: %s</td>' % (pad_tag(skipped_quantity, 'b') if skipped_quantity else '0')
469823b8294SYaroslav Brustinov#    html_output += '''
470823b8294SYaroslav Brustinov#    </tr>
471823b8294SYaroslav Brustinov#</table>'''
472823b8294SYaroslav Brustinov
473823b8294SYaroslav Brustinov    category_arr = [FUNCTIONAL_CATEGORY, ERROR_CATEGORY]
474823b8294SYaroslav Brustinov
475823b8294SYaroslav Brustinov# Adding buttons
476823b8294SYaroslav Brustinov    # Error button
477823b8294SYaroslav Brustinov    if len(error_tests):
478823b8294SYaroslav Brustinov        html_output += '\n<button onclick=tgl_cat("cat_tglr_{error}")>{error}</button>'.format(error = ERROR_CATEGORY)
479823b8294SYaroslav Brustinov    # Setups buttons
480429072c5SYaroslav Brustinov    for category in sorted(setups.keys()):
481823b8294SYaroslav Brustinov        category_arr.append(category)
482823b8294SYaroslav Brustinov        html_output += '\n<button onclick=tgl_cat("cat_tglr_%s")>%s</button>' % (category_arr[-1], category)
483823b8294SYaroslav Brustinov    # Functional buttons
484823b8294SYaroslav Brustinov    if len(functional_tests):
485823b8294SYaroslav Brustinov        html_output += '\n<button onclick=tgl_cat("cat_tglr_%s")>%s</button>' % (FUNCTIONAL_CATEGORY, FUNCTIONAL_CATEGORY)
486823b8294SYaroslav Brustinov
487823b8294SYaroslav Brustinov# Adding tests
488823b8294SYaroslav Brustinov    # Error tests
489823b8294SYaroslav Brustinov    if len(error_tests):
490afefddfaSYaroslav Brustinov        html_output += '<div style="display:block;" id="cat_tglr_%s">' % ERROR_CATEGORY
491afefddfaSYaroslav Brustinov        html_output += add_category_of_tests(ERROR_CATEGORY, error_tests)
492afefddfaSYaroslav Brustinov        html_output += '</div>'
493823b8294SYaroslav Brustinov    # Setups tests
494823b8294SYaroslav Brustinov    for category, tests in setups.items():
495afefddfaSYaroslav Brustinov        html_output += '<div style="display:none;" id="cat_tglr_%s">' % category
4963f747bcfSYaroslav Brustinov        if 'stateful' in tests:
497afefddfaSYaroslav Brustinov            html_output += add_category_of_tests(category, tests['stateful'], 'stateful', category_info_dir=args.input_dir)
4983f747bcfSYaroslav Brustinov        if 'stateless' in tests:
4993f747bcfSYaroslav Brustinov            html_output += add_category_of_tests(category, tests['stateless'], 'stateless', category_info_dir=(None if 'stateful' in tests else args.input_dir))
500afefddfaSYaroslav Brustinov        html_output += '</div>'
501823b8294SYaroslav Brustinov    # Functional tests
502823b8294SYaroslav Brustinov    if len(functional_tests):
503afefddfaSYaroslav Brustinov        html_output += '<div style="display:none;" id="cat_tglr_%s">' % FUNCTIONAL_CATEGORY
504afefddfaSYaroslav Brustinov        html_output += add_category_of_tests(FUNCTIONAL_CATEGORY, functional_tests.values())
505afefddfaSYaroslav Brustinov        html_output += '</div>'
506823b8294SYaroslav Brustinov
507823b8294SYaroslav Brustinov    html_output += '\n\n<script type="text/javascript">\n    var category_arr = %s\n' % ['cat_tglr_%s' % x for x in category_arr]
508823b8294SYaroslav Brustinov    html_output += '''
509823b8294SYaroslav Brustinov    function tgl_cat(id)
510823b8294SYaroslav Brustinov        {
511823b8294SYaroslav Brustinov        for(var i=0; i<category_arr.length; i++)
512823b8294SYaroslav Brustinov            {
513823b8294SYaroslav Brustinov            var e = document.getElementById(category_arr[i]);
514823b8294SYaroslav Brustinov            if (id == category_arr[i])
515823b8294SYaroslav Brustinov                {
516823b8294SYaroslav Brustinov                if(e.style.display == 'block')
517823b8294SYaroslav Brustinov                    e.style.display = 'none';
518823b8294SYaroslav Brustinov                else
519823b8294SYaroslav Brustinov                    e.style.display = 'block';
520823b8294SYaroslav Brustinov                }
521823b8294SYaroslav Brustinov            else
522823b8294SYaroslav Brustinov                {
523823b8294SYaroslav Brustinov                if (e) e.style.display = 'none';
524823b8294SYaroslav Brustinov                }
525823b8294SYaroslav Brustinov            }
526823b8294SYaroslav Brustinov        }
527823b8294SYaroslav Brustinov    function tgl_test(id)
528823b8294SYaroslav Brustinov        {
529823b8294SYaroslav Brustinov        var e = document.getElementById(id);
530823b8294SYaroslav Brustinov        if(e.style.display == 'table-row')
531823b8294SYaroslav Brustinov            e.style.display = 'none';
532823b8294SYaroslav Brustinov        else
533823b8294SYaroslav Brustinov            e.style.display = 'table-row';
534823b8294SYaroslav Brustinov        }
535823b8294SYaroslav Brustinov</script>
536823b8294SYaroslav Brustinov</body>
537823b8294SYaroslav Brustinov</html>\
538823b8294SYaroslav Brustinov'''
539823b8294SYaroslav Brustinov
540148fd251SYaroslav Brustinov# save html
541148fd251SYaroslav Brustinov    with open(args.output_htmlfile, 'w') as f:
542148fd251SYaroslav Brustinov        print('Writing output file: %s' % args.output_htmlfile)
5432766ec01SYaroslav Brustinov        try_write(f, html_output)
544148fd251SYaroslav Brustinov    html_output = None
545148fd251SYaroslav Brustinov
546823b8294SYaroslav Brustinov# mail report (only error tests, expanded)
547823b8294SYaroslav Brustinov
548823b8294SYaroslav Brustinov    mail_output = '''\
549823b8294SYaroslav Brustinov<html>
550823b8294SYaroslav Brustinov<head>
551823b8294SYaroslav Brustinov<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
552823b8294SYaroslav Brustinov<style type="text/css">
553823b8294SYaroslav Brustinov'''
554823b8294SYaroslav Brustinov    mail_output += style_css
555823b8294SYaroslav Brustinov    mail_output +='''
556823b8294SYaroslav Brustinov</style>
557823b8294SYaroslav Brustinov</head>
558823b8294SYaroslav Brustinov
559823b8294SYaroslav Brustinov<body>
560823b8294SYaroslav Brustinov<table class="reference">
561823b8294SYaroslav Brustinov'''
562501fb3b4SYaroslav Brustinov    if scenario:
563501fb3b4SYaroslav Brustinov        mail_output += add_th_td('Scenario:', scenario.capitalize())
564501fb3b4SYaroslav Brustinov    if python_ver:
565501fb3b4SYaroslav Brustinov        mail_output += add_th_td('Python:', python_ver)
566823b8294SYaroslav Brustinov    if build_url:
567823b8294SYaroslav Brustinov        mail_output += add_th_td('Full HTML report:', '<a class="example" href="%s/HTML_Report">link</a>' % build_url)
568823b8294SYaroslav Brustinov    start_time_file = '%s/start_time.info' % args.input_dir
569823b8294SYaroslav Brustinov    if os.path.exists(start_time_file):
570823b8294SYaroslav Brustinov        with open(start_time_file) as f:
571823b8294SYaroslav Brustinov            start_time = int(f.read())
572823b8294SYaroslav Brustinov        total_time = int(time.time()) - start_time
5731a935f29SYaroslav Brustinov        mail_output += add_th_td('Regression start:', datetime.datetime.fromtimestamp(start_time).strftime('%d/%m/%Y %H:%M'))
574148fd251SYaroslav Brustinov        mail_output += add_th_td('Regression duration:', datetime.timedelta(seconds = total_time))
575d4b2ae7dSYaroslav Brustinov    mail_output += add_th_td('Tests count:', tests_count_string)
576148fd251SYaroslav Brustinov    for key in trex_info_dict:
577148fd251SYaroslav Brustinov        if key == 'Git SHA':
578148fd251SYaroslav Brustinov            continue
579148fd251SYaroslav Brustinov        mail_output += add_th_td(key, trex_info_dict[key])
580148fd251SYaroslav Brustinov
581823b8294SYaroslav Brustinov    if trex_last_commit_info:
582823b8294SYaroslav Brustinov        mail_output += add_th_td('Last commit:', trex_last_commit_info)
583823b8294SYaroslav Brustinov    mail_output += '</table><br>\n<table width=100%><tr><td>\n'
584823b8294SYaroslav Brustinov
585823b8294SYaroslav Brustinov    for category in setups.keys():
586823b8294SYaroslav Brustinov        failing_category = False
587823b8294SYaroslav Brustinov        for test in error_tests:
588823b8294SYaroslav Brustinov            if test.attrib['classname'] == category:
589823b8294SYaroslav Brustinov                failing_category = True
590afefddfaSYaroslav Brustinov        if failing_category or not len(setups[category]) or not sum([len(x) for x in setups[category]]):
591823b8294SYaroslav Brustinov            mail_output += '<table class="reference_fail" align=left style="Margin-bottom:10;Margin-right:10;">\n'
592823b8294SYaroslav Brustinov        else:
593823b8294SYaroslav Brustinov            mail_output += '<table class="reference" align=left style="Margin-bottom:10;Margin-right:10;">\n'
594823b8294SYaroslav Brustinov        mail_output += add_th_th('Setup:', pad_tag(category.replace('.', '/'), 'b'))
595823b8294SYaroslav Brustinov        category_info_file = '%s/report_%s.info' % (args.input_dir, category.replace('.', '_'))
596823b8294SYaroslav Brustinov        if os.path.exists(category_info_file):
597823b8294SYaroslav Brustinov            with open(category_info_file) as f:
598823b8294SYaroslav Brustinov                for info_line in f.readlines():
599823b8294SYaroslav Brustinov                    key_value = info_line.split(':', 1)
600501fb3b4SYaroslav Brustinov                    if key_value[0].strip() in list(trex_info_dict.keys()) + ['User']: # always 'hhaim', no need to show
601823b8294SYaroslav Brustinov                        continue
602148fd251SYaroslav Brustinov                    mail_output += add_th_td('%s:' % key_value[0].strip(), key_value[1].strip())
603823b8294SYaroslav Brustinov        else:
604823b8294SYaroslav Brustinov            mail_output += add_th_td('Info:', 'No info')
605823b8294SYaroslav Brustinov        mail_output += '</table>\n'
606823b8294SYaroslav Brustinov    mail_output += '</td></tr></table>\n'
607823b8294SYaroslav Brustinov
608823b8294SYaroslav Brustinov    # Error tests
609823b8294SYaroslav Brustinov    if len(error_tests) or err:
610823b8294SYaroslav Brustinov        if err:
611823b8294SYaroslav Brustinov            mail_output += '<font color=red>%s<font>' % '\n<br>'.join(err)
612823b8294SYaroslav Brustinov        if len(error_tests) > 5:
613148fd251SYaroslav Brustinov            mail_output += '\n<font color=red>More than 5 failed tests, showing brief output.<font>\n<br>'
614823b8294SYaroslav Brustinov            # show only brief version (cut some info)
615afefddfaSYaroslav Brustinov            mail_output += add_category_of_tests(ERROR_CATEGORY, error_tests, expanded=True, brief=True)
616823b8294SYaroslav Brustinov        else:
617afefddfaSYaroslav Brustinov            mail_output += add_category_of_tests(ERROR_CATEGORY, error_tests, expanded=True)
618823b8294SYaroslav Brustinov    else:
6192766ec01SYaroslav Brustinov        mail_output += u'<table><tr style="font-size:120;color:green;font-family:arial"><td>☺</td><td style="font-size:20">All passed.</td></tr></table>\n'
620823b8294SYaroslav Brustinov    mail_output += '\n</body>\n</html>'
621823b8294SYaroslav Brustinov
622823b8294SYaroslav Brustinov##### save outputs
623823b8294SYaroslav Brustinov
624823b8294SYaroslav Brustinov
625823b8294SYaroslav Brustinov# mail content
626823b8294SYaroslav Brustinov    with open(args.output_mailfile, 'w') as f:
627823b8294SYaroslav Brustinov        print('Writing output file: %s' % args.output_mailfile)
6282766ec01SYaroslav Brustinov        try_write(f, mail_output)
629823b8294SYaroslav Brustinov
630823b8294SYaroslav Brustinov# build status
631823b8294SYaroslav Brustinov    category_dict_status = {}
632823b8294SYaroslav Brustinov    if os.path.exists(args.build_status_file):
633501fb3b4SYaroslav Brustinov        print('Reading: %s' % args.build_status_file)
6342766ec01SYaroslav Brustinov        with open(args.build_status_file, 'r') as f:
635501fb3b4SYaroslav Brustinov            try:
6362766ec01SYaroslav Brustinov                category_dict_status = yaml.safe_load(f.read())
637501fb3b4SYaroslav Brustinov            except Exception as e:
6382766ec01SYaroslav Brustinov                print('Error during YAML load: %s' % e)
639501fb3b4SYaroslav Brustinov        if type(category_dict_status) is not dict:
640501fb3b4SYaroslav Brustinov            print('%s is corrupt, truncating' % args.build_status_file)
641501fb3b4SYaroslav Brustinov            category_dict_status = {}
642823b8294SYaroslav Brustinov
643823b8294SYaroslav Brustinov    last_status = category_dict_status.get(scenario, 'Successful') # assume last is passed if no history
644823b8294SYaroslav Brustinov    if err or len(error_tests): # has fails
645a5cc1c90SYaroslav Brustinov        exit_status = 1
646823b8294SYaroslav Brustinov        if is_good_status(last_status):
647823b8294SYaroslav Brustinov            current_status = 'Failure'
648823b8294SYaroslav Brustinov        else:
649823b8294SYaroslav Brustinov            current_status = 'Still Failing'
650823b8294SYaroslav Brustinov    else:
651a5cc1c90SYaroslav Brustinov        exit_status = 0
652823b8294SYaroslav Brustinov        if is_good_status(last_status):
653823b8294SYaroslav Brustinov            current_status = 'Successful'
654823b8294SYaroslav Brustinov        else:
655823b8294SYaroslav Brustinov            current_status = 'Fixed'
656823b8294SYaroslav Brustinov    category_dict_status[scenario] = current_status
657823b8294SYaroslav Brustinov
6582766ec01SYaroslav Brustinov    with open(args.build_status_file, 'w') as f:
659823b8294SYaroslav Brustinov        print('Writing output file: %s' % args.build_status_file)
6602766ec01SYaroslav Brustinov        yaml.dump(category_dict_status, f)
661823b8294SYaroslav Brustinov
662148fd251SYaroslav Brustinov# last successful commit
6632766ec01SYaroslav Brustinov    if (current_status in ('Successful', 'Fixed')) and trex_last_commit_hash and len(jobs_list) > 0 and scenario == 'nightly':
664148fd251SYaroslav Brustinov        with open(args.last_passed_commit, 'w') as f:
665148fd251SYaroslav Brustinov            print('Writing output file: %s' % args.last_passed_commit)
6662766ec01SYaroslav Brustinov            try_write(f, trex_last_commit_hash)
667148fd251SYaroslav Brustinov
668823b8294SYaroslav Brustinov# mail title
669823b8294SYaroslav Brustinov    mailtitle_output = scenario.capitalize()
6708ed3bb72SYaroslav Brustinov    if branch_name:
6718ed3bb72SYaroslav Brustinov        mailtitle_output += ' (%s)' % branch_name
672823b8294SYaroslav Brustinov    if build_id:
673823b8294SYaroslav Brustinov        mailtitle_output += ' - Build #%s' % build_id
674823b8294SYaroslav Brustinov    mailtitle_output += ' - %s!' % current_status
675148fd251SYaroslav Brustinov
676823b8294SYaroslav Brustinov    with open(args.output_titlefile, 'w') as f:
677823b8294SYaroslav Brustinov        print('Writing output file: %s' % args.output_titlefile)
6782766ec01SYaroslav Brustinov        try_write(f, mailtitle_output)
679a5cc1c90SYaroslav Brustinov
680a5cc1c90SYaroslav Brustinov# exit
6812766ec01SYaroslav Brustinov    print('Status: %s' % current_status)
682a5cc1c90SYaroslav Brustinov    sys.exit(exit_status)
683