Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(20)

Side by Side Diff: build/android/pylib/results/presentation/test_results_presentation.py

Issue 2786773002: (Reland) Add failure screenshots and images to results detail. (Closed)
Patch Set: (Reland) Add failure screenshots and images to results detail. Created 3 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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
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())
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698