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

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

Issue 7693018: Intial checkin of layout test analyzer. (Closed) Base URL: http://git.chromium.org/git/chromium.git@trunk
Patch Set: Minor modifications. Created 9 years, 3 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
(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.
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698