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

Unified 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: Modified based on CR comments. Created 9 years, 4 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 side-by-side diff with in-line comments
Download patch
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..ab4790601010a05d8d4d2dc5bf1384e6b1c50e53
--- /dev/null
+++ b/media/tools/layout_tests/layouttests.py
@@ -0,0 +1,259 @@
+#!/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.
+
+"""Layout tests module that is necessary for tha layout analyzer.
+
+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 finding the test description,
+# this script tries to find the test description using sentences
+# starting with these keywords. This is adhoc but it is the only way
+# since there is no standard for writing test description.
+KEYWORDS_FOR_TEST_DESCRIPTION = ['This test', 'Tests that', 'Test ']
+
+# If cannot find the keywords, this script tries to find test case
+# description by the following tags.
+TAGS_FOR_TEST_DESCRIPTION = ['title', 'p', 'div']
+
+# 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 using root and CSV file.
+
+ 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. When this parameter is not specified, the names of
+ all layout tests are retrieved under the root directory.
+ """
+ filter_names = []
+ if csv_file_path:
+ filter_names = LayoutTests.GetLayoutTestNamesFromCSV(csv_file_path)
+ parent_location_list = LayoutTests.GetParentDirectoryList(filter_names)
+ if layouttest_root_path.startswith('http://'):
+ name_map = self.GetLayoutTestNamesFromSVN(parent_location_list,
+ layouttest_root_path)
+ 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.
+ (This example falls into rule 1):
+ <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>
+ 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 that contains keywords such as
+ # 'test that' and surrounded by p tag.
+ # This is the most common case.
+ for keyword in KEYWORDS_FOR_TEST_DESCRIPTION:
+ # Try to find <p> and </p>.
+ 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 HTML tag such as title.
+ 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.
+ 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 parent directories. This is
+ 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 = [row[0] for row in reader]
+ 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 names also have path information as
+ 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_expectations):
+ """Join layout tests with the test expectation file using test name as key.
+
+ Args:
+ test_expectations: 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). The map data structure is used since we
+ have to look up these keywords several times.
+ """
+ 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_expectations.all_test_expectation_info.items()):
+ if te_name == lt_name or (
+ te_name in lt_name and te_name.endswith('/')):
+ # Only keep the first match when found.
+ test_info_map[lt_name]['te_info'] = te_info
+ break
+ return test_info_map
+
+ @staticmethod
+ def GetTestDescriptionFromSVN(test_location,
+ root_path=DEFAULT_LAYOUTTEST_LOCATION):
+ """Get test description of a layout test from SVN.
+
+ Using urllib2.urlopen(), this method gets the entire HTML and extracts its
+ test description using |ExtractTestDescription()|.
+
+ Args:
+ test_location: the location of the layout test.
+ root_path: the root path of the Webkit SVN directory.
+
+ Returns:
+ A test description string.
+
+ Raises:
+ A URLError when the layout test is not available.
+ """
+ if test_location.endswith('.html'):
+ url = root_path + test_location
+ resp = urllib2.urlopen(url)
+ if resp.code == 200:
+ return LayoutTests.ExtractTestDescription(resp.read())
+ raise URLError('Fail to get layout test HTML file from %s.' % url)
« no previous file with comments | « media/tools/layout_tests/layouttest_analyzer_helpers_unittest.py ('k') | media/tools/layout_tests/layouttests_unittest.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698