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

Side by Side Diff: content/test/gpu/gpu_tests/cloud_storage_test_base.py

Issue 2618983004: Port Maps test to browser_test_runner harness. (Closed)
Patch Set: Created 3 years, 11 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
(Empty)
1 # Copyright 2013 The Chromium Authors. All rights reserved.
2 # Use of this source code is governed by a BSD-style license that can be
3 # found in the LICENSE file.
4
5 """Base classes for a test and validator which upload results
6 (reference images, error images) to cloud storage."""
7
8 import logging
9 import os
10 import re
11 import tempfile
12
13 from py_utils import cloud_storage
14 from telemetry.page import legacy_page_test
15 from telemetry.util import image_util
16 from telemetry.util import rgba_color
17
18 from gpu_tests import gpu_test_base
19
20 test_data_dir = os.path.abspath(os.path.join(
21 os.path.dirname(__file__), '..', '..', 'data', 'gpu'))
22
23 default_generated_data_dir = os.path.join(test_data_dir, 'generated')
24
25 error_image_cloud_storage_bucket = 'chromium-browser-gpu-tests'
26
27 def _CompareScreenshotSamples(tab, screenshot, expectations, device_pixel_ratio,
28 test_machine_name):
29 # First scan through the expectations and see if there are any scale
30 # factor overrides that would preempt the device pixel ratio. This
31 # is mainly a workaround for complex tests like the Maps test.
32 for expectation in expectations:
33 if 'scale_factor_overrides' in expectation:
34 for override in expectation['scale_factor_overrides']:
35 # Require exact matches to avoid confusion, because some
36 # machine models and names might be subsets of others
37 # (e.g. Nexus 5 vs Nexus 5X).
38 if ('device_type' in override and
39 (tab.browser.platform.GetDeviceTypeName() ==
40 override['device_type'])):
41 logging.warning('Overriding device_pixel_ratio ' +
42 str(device_pixel_ratio) + ' with scale factor ' +
43 str(override['scale_factor']) + ' for device type ' +
44 override['device_type'])
45 device_pixel_ratio = override['scale_factor']
46 break
47 if (test_machine_name and 'machine_name' in override and
48 override["machine_name"] == test_machine_name):
49 logging.warning('Overriding device_pixel_ratio ' +
50 str(device_pixel_ratio) + ' with scale factor ' +
51 str(override['scale_factor']) + ' for machine name ' +
52 test_machine_name)
53 device_pixel_ratio = override['scale_factor']
54 break
55 # Only support one "scale_factor_overrides" in the expectation format.
56 break
57 for expectation in expectations:
58 if "scale_factor_overrides" in expectation:
59 continue
60 location = expectation["location"]
61 size = expectation["size"]
62 x0 = int(location[0] * device_pixel_ratio)
63 x1 = int((location[0] + size[0]) * device_pixel_ratio)
64 y0 = int(location[1] * device_pixel_ratio)
65 y1 = int((location[1] + size[1]) * device_pixel_ratio)
66 for x in range(x0, x1):
67 for y in range(y0, y1):
68 if (x < 0 or y < 0 or x >= image_util.Width(screenshot) or
69 y >= image_util.Height(screenshot)):
70 raise legacy_page_test.Failure(
71 ('Expected pixel location [%d, %d] is out of range on ' +
72 '[%d, %d] image') %
73 (x, y, image_util.Width(screenshot),
74 image_util.Height(screenshot)))
75
76 actual_color = image_util.GetPixelColor(screenshot, x, y)
77 expected_color = rgba_color.RgbaColor(
78 expectation["color"][0],
79 expectation["color"][1],
80 expectation["color"][2])
81 if not actual_color.IsEqual(expected_color, expectation["tolerance"]):
82 raise legacy_page_test.Failure('Expected pixel at ' + str(location) +
83 ' (actual pixel (' + str(x) + ', ' + str(y) + ')) ' +
84 ' to be ' +
85 str(expectation["color"]) + " but got [" +
86 str(actual_color.r) + ", " +
87 str(actual_color.g) + ", " +
88 str(actual_color.b) + "]")
89
90 class ValidatorBase(gpu_test_base.ValidatorBase):
91 def __init__(self):
92 super(ValidatorBase, self).__init__()
93 # Parameters for cloud storage reference images.
94 self.vendor_id = None
95 self.device_id = None
96 self.vendor_string = None
97 self.device_string = None
98 self.msaa = False
99 self.model_name = None
100
101 ###
102 ### Routines working with the local disk (only used for local
103 ### testing without a cloud storage account -- the bots do not use
104 ### this code path).
105 ###
106
107 def _UrlToImageName(self, url):
108 image_name = re.sub(r'^(http|https|file)://(/*)', '', url)
109 image_name = re.sub(r'\.\./', '', image_name)
110 image_name = re.sub(r'(\.|/|-)', '_', image_name)
111 return image_name
112
113 def _WriteImage(self, image_path, png_image):
114 output_dir = os.path.dirname(image_path)
115 if not os.path.exists(output_dir):
116 os.makedirs(output_dir)
117 image_util.WritePngFile(png_image, image_path)
118
119 def _WriteErrorImages(self, img_dir, img_name, screenshot, ref_png):
120 full_image_name = img_name + '_' + str(self.options.build_revision)
121 full_image_name = full_image_name + '.png'
122
123 # Always write the failing image.
124 self._WriteImage(
125 os.path.join(img_dir, 'FAIL_' + full_image_name), screenshot)
126
127 if ref_png is not None:
128 # Save the reference image.
129 # This ensures that we get the right revision number.
130 self._WriteImage(
131 os.path.join(img_dir, full_image_name), ref_png)
132
133 # Save the difference image.
134 diff_png = image_util.Diff(screenshot, ref_png)
135 self._WriteImage(
136 os.path.join(img_dir, 'DIFF_' + full_image_name), diff_png)
137
138 ###
139 ### Cloud storage code path -- the bots use this.
140 ###
141
142 def _ComputeGpuInfo(self, tab):
143 if ((self.vendor_id and self.device_id) or
144 (self.vendor_string and self.device_string)):
145 return
146 browser = tab.browser
147 if not browser.supports_system_info:
148 raise Exception('System info must be supported by the browser')
149 system_info = browser.GetSystemInfo()
150 if not system_info.gpu:
151 raise Exception('GPU information was absent')
152 device = system_info.gpu.devices[0]
153 if device.vendor_id and device.device_id:
154 self.vendor_id = device.vendor_id
155 self.device_id = device.device_id
156 elif device.vendor_string and device.device_string:
157 self.vendor_string = device.vendor_string
158 self.device_string = device.device_string
159 else:
160 raise Exception('GPU device information was incomplete')
161 # TODO(senorblanco): This should probably be checking
162 # for the presence of the extensions in system_info.gpu_aux_attributes
163 # in order to check for MSAA, rather than sniffing the blacklist.
164 self.msaa = not (
165 ('disable_chromium_framebuffer_multisample' in
166 system_info.gpu.driver_bug_workarounds) or
167 ('disable_multisample_render_to_texture' in
168 system_info.gpu.driver_bug_workarounds))
169 self.model_name = system_info.model_name
170
171 def _FormatGpuInfo(self, tab):
172 self._ComputeGpuInfo(tab)
173 msaa_string = '_msaa' if self.msaa else '_non_msaa'
174 if self.vendor_id:
175 return '%s_%04x_%04x%s' % (
176 self.options.os_type, self.vendor_id, self.device_id, msaa_string)
177 else:
178 # This is the code path for Android devices. Include the model
179 # name (e.g. "Nexus 9") in the GPU string to disambiguate
180 # multiple devices on the waterfall which might have the same
181 # device string ("NVIDIA Tegra") but different screen
182 # resolutions and device pixel ratios.
183 return '%s_%s_%s_%s%s' % (
184 self.options.os_type, self.vendor_string, self.device_string,
185 self.model_name, msaa_string)
186
187 def _FormatReferenceImageName(self, img_name, page, tab):
188 return '%s_v%s_%s.png' % (
189 img_name,
190 page.revision,
191 self._FormatGpuInfo(tab))
192
193 def _UploadBitmapToCloudStorage(self, bucket, name, bitmap, public=False):
194 # This sequence of steps works on all platforms to write a temporary
195 # PNG to disk, following the pattern in bitmap_unittest.py. The key to
196 # avoiding PermissionErrors seems to be to not actually try to write to
197 # the temporary file object, but to re-open its name for all operations.
198 temp_file = tempfile.NamedTemporaryFile(suffix='.png').name
199 image_util.WritePngFile(bitmap, temp_file)
200 cloud_storage.Insert(bucket, name, temp_file, publicly_readable=public)
201
202 def _ConditionallyUploadToCloudStorage(self, img_name, page, tab, screenshot):
203 """Uploads the screenshot to cloud storage as the reference image
204 for this test, unless it already exists. Returns True if the
205 upload was actually performed."""
206 if not self.options.refimg_cloud_storage_bucket:
207 raise Exception('--refimg-cloud-storage-bucket argument is required')
208 cloud_name = self._FormatReferenceImageName(img_name, page, tab)
209 if not cloud_storage.Exists(self.options.refimg_cloud_storage_bucket,
210 cloud_name):
211 self._UploadBitmapToCloudStorage(self.options.refimg_cloud_storage_bucket,
212 cloud_name,
213 screenshot)
214 return True
215 return False
216
217 def _DownloadFromCloudStorage(self, img_name, page, tab):
218 """Downloads the reference image for the given test from cloud
219 storage, returning it as a Telemetry Bitmap object."""
220 # TODO(kbr): there's a race condition between the deletion of the
221 # temporary file and gsutil's overwriting it.
222 if not self.options.refimg_cloud_storage_bucket:
223 raise Exception('--refimg-cloud-storage-bucket argument is required')
224 temp_file = tempfile.NamedTemporaryFile(suffix='.png').name
225 cloud_storage.Get(self.options.refimg_cloud_storage_bucket,
226 self._FormatReferenceImageName(img_name, page, tab),
227 temp_file)
228 return image_util.FromPngFile(temp_file)
229
230 def _UploadErrorImagesToCloudStorage(self, image_name, screenshot, ref_img):
231 """For a failing run, uploads the failing image, reference image (if
232 supplied), and diff image (if reference image was supplied) to cloud
233 storage. This subsumes the functionality of the
234 archive_gpu_pixel_test_results.py script."""
235 machine_name = re.sub(r'\W+', '_', self.options.test_machine_name)
236 upload_dir = '%s_%s_telemetry' % (self.options.build_revision, machine_name)
237 base_bucket = '%s/runs/%s' % (error_image_cloud_storage_bucket, upload_dir)
238 image_name_with_revision = '%s_%s.png' % (
239 image_name, self.options.build_revision)
240 self._UploadBitmapToCloudStorage(
241 base_bucket + '/gen', image_name_with_revision, screenshot,
242 public=True)
243 if ref_img is not None:
244 self._UploadBitmapToCloudStorage(
245 base_bucket + '/ref', image_name_with_revision, ref_img, public=True)
246 diff_img = image_util.Diff(screenshot, ref_img)
247 self._UploadBitmapToCloudStorage(
248 base_bucket + '/diff', image_name_with_revision, diff_img,
249 public=True)
250 print ('See http://%s.commondatastorage.googleapis.com/'
251 'view_test_results.html?%s for this run\'s test results') % (
252 error_image_cloud_storage_bucket, upload_dir)
253
254 def _ValidateScreenshotSamples(self, tab, url,
255 screenshot, expectations, device_pixel_ratio):
256 """Samples the given screenshot and verifies pixel color values.
257 The sample locations and expected color values are given in expectations.
258 In case any of the samples do not match the expected color, it raises
259 a Failure and dumps the screenshot locally or cloud storage depending on
260 what machine the test is being run."""
261 try:
262 _CompareScreenshotSamples(tab, screenshot, expectations,
263 device_pixel_ratio,
264 self.options.test_machine_name)
265 except legacy_page_test.Failure:
266 image_name = self._UrlToImageName(url)
267 if self.options.test_machine_name:
268 self._UploadErrorImagesToCloudStorage(image_name, screenshot, None)
269 else:
270 self._WriteErrorImages(self.options.generated_dir, image_name,
271 screenshot, None)
272 raise
273
274
275 class CloudStorageTestBase(gpu_test_base.TestBase):
276 @classmethod
277 def AddBenchmarkCommandLineArgs(cls, group):
278 group.add_option('--build-revision',
279 help='Chrome revision being tested.',
280 default="unknownrev")
281 group.add_option('--upload-refimg-to-cloud-storage',
282 dest='upload_refimg_to_cloud_storage',
283 action='store_true', default=False,
284 help='Upload resulting images to cloud storage as reference images')
285 group.add_option('--download-refimg-from-cloud-storage',
286 dest='download_refimg_from_cloud_storage',
287 action='store_true', default=False,
288 help='Download reference images from cloud storage')
289 group.add_option('--refimg-cloud-storage-bucket',
290 help='Name of the cloud storage bucket to use for reference images; '
291 'required with --upload-refimg-to-cloud-storage and '
292 '--download-refimg-from-cloud-storage. Example: '
293 '"chromium-gpu-archive/reference-images"')
294 group.add_option('--os-type',
295 help='Type of operating system on which the pixel test is being run, '
296 'used only to distinguish different operating systems with the same '
297 'graphics card. Any value is acceptable, but canonical values are '
298 '"win", "mac", and "linux", and probably, eventually, "chromeos" '
299 'and "android").',
300 default='')
301 group.add_option('--test-machine-name',
302 help='Name of the test machine. Specifying this argument causes this '
303 'script to upload failure images and diffs to cloud storage directly, '
304 'instead of relying on the archive_gpu_pixel_test_results.py script.',
305 default='')
306 group.add_option('--generated-dir',
307 help='Overrides the default on-disk location for generated test images '
308 '(only used for local testing without a cloud storage account)',
309 default=default_generated_data_dir)
OLDNEW
« no previous file with comments | « content/test/gpu/generate_buildbot_json.py ('k') | content/test/gpu/gpu_tests/context_lost_integration_test.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698