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

Side by Side Diff: build/android/render_tests/process_render_test_results.py

Issue 2406593002: Add script to process results from Android render tests. (Closed)
Patch Set: John's comments Created 4 years, 2 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
« no previous file with comments | « no previous file | build/android/render_tests/render_webpage.html.jinja2 » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 #!/usr/bin/env python
2 #
3 # Copyright 2016 The Chromium Authors. All rights reserved.
4 # Use of this source code is governed by a BSD-style license that can be
5 # found in the LICENSE file.
6
7 import argparse
8 import collections
9 import logging
10 import os
11 import posixpath
12 import re
13 import shutil
14 import sys
15 import tempfile
16 import zipfile
17 from PIL import ImageChops
18 from PIL import Image
19
20 sys.path.append(os.path.join(os.path.dirname(__file__), os.pardir))
21 import devil_chromium
22 from devil.android import device_utils
23 from devil.utils import cmd_helper
24 from pylib.constants import host_paths
25
26 sys.path.append(os.path.join(host_paths.DIR_SOURCE_ROOT, 'build'))
27 import find_depot_tools # pylint: disable=import-error
28
29 sys.path.append(os.path.join(host_paths.DIR_SOURCE_ROOT, 'third_party'))
30 import jinja2 # pylint: disable=import-error
31
32 _RE_IMAGE_NAME = re.compile(
33 r'(?P<test_class>\w+)\.'
34 r'(?P<description>\w+)\.'
35 r'(?P<device_model>\w+)\.'
36 r'(?P<orientation>port|land)\.png')
37
38 _RENDER_TEST_BASE_URL = 'https://storage.googleapis.com/chromium-render-tests/'
39 _RENDER_TEST_BUCKET = 'gs://chromium-render-tests/'
40
41 _JINJA_TEMPLATE_DIR = os.path.dirname(os.path.abspath(__file__))
42 _JINJA_TEMPLATE_FILENAME = 'render_webpage.html.jinja2'
43
44
45 def _UploadFiles(upload_dir, files):
46 """Upload files to the render tests GS bucket."""
47 google_storage_upload_dir = os.path.join(_RENDER_TEST_BUCKET, upload_dir)
48 cmd = [os.path.join(find_depot_tools.DEPOT_TOOLS_PATH, 'gsutil.py'),
49 '-m', 'cp']
50 cmd.extend(files)
51 cmd.append(google_storage_upload_dir)
52 cmd_helper.RunCmd(cmd)
53
54
55 def _GoogleStorageUrl(upload_dir, filename):
56 return os.path.join(
57 _RENDER_TEST_BASE_URL, upload_dir, os.path.basename(filename))
58
59
60 def _ComputeImageDiff(failure_image, golden_image):
61 """Compute mask showing which pixels are different between two images."""
62 return (ImageChops.difference(failure_image, golden_image)
63 .convert('L')
64 .point(lambda i: 255 if i else 0))
65
66
67 def ProcessRenderTestResults(devices, render_results_dir,
68 upload_dir, html_file):
69 """Grabs render results from device and generates webpage displaying results.
70
71 Args:
72 devices: List of DeviceUtils objects to grab results from.
73 render_results_path: Path where render test results are storage.
74 Will look for failures render test results on the device in
75 /sdcard/chromium_tests_root/<render_results_path>/failures/
76 and will look for golden images at Chromium src/<render_results_path>/.
77 upload_dir: Directory to upload the render test results to.
78 html_file: File to write the test results to.
79 """
80 results_dict = collections.defaultdict(lambda: collections.defaultdict(list))
81
82 diff_upload_dir = os.path.join(upload_dir, 'diffs')
83 failure_upload_dir = os.path.join(upload_dir, 'failures')
84 golden_upload_dir = os.path.join(upload_dir, 'goldens')
85
86 diff_images = []
87 failure_images = []
88 golden_images = []
89
90 temp_dir = None
91 try:
92 temp_dir = tempfile.mkdtemp()
93
94 for device in devices:
95 failures_device_dir = posixpath.join(
96 device.GetExternalStoragePath(),
97 'chromium_tests_root', render_results_dir, 'failures')
98 device.PullFile(failures_device_dir, temp_dir)
99
100 for failure_filename in os.listdir(temp_dir):
101 m = _RE_IMAGE_NAME.match(failure_filename)
102 if not m:
103 logging.warning(
104 'Unexpected file in render test failures, %s', failure_filename)
105 continue
106 failure_file = os.path.join(temp_dir, failure_filename)
107
108 # Check to make sure we have golden image for this failure.
109 golden_file = os.path.join(
110 host_paths.DIR_SOURCE_ROOT, render_results_dir, failure_filename)
111 if not os.path.exists(golden_file):
112 logging.error('Cannot find golden image for %s', failure_filename)
113 continue
114
115 # Compute image diff between failure and golden.
116 diff_image = _ComputeImageDiff(
117 Image.open(failure_file), Image.open(golden_file))
118 diff_filename = '_diff'.join(
119 os.path.splitext(os.path.basename(failure_file)))
120 diff_file = os.path.join(temp_dir, diff_filename)
121 diff_image.save(diff_file)
122
123 diff_images.append(diff_file)
124 failure_images.append(failure_file)
125 golden_images.append(golden_file)
126
127 test_class = m.group('test_class')
128 device_model = m.group('device_model')
129 results_dict[test_class][device_model].append({
130 'description': m.group('description'),
131 'orientation': m.group('orientation'),
132 'diff_image': _GoogleStorageUrl(diff_upload_dir, diff_file),
133 'failure_image': _GoogleStorageUrl(failure_upload_dir, failure_file),
134 'golden_image': _GoogleStorageUrl(golden_upload_dir, golden_file),
135 })
136
137 _UploadFiles(diff_upload_dir, diff_images)
138 _UploadFiles(failure_upload_dir, failure_images)
139 _UploadFiles(golden_upload_dir, golden_images)
140
141 if failure_images:
142 failures_zipfile = os.path.join(temp_dir, 'failures.zip')
143 with zipfile.ZipFile(failures_zipfile, mode='w') as zf:
144 for failure_file in failure_images:
145 zf.write(failure_file, os.path.join(
146 render_results_dir, os.path.basename(failure_file)))
147 failure_zip_url = _GoogleStorageUrl(upload_dir, failures_zipfile)
148 _UploadFiles(upload_dir, [failures_zipfile])
jbudorick 2016/11/07 01:44:45 Why are we uploading the failures images both zipp
mikecase (-- gone --) 2016/11/07 18:51:44 Because, I want to display the images on the gener
149 else:
150 failure_zip_url = None
151
152 jinja2_env = jinja2.Environment(
153 loader=jinja2.FileSystemLoader(_JINJA_TEMPLATE_DIR),
154 trim_blocks=True)
155 template = jinja2_env.get_template(_JINJA_TEMPLATE_FILENAME)
156 # pylint: disable=no-member
jbudorick 2016/11/07 01:44:45 ?
mikecase (-- gone --) 2016/11/07 18:51:44 Getting this error... E:157,32: Instance of 'str'
157 processed_template_output = template.render(
158 full_results=dict(results_dict), failure_zip_url=failure_zip_url)
159 with open(html_file, 'wb') as f:
160 f.write(processed_template_output)
161 finally:
162 if temp_dir:
163 shutil.rmtree(temp_dir)
164
165
166 def main():
167 parser = argparse.ArgumentParser()
168
169 parser.add_argument('--render-results-dir',
170 required=True,
171 help='Path on device to look for render test images')
172 parser.add_argument('--output-html-file',
173 required=True,
174 help='File to output the results webpage.')
175 parser.add_argument('-d', '--device', dest='devices', action='append',
176 default=[],
177 help='Device to look for render test results on. '
178 'Default is to look on all connected devices.')
179 parser.add_argument('--adb-path', type=os.path.abspath,
180 help='Absolute path to the adb binary to use.')
181 parser.add_argument('--buildername', type=str, required=True,
182 help='Bot buildername. Used to generate path to upload '
183 'render test results')
184 parser.add_argument('--build-number', type=str, required=True,
185 help='Bot build number. Used to generate path to upload '
186 'render test results')
187
188 args = parser.parse_args()
189 devil_chromium.Initialize(adb_path=args.adb_path)
190 devices = device_utils.DeviceUtils.HealthyDevices(device_arg=args.devices)
191
192 upload_dir = os.path.join(args.buildername, args.build_number)
193 ProcessRenderTestResults(
194 devices, args.render_results_dir, upload_dir, args.output_html_file)
195
196
197 if __name__ == '__main__':
198 sys.exit(main())
OLDNEW
« no previous file with comments | « no previous file | build/android/render_tests/render_webpage.html.jinja2 » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698