Index: media/tools/layout_tests/layouttests.py |
diff --git a/media/tools/layout_tests/layouttests.py b/media/tools/layout_tests/layouttests.py |
new file mode 100755 |
index 0000000000000000000000000000000000000000..2b4f81f714c205dcb2adc286f77fba549770d308 |
--- /dev/null |
+++ b/media/tools/layout_tests/layouttests.py |
@@ -0,0 +1,264 @@ |
+#!/usr/bin/python |
+# Copyright (c) 2011 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 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.
|
+ |
+Layout tests are stored in Webkit SVN and LayoutTestCaseManager collects these |
+layout test cases (including description). |
+""" |
+ |
+import copy |
+import csv |
+import locale |
+import pysvn |
+import re |
+import sys |
+import urllib2 |
+ |
+ |
+# Webkit SVN root location. |
+DEFAULT_LAYOUTTEST_LOCATION = ( |
+ 'http://svn.webkit.org/repository/webkit/trunk/LayoutTests/') |
+ |
+# 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.
|
+# 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.
|
+# starting with these keywords. This is adhoc but it is the only way |
+# since there is no standard for writing test description. |
+ |
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.
|
+KEYWORDS_FOR_TEST_DESCRIPTION = ( |
+ ['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.
|
+ |
+# If cannot find the keywords, this script tries to find test case |
+# description by the following tags. |
+TAGS_FOR_TEST_DESCRIPTION = ( |
+ ['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.
|
+ |
+# If cannot find the tags, this script tries to find the test case |
+# description in the sentence containing following words. |
+KEYWORD_FOR_TEST_DESCRIPTION_FAIL_SAFE = ( |
+ ['PASSED ', 'PASS:']) |
+ |
+ |
+class LayoutTests(object): |
+ """A class to store test names in layout tests. |
+ |
+ The test names (including regular expression patterns) are read from a CSV |
+ file and used for getting layout test names from Webkit SVN. |
+ """ |
+ |
+ def __init__(self, layouttest_root_path=DEFAULT_LAYOUTTEST_LOCATION, |
+ csv_file_path=None): |
+ """Initialize LayoutTests |
dennis_jeffrey
2011/08/25 00:36:46
Add period at end of line
imasaki1
2011/08/25 23:57:03
Done.
|
+ |
+ Args: |
+ layouttest_root_path: A location string where Webkit layout tests are |
+ stored. |
+ csv_file_path: CSV file path. The file contains a list of test names |
+ 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.
|
+ """ |
+ if csv_file_path: |
+ filter_names = LayoutTests.GetLayoutTestNamesFromCSV(csv_file_path) |
+ else: |
+ 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.
|
+ parent_location_list = LayoutTests.GetParentDirectoryList(filter_names) |
+ if layouttest_root_path.startswith('http://'): |
+ name_map = self.GetLayoutTestNamesFromSVN(parent_location_list, |
+ 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.
|
+ else: |
+ # TODO(imasaki): support other forms such as CSV for reading test names. |
+ pass |
+ self.name_map = copy.copy(name_map) |
+ # Filter names. |
+ for lt_name in name_map.keys(): |
+ match = False |
+ for filter_name in filter_names: |
+ if re.search(filter_name, lt_name): |
+ match = True |
+ break |
+ if not match: |
+ del self.name_map[lt_name] |
+ # We get description only for the filtered names. |
+ for lt_name in self.name_map.keys(): |
+ self.name_map[lt_name] = LayoutTests.GetTestDescriptionFromSVN(lt_name) |
+ |
+ @staticmethod |
+ def ExtractTestDescription(txt): |
+ """Extract the description description from test code in HTML. |
+ |
+ Currently, we have 4 rules described in the code below. |
+ 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.
|
+ <p> |
+ This tests the intrinsic size of a video element is the default |
+ 300,150 before metadata is loaded, and 0,0 after |
+ metadata is loaded for an audio-only file. |
+ </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.
|
+ The strategy is very adhoc since the original test case files |
+ (in HTML format) do not have standard way to store test description. |
+ |
+ Args: |
+ txt: A HTML text which may or may not contain test description. |
+ |
+ Returns: |
+ A string that contains test description. Returns 'UNKNOWN' if the |
+ test description is not found. |
+ """ |
+ # (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.
|
+ # 'test that' and surrounded by p tag. |
+ # 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.
|
+ for keyword in KEYWORDS_FOR_TEST_DESCRIPTION: |
+ # 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.
|
+ pattern = r'<p>(.*' + keyword + '.*)</p>' |
+ matches = re.search(pattern, txt) |
+ if matches is not None: |
+ return matches.group(1).strip() |
+ |
+ # (2) Try to find it by using more generic keywords such as 'PASS' etc. |
+ for keyword in KEYWORD_FOR_TEST_DESCRIPTION_FAIL_SAFE: |
+ # Try to find new lines. |
+ pattern = r'\n(.*' + keyword + '.*)\n' |
+ matches = re.search(pattern, txt) |
+ if matches is not None: |
+ # Remove 'p' tag. |
+ text = matches.group(1).strip() |
+ return text.replace('<p>', '').replace('</p>', '') |
+ |
+ # (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.
|
+ for tag in TAGS_FOR_TEST_DESCRIPTION: |
+ pattern = r'<' + tag + '>(.*)</' + tag + '>' |
+ matches = re.search(pattern, txt) |
+ if matches is not None: |
+ return matches.group(1).strip() |
+ |
+ # (4) Try to find it by using test description and remove 'p' tag. |
+ for keyword in KEYWORDS_FOR_TEST_DESCRIPTION: |
+ # Try to find <p> and </p>. |
+ pattern = r'\n(.*' + keyword + '.*)\n' |
+ matches = re.search(pattern, txt) |
+ if matches is not None: |
+ # Remove 'p' tag. |
+ text = matches.group(1).strip() |
+ return text.replace('<p>', '').replace('</p>', '') |
+ |
+ # (5) cannot find test description using existing rules. |
+ # 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.
|
+ return 'UNKNOWN' |
+ |
+ @staticmethod |
+ def GetLayoutTestNamesFromSVN(parent_location_list, |
+ layouttest_root_path): |
+ """Get LayoutTest names from Webkit SVN. |
+ |
+ Args: |
+ 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.
|
+ used when getting layout tests using PySVN.list(). |
+ layouttest_root_path: the root path of the Webkit SVN directory. |
+ |
+ Returns: |
+ a map containing test names as keys for de-dupe. |
+ """ |
+ client = pysvn.Client() |
+ # Get directory structure in the Webkit SVN. |
+ if parent_location_list is None: |
+ # Try to get all the layout tests by enabling recursion option. |
+ parent_location_list = ['/\S+\.html$'] |
+ recursion = True |
+ else: |
+ recursion = False |
+ name_map = {} |
+ for parent_location in parent_location_list: |
+ if parent_location.endswith('/'): |
+ file_list = client.list(layouttest_root_path + parent_location, |
+ recurse=recursion) |
+ for file_name in file_list: |
+ if sys.stdout.isatty(): |
+ default_encoding = sys.stdout.encoding |
+ else: |
+ default_encoding = locale.getpreferredencoding() |
+ file_name = file_name[0].repos_path.encode(default_encoding) |
+ # Remove the word '/truck/LayoutTests'. |
+ file_name = file_name.replace('/trunk/LayoutTests/', '') |
+ if file_name.endswith('.html'): |
+ name_map[file_name] = True |
+ return name_map |
+ |
+ @staticmethod |
+ def GetLayoutTestNamesFromCSV(csv_file_path): |
+ """Get layout test names from CSV file. |
+ |
+ Args: |
+ csv_file_path: the path for the CSV file containing test names (including |
+ regular expression patterns). The CSV file content has one column and |
+ each row contains a test name. |
+ """ |
+ file_object = file(csv_file_path, 'r') |
+ reader = csv.reader(file_object) |
+ names = [] |
+ for row in reader: |
+ 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.
|
+ file_object.close() |
+ return names |
+ |
+ @staticmethod |
+ def GetParentDirectoryList(names): |
+ """Get parent directory list from test names. |
+ |
+ Args: |
+ 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.
|
+ well (e.g., media/video-zoom.html). |
+ """ |
+ pd_map = {} |
+ for name in names: |
+ p_dir = name[0:name.rfind('/') + 1] |
+ pd_map[p_dir] = True |
+ return list(pd_map.iterkeys()) |
+ |
+ 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.
|
+ """Join layout tests with the test expectation file using test name as key. |
+ |
+ Args: |
+ test_expectaions: a test expectations object. |
+ |
+ Returns: |
+ test_info_map contains test name as key and another map as value. The |
+ other map contains test description and the test expectation |
+ information which contains keyword (e.g., 'GPU') as key (we do |
+ 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
|
+ """ |
+ test_info_map = {} |
+ for (lt_name, desc) in self.name_map.items(): |
+ test_info_map[lt_name] = {} |
+ test_info_map[lt_name]['desc'] = desc |
+ for (te_name, te_info) in ( |
+ test_expections.all_test_expectation_info.items()): |
+ if te_name == lt_name or ( |
+ te_name in lt_name and te_name.endswith('/')): |
+ # 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.
|
+ test_info_map[lt_name]['te_info'] = te_info |
+ break |
+ return test_info_map |
+ |
+ @staticmethod |
+ def GetTestDescriptionFromSVN(test_location, |
+ 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.
|
+ """Get test description of a layout test from SVN. |
+ |
+ 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.
|
+ test description using |ExtractTestDescription()|. |
+ |
+ Args: |
+ 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.
|
+ layouttest_root_path: the root path of the Webkit SVN directory. |
+ |
+ Returns: |
+ A test description string. |
+ |
+ raises: |
dennis_jeffrey
2011/08/25 00:36:46
'raises:' --> 'Raises:'
imasaki1
2011/08/25 23:57:03
Done.
|
+ A URLError when the layout test is not available. |
+ """ |
+ if test_location.endswith('.html'): |
+ resp = urllib2.urlopen(layouttest_root_path + test_location) |
+ if resp.code == 200: |
+ return LayoutTests.ExtractTestDescription(resp.read()) |
+ 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.
|