| 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 import fnmatch | 5 import fnmatch |
| 6 import urlparse | 6 import urlparse |
| 7 | 7 |
| 8 # Valid expectation conditions are: | 8 # Valid expectation conditions are: |
| 9 # | 9 # |
| 10 # Operating systems: | 10 # Operating systems: |
| (...skipping 18 matching lines...) Expand all Loading... |
| 29 BROWSER_TYPE_CONDITIONS = [ | 29 BROWSER_TYPE_CONDITIONS = [ |
| 30 'android-webview-shell', 'android-content-shell', 'android-chromium', | 30 'android-webview-shell', 'android-content-shell', 'android-chromium', |
| 31 'debug', 'release'] | 31 'debug', 'release'] |
| 32 | 32 |
| 33 class Expectation(object): | 33 class Expectation(object): |
| 34 """Represents a single test expectation for a page. | 34 """Represents a single test expectation for a page. |
| 35 | 35 |
| 36 Supports conditions based on operating system (e.g., win, mac) and | 36 Supports conditions based on operating system (e.g., win, mac) and |
| 37 browser type (e.g. 'debug', 'release'). | 37 browser type (e.g. 'debug', 'release'). |
| 38 | 38 |
| 39 The pattern, if a URL, *must* be a relative URL. Absolute URLs |
| 40 (e.g. starting with a scheme like http://, https://, file://) are |
| 41 not allowed. A ValueError is raised if one is passed to __init__. |
| 42 |
| 39 Subclass this class and call super.__init__ last in your constructor | 43 Subclass this class and call super.__init__ last in your constructor |
| 40 in order to add new user-defined conditions. The conditions are | 44 in order to add new user-defined conditions. The conditions are |
| 41 parsed at the end of this class's constructor, so be careful not to | 45 parsed at the end of this class's constructor, so be careful not to |
| 42 overwrite the results of the constructor call! | 46 overwrite the results of the constructor call! |
| 47 |
| 43 """ | 48 """ |
| 44 | 49 |
| 45 def __init__(self, expectation, pattern, conditions=None, bug=None): | 50 def __init__(self, expectation, pattern, conditions=None, bug=None): |
| 46 self.expectation = expectation.lower() | 51 self.expectation = expectation.lower() |
| 52 if pattern.find('://') > 0: |
| 53 raise ValueError('Absolute URLs are not allowed in patterns') |
| 47 self.pattern = pattern | 54 self.pattern = pattern |
| 48 self.bug = bug | 55 self.bug = bug |
| 49 | 56 |
| 50 self.os_conditions = [] | 57 self.os_conditions = [] |
| 51 self.browser_conditions = [] | 58 self.browser_conditions = [] |
| 52 | 59 |
| 53 if conditions: | 60 if conditions: |
| 54 for c in conditions: | 61 for c in conditions: |
| 55 self.ParseCondition(c) | 62 self.ParseCondition(c) |
| 56 | 63 |
| (...skipping 25 matching lines...) Expand all Loading... |
| 82 self.os_conditions.append(cl) | 89 self.os_conditions.append(cl) |
| 83 elif cl in BROWSER_TYPE_CONDITIONS: | 90 elif cl in BROWSER_TYPE_CONDITIONS: |
| 84 self.browser_conditions.append(condition) | 91 self.browser_conditions.append(condition) |
| 85 else: | 92 else: |
| 86 raise ValueError('Unknown expectation condition: "%s"' % cl) | 93 raise ValueError('Unknown expectation condition: "%s"' % cl) |
| 87 | 94 |
| 88 | 95 |
| 89 class TestExpectations(object): | 96 class TestExpectations(object): |
| 90 """A class which defines the expectations for a page set test execution""" | 97 """A class which defines the expectations for a page set test execution""" |
| 91 | 98 |
| 92 def __init__(self): | 99 def __init__(self, url_prefixes=None): |
| 93 self._expectations = [] | 100 self._expectations = [] |
| 101 self._url_prefixes = [] |
| 94 self._skip_matching_names = False | 102 self._skip_matching_names = False |
| 95 self._built_expectation_cache = True | 103 self._built_expectation_cache = True |
| 96 self._ClearExpectationsCache() | 104 self._ClearExpectationsCache() |
| 97 self.SetExpectations() | 105 self.SetExpectations() |
| 106 if url_prefixes: |
| 107 for p in url_prefixes: |
| 108 self._url_prefixes.append(p) |
| 98 | 109 |
| 99 def SetExpectations(self): | 110 def SetExpectations(self): |
| 100 """Called on creation. Override to set up custom expectations.""" | 111 """Called on creation. Override to set up custom expectations.""" |
| 101 pass | 112 pass |
| 102 | 113 |
| 103 def Fail(self, pattern, conditions=None, bug=None): | 114 def Fail(self, pattern, conditions=None, bug=None): |
| 104 self._Expect('fail', pattern, conditions, bug) | 115 self._Expect('fail', pattern, conditions, bug) |
| 105 | 116 |
| 106 def Skip(self, pattern, conditions=None, bug=None): | 117 def Skip(self, pattern, conditions=None, bug=None): |
| 107 self._Expect('skip', pattern, conditions, bug) | 118 self._Expect('skip', pattern, conditions, bug) |
| (...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 153 self._built_expectation_cache = True | 164 self._built_expectation_cache = True |
| 154 self._skip_matching_names = False | 165 self._skip_matching_names = False |
| 155 | 166 |
| 156 def _GetNormalizedURL(self, url, browser): | 167 def _GetNormalizedURL(self, url, browser): |
| 157 # Telemetry uses backslashes in its file:// URLs on Windows, | 168 # Telemetry uses backslashes in its file:// URLs on Windows, |
| 158 # breaking matching of test expectations. | 169 # breaking matching of test expectations. |
| 159 if not browser.platform.GetOSName() == 'win': | 170 if not browser.platform.GetOSName() == 'win': |
| 160 return url | 171 return url |
| 161 return url.replace('\\', '/') | 172 return url.replace('\\', '/') |
| 162 | 173 |
| 163 def _GetURLPath(self, url): | 174 def _GetURLPath(self, url, browser): |
| 164 components = urlparse.urlsplit(url) | 175 normalized_url = self._GetNormalizedURL(url, browser) |
| 176 components = urlparse.urlsplit(normalized_url) |
| 165 # For compatibility, the file:// scheme must be treated specially. | 177 # For compatibility, the file:// scheme must be treated specially. |
| 166 # The top-level directory shows up in the netloc portion of the URL. | 178 # The top-level directory shows up in the netloc portion of the URL. |
| 167 if components[0] == 'file': | 179 if components[0] == 'file': |
| 168 url_path = components[1] + components[2] | 180 url_path = components[1] + components[2] |
| 169 else: | 181 else: |
| 170 url_path = components[2] | 182 url_path = components[2] |
| 171 # Chop any leading slash since the expectations used by this class | 183 # Chop any leading slash since the expectations used by this class |
| 172 # assume that. | 184 # assume that. |
| 173 if url_path and url_path[0] == '/': | 185 if url_path and url_path[0] == '/': |
| 174 url_path = url_path[1:] | 186 url_path = url_path[1:] |
| 175 # Python's urlsplit doesn't seem to handle query arguments for | 187 # Python's urlsplit doesn't seem to handle query arguments for |
| 176 # file:// URLs properly. Split them off manually. | 188 # file:// URLs properly. Split them off manually. |
| 177 query_index = url_path.find('?') | 189 query_index = url_path.find('?') |
| 178 if query_index > 0: | 190 if query_index > 0: |
| 179 url_path = url_path[0:query_index] | 191 url_path = url_path[0:query_index] |
| 192 # Look for the URL prefixes specified at construction time, and |
| 193 # trim the first one found, if any. |
| 194 if self._url_prefixes: |
| 195 for p in self._url_prefixes: |
| 196 if url_path.startswith(p): |
| 197 url_path = url_path[len(p):] |
| 198 break |
| 180 return url_path | 199 return url_path |
| 181 | 200 |
| 182 def _GetExpectationObjectForPage(self, browser, page): | 201 def _GetExpectationObjectForPage(self, browser, page): |
| 183 if not self._built_expectation_cache: | 202 if not self._built_expectation_cache: |
| 184 self._BuildExpectationsCache(browser, page) | 203 self._BuildExpectationsCache(browser, page) |
| 185 # First attempt to look up by the page's URL or name. | 204 # First attempt to look up by the page's URL or name. |
| 186 e = None | 205 e = None |
| 187 # Relative URL (common case). | 206 # Relative URL (common case). |
| 188 url = self._GetNormalizedURL(page.url, browser) | 207 url_path = self._GetURLPath(page.url, browser) |
| 189 url_path = self._GetURLPath(url) | |
| 190 if url_path: | 208 if url_path: |
| 191 e = self._expectations_by_pattern.get(url_path) | 209 e = self._expectations_by_pattern.get(url_path) |
| 192 if e: | 210 if e: |
| 193 return e | 211 return e |
| 194 e = self._expectations_by_pattern.get(url) | |
| 195 if e: | |
| 196 return e | |
| 197 if page.name: | 212 if page.name: |
| 198 e = self._expectations_by_pattern.get(page.name) | 213 e = self._expectations_by_pattern.get(page.name) |
| 199 if e: | 214 if e: |
| 200 return e | 215 return e |
| 201 # Fall back to scanning through the expectations containing | 216 # Fall back to scanning through the expectations containing |
| 202 # wildcards. | 217 # wildcards. |
| 203 for e in self._expectations_with_wildcards: | 218 for e in self._expectations_with_wildcards: |
| 204 if self.ExpectationAppliesToPage(e, browser, page): | 219 if self.ExpectationAppliesToPage(e, browser, page): |
| 205 return e | 220 return e |
| 206 return None | 221 return None |
| (...skipping 28 matching lines...) Expand all Loading... |
| 235 Args: | 250 Args: |
| 236 expectation: an instance of a subclass of Expectation, created | 251 expectation: an instance of a subclass of Expectation, created |
| 237 by a call to CreateExpectation. | 252 by a call to CreateExpectation. |
| 238 browser: the currently running browser. | 253 browser: the currently running browser. |
| 239 page: the page to be run. | 254 page: the page to be run. |
| 240 """ | 255 """ |
| 241 # While building the expectations cache we need to match | 256 # While building the expectations cache we need to match |
| 242 # everything except the page's name or URL. | 257 # everything except the page's name or URL. |
| 243 if not self._skip_matching_names: | 258 if not self._skip_matching_names: |
| 244 # Relative URL. | 259 # Relative URL. |
| 245 if not fnmatch.fnmatch(self._GetURLPath(page.url), | 260 if not fnmatch.fnmatch(self._GetURLPath(page.url, browser), |
| 246 expectation.pattern): | 261 expectation.pattern): |
| 247 # Absolute URL. | 262 # Name. |
| 248 if not fnmatch.fnmatch(page.url, | 263 if not (page.name and fnmatch.fnmatch(page.name, |
| 249 expectation.pattern): | 264 expectation.pattern)): |
| 250 # Name. | 265 return False |
| 251 if not (page.name and fnmatch.fnmatch(page.name, | |
| 252 expectation.pattern)): | |
| 253 return False | |
| 254 | 266 |
| 255 platform = browser.platform | 267 platform = browser.platform |
| 256 os_matches = (not expectation.os_conditions or | 268 os_matches = (not expectation.os_conditions or |
| 257 platform.GetOSName() in expectation.os_conditions or | 269 platform.GetOSName() in expectation.os_conditions or |
| 258 platform.GetOSVersionName() in expectation.os_conditions) | 270 platform.GetOSVersionName() in expectation.os_conditions) |
| 259 | 271 |
| 260 browser_matches = ( | 272 browser_matches = ( |
| 261 (not expectation.browser_conditions) or | 273 (not expectation.browser_conditions) or |
| 262 browser.browser_type in expectation.browser_conditions) | 274 browser.browser_type in expectation.browser_conditions) |
| 263 | 275 |
| 264 return os_matches and browser_matches | 276 return os_matches and browser_matches |
| OLD | NEW |