OLD | NEW |
---|---|
(Empty) | |
1 #!/usr/bin/python | |
2 # Copyright (c) 2011 The Chromium Authors. All rights reserved. | |
3 # Use of this source code is governed by a BSD-style license that can be | |
4 # found in the LICENSE file. | |
5 | |
6 """Layout tests module that is necessary for tha layout analyzer. | |
7 | |
8 Layout tests are stored in Webkit SVN and LayoutTestCaseManager collects these | |
9 layout test cases (including description). | |
10 """ | |
11 | |
12 import copy | |
13 import csv | |
14 import locale | |
15 import pysvn | |
16 import re | |
17 import sys | |
18 import urllib2 | |
19 | |
20 | |
21 # Webkit SVN root location. | |
22 DEFAULT_LAYOUTTEST_LOCATION = ( | |
23 'http://svn.webkit.org/repository/webkit/trunk/LayoutTests/') | |
24 | |
25 # When parsing the test HTML file and finding the test description, | |
26 # this script tries to find the test description using sentences | |
27 # starting with these keywords. This is adhoc but it is the only way | |
28 # since there is no standard for writing test description. | |
29 KEYWORDS_FOR_TEST_DESCRIPTION = ['This test', 'Tests that', 'Test '] | |
30 | |
31 # If cannot find the keywords, this script tries to find test case | |
32 # description by the following tags. | |
33 TAGS_FOR_TEST_DESCRIPTION = ['title', 'p', 'div'] | |
34 | |
35 # If cannot find the tags, this script tries to find the test case | |
36 # description in the sentence containing following words. | |
37 KEYWORD_FOR_TEST_DESCRIPTION_FAIL_SAFE = ['PASSED ', 'PASS:'] | |
38 | |
39 | |
40 class LayoutTests(object): | |
41 """A class to store test names in layout tests. | |
42 | |
43 The test names (including regular expression patterns) are read from a CSV | |
44 file and used for getting layout test names from Webkit SVN. | |
45 """ | |
46 | |
47 def __init__(self, layouttest_root_path=DEFAULT_LAYOUTTEST_LOCATION, | |
48 csv_file_path=None): | |
49 """Initialize LayoutTests using root and CSV file. | |
50 | |
51 Args: | |
52 layouttest_root_path: A location string where Webkit layout tests are | |
53 stored. | |
54 csv_file_path: CSV file path. The file contains a list of test names | |
55 in CSV format. When this parameter is not specified, the names of | |
56 all layout tests are retrieved under the root directory. | |
57 """ | |
58 filter_names = [] | |
59 if csv_file_path: | |
60 filter_names = LayoutTests.GetLayoutTestNamesFromCSV(csv_file_path) | |
61 parent_location_list = LayoutTests.GetParentDirectoryList(filter_names) | |
62 if layouttest_root_path.startswith('http://'): | |
63 name_map = self.GetLayoutTestNamesFromSVN(parent_location_list, | |
64 layouttest_root_path) | |
65 else: | |
66 # TODO(imasaki): support other forms such as CSV for reading test names. | |
67 pass | |
68 self.name_map = copy.copy(name_map) | |
69 # Filter names. | |
70 for lt_name in name_map.keys(): | |
71 match = False | |
72 for filter_name in filter_names: | |
73 if re.search(filter_name, lt_name): | |
74 match = True | |
75 break | |
76 if not match: | |
77 del self.name_map[lt_name] | |
78 # We get description only for the filtered names. | |
79 for lt_name in self.name_map.keys(): | |
80 self.name_map[lt_name] = LayoutTests.GetTestDescriptionFromSVN(lt_name) | |
81 | |
82 @staticmethod | |
83 def ExtractTestDescription(txt): | |
84 """Extract the description description from test code in HTML. | |
85 | |
86 Currently, we have 4 rules described in the code below. | |
87 (This example falls into rule 1): | |
88 <p> | |
89 This tests the intrinsic size of a video element is the default | |
90 300,150 before metadata is loaded, and 0,0 after | |
91 metadata is loaded for an audio-only file. | |
92 </p> | |
93 The strategy is very adhoc since the original test case files | |
94 (in HTML format) do not have standard way to store test description. | |
95 | |
96 Args: | |
97 txt: A HTML text which may or may not contain test description. | |
98 | |
99 Returns: | |
100 A string that contains test description. Returns 'UNKNOWN' if the | |
101 test description is not found. | |
102 """ | |
103 # (1) Try to find test description that contains keywords such as | |
104 # 'test that' and surrounded by p tag. | |
105 # This is the most common case. | |
106 for keyword in KEYWORDS_FOR_TEST_DESCRIPTION: | |
107 # Try to find <p> and </p>. | |
108 pattern = r'<p>(.*' + keyword + '.*)</p>' | |
109 matches = re.search(pattern, txt) | |
110 if matches is not None: | |
111 return matches.group(1).strip() | |
112 | |
113 # (2) Try to find it by using more generic keywords such as 'PASS' etc. | |
114 for keyword in KEYWORD_FOR_TEST_DESCRIPTION_FAIL_SAFE: | |
115 # Try to find new lines. | |
116 pattern = r'\n(.*' + keyword + '.*)\n' | |
117 matches = re.search(pattern, txt) | |
118 if matches is not None: | |
119 # Remove 'p' tag. | |
120 text = matches.group(1).strip() | |
121 return text.replace('<p>', '').replace('</p>', '') | |
122 | |
123 # (3) Try to find it by using HTML tag such as title. | |
124 for tag in TAGS_FOR_TEST_DESCRIPTION: | |
125 pattern = r'<' + tag + '>(.*)</' + tag + '>' | |
126 matches = re.search(pattern, txt) | |
127 if matches is not None: | |
128 return matches.group(1).strip() | |
129 | |
130 # (4) Try to find it by using test description and remove 'p' tag. | |
131 for keyword in KEYWORDS_FOR_TEST_DESCRIPTION: | |
132 # Try to find <p> and </p>. | |
133 pattern = r'\n(.*' + keyword + '.*)\n' | |
134 matches = re.search(pattern, txt) | |
135 if matches is not None: | |
136 # Remove 'p' tag. | |
137 text = matches.group(1).strip() | |
138 return text.replace('<p>', '').replace('</p>', '') | |
139 | |
140 # (5) cannot find test description using existing rules. | |
141 return 'UNKNOWN' | |
142 | |
143 @staticmethod | |
144 def GetLayoutTestNamesFromSVN(parent_location_list, | |
145 layouttest_root_path): | |
146 """Get LayoutTest names from Webkit SVN. | |
147 | |
148 Args: | |
149 parent_location_list: a list of locations of parent directory. This is | |
dennis_jeffrey
2011/08/26 19:01:26
nit: 'directory' --> 'directories'
imasaki1
2011/08/26 22:28:44
Done.
| |
150 used when getting layout tests using PySVN.list(). | |
151 layouttest_root_path: the root path of the Webkit SVN directory. | |
152 | |
153 Returns: | |
154 a map containing test names as keys for de-dupe. | |
155 """ | |
156 client = pysvn.Client() | |
157 # Get directory structure in the Webkit SVN. | |
158 if parent_location_list is None: | |
159 # Try to get all the layout tests by enabling recursion option. | |
160 parent_location_list = ['/\S+\.html$'] | |
161 recursion = True | |
162 else: | |
163 recursion = False | |
164 name_map = {} | |
165 for parent_location in parent_location_list: | |
166 if parent_location.endswith('/'): | |
167 file_list = client.list(layouttest_root_path + parent_location, | |
168 recurse=recursion) | |
169 for file_name in file_list: | |
170 if sys.stdout.isatty(): | |
171 default_encoding = sys.stdout.encoding | |
172 else: | |
173 default_encoding = locale.getpreferredencoding() | |
174 file_name = file_name[0].repos_path.encode(default_encoding) | |
175 # Remove the word '/truck/LayoutTests'. | |
176 file_name = file_name.replace('/trunk/LayoutTests/', '') | |
177 if file_name.endswith('.html'): | |
178 name_map[file_name] = True | |
179 return name_map | |
180 | |
181 @staticmethod | |
182 def GetLayoutTestNamesFromCSV(csv_file_path): | |
183 """Get layout test names from CSV file. | |
184 | |
185 Args: | |
186 csv_file_path: the path for the CSV file containing test names (including | |
187 regular expression patterns). The CSV file content has one column and | |
188 each row contains a test name. | |
189 """ | |
190 file_object = file(csv_file_path, 'r') | |
191 reader = csv.reader(file_object) | |
192 names = [row[0] for row in reader] | |
193 file_object.close() | |
194 return names | |
195 | |
196 @staticmethod | |
197 def GetParentDirectoryList(names): | |
198 """Get parent directory list from test names. | |
199 | |
200 Args: | |
201 names: a list of test names. The test names also have path information as | |
202 well (e.g., media/video-zoom.html). | |
203 """ | |
204 pd_map = {} | |
205 for name in names: | |
206 p_dir = name[0:name.rfind('/') + 1] | |
207 pd_map[p_dir] = True | |
208 return list(pd_map.iterkeys()) | |
209 | |
210 def JoinWithTestExpectation(self, test_expectations): | |
211 """Join layout tests with the test expectation file using test name as key. | |
212 | |
213 Args: | |
214 test_expectations: a test expectations object. | |
215 | |
216 Returns: | |
217 test_info_map contains test name as key and another map as value. The | |
218 other map contains test description and the test expectation | |
219 information which contains keyword (e.g., 'GPU') as key (we do | |
220 not care about values). The map data structure is used since we | |
221 have to look up these keywords several times. | |
222 """ | |
223 test_info_map = {} | |
224 for (lt_name, desc) in self.name_map.items(): | |
225 test_info_map[lt_name] = {} | |
226 test_info_map[lt_name]['desc'] = desc | |
227 for (te_name, te_info) in ( | |
228 test_expectations.all_test_expectation_info.items()): | |
229 if te_name == lt_name or ( | |
230 te_name in lt_name and te_name.endswith('/')): | |
231 # Only keep the first match when found. | |
232 test_info_map[lt_name]['te_info'] = te_info | |
233 break | |
234 return test_info_map | |
235 | |
236 @staticmethod | |
237 def GetTestDescriptionFromSVN(test_location, | |
238 root_path=DEFAULT_LAYOUTTEST_LOCATION): | |
239 """Get test description of a layout test from SVN. | |
240 | |
241 Using urllib2.urlopen(), this method gets the entire HTML and extracts its | |
242 test description using |ExtractTestDescription()|. | |
243 | |
244 Args: | |
245 test_location: the location of the layout test. | |
246 root_path: the root path of the Webkit SVN directory. | |
247 | |
248 Returns: | |
249 A test description string. | |
250 | |
251 Raises: | |
252 A URLError when the layout test is not available. | |
253 """ | |
254 if test_location.endswith('.html'): | |
255 url = root_path + test_location | |
256 resp = urllib2.urlopen(url) | |
257 if resp.code == 200: | |
258 return LayoutTests.ExtractTestDescription(resp.read()) | |
259 raise URLError('Fail to get layout test HTML file from %s.' % url) | |
OLD | NEW |