OLD | NEW |
1 #! /usr/bin/env python | 1 #! /usr/bin/env python |
2 # | 2 # |
3 # Copyright 2017 The Chromium Authors. All rights reserved. | 3 # Copyright 2017 The Chromium Authors. All rights reserved. |
4 # Use of this source code is governed by a BSD-style license that can be | 4 # Use of this source code is governed by a BSD-style license that can be |
5 # found in the LICENSE file. | 5 # found in the LICENSE file. |
6 | 6 |
7 import argparse | 7 import argparse |
8 import collections | 8 import collections |
9 import json | 9 import json |
10 import tempfile | 10 import tempfile |
11 import time | |
12 import os | 11 import os |
13 import subprocess | |
14 import sys | 12 import sys |
15 | 13 |
16 CURRENT_DIR = os.path.dirname(os.path.abspath(__file__)) | 14 CURRENT_DIR = os.path.dirname(os.path.abspath(__file__)) |
17 BASE_DIR = os.path.abspath(os.path.join( | 15 BASE_DIR = os.path.abspath(os.path.join( |
18 CURRENT_DIR, '..', '..', '..', '..', '..')) | 16 CURRENT_DIR, '..', '..', '..', '..', '..')) |
| 17 |
| 18 sys.path.append(os.path.join(BASE_DIR, 'build', 'android')) |
| 19 from pylib.utils import google_storage_helper # pylint: disable=import-error |
| 20 |
19 sys.path.append(os.path.join(BASE_DIR, 'third_party')) | 21 sys.path.append(os.path.join(BASE_DIR, 'third_party')) |
20 import jinja2 # pylint: disable=import-error | 22 import jinja2 # pylint: disable=import-error |
21 JINJA_ENVIRONMENT = jinja2.Environment( | 23 JINJA_ENVIRONMENT = jinja2.Environment( |
22 loader=jinja2.FileSystemLoader(os.path.dirname(__file__)), | 24 loader=jinja2.FileSystemLoader(os.path.dirname(__file__)), |
23 autoescape=True) | 25 autoescape=True) |
24 | 26 |
25 | 27 |
26 def cell(data, html_class='center'): | 28 def cell(data, html_class='center'): |
27 """Formats table cell data for processing in jinja template.""" | 29 """Formats table cell data for processing in jinja template.""" |
28 return { | 30 return { |
(...skipping 199 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
228 if footer_row[FAIL_COUNT_INDEX]['data'] > 0: | 230 if footer_row[FAIL_COUNT_INDEX]['data'] > 0: |
229 footer_row[FAIL_COUNT_INDEX]['class'] += ' failure' | 231 footer_row[FAIL_COUNT_INDEX]['class'] += ' failure' |
230 else: | 232 else: |
231 footer_row[FAIL_COUNT_INDEX]['class'] += ' success' | 233 footer_row[FAIL_COUNT_INDEX]['class'] += ' success' |
232 | 234 |
233 return (header_row, | 235 return (header_row, |
234 [[suite_row] for suite_row in suite_row_dict.values()], | 236 [[suite_row] for suite_row in suite_row_dict.values()], |
235 footer_row) | 237 footer_row) |
236 | 238 |
237 | 239 |
238 def results_to_html(results_dict, cs_base_url, bucket, server_url): | 240 def results_to_html(results_dict, cs_base_url, bucket): |
239 """Convert list of test results into html format.""" | 241 """Convert list of test results into html format.""" |
240 | 242 |
241 test_rows_header, test_rows = create_test_table(results_dict, cs_base_url) | 243 test_rows_header, test_rows = create_test_table(results_dict, cs_base_url) |
242 suite_rows_header, suite_rows, suite_row_footer = create_suite_table( | 244 suite_rows_header, suite_rows, suite_row_footer = create_suite_table( |
243 results_dict) | 245 results_dict) |
244 | 246 |
245 suite_table_values = { | 247 suite_table_values = { |
246 'table_id': 'suite-table', | 248 'table_id': 'suite-table', |
247 'table_headers': suite_rows_header, | 249 'table_headers': suite_rows_header, |
248 'table_row_blocks': suite_rows, | 250 'table_row_blocks': suite_rows, |
249 'table_footer': suite_row_footer, | 251 'table_footer': suite_row_footer, |
250 } | 252 } |
251 | 253 |
252 test_table_values = { | 254 test_table_values = { |
253 'table_id': 'test-table', | 255 'table_id': 'test-table', |
254 'table_headers': test_rows_header, | 256 'table_headers': test_rows_header, |
255 'table_row_blocks': test_rows, | 257 'table_row_blocks': test_rows, |
256 } | 258 } |
257 | 259 |
258 main_template = JINJA_ENVIRONMENT.get_template( | 260 main_template = JINJA_ENVIRONMENT.get_template( |
259 os.path.join('template', 'main.html')) | 261 os.path.join('template', 'main.html')) |
260 return main_template.render( # pylint: disable=no-member | 262 return main_template.render( # pylint: disable=no-member |
261 {'tb_values': [suite_table_values, test_table_values], | 263 {'tb_values': [suite_table_values, test_table_values], |
262 'bucket': bucket, 'server_url': server_url}) | 264 'bucket': bucket}) |
263 | 265 |
264 | 266 |
265 def result_details(json_path, cs_base_url, bucket, server_url): | 267 def result_details(json_path, cs_base_url, bucket): |
266 """Get result details from json path and then convert results to html.""" | 268 """Get result details from json path and then convert results to html.""" |
267 | 269 |
268 with open(json_path) as json_file: | 270 with open(json_path) as json_file: |
269 json_object = json.loads(json_file.read()) | 271 json_object = json.loads(json_file.read()) |
270 | 272 |
271 if not 'per_iteration_data' in json_object: | 273 if not 'per_iteration_data' in json_object: |
272 return 'Error: json file missing per_iteration_data.' | 274 return 'Error: json file missing per_iteration_data.' |
273 | 275 |
274 results_dict = collections.defaultdict(list) | 276 results_dict = collections.defaultdict(list) |
275 for testsuite_run in json_object['per_iteration_data']: | 277 for testsuite_run in json_object['per_iteration_data']: |
276 for test, test_runs in testsuite_run.iteritems(): | 278 for test, test_runs in testsuite_run.iteritems(): |
277 results_dict[test].extend(test_runs) | 279 results_dict[test].extend(test_runs) |
278 return results_to_html(results_dict, cs_base_url, bucket, server_url) | 280 return results_to_html(results_dict, cs_base_url, bucket) |
279 | 281 |
280 | 282 |
281 def upload_to_google_bucket(html, test_name, builder_name, build_number, | 283 def upload_to_google_bucket(html, test_name, builder_name, build_number, |
282 bucket, server_url, content_type): | 284 bucket): |
283 with tempfile.NamedTemporaryFile(suffix='.html') as temp_file: | 285 with tempfile.NamedTemporaryFile(suffix='.html') as temp_file: |
284 temp_file.write(html) | 286 temp_file.write(html) |
285 temp_file.flush() | 287 temp_file.flush() |
286 dest = 'html/%s_%s_%s_%s.html' % ( | |
287 test_name, builder_name, build_number, | |
288 time.strftime('%Y_%m_%d_T%H_%M_%S')) | |
289 gsutil_path = os.path.join(BASE_DIR, 'third_party', 'catapult', | |
290 'third_party', 'gsutil', 'gsutil.py') | |
291 subprocess.check_call([ | |
292 sys.executable, gsutil_path, '-h', "Content-Type:%s" % content_type, | |
293 'cp', temp_file.name, 'gs://%s/%s' % (bucket, dest)]) | |
294 | 288 |
295 return '%s/%s/%s' % (server_url, bucket, dest) | 289 return google_storage_helper.upload( |
| 290 name=google_storage_helper.unique_name( |
| 291 '%s_%s_%s' % (test_name, builder_name, build_number), suffix='.html'), |
| 292 filepath=temp_file.name, |
| 293 bucket='%s/html' % bucket, |
| 294 content_type='text/html') |
296 | 295 |
297 def main(): | 296 def main(): |
298 parser = argparse.ArgumentParser() | 297 parser = argparse.ArgumentParser() |
299 parser.add_argument('--json-file', help='Path of json file.', required=True) | 298 parser.add_argument('--json-file', help='Path of json file.', required=True) |
300 parser.add_argument('--cs-base-url', help='Base url for code search.', | 299 parser.add_argument('--cs-base-url', help='Base url for code search.', |
301 default='http://cs.chromium.org') | 300 default='http://cs.chromium.org') |
302 parser.add_argument('--bucket', default='chromium-result-details') | 301 parser.add_argument('--bucket', default='chromium-result-details') |
303 parser.add_argument('--builder-name', help='Builder name.', required=True) | 302 parser.add_argument('--builder-name', help='Builder name.', required=True) |
304 parser.add_argument('--build-number', help='Build number.', required=True) | 303 parser.add_argument('--build-number', help='Build number.', required=True) |
305 parser.add_argument('--test-name', help='The name of the test.', | 304 parser.add_argument('--test-name', help='The name of the test.', |
306 required=True) | 305 required=True) |
307 parser.add_argument('--server-url', help='The url of the server.', | |
308 default='https://storage.googleapis.com') | |
309 parser.add_argument( | |
310 '--content-type', | |
311 help=('Content type, which is used to determine ' | |
312 'whether to download the file, or view in browser.'), | |
313 default='text/html', | |
314 choices=['text/html', 'application/octet-stream']) | |
315 | 306 |
316 args = parser.parse_args() | 307 args = parser.parse_args() |
317 if os.path.exists(args.json_file): | 308 if os.path.exists(args.json_file): |
318 result_html_string = result_details(args.json_file, args.cs_base_url, | 309 result_html_string = result_details(args.json_file, args.cs_base_url, |
319 args.bucket, args.server_url) | 310 args.bucket) |
| 311 |
320 print upload_to_google_bucket(result_html_string.encode('UTF-8'), | 312 print upload_to_google_bucket(result_html_string.encode('UTF-8'), |
321 args.test_name, args.builder_name, | 313 args.test_name, args.builder_name, |
322 args.build_number, args.bucket, | 314 args.build_number, args.bucket) |
323 args.server_url, args.content_type) | |
324 else: | 315 else: |
325 raise IOError('--json-file %s not found.' % args.json_file) | 316 raise IOError('--json-file %s not found.' % args.json_file) |
326 | 317 |
327 | 318 |
328 if __name__ == '__main__': | 319 if __name__ == '__main__': |
329 sys.exit(main()) | 320 sys.exit(main()) |
OLD | NEW |