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

Side by Side Diff: media/tools/layout_tests/test_expectations.py

Issue 10986008: Switch to using WebKit's test expecation parser. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Use no-reply. Created 8 years, 2 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 | Annotate | Revision Log
OLDNEW
1 # Copyright (c) 2012 The Chromium Authors. All rights reserved. 1 # Copyright (c) 2012 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 """A module to analyze test expectations for Webkit layout tests.""" 5 """A module to analyze test expectations for Webkit layout tests."""
6 6
7 import re
8 import urllib2 7 import urllib2
9 8
9 from webkitpy.layout_tests.models.test_expectations import *
10
10 # Default Webkit SVN location for chromium test expectation file. 11 # Default Webkit SVN location for chromium test expectation file.
11 # TODO(imasaki): support multiple test expectations files. 12 # TODO(imasaki): support multiple test expectations files.
12 DEFAULT_TEST_EXPECTATION_LOCATION = ( 13 DEFAULT_TEST_EXPECTATION_LOCATION = (
13 'http://svn.webkit.org/repository/webkit/trunk/' 14 'http://svn.webkit.org/repository/webkit/trunk/'
14 'LayoutTests/platform/chromium/TestExpectations') 15 'LayoutTests/platform/chromium/TestExpectations')
15 16
16 # The following is from test expectation syntax. The detail can be found in 17 # The following is from test expectation syntax. The detail can be found in
17 # http://www.chromium.org/developers/testing/ 18 # http://www.chromium.org/developers/testing/webkit-layout-tests#TOC-Test-Expect ations
18 # webkit-layout-tests#TOC-Test-Expectations
19 # <decision> ::== [SKIP] [WONTFIX] [SLOW] 19 # <decision> ::== [SKIP] [WONTFIX] [SLOW]
20 DECISION_NAMES = ['SKIP', 'WONTFIX', 'SLOW'] 20 DECISION_NAMES = ['SKIP', 'WONTFIX', 'SLOW']
21 # <config> ::== RELEASE | DEBUG 21 # <config> ::== RELEASE | DEBUG
22 CONFIG_NAMES = ['RELEASE', 'DEBUG'] 22 CONFIG_NAMES = ['RELEASE', 'DEBUG']
23 # Only hard code keywords we don't expect to change. Determine the rest from 23 # Only hard code keywords we don't expect to change. Determine the rest from
24 # the format of the status line. 24 # the format of the status line.
25 KNOWN_TE_KEYWORDS = DECISION_NAMES + CONFIG_NAMES 25 KNOWN_TE_KEYWORDS = DECISION_NAMES + CONFIG_NAMES
26 26
27 27
28 class TestExpectations(object): 28 class TestExpectations(object):
29 """A class to model the content of test expectation file for analysis. 29 """A class to model the content of test expectation file for analysis.
30 30
31 The raw test expectation file can be found in 31 This class retrieves the TestExpectations file via HTTP from WebKit and uses
32 |DEFAULT_TEST_EXPECTATION_LOCATION|. 32 the WebKit layout test processor to process each line.
33 It is necessary to parse this file and store meaningful information for 33
34 the analysis (joining with existing layout tests using a test name). 34 The resulting dictionary is stored in |all_test_expectation_info| and looks
35 Instance variable |all_test_expectation_info| is used. 35 like:
36 A test name such as 'media/video-source-type.html' is used for the key 36
37 to store information. However, a test name can appear multiple times in 37 {'<test name>': [{'<modifier0>': True, '<modifier1>': True, ...,
38 the test expectation file. So, the map should keep all the occurrence 38 'Platforms: ['<platform0>', ... ], 'Bugs': ['....']}]}
39 information. For example, the current test expectation file has the following 39
40 two entries: 40 Duplicate keys are merged (though technically they shouldn't exist).
41 BUGWK58587 LINUX DEBUG GPU : media/video-zoom.html = IMAGE 41
42 BUGCR86714 MAC GPU : media/video-zoom.html = CRASH IMAGE 42 Example:
43 In this case, all_test_expectation_info['media/video-zoom.html'] will have 43 crbug.com/145590 [ Android ] \
44 a list with two elements, each of which is the map of the test expectation 44 platform/chromium/media/video-frame-size-change.html [ Timeout ]
45 information. 45 webkit.org/b/84724 [ SnowLeopard ] \
46 platform/chromium/media/video-frame-size-change.html \
47 [ ImageOnlyFailure Pass ]
48
49 {'platform/chromium/media/video-frame-size-change.html': [{'IMAGE': True,
50 'Bugs': ['BUGWK84724', 'BUGCR145590'], 'Comments': '',
51 'Platforms': ['SNOWLEOPARD', 'ANDROID'], 'TIMEOUT': True, 'PASS': True}]}
46 """ 52 """
47 53
48 def __init__(self, url=DEFAULT_TEST_EXPECTATION_LOCATION): 54 def __init__(self, url=DEFAULT_TEST_EXPECTATION_LOCATION):
49 """Read the test expectation file from the specified URL and parse it. 55 """Read the test expectation file from the specified URL and parse it.
50 56
51 All parsed information is stored into instance variable
52 |all_test_expectation_info|, which is a dictionary mapping a string test
53 name to a list of dictionaries containing test expectation entry
54 information. An example of such dictionary:
55 {'media/video-zoom.html': [{'LINUX': True, 'DEBUG': True ....},
56 {'MAC': True, 'GPU': True ....}]
57 which is produced from the lines:
58 BUGCR86714 MAC GPU : media/video-zoom.html = CRASH IMAGE
59 BUGCR86714 LINUX DEBUG : media/video-zoom.html = IMAGE
60
61 Args: 57 Args:
62 url: A URL string for the test expectation file. 58 url: A URL string for the test expectation file.
63 59
64 Raises: 60 Raises:
65 NameError when the test expectation file cannot be retrieved from |url|. 61 NameError when the test expectation file cannot be retrieved from |url|.
66 """ 62 """
67 self.all_test_expectation_info = {} 63 self.all_test_expectation_info = {}
68 resp = urllib2.urlopen(url) 64 resp = urllib2.urlopen(url)
69 if resp.code != 200: 65 if resp.code != 200:
70 raise NameError('Test expectation file does not exist in %s' % url) 66 raise NameError('Test expectation file does not exist in %s' % url)
71 # Start parsing each line. 67 # Start parsing each line.
72 comments = ''
73 for line in resp.read().split('\n'): 68 for line in resp.read().split('\n'):
74 if line.startswith('//'): 69 line = line.strip()
75 # Comments can be multiple lines. 70 # Skip comments.
76 comments += line.replace('//', '') 71 if line.startswith('#'):
77 elif not line: 72 continue
78 comments = '' 73 testname, te_info = self.ParseLine(line)
74 if not testname or not te_info:
75 continue
76 if testname in self.all_test_expectation_info:
77 # Merge keys if entry already exists.
78 for k in te_info.keys():
79 if (isinstance(te_info[k], list) and
80 k in self.all_test_expectation_info[testname]):
81 self.all_test_expectation_info[testname][0][k] += te_info[k]
82 else:
83 self.all_test_expectation_info[testname][0][k] = te_info[k]
79 else: 84 else:
80 test_expectation_info = self.ParseLine(line, comments) 85 self.all_test_expectation_info[testname] = [te_info]
81 testname = TestExpectations.ExtractTestOrDirectoryName(line)
82 if not testname in self.all_test_expectation_info:
83 self.all_test_expectation_info[testname] = []
84 # This is a list for multiple entries.
85 self.all_test_expectation_info[testname].append(test_expectation_info)
86 86
87 @staticmethod 87 @staticmethod
88 def ExtractTestOrDirectoryName(line): 88 def ParseLine(line):
89 """Extract either a test name or a directory name from each line. 89 """Parses the provided line using WebKit's TextExpecations parser.
90
91 Please note the name in the test expectation entry can be test name or
92 directory: Such examples are:
93 BUGWK43668 SKIP : media/track/ = TIMEOUT
94
95 Args:
96 line: a line in the test expectation file.
97 90
98 Returns: 91 Returns:
99 a test name or directory name string. Returns '' if no match. 92 Tuple of test name, test expectations dictionary. See class documentation
100 93 for the format of the dictionary
101 Raises:
102 ValueError when there is no test name match.
103 """
104 # First try to find test name ending with .html.
105 matches = re.search(r':\s+(\S+(.html|.svg))', line)
106 # Next try to find directory name.
107 if matches:
108 return matches.group(1)
109 matches = re.search(r':\s+(\S+)', line)
110 if matches:
111 return matches.group(1)
112 else:
113 raise ValueError('test or dictionary name cannot be found in the line')
114
115 @staticmethod
116 def ParseLine(line, comment_prefix):
117 """Parse each line in test expectation and update test expectation info.
118
119 This function checks for each entry from |ALL_TE_KEYWORDS| in the current
120 line and stores it in the test expectation info map if found. Comment
121 and bug information is also stored in the map.
122
123 Args:
124 line: a line in the test expectation file. For example,
125 "BUGCR86714 MAC GPU : media/video-zoom.html = CRASH IMAGE"
126 comment_prefix: comments from the test expectation file occurring just
127 before the current line being parsed.
128
129 Returns:
130 a dictionary containing test expectation info, including comment and bug
131 info.
132 """ 94 """
133 test_expectation_info = {} 95 test_expectation_info = {}
134 # Store comments. 96 parsed = TestExpectationParser._tokenize_line('TestExpectations', line, 0)
135 inline_comments = '' 97 if parsed.is_invalid():
136 if '//' in line: 98 return None, None
137 inline_comments = line[line.rindex('//') + 2:] 99
138 # Remove the inline comments to avoid the case where keywords are in 100 test_expectation_info['Comments'] = parsed.comment or ''
139 # inline comments. 101
140 line = line[0:line.rindex('//')] 102 # Split the modifiers dictionary into the format we want.
141 for name in KNOWN_TE_KEYWORDS: 103 remaining_modifiers = list(parsed.modifiers)
142 if name in line: 104 test_expectation_info['Bugs'] = []
143 test_expectation_info[name] = True 105 for m in parsed.modifiers:
144 test_expectation_info['Comments'] = comment_prefix + inline_comments 106 if m.startswith('BUG'):
145 # Store bug informations. 107 test_expectation_info['Bugs'].append(m)
146 bugs = re.findall(r'BUG\w+', line) 108 remaining_modifiers.remove(m)
147 if bugs: 109 elif m in KNOWN_TE_KEYWORDS:
148 test_expectation_info['Bugs'] = bugs 110 test_expectation_info[m] = True
149 # Platforms should be the first tags to the left of the " : " after the BUG 111 remaining_modifiers.remove(m)
150 # information and known keywords have been removed. 112
151 test_expectation_info['Platforms'] = [ 113 # The modifiers left over should all be platform names.
152 x for x in line.split(' : ')[0].split(' ') if x and x not in bugs and 114 test_expectation_info['Platforms'] = list(remaining_modifiers)
153 x not in KNOWN_TE_KEYWORDS] 115
154 # Test expectations should be all the keywords to the right of " = " 116 # Shovel the expectations and modifiers in as "<key>: True" entries. Ugly,
155 test_expectation = [x for x in line.split(' = ')[-1].split(' ') if x] 117 # but required by the rest of the pipeline for parsing.
156 # Dump keywords into test expectations dictionary. 118 for m in parsed.expectations + remaining_modifiers:
157 for name in test_expectation_info['Platforms'] + test_expectation: 119 test_expectation_info[m] = True
158 test_expectation_info[name] = True 120
159 return test_expectation_info 121 return parsed.name, test_expectation_info
OLDNEW
« no previous file with comments | « media/tools/layout_tests/layouttest_analyzer_helpers.py ('k') | media/tools/layout_tests/test_expectations_unittest.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698