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 """A Module for LayoutTests. | |
dennis_jeffrey
2011/08/25 00:36:46
We can see this from the file name; is there any o
imasaki1
2011/08/25 23:57:03
Done.
| |
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 find test description, | |
dennis_jeffrey
2011/08/25 00:36:46
'find' --> 'finding the'
imasaki1
2011/08/25 23:57:03
Done.
| |
26 # this script tries to find test description using sentences | |
dennis_jeffrey
2011/08/25 00:36:46
'find test' --> 'find the test'
imasaki1
2011/08/25 23:57:03
Done.
| |
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 | |
dennis_jeffrey
2011/08/25 00:36:46
Remove this blank line; the comment above is assoc
imasaki1
2011/08/25 23:57:03
Done.
| |
30 KEYWORDS_FOR_TEST_DESCRIPTION = ( | |
31 ['This tests', 'This test', 'Test that', 'Tests that', 'Test ']) | |
dennis_jeffrey
2011/08/25 00:36:46
I think this list can be shortened.
'This test' i
imasaki1
2011/08/25 23:57:03
Done.
| |
32 | |
33 # If cannot find the keywords, this script tries to find test case | |
34 # description by the following tags. | |
35 TAGS_FOR_TEST_DESCRIPTION = ( | |
36 ['title', 'p', 'div']) | |
dennis_jeffrey
2011/08/25 00:36:46
Can fit on the previous line?
imasaki1
2011/08/25 23:57:03
Done.
| |
37 | |
38 # If cannot find the tags, this script tries to find the test case | |
39 # description in the sentence containing following words. | |
40 KEYWORD_FOR_TEST_DESCRIPTION_FAIL_SAFE = ( | |
41 ['PASSED ', 'PASS:']) | |
42 | |
43 | |
44 class LayoutTests(object): | |
45 """A class to store test names in layout tests. | |
46 | |
47 The test names (including regular expression patterns) are read from a CSV | |
48 file and used for getting layout test names from Webkit SVN. | |
49 """ | |
50 | |
51 def __init__(self, layouttest_root_path=DEFAULT_LAYOUTTEST_LOCATION, | |
52 csv_file_path=None): | |
53 """Initialize LayoutTests | |
dennis_jeffrey
2011/08/25 00:36:46
Add period at end of line
imasaki1
2011/08/25 23:57:03
Done.
| |
54 | |
55 Args: | |
56 layouttest_root_path: A location string where Webkit layout tests are | |
57 stored. | |
58 csv_file_path: CSV file path. The file contains a list of test names | |
59 in CSV format. | |
dennis_jeffrey
2011/08/25 00:36:46
Describe what happens when this parameter is None
imasaki1
2011/08/25 23:57:03
Done.
| |
60 """ | |
61 if csv_file_path: | |
62 filter_names = LayoutTests.GetLayoutTestNamesFromCSV(csv_file_path) | |
63 else: | |
64 filter_names = [] | |
dennis_jeffrey
2011/08/25 00:36:46
for the above 4 lines:
filter_names = []
if csv_f
imasaki1
2011/08/25 23:57:03
Done.
| |
65 parent_location_list = LayoutTests.GetParentDirectoryList(filter_names) | |
66 if layouttest_root_path.startswith('http://'): | |
67 name_map = self.GetLayoutTestNamesFromSVN(parent_location_list, | |
68 layouttest_root_path) | |
dennis_jeffrey
2011/08/25 00:36:46
indent this line by 5 fewer spaces.
imasaki1
2011/08/25 23:57:03
Done.
| |
69 else: | |
70 # TODO(imasaki): support other forms such as CSV for reading test names. | |
71 pass | |
72 self.name_map = copy.copy(name_map) | |
73 # Filter names. | |
74 for lt_name in name_map.keys(): | |
75 match = False | |
76 for filter_name in filter_names: | |
77 if re.search(filter_name, lt_name): | |
78 match = True | |
79 break | |
80 if not match: | |
81 del self.name_map[lt_name] | |
82 # We get description only for the filtered names. | |
83 for lt_name in self.name_map.keys(): | |
84 self.name_map[lt_name] = LayoutTests.GetTestDescriptionFromSVN(lt_name) | |
85 | |
86 @staticmethod | |
87 def ExtractTestDescription(txt): | |
88 """Extract the description description from test code in HTML. | |
89 | |
90 Currently, we have 4 rules described in the code below. | |
91 example can be fallen into rule 1): | |
dennis_jeffrey
2011/08/25 00:36:46
(This example falls into rule 1):
imasaki1
2011/08/25 23:57:03
Done.
| |
92 <p> | |
93 This tests the intrinsic size of a video element is the default | |
94 300,150 before metadata is loaded, and 0,0 after | |
95 metadata is loaded for an audio-only file. | |
96 </p> | |
dennis_jeffrey
2011/08/25 00:36:46
Indent lines 92-96 a bit since this is an example
imasaki1
2011/08/25 23:57:03
Done.
| |
97 The strategy is very adhoc since the original test case files | |
98 (in HTML format) do not have standard way to store test description. | |
99 | |
100 Args: | |
101 txt: A HTML text which may or may not contain test description. | |
102 | |
103 Returns: | |
104 A string that contains test description. Returns 'UNKNOWN' if the | |
105 test description is not found. | |
106 """ | |
107 # (1) Try to find test description contains keywords such as | |
dennis_jeffrey
2011/08/25 00:36:46
'contains' --> 'that contains'
imasaki1
2011/08/25 23:57:03
Done.
| |
108 # 'test that' and surrounded by p tag. | |
109 # We found this is the most common case. | |
dennis_jeffrey
2011/08/25 00:36:46
'This is the most common case'.
imasaki1
2011/08/25 23:57:03
Done.
| |
110 for keyword in KEYWORDS_FOR_TEST_DESCRIPTION: | |
111 # Try to find <p> and </p>. | |
dennis_jeffrey
2011/08/25 00:36:46
Only indent by 2 spaces, not 4, within this functi
imasaki1
2011/08/25 23:57:03
Done.
| |
112 pattern = r'<p>(.*' + keyword + '.*)</p>' | |
113 matches = re.search(pattern, txt) | |
114 if matches is not None: | |
115 return matches.group(1).strip() | |
116 | |
117 # (2) Try to find it by using more generic keywords such as 'PASS' etc. | |
118 for keyword in KEYWORD_FOR_TEST_DESCRIPTION_FAIL_SAFE: | |
119 # Try to find new lines. | |
120 pattern = r'\n(.*' + keyword + '.*)\n' | |
121 matches = re.search(pattern, txt) | |
122 if matches is not None: | |
123 # Remove 'p' tag. | |
124 text = matches.group(1).strip() | |
125 return text.replace('<p>', '').replace('</p>', '') | |
126 | |
127 # (3) Try to find it by using THML tag such as title. | |
dennis_jeffrey
2011/08/25 00:36:46
'THML' --> 'HTML'
imasaki1
2011/08/25 23:57:03
Done.
| |
128 for tag in TAGS_FOR_TEST_DESCRIPTION: | |
129 pattern = r'<' + tag + '>(.*)</' + tag + '>' | |
130 matches = re.search(pattern, txt) | |
131 if matches is not None: | |
132 return matches.group(1).strip() | |
133 | |
134 # (4) Try to find it by using test description and remove 'p' tag. | |
135 for keyword in KEYWORDS_FOR_TEST_DESCRIPTION: | |
136 # Try to find <p> and </p>. | |
137 pattern = r'\n(.*' + keyword + '.*)\n' | |
138 matches = re.search(pattern, txt) | |
139 if matches is not None: | |
140 # Remove 'p' tag. | |
141 text = matches.group(1).strip() | |
142 return text.replace('<p>', '').replace('</p>', '') | |
143 | |
144 # (5) cannot find test description using existing rules. | |
145 # In this case, just returns 'UNKNOWN' | |
dennis_jeffrey
2011/08/25 00:36:46
Remove this line.
imasaki1
2011/08/25 23:57:03
Done.
| |
146 return 'UNKNOWN' | |
147 | |
148 @staticmethod | |
149 def GetLayoutTestNamesFromSVN(parent_location_list, | |
150 layouttest_root_path): | |
151 """Get LayoutTest names from Webkit SVN. | |
152 | |
153 Args: | |
154 parent_location_list: a list of locations of parents directory. This is | |
dennis_jeffrey
2011/08/25 00:36:46
'parents directory' --> 'parent directories'
imasaki1
2011/08/25 23:57:03
Done.
| |
155 used when getting layout tests using PySVN.list(). | |
156 layouttest_root_path: the root path of the Webkit SVN directory. | |
157 | |
158 Returns: | |
159 a map containing test names as keys for de-dupe. | |
160 """ | |
161 client = pysvn.Client() | |
162 # Get directory structure in the Webkit SVN. | |
163 if parent_location_list is None: | |
164 # Try to get all the layout tests by enabling recursion option. | |
165 parent_location_list = ['/\S+\.html$'] | |
166 recursion = True | |
167 else: | |
168 recursion = False | |
169 name_map = {} | |
170 for parent_location in parent_location_list: | |
171 if parent_location.endswith('/'): | |
172 file_list = client.list(layouttest_root_path + parent_location, | |
173 recurse=recursion) | |
174 for file_name in file_list: | |
175 if sys.stdout.isatty(): | |
176 default_encoding = sys.stdout.encoding | |
177 else: | |
178 default_encoding = locale.getpreferredencoding() | |
179 file_name = file_name[0].repos_path.encode(default_encoding) | |
180 # Remove the word '/truck/LayoutTests'. | |
181 file_name = file_name.replace('/trunk/LayoutTests/', '') | |
182 if file_name.endswith('.html'): | |
183 name_map[file_name] = True | |
184 return name_map | |
185 | |
186 @staticmethod | |
187 def GetLayoutTestNamesFromCSV(csv_file_path): | |
188 """Get layout test names from CSV file. | |
189 | |
190 Args: | |
191 csv_file_path: the path for the CSV file containing test names (including | |
192 regular expression patterns). The CSV file content has one column and | |
193 each row contains a test name. | |
194 """ | |
195 file_object = file(csv_file_path, 'r') | |
196 reader = csv.reader(file_object) | |
197 names = [] | |
198 for row in reader: | |
199 names.append(row[0]) | |
dennis_jeffrey
2011/08/25 00:36:46
For the above 3 lines:
names = [row[0] for row in
imasaki1
2011/08/25 23:57:03
Done.
| |
200 file_object.close() | |
201 return names | |
202 | |
203 @staticmethod | |
204 def GetParentDirectoryList(names): | |
205 """Get parent directory list from test names. | |
206 | |
207 Args: | |
208 names : a list of test names. The test name also have path information as | |
dennis_jeffrey
2011/08/25 00:36:46
remove the space before the ':'
dennis_jeffrey
2011/08/25 00:36:46
'test name also' -->
'test names also'
imasaki1
2011/08/25 23:57:03
Done.
imasaki1
2011/08/25 23:57:03
Done.
| |
209 well (e.g., media/video-zoom.html). | |
210 """ | |
211 pd_map = {} | |
212 for name in names: | |
213 p_dir = name[0:name.rfind('/') + 1] | |
214 pd_map[p_dir] = True | |
215 return list(pd_map.iterkeys()) | |
216 | |
217 def JoinWithTestExpectation(self, test_expections): | |
dennis_jeffrey
2011/08/25 00:36:46
'test_expections' -->
'test_expectations'
imasaki1
2011/08/25 23:57:03
Done.
| |
218 """Join layout tests with the test expectation file using test name as key. | |
219 | |
220 Args: | |
221 test_expectaions: a test expectations object. | |
222 | |
223 Returns: | |
224 test_info_map contains test name as key and another map as value. The | |
225 other map contains test description and the test expectation | |
226 information which contains keyword (e.g., 'GPU') as key (we do | |
227 not care about values). | |
dennis_jeffrey
2011/08/25 00:36:46
If you don't care about values, why not use a list
imasaki1
2011/08/25 23:57:03
But, I have to look the value up several times so
| |
228 """ | |
229 test_info_map = {} | |
230 for (lt_name, desc) in self.name_map.items(): | |
231 test_info_map[lt_name] = {} | |
232 test_info_map[lt_name]['desc'] = desc | |
233 for (te_name, te_info) in ( | |
234 test_expections.all_test_expectation_info.items()): | |
235 if te_name == lt_name or ( | |
236 te_name in lt_name and te_name.endswith('/')): | |
237 # Only 1 match (if exist) | |
dennis_jeffrey
2011/08/25 00:36:46
# Only keep the first match when found.
imasaki1
2011/08/25 23:57:03
Done.
| |
238 test_info_map[lt_name]['te_info'] = te_info | |
239 break | |
240 return test_info_map | |
241 | |
242 @staticmethod | |
243 def GetTestDescriptionFromSVN(test_location, | |
244 layouttest_root_path=DEFAULT_LAYOUTTEST_LOCATION): | |
dennis_jeffrey
2011/08/25 00:36:46
indent this line so it lines up underneath the fir
imasaki1
2011/08/25 23:57:03
Done.
| |
245 """Get test description of a layout test from SVN. | |
246 | |
247 Using urllib2.urlopen(), this method gets the entire HTML and extract its | |
dennis_jeffrey
2011/08/25 00:36:46
'extract' --> 'extracts'
imasaki1
2011/08/25 23:57:03
Done.
| |
248 test description using |ExtractTestDescription()|. | |
249 | |
250 Args: | |
251 test_location: the location of the layout test | |
dennis_jeffrey
2011/08/25 00:36:46
add period at end of line
imasaki1
2011/08/25 23:57:03
Done.
| |
252 layouttest_root_path: the root path of the Webkit SVN directory. | |
253 | |
254 Returns: | |
255 A test description string. | |
256 | |
257 raises: | |
dennis_jeffrey
2011/08/25 00:36:46
'raises:' --> 'Raises:'
imasaki1
2011/08/25 23:57:03
Done.
| |
258 A URLError when the layout test is not available. | |
259 """ | |
260 if test_location.endswith('.html'): | |
261 resp = urllib2.urlopen(layouttest_root_path + test_location) | |
262 if resp.code == 200: | |
263 return LayoutTests.ExtractTestDescription(resp.read()) | |
264 raise URLError | |
dennis_jeffrey
2011/08/25 00:36:46
Maybe add an informative message to go along with
imasaki1
2011/08/25 23:57:03
Done.
| |
OLD | NEW |