Upstream version 7.36.149.0
[platform/framework/web/crosswalk.git] / src / content / test / gpu / gpu_tests / cloud_storage_test_base.py
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 os
9 import re
10 import tempfile
11
12 from telemetry import test
13 from telemetry.core import bitmap
14 from telemetry.page import cloud_storage
15 from telemetry.page import page_test
16
17 test_data_dir = os.path.abspath(os.path.join(
18     os.path.dirname(__file__), '..', '..', 'data', 'gpu'))
19
20 default_generated_data_dir = os.path.join(test_data_dir, 'generated')
21
22 error_image_cloud_storage_bucket = 'chromium-browser-gpu-tests'
23
24 def _CompareScreenshotSamples(screenshot, expectations, device_pixel_ratio):
25   for expectation in expectations:
26     location = expectation["location"]
27     x = location[0] * device_pixel_ratio
28     y = location[1] * device_pixel_ratio
29
30     if x < 0 or y < 0 or x > screenshot.width or y > screenshot.height:
31       raise page_test.Failure(
32           'Expected pixel location [%d, %d] is out of range on [%d, %d] image' %
33           (x, y, screenshot.width, screenshot.height))
34
35     actual_color = screenshot.GetPixelColor(x, y)
36     expected_color = bitmap.RgbaColor(
37         expectation["color"][0],
38         expectation["color"][1],
39         expectation["color"][2])
40     if not actual_color.IsEqual(expected_color, expectation["tolerance"]):
41       raise page_test.Failure('Expected pixel at ' + str(location) +
42           ' to be ' +
43           str(expectation["color"]) + " but got [" +
44           str(actual_color.r) + ", " +
45           str(actual_color.g) + ", " +
46           str(actual_color.b) + "]")
47
48 class ValidatorBase(page_test.PageTest):
49   def __init__(self):
50     super(ValidatorBase, self).__init__()
51     # Parameters for cloud storage reference images.
52     self.vendor_id = None
53     self.device_id = None
54     self.vendor_string = None
55     self.device_string = None
56     self.msaa = False
57
58   ###
59   ### Routines working with the local disk (only used for local
60   ### testing without a cloud storage account -- the bots do not use
61   ### this code path).
62   ###
63
64   def _UrlToImageName(self, url):
65     image_name = re.sub(r'^(http|https|file)://(/*)', '', url)
66     image_name = re.sub(r'\.\./', '', image_name)
67     image_name = re.sub(r'(\.|/|-)', '_', image_name)
68     return image_name
69
70   def _WriteImage(self, image_path, png_image):
71     output_dir = os.path.dirname(image_path)
72     if not os.path.exists(output_dir):
73       os.makedirs(output_dir)
74     png_image.WritePngFile(image_path)
75
76   def _WriteErrorImages(self, img_dir, img_name, screenshot, ref_png):
77     full_image_name = img_name + '_' + str(self.options.build_revision)
78     full_image_name = full_image_name + '.png'
79
80     # Always write the failing image.
81     self._WriteImage(
82         os.path.join(img_dir, 'FAIL_' + full_image_name), screenshot)
83
84     if ref_png:
85       # Save the reference image.
86       # This ensures that we get the right revision number.
87       self._WriteImage(
88           os.path.join(img_dir, full_image_name), ref_png)
89
90       # Save the difference image.
91       diff_png = screenshot.Diff(ref_png)
92       self._WriteImage(
93           os.path.join(img_dir, 'DIFF_' + full_image_name), diff_png)
94
95   ###
96   ### Cloud storage code path -- the bots use this.
97   ###
98
99   def _ComputeGpuInfo(self, tab):
100     if ((self.vendor_id and self.device_id) or
101         (self.vendor_string and self.device_string)):
102       return
103     browser = tab.browser
104     if not browser.supports_system_info:
105       raise Exception('System info must be supported by the browser')
106     system_info = browser.GetSystemInfo()
107     if not system_info.gpu:
108       raise Exception('GPU information was absent')
109     device = system_info.gpu.devices[0]
110     if device.vendor_id and device.device_id:
111       self.vendor_id = device.vendor_id
112       self.device_id = device.device_id
113     elif device.vendor_string and device.device_string:
114       self.vendor_string = device.vendor_string
115       self.device_string = device.device_string
116     else:
117       raise Exception('GPU device information was incomplete')
118     self.msaa = not (
119         'disable_multisampling' in system_info.gpu.driver_bug_workarounds)
120
121   def _FormatGpuInfo(self, tab):
122     self._ComputeGpuInfo(tab)
123     msaa_string = '_msaa' if self.msaa else '_non_msaa'
124     if self.vendor_id:
125       return '%s_%04x_%04x%s' % (
126         self.options.os_type, self.vendor_id, self.device_id, msaa_string)
127     else:
128       return '%s_%s_%s%s' % (
129         self.options.os_type, self.vendor_string, self.device_string,
130         msaa_string)
131
132   def _FormatReferenceImageName(self, img_name, page, tab):
133     return '%s_v%s_%s.png' % (
134       img_name,
135       page.revision,
136       self._FormatGpuInfo(tab))
137
138   def _UploadBitmapToCloudStorage(self, bucket, name, bitmap, public=False):
139     # This sequence of steps works on all platforms to write a temporary
140     # PNG to disk, following the pattern in bitmap_unittest.py. The key to
141     # avoiding PermissionErrors seems to be to not actually try to write to
142     # the temporary file object, but to re-open its name for all operations.
143     temp_file = tempfile.NamedTemporaryFile().name
144     bitmap.WritePngFile(temp_file)
145     cloud_storage.Insert(bucket, name, temp_file, publicly_readable=public)
146
147   def _ConditionallyUploadToCloudStorage(self, img_name, page, tab, screenshot):
148     """Uploads the screenshot to cloud storage as the reference image
149     for this test, unless it already exists. Returns True if the
150     upload was actually performed."""
151     if not self.options.refimg_cloud_storage_bucket:
152       raise Exception('--refimg-cloud-storage-bucket argument is required')
153     cloud_name = self._FormatReferenceImageName(img_name, page, tab)
154     if not cloud_storage.Exists(self.options.refimg_cloud_storage_bucket,
155                                 cloud_name):
156       self._UploadBitmapToCloudStorage(self.options.refimg_cloud_storage_bucket,
157                                        cloud_name,
158                                        screenshot)
159       return True
160     return False
161
162   def _DownloadFromCloudStorage(self, img_name, page, tab):
163     """Downloads the reference image for the given test from cloud
164     storage, returning it as a Telemetry Bitmap object."""
165     # TODO(kbr): there's a race condition between the deletion of the
166     # temporary file and gsutil's overwriting it.
167     if not self.options.refimg_cloud_storage_bucket:
168       raise Exception('--refimg-cloud-storage-bucket argument is required')
169     temp_file = tempfile.NamedTemporaryFile().name
170     cloud_storage.Get(self.options.refimg_cloud_storage_bucket,
171                       self._FormatReferenceImageName(img_name, page, tab),
172                       temp_file)
173     return bitmap.Bitmap.FromPngFile(temp_file)
174
175   def _UploadErrorImagesToCloudStorage(self, image_name, screenshot, ref_img):
176     """For a failing run, uploads the failing image, reference image (if
177     supplied), and diff image (if reference image was supplied) to cloud
178     storage. This subsumes the functionality of the
179     archive_gpu_pixel_test_results.py script."""
180     machine_name = re.sub('\W+', '_', self.options.test_machine_name)
181     upload_dir = '%s_%s_telemetry' % (self.options.build_revision, machine_name)
182     base_bucket = '%s/runs/%s' % (error_image_cloud_storage_bucket, upload_dir)
183     image_name_with_revision = '%s_%s.png' % (
184       image_name, self.options.build_revision)
185     self._UploadBitmapToCloudStorage(
186       base_bucket + '/gen', image_name_with_revision, screenshot,
187       public=True)
188     if ref_img:
189       self._UploadBitmapToCloudStorage(
190         base_bucket + '/ref', image_name_with_revision, ref_img, public=True)
191       diff_img = screenshot.Diff(ref_img)
192       self._UploadBitmapToCloudStorage(
193         base_bucket + '/diff', image_name_with_revision, diff_img,
194         public=True)
195     print ('See http://%s.commondatastorage.googleapis.com/'
196            'view_test_results.html?%s for this run\'s test results') % (
197       error_image_cloud_storage_bucket, upload_dir)
198
199   def _ValidateScreenshotSamples(self, url,
200                                  screenshot, expectations, device_pixel_ratio):
201     """Samples the given screenshot and verifies pixel color values.
202        The sample locations and expected color values are given in expectations.
203        In case any of the samples do not match the expected color, it raises
204        a Failure and dumps the screenshot locally or cloud storage depending on
205        what machine the test is being run."""
206     try:
207       _CompareScreenshotSamples(screenshot, expectations, device_pixel_ratio)
208     except page_test.Failure:
209       image_name = self._UrlToImageName(url)
210       if self.options.test_machine_name:
211         self._UploadErrorImagesToCloudStorage(image_name, screenshot, None)
212       else:
213         self._WriteErrorImages(self.options.generated_dir, image_name,
214                                screenshot, None)
215       raise
216
217
218 class TestBase(test.Test):
219   @classmethod
220   def AddTestCommandLineArgs(cls, group):
221     group.add_option('--build-revision',
222         help='Chrome revision being tested.',
223         default="unknownrev")
224     group.add_option('--upload-refimg-to-cloud-storage',
225         dest='upload_refimg_to_cloud_storage',
226         action='store_true', default=False,
227         help='Upload resulting images to cloud storage as reference images')
228     group.add_option('--download-refimg-from-cloud-storage',
229         dest='download_refimg_from_cloud_storage',
230         action='store_true', default=False,
231         help='Download reference images from cloud storage')
232     group.add_option('--refimg-cloud-storage-bucket',
233         help='Name of the cloud storage bucket to use for reference images; '
234         'required with --upload-refimg-to-cloud-storage and '
235         '--download-refimg-from-cloud-storage. Example: '
236         '"chromium-gpu-archive/reference-images"')
237     group.add_option('--os-type',
238         help='Type of operating system on which the pixel test is being run, '
239         'used only to distinguish different operating systems with the same '
240         'graphics card. Any value is acceptable, but canonical values are '
241         '"win", "mac", and "linux", and probably, eventually, "chromeos" '
242         'and "android").',
243         default='')
244     group.add_option('--test-machine-name',
245         help='Name of the test machine. Specifying this argument causes this '
246         'script to upload failure images and diffs to cloud storage directly, '
247         'instead of relying on the archive_gpu_pixel_test_results.py script.',
248         default='')
249     group.add_option('--generated-dir',
250         help='Overrides the default on-disk location for generated test images '
251         '(only used for local testing without a cloud storage account)',
252         default=default_generated_data_dir)