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

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: Add script to process results from Android render tests. 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 logging
9 import os
10 import posixpath
11 import re
12 import shutil
13 import sys
14 import tempfile
15 import zipfile
16 from PIL import ImageChops
17 from PIL import Image
18 from collections import defaultdict
jbudorick 2016/10/19 16:11:32 nit: import collections, then use collections.defa
mikecase (-- gone --) 2016/10/19 19:04:44 Done
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.abspath(
jbudorick 2016/10/19 16:11:32 nit: This doesn't need to be abspath, DIR_SOURCE_R
mikecase (-- gone --) 2016/10/19 19:04:44 Done
27 os.path.join(host_paths.DIR_SOURCE_ROOT, 'build')))
28 import find_depot_tools # pylint: disable=import-error,unused-import
29 import download_from_google_storage
30
31 sys.path.append(os.path.join(host_paths.DIR_SOURCE_ROOT, 'third_party'))
32 import jinja2 # pylint: disable=import-error
33
34 _RE_IMAGE_NAME = re.compile(
35 r'(?P<test_class>\w+)\.(?P<description>\w+)\.'
jbudorick 2016/10/19 16:11:32 super nit: I would split this s.t. each capture is
mikecase (-- gone --) 2016/10/19 19:04:44 Done
36 r'(?P<device_model>\w+)\.(?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 _CHROMIUM_SRC = os.path.join(
jbudorick 2016/10/19 16:11:32 This is host_paths.DIR_SOURCE_ROOT.
mikecase (-- gone --) 2016/10/19 19:04:43 Done
45 os.path.dirname(__file__), os.pardir, os.pardir, os.pardir)
46
47
48 def _UploadFiles(upload_dir, files):
49 """Upload files to the render tests GS bucket."""
50 google_storage_upload_dir = os.path.join(_RENDER_TEST_BUCKET, upload_dir)
51 cmd = [download_from_google_storage.GSUTIL_DEFAULT_PATH, '-m', 'cp']
jbudorick 2016/10/19 16:11:33 using "download_from_google_storage" to upload fil
mikecase (-- gone --) 2016/10/19 19:04:44 I thought it was silly too. upload_to_google_stor
52 cmd.extend(files)
53 cmd.append(google_storage_upload_dir)
54 cmd_helper.RunCmd(cmd)
55
56
57 def _GoogleStorageUrl(upload_dir, filename):
58 return os.path.join(
59 _RENDER_TEST_BASE_URL, upload_dir, os.path.basename(filename))
60
61
62 def _ComputeImageDiff(failure_image, golden_image):
63 """Compute mask showing which pixels are different between two images."""
64 return (ImageChops.difference(failure_image, golden_image)
65 .convert('L')
66 .point(lambda i: 255 if i else 0))
67
68
69 def ProcessRenderTestResults(devices, render_results_dir,
70 upload_dir, html_file):
71 """Grabs render results from device and generates webpage displaying results.
72
73 Args:
74 devices: List of DeviceUtils objects to grab results from.
75 render_results_path: Path where render test results are storage.
76 Will look for failures render test results on the device in
77 /sdcard/chromium_tests_root/<render_results_path>/failures/
78 and will look for golden images at Chromium src/<render_results_path>/.
79 upload_dir: Directory to upload the render test results to.
80 html_file: File to write the test results to.
81 """
82 results_dict = defaultdict(lambda: defaultdict(list))
83
84 diff_upload_dir = os.path.join(upload_dir, 'diffs')
85 failure_upload_dir = os.path.join(upload_dir, 'failures')
86 golden_upload_dir = os.path.join(upload_dir, 'goldens')
87
88 image_files = {
jbudorick 2016/10/19 16:11:33 Why aren't these just three separate lists?
mikecase (-- gone --) 2016/10/19 19:04:44 Done
89 'diffs': [],
90 'failures': [],
91 'goldens': [],
92 }
93
94 temp_dir = None
95 try:
96 temp_dir = tempfile.mkdtemp()
97
98 for device in devices:
99 failures_device_dir = posixpath.join(
100 device.GetExternalStoragePath(),
101 'chromium_tests_root', render_results_dir, 'failures')
102 device.adb.Pull(failures_device_dir, temp_dir)
jbudorick 2016/10/19 16:11:32 device.PullFile plz
mikecase (-- gone --) 2016/10/19 19:04:44 Hmmm, didn't know this would work. Name and docstr
103
104 for failure_filename in os.listdir(temp_dir):
105 m = _RE_IMAGE_NAME.match(failure_filename)
106 if not m:
jbudorick 2016/10/19 16:11:33 This should log something if we hit an unexpected
mikecase (-- gone --) 2016/10/19 19:04:44 Done
107 continue
108 failure_file = os.path.join(temp_dir, failure_filename)
109
110 # Check to make sure we have golden image for this failure.
111 golden_file = os.path.join(
112 _CHROMIUM_SRC, render_results_dir, failure_filename)
113 if not os.path.exists(golden_file):
114 logging.error('Cannot find golden image for %s', failure_filename)
115 continue
116
117 # Compute image diff between failure and golden.
118 diff_image = _ComputeImageDiff(
119 Image.open(failure_file), Image.open(golden_file))
120 diff_filename = '_diff'.join(
121 os.path.splitext(os.path.basename(failure_file)))
122 diff_file = os.path.join(temp_dir, diff_filename)
123 diff_image.save(diff_file)
124
125 image_files['diffs'].append(diff_file)
126 image_files['failures'].append(failure_file)
127 image_files['goldens'].append(golden_file)
mikecase (-- gone --) 2016/10/18 16:00:13 I dont upload the file immediately since it is muc
128
129 test_class = m.group('test_class')
130 device_model = m.group('device_model')
131 results_dict[test_class][device_model].append({
132 'description': m.group('description'),
133 'orientation': m.group('orientation'),
134 'diff_image': _GoogleStorageUrl(diff_upload_dir, diff_file),
135 'failure_image': _GoogleStorageUrl(failure_upload_dir, failure_file),
136 'golden_image': _GoogleStorageUrl(golden_upload_dir, golden_file),
137 })
138
139 failures_zipfile = os.path.join(temp_dir, 'failures.zip')
mikecase (-- gone --) 2016/10/19 19:04:44 Added check so that no zip is created/uploaded if
140 with zipfile.ZipFile(failures_zipfile, mode='w') as zf:
141 for failure_file in image_files['failures']:
142 zf.write(failure_file, os.path.join(
143 render_results_dir, os.path.basename(failure_file)))
144 failure_zip_url = _GoogleStorageUrl(upload_dir, failures_zipfile)
145
146 _UploadFiles(diff_upload_dir, image_files['diffs'])
147 _UploadFiles(failure_upload_dir, image_files['failures'])
148 _UploadFiles(golden_upload_dir, image_files['goldens'])
149 _UploadFiles(upload_dir, [failures_zipfile])
150
151 jinja2_env = jinja2.Environment(
152 loader=jinja2.FileSystemLoader(_JINJA_TEMPLATE_DIR),
153 trim_blocks=True)
154 template = jinja2_env.get_template(_JINJA_TEMPLATE_FILENAME)
155 processed_template_output = template.render(
156 full_results=dict(results_dict), failure_zip_url=failure_zip_url)
157 with open(html_file, "wb") as f:
jbudorick 2016/10/19 16:11:33 nit: single quotes
mikecase (-- gone --) 2016/10/19 19:04:44 Done
158 f.write(processed_template_output)
159 finally:
160 if temp_dir:
jbudorick 2016/10/19 16:11:32 we really ought to extract https://codesearch.chro
mikecase (-- gone --) 2016/10/19 19:04:44 Ack
161 shutil.rmtree(temp_dir)
162
163
164 def main():
165 parser = argparse.ArgumentParser()
166
167 parser.add_argument('--render-results-dir',
168 required=True,
169 help='Path on device to look for render test images')
170 parser.add_argument('--output-html-file',
171 required=True,
172 help='File to output the results webpage.')
173 parser.add_argument('-d', '--device', dest='devices', action='append',
174 default=[],
175 help='Device to look for render test results on. '
176 'Default is to look on all connected devices.')
177 parser.add_argument('--adb-path', type=os.path.abspath,
178 help='Absolute path to the adb binary to use.')
179 parser.add_argument('--buildername', type=str, required=True,
180 help='Bot buildername. Used to generate path to upload '
181 'render test results')
182 parser.add_argument('--build-number', type=str, required=True,
183 help='Bot build number. Used to generate path to upload '
184 'render test results')
185
186 args = parser.parse_args()
187 devil_chromium.Initialize(adb_path=args.adb_path)
188 devices = device_utils.DeviceUtils.HealthyDevices(device_arg=args.devices)
189
190 upload_dir = os.path.join(args.buildername, args.build_number)
191 ProcessRenderTestResults(
192 devices, args.render_results_dir, upload_dir, args.output_html_file)
193
194
195 if __name__ == '__main__':
196 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