Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 # Copyright 2013 The Chromium Authors. All rights reserved. | 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 | 2 # Use of this source code is governed by a BSD-style license that can be |
| 3 # found in the LICENSE file. | 3 # found in the LICENSE file. |
| 4 | 4 |
| 5 """Runs a Google Maps pixel test. | 5 """Runs a Google Maps pixel test. |
| 6 Performs several common navigation actions on the map (pan, zoom, rotate) then | 6 Performs several common navigation actions on the map (pan, zoom, rotate) then |
| 7 captures a screenshot and compares selected pixels against expected values""" | 7 captures a screenshot and compares selected pixels against expected values""" |
| 8 | 8 |
| 9 import json | 9 import json |
| 10 import optparse | |
| 10 import os | 11 import os |
| 11 import re | 12 import re |
| 12 | 13 |
| 14 import maps_expectations | |
| 15 | |
| 13 from telemetry import test | 16 from telemetry import test |
| 14 from telemetry.core.backends import png_bitmap | 17 from telemetry.core.backends import png_bitmap |
| 15 from telemetry.core import util | 18 from telemetry.core import util |
| 16 from telemetry.page import page_test | 19 from telemetry.page import page_test |
| 17 from telemetry.page import page_set | 20 from telemetry.page import page_set |
| 18 | 21 |
| 22 test_data_dir = os.path.abspath(os.path.join( | |
| 23 os.path.dirname(__file__), '..', '..', 'data', 'gpu')) | |
| 24 | |
| 25 default_generated_data_dir = os.path.join(test_data_dir, 'generated') | |
| 26 | |
| 19 class MapsValidator(page_test.PageTest): | 27 class MapsValidator(page_test.PageTest): |
| 20 def __init__(self): | 28 def __init__(self): |
| 21 super(MapsValidator, self).__init__('ValidatePage') | 29 super(MapsValidator, self).__init__('ValidatePage') |
| 22 | 30 |
| 23 def CustomizeBrowserOptions(self, options): | 31 def CustomizeBrowserOptions(self, options): |
| 24 options.AppendExtraBrowserArgs('--enable-gpu-benchmarking') | 32 options.AppendExtraBrowserArgs('--enable-gpu-benchmarking') |
| 25 | 33 |
| 26 def ValidatePage(self, page, tab, results): | 34 def ValidatePage(self, page, tab, results): |
| 35 MapsValidator.SpinWaitOnRAF(tab, 3) | |
|
Ken Russell (switch to Gerrit)
2013/11/19 01:22:30
Please add a TODO about removing this, since theor
bajones
2013/11/19 18:27:31
Done.
| |
| 36 | |
| 27 if not tab.screenshot_supported: | 37 if not tab.screenshot_supported: |
| 28 raise page_test.Failure('Browser does not support screenshot capture') | 38 raise page_test.Failure('Browser does not support screenshot capture') |
| 29 screenshot = tab.Screenshot(5) | 39 screenshot = tab.Screenshot(5) |
| 30 if not screenshot: | 40 if not screenshot: |
| 31 raise page_test.Failure('Could not capture screenshot') | 41 raise page_test.Failure('Could not capture screenshot') |
| 32 | 42 |
| 43 dpr = tab.EvaluateJavaScript('window.devicePixelRatio') | |
| 33 expected = MapsValidator.ReadPixelExpectations(page) | 44 expected = MapsValidator.ReadPixelExpectations(page) |
| 34 MapsValidator.CompareToExpectations(screenshot, expected) | 45 |
| 46 try: | |
| 47 MapsValidator.CompareToExpectations(screenshot, expected, dpr) | |
| 48 except page_test.Failure: | |
| 49 image_name = MapsValidator.UrlToImageName(page.display_name) | |
| 50 MapsValidator.WriteErrorImage(self.options.generated_dir, | |
| 51 image_name, self.options.build_revision, screenshot) | |
| 52 raise | |
| 53 | |
| 54 @staticmethod | |
| 55 def SpinWaitOnRAF(tab, iterations, timeout = 60): | |
| 56 waitScript = r""" | |
| 57 window.__spinWaitOnRAFDone = false; | |
| 58 var iterationsLeft = %d; | |
| 59 | |
| 60 function spin() { | |
| 61 iterationsLeft--; | |
| 62 if (iterationsLeft == 0) { | |
| 63 window.__spinWaitOnRAFDone = true; | |
| 64 return; | |
| 65 } | |
| 66 window.requestAnimationFrame(spin); | |
| 67 } | |
| 68 window.requestAnimationFrame(spin); | |
| 69 """ % iterations | |
| 70 | |
| 71 def IsWaitComplete(): | |
| 72 return tab.EvaluateJavaScript('window.__spinWaitOnRAFDone') | |
| 73 | |
| 74 tab.ExecuteJavaScript(waitScript) | |
| 75 util.WaitFor(IsWaitComplete, timeout) | |
| 35 | 76 |
| 36 @staticmethod | 77 @staticmethod |
| 37 def ReadPixelExpectations(page): | 78 def ReadPixelExpectations(page): |
| 38 expectations_path = os.path.join(page._base_dir, page.pixel_expectations) | 79 expectations_path = os.path.join(page._base_dir, page.pixel_expectations) |
| 39 with open(expectations_path, 'r') as f: | 80 with open(expectations_path, 'r') as f: |
| 40 json_contents = json.load(f) | 81 json_contents = json.load(f) |
| 41 return json_contents | 82 return json_contents |
| 42 | 83 |
| 43 @staticmethod | 84 @staticmethod |
| 44 def CompareToExpectations(screenshot, expectations): | 85 def CompareToExpectations(screenshot, expectations, devicePixelRatio): |
| 45 for expectation in expectations: | 86 for expectation in expectations: |
| 46 location = expectation["location"] | 87 location = expectation["location"] |
| 47 pixel_color = screenshot.GetPixelColor(location[0], location[1]) | 88 pixel_color = screenshot.GetPixelColor( |
| 89 location[0] * devicePixelRatio, | |
| 90 location[1] * devicePixelRatio) | |
| 48 expect_color = png_bitmap.PngColor( | 91 expect_color = png_bitmap.PngColor( |
| 49 expectation["color"][0], | 92 expectation["color"][0], |
| 50 expectation["color"][1], | 93 expectation["color"][1], |
| 51 expectation["color"][2]) | 94 expectation["color"][2]) |
| 52 iter_result = pixel_color.IsEqual(expect_color, expectation["tolerance"]) | 95 iter_result = pixel_color.IsEqual(expect_color, expectation["tolerance"]) |
| 53 if not iter_result: | 96 if not iter_result: |
| 54 raise page_test.Failure('FAILURE: Expected pixel at ' + str(location) + | 97 raise page_test.Failure('FAILURE: Expected pixel at ' + str(location) + |
| 55 ' to be ' + | 98 ' to be ' + |
| 56 str(expectation["color"]) + " but got [" + | 99 str(expectation["color"]) + " but got [" + |
| 57 str(pixel_color.r) + ", " + | 100 str(pixel_color.r) + ", " + |
| 58 str(pixel_color.g) + ", " + | 101 str(pixel_color.g) + ", " + |
| 59 str(pixel_color.b) + "]") | 102 str(pixel_color.b) + "]") |
| 60 | 103 |
| 104 @staticmethod | |
| 105 def UrlToImageName(url): | |
| 106 image_name = re.sub(r'^(http|https|file)://(/*)', '', url) | |
| 107 image_name = re.sub(r'\.\./', '', image_name) | |
| 108 image_name = re.sub(r'(\.|/|-)', '_', image_name) | |
| 109 return image_name | |
| 110 | |
| 111 @staticmethod | |
| 112 def WriteErrorImage(img_dir, img_name, build_revision, screenshot): | |
| 113 full_image_name = img_name + '_' + str(build_revision) | |
| 114 full_image_name = full_image_name + '.png' | |
| 115 | |
| 116 # This is a nasty and temporary hack: The pixel test archive step will copy | |
| 117 # DIFF images directly, but for FAIL images it also requires a ref. This | |
| 118 # allows us to archive the erronous image while the archiving step is being | |
| 119 # refactored | |
| 120 image_path = os.path.join(img_dir, 'DIFF_' + full_image_name) | |
| 121 | |
| 122 output_dir = os.path.dirname(image_path) | |
| 123 if not os.path.exists(output_dir): | |
| 124 os.makedirs(output_dir) | |
| 125 | |
| 126 screenshot.WriteFile(image_path) | |
| 127 | |
| 61 class Maps(test.Test): | 128 class Maps(test.Test): |
| 62 """Google Maps pixel tests.""" | 129 """Google Maps pixel tests.""" |
| 63 test = MapsValidator | 130 test = MapsValidator |
| 64 | 131 |
| 132 @staticmethod | |
| 133 def AddTestCommandLineOptions(parser): | |
| 134 group = optparse.OptionGroup(parser, 'Maps test options') | |
| 135 group.add_option('--generated-dir', | |
| 136 help='Overrides the default location for generated test images that ' | |
| 137 'fail expectations checks', | |
| 138 default=default_generated_data_dir) | |
| 139 group.add_option('--build-revision', | |
| 140 help='Chrome revision being tested.', | |
| 141 default="unknownrev") | |
|
Ken Russell (switch to Gerrit)
2013/11/19 01:22:30
Presumably this command line argument will be adde
bajones
2013/11/19 18:27:31
The maps test has been removed from the recipe at
| |
| 142 parser.add_option_group(group) | |
| 143 | |
| 144 def CreateExpectations(self, page_set): | |
| 145 return maps_expectations.MapsExpectations() | |
| 146 | |
| 65 def CreatePageSet(self, options): | 147 def CreatePageSet(self, options): |
| 66 page_set_path = os.path.join( | 148 page_set_path = os.path.join( |
| 67 util.GetChromiumSrcDir(), 'content', 'test', 'gpu', 'page_sets') | 149 util.GetChromiumSrcDir(), 'content', 'test', 'gpu', 'page_sets') |
| 68 page_set_dict = { | 150 page_set_dict = { |
| 69 'archive_data_file': 'data/maps.json', | 151 'archive_data_file': 'data/maps.json', |
| 70 'make_javascript_deterministic': False, | 152 'make_javascript_deterministic': False, |
| 71 'pages': [ | 153 'pages': [ |
| 72 { | 154 { |
| 155 'name': 'Maps.maps_001', | |
| 73 'url': 'http://localhost:10020/tracker.html', | 156 'url': 'http://localhost:10020/tracker.html', |
| 74 'navigate_steps': [ | 157 'navigate_steps': [ |
| 75 { 'action': 'navigate' }, | 158 { 'action': 'navigate' }, |
| 76 { 'action': 'wait', 'javascript': 'window.testDone' } | 159 { 'action': 'wait', 'javascript': 'window.testDone' } |
| 77 ], | 160 ], |
| 78 'pixel_expectations': 'data/maps_001_expectations.json' | 161 'pixel_expectations': 'data/maps_001_expectations.json' |
| 79 } | 162 } |
| 80 ] | 163 ] |
| 81 } | 164 } |
| 82 | 165 |
| 83 return page_set.PageSet.FromDict(page_set_dict, page_set_path) | 166 return page_set.PageSet.FromDict(page_set_dict, page_set_path) |
| OLD | NEW |