| # Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| # Use of this source code is governed by a BSD-style license that can be |
| # found in the LICENSE file. |
| |
| """A module to analyze test expectations for Webkit layout tests.""" |
| |
| import urllib2 |
| |
| from webkitpy.layout_tests.models.test_expectations import * |
| |
| # Default Webkit SVN location for chromium test expectation file. |
| # TODO(imasaki): support multiple test expectations files. |
| DEFAULT_TEST_EXPECTATION_LOCATION = ( |
| 'http://svn.webkit.org/repository/webkit/trunk/' |
| 'LayoutTests/platform/chromium/TestExpectations') |
| |
| # The following is from test expectation syntax. The detail can be found in |
| # http://www.chromium.org/developers/testing/webkit-layout-tests#TOC-Test-Expectations |
| # <decision> ::== [SKIP] [WONTFIX] [SLOW] |
| DECISION_NAMES = ['SKIP', 'WONTFIX', 'SLOW'] |
| # <config> ::== RELEASE | DEBUG |
| CONFIG_NAMES = ['RELEASE', 'DEBUG'] |
| # Only hard code keywords we don't expect to change. Determine the rest from |
| # the format of the status line. |
| KNOWN_TE_KEYWORDS = DECISION_NAMES + CONFIG_NAMES |
| |
| |
| class TestExpectations(object): |
| """A class to model the content of test expectation file for analysis. |
| |
| This class retrieves the TestExpectations file via HTTP from WebKit and uses |
| the WebKit layout test processor to process each line. |
| |
| The resulting dictionary is stored in |all_test_expectation_info| and looks |
| like: |
| |
| {'<test name>': [{'<modifier0>': True, '<modifier1>': True, ..., |
| 'Platforms: ['<platform0>', ... ], 'Bugs': ['....']}]} |
| |
| Duplicate keys are merged (though technically they shouldn't exist). |
| |
| Example: |
| crbug.com/145590 [ Android ] \ |
| platform/chromium/media/video-frame-size-change.html [ Timeout ] |
| webkit.org/b/84724 [ SnowLeopard ] \ |
| platform/chromium/media/video-frame-size-change.html \ |
| [ ImageOnlyFailure Pass ] |
| |
| {'platform/chromium/media/video-frame-size-change.html': [{'IMAGE': True, |
| 'Bugs': ['BUGWK84724', 'BUGCR145590'], 'Comments': '', |
| 'Platforms': ['SNOWLEOPARD', 'ANDROID'], 'TIMEOUT': True, 'PASS': True}]} |
| """ |
| |
| def __init__(self, url=DEFAULT_TEST_EXPECTATION_LOCATION): |
| """Read the test expectation file from the specified URL and parse it. |
| |
| Args: |
| url: A URL string for the test expectation file. |
| |
| Raises: |
| NameError when the test expectation file cannot be retrieved from |url|. |
| """ |
| self.all_test_expectation_info = {} |
| resp = urllib2.urlopen(url) |
| if resp.code != 200: |
| raise NameError('Test expectation file does not exist in %s' % url) |
| # Start parsing each line. |
| for line in resp.read().split('\n'): |
| line = line.strip() |
| # Skip comments. |
| if line.startswith('#'): |
| continue |
| testname, te_info = self.ParseLine(line) |
| if not testname or not te_info: |
| continue |
| if testname in self.all_test_expectation_info: |
| # Merge keys if entry already exists. |
| for k in te_info.keys(): |
| if (isinstance(te_info[k], list) and |
| k in self.all_test_expectation_info[testname]): |
| self.all_test_expectation_info[testname][0][k] += te_info[k] |
| else: |
| self.all_test_expectation_info[testname][0][k] = te_info[k] |
| else: |
| self.all_test_expectation_info[testname] = [te_info] |
| |
| @staticmethod |
| def ParseLine(line): |
| """Parses the provided line using WebKit's TextExpecations parser. |
| |
| Returns: |
| Tuple of test name, test expectations dictionary. See class documentation |
| for the format of the dictionary |
| """ |
| test_expectation_info = {} |
| parsed = TestExpectationParser._tokenize_line('TestExpectations', line, 0) |
| if parsed.is_invalid(): |
| return None, None |
| |
| test_expectation_info['Comments'] = parsed.comment or '' |
| |
| # Split the modifiers dictionary into the format we want. |
| remaining_modifiers = list(parsed.modifiers) |
| test_expectation_info['Bugs'] = [] |
| for m in parsed.modifiers: |
| if m.startswith('BUG'): |
| test_expectation_info['Bugs'].append(m) |
| remaining_modifiers.remove(m) |
| elif m in KNOWN_TE_KEYWORDS: |
| test_expectation_info[m] = True |
| remaining_modifiers.remove(m) |
| |
| # The modifiers left over should all be platform names. |
| test_expectation_info['Platforms'] = list(remaining_modifiers) |
| |
| # Shovel the expectations and modifiers in as "<key>: True" entries. Ugly, |
| # but required by the rest of the pipeline for parsing. |
| for m in parsed.expectations + remaining_modifiers: |
| test_expectation_info[m] = True |
| |
| return parsed.name, test_expectation_info |