Title: [112437] trunk/Tools
Revision
112437
Author
[email protected]
Date
2012-03-28 13:59:06 -0700 (Wed, 28 Mar 2012)

Log Message

test-webkitpy should support files, directories, and packages as command line args
https://bugs.webkit.org/show_bug.cgi?id=76765

Reviewed by Adam Barth.

This patch adds support for specifying files, directories, and
packages to test-webkitpy along with the already existing
support for modules, test classes, and individual test names.

Also, fix a bug in filesystem_mock where we wouldn't normalize a
path containing a reference to the current directory properly,
for example, '/foo/./bar.py'.

* Scripts/webkitpy/common/system/filesystem_mock.py:
(MockFileSystem.normpath):
* Scripts/webkitpy/test/main.py:
(Tester._configure_logging):
(Tester._run_tests):
* Scripts/webkitpy/test/test_finder.py:
(TestDirectoryTree.find_modules):
(TestDirectoryTree.subpath):
(TestFinder.is_dotted_name):
(TestFinder.find_names):
(TestFinder):
(TestFinder._find_names_for_arg):
(TestFinder._find_in_trees):
(TestFinder._default_names):
* Scripts/webkitpy/test/test_finder_unittest.py:
(TestFinderTest.setUp):
(TestFinderTest.tearDown):
(TestFinderTest.check_names):
(TestFinderTest.test_default_names):
(TestFinderTest):
(TestFinderTest.test_paths):

Modified Paths

Diff

Modified: trunk/Tools/ChangeLog (112436 => 112437)


--- trunk/Tools/ChangeLog	2012-03-28 20:42:19 UTC (rev 112436)
+++ trunk/Tools/ChangeLog	2012-03-28 20:59:06 UTC (rev 112437)
@@ -1,3 +1,40 @@
+2012-03-28  Dirk Pranke  <[email protected]>
+
+        test-webkitpy should support files, directories, and packages as command line args
+        https://bugs.webkit.org/show_bug.cgi?id=76765
+
+        Reviewed by Adam Barth.
+
+        This patch adds support for specifying files, directories, and
+        packages to test-webkitpy along with the already existing
+        support for modules, test classes, and individual test names.
+
+        Also, fix a bug in filesystem_mock where we wouldn't normalize a
+        path containing a reference to the current directory properly,
+        for example, '/foo/./bar.py'.
+
+        * Scripts/webkitpy/common/system/filesystem_mock.py:
+        (MockFileSystem.normpath):
+        * Scripts/webkitpy/test/main.py:
+        (Tester._configure_logging):
+        (Tester._run_tests):
+        * Scripts/webkitpy/test/test_finder.py:
+        (TestDirectoryTree.find_modules):
+        (TestDirectoryTree.subpath):
+        (TestFinder.is_dotted_name):
+        (TestFinder.find_names):
+        (TestFinder):
+        (TestFinder._find_names_for_arg):
+        (TestFinder._find_in_trees):
+        (TestFinder._default_names):
+        * Scripts/webkitpy/test/test_finder_unittest.py:
+        (TestFinderTest.setUp):
+        (TestFinderTest.tearDown):
+        (TestFinderTest.check_names):
+        (TestFinderTest.test_default_names):
+        (TestFinderTest):
+        (TestFinderTest.test_paths):
+
 2012-03-28  Simon Fraser  <[email protected]>
 
         Fix inspector tests when running in internal builds.

Modified: trunk/Tools/Scripts/webkitpy/common/system/filesystem_mock.py (112436 => 112437)


--- trunk/Tools/Scripts/webkitpy/common/system/filesystem_mock.py	2012-03-28 20:42:19 UTC (rev 112436)
+++ trunk/Tools/Scripts/webkitpy/common/system/filesystem_mock.py	2012-03-28 20:59:06 UTC (rev 112437)
@@ -278,7 +278,7 @@
     def normpath(self, path):
         # This function is called a lot, so we try to optimize the common cases
         # instead of always calling _slow_but_correct_normpath(), above.
-        if '..' in path:
+        if '..' in path or '/./' in path:
             # This doesn't happen very often; don't bother trying to optimize it.
             return self._slow_but_correct_normpath(path)
         if not path:

Modified: trunk/Tools/Scripts/webkitpy/test/main.py (112436 => 112437)


--- trunk/Tools/Scripts/webkitpy/test/main.py	2012-03-28 20:42:19 UTC (rev 112436)
+++ trunk/Tools/Scripts/webkitpy/test/main.py	2012-03-28 20:59:06 UTC (rev 112437)
@@ -24,7 +24,6 @@
 
 import logging
 import optparse
-import os
 import StringIO
 import sys
 import traceback
@@ -102,6 +101,7 @@
         # Modifying the handler, then, is less intrusive and less likely to
         # interfere with modifications made by other modules (e.g. in unit
         # tests).
+        handler.name = __name__
         handler.setLevel(log_level)
         formatter = logging.Formatter("%(message)s")
         handler.setFormatter(formatter)

Modified: trunk/Tools/Scripts/webkitpy/test/test_finder.py (112436 => 112437)


--- trunk/Tools/Scripts/webkitpy/test/test_finder.py	2012-03-28 20:42:19 UTC (rev 112436)
+++ trunk/Tools/Scripts/webkitpy/test/test_finder.py	2012-03-28 20:59:06 UTC (rev 112437)
@@ -24,6 +24,7 @@
 """this module is responsible for finding python tests."""
 
 import logging
+import re
 import sys
 
 
@@ -40,17 +41,29 @@
             self.top_package = starting_subdirectory.replace(filesystem.sep, '.') + '.'
             self.search_directory = filesystem.join(self.top_directory, starting_subdirectory)
 
-    def find_modules(self, suffixes):
+    def find_modules(self, suffixes, sub_directory=None):
+        if sub_directory:
+            search_directory = self.filesystem.join(self.top_directory, sub_directory)
+        else:
+            search_directory = self.search_directory
 
         def file_filter(filesystem, dirname, basename):
             return any(basename.endswith(suffix) for suffix in suffixes)
 
-        filenames = self.filesystem.files_under(self.search_directory, file_filter=file_filter)
+        filenames = self.filesystem.files_under(search_directory, file_filter=file_filter)
         return [self.to_module(filename) for filename in filenames]
 
     def to_module(self, path):
         return path.replace(self.top_directory + self.filesystem.sep, '').replace(self.filesystem.sep, '.')[:-3]
 
+    def subpath(self, path):
+        """Returns the relative path from the top of the tree to the path, or None if the path is not under the top of the tree."""
+        realpath = self.filesystem.realpath(self.filesystem.join(self.top_directory, path))
+        if realpath.startswith(self.top_directory + self.filesystem.sep):
+            return realpath.replace(self.top_directory + self.filesystem.sep, '')
+        return None
+
+
     def clean(self):
         """Delete all .pyc files in the tree that have no matching .py file."""
         _log.debug("Cleaning orphaned *.pyc files from: %s" % self.search_directory)
@@ -80,6 +93,9 @@
         relpath = name.replace('.', self.filesystem.sep) + '.py'
         return any(self.filesystem.exists(self.filesystem.join(tree.top_directory, relpath)) for tree in self.trees)
 
+    def is_dotted_name(self, name):
+        return re.match(r'[a-zA-Z.][a-zA-Z0-9_.]*', name)
+
     def to_module(self, path):
         for tree in self.trees:
             if path.startswith(tree.top_directory):
@@ -87,13 +103,50 @@
         return None
 
     def find_names(self, args, skip_integrationtests, find_all):
-        if args:
-            return args
-
         suffixes = ['_unittest.py']
         if not skip_integrationtests:
             suffixes.append('_integrationtest.py')
 
+        if args:
+            names = []
+            for arg in args:
+                names.extend(self._find_names_for_arg(arg, suffixes))
+            return names
+
+        return self._default_names(suffixes, find_all)
+
+    def _find_names_for_arg(self, arg, suffixes):
+        realpath = self.filesystem.realpath(arg)
+        if self.filesystem.exists(realpath):
+            names = self._find_in_trees(realpath, suffixes)
+            if not names:
+                _log.error("%s is not in one of the test trees." % arg)
+            return names
+
+        # See if it's a python package in a tree (or a relative path from the top of a tree).
+        names = self._find_in_trees(arg.replace('.', self.filesystem.sep), suffixes)
+        if names:
+            return names
+
+        if self.is_dotted_name(arg):
+            # The name may not exist, but that's okay; we'll find out later.
+            return [arg]
+
+        _log.error("%s is not a python name or an existing file or directory." % arg)
+        return []
+
+    def _find_in_trees(self, path, suffixes):
+        for tree in self.trees:
+            relpath = tree.subpath(path)
+            if not relpath:
+                continue
+            if self.filesystem.isfile(path):
+                return [tree.to_module(path)]
+            else:
+                return tree.find_modules(suffixes, path)
+        return []
+
+    def _default_names(self, suffixes, find_all):
         modules = []
         for tree in self.trees:
             modules.extend(tree.find_modules(suffixes))

Modified: trunk/Tools/Scripts/webkitpy/test/test_finder_unittest.py (112436 => 112437)


--- trunk/Tools/Scripts/webkitpy/test/test_finder_unittest.py	2012-03-28 20:42:19 UTC (rev 112436)
+++ trunk/Tools/Scripts/webkitpy/test/test_finder_unittest.py	2012-03-28 20:59:06 UTC (rev 112437)
@@ -24,6 +24,7 @@
 import unittest
 
 from webkitpy.common.system.filesystem_mock import MockFileSystem
+from webkitpy.common.system.outputcapture import OutputCapture
 from webkitpy.test.test_finder import TestFinder
 
 
@@ -36,17 +37,28 @@
           '/foo2/bar2/baz2.pyc': '',
           '/foo2/bar2/baz2_integrationtest.py': '',
           '/foo2/bar2/missing.pyc': '',
+          '/tmp/another_unittest.py': '',
         }
         self.fs = MockFileSystem(files)
         self.finder = TestFinder(self.fs)
         self.finder.add_tree('/foo', 'bar')
         self.finder.add_tree('/foo2')
+
+        # Here we have to jump through a hoop to make sure test-webkitpy doesn't log
+        # any messages from these tests :(.
         self.root_logger = logging.getLogger()
-        self.log_level = self.root_logger.level
-        self.root_logger.setLevel(logging.WARNING)
+        self.log_handler = None
+        for h in self.root_logger.handlers:
+            if getattr(h, 'name', None) == 'webkitpy.test.main':
+                self.log_handler = h
+                break
+        if self.log_handler:
+            self.log_level = self.log_handler.level
+            self.log_handler.level = logging.CRITICAL
 
     def tearDown(self):
-        self.root_logger.setLevel(self.log_level)
+        if self.log_handler:
+            self.log_handler.setLevel(self.log_level)
 
     def test_additional_system_paths(self):
         self.assertEquals(self.finder.additional_paths(['/usr']),
@@ -70,19 +82,53 @@
         self.finder.clean_trees()
         self.assertFalse(self.fs.exists('/foo2/bar2/missing.pyc'))
 
-    def test_find_names(self):
-        self.assertEquals(self.finder.find_names([], skip_integrationtests=False, find_all=True),
-            ['bar.baz_unittest', 'bar2.baz2_integrationtest'])
+    def check_names(self, names, expected_names, skip_integrationtests=False, find_all=False):
+        self.assertEquals(self.finder.find_names(names, skip_integrationtests, find_all),
+                          expected_names)
 
-        self.assertEquals(self.finder.find_names([], skip_integrationtests=True, find_all=True),
-            ['bar.baz_unittest'])
-        self.assertEquals(self.finder.find_names([], skip_integrationtests=True, find_all=False),
-            ['bar.baz_unittest'])
+    def test_default_names(self):
+        self.check_names([], ['bar.baz_unittest', 'bar2.baz2_integrationtest'])
+        self.check_names([], ['bar.baz_unittest'], skip_integrationtests=True, find_all=True)
+        self.check_names([], ['bar.baz_unittest'], skip_integrationtests=True, find_all=False)
 
         # Should return the names given it, even if they don't exist.
-        self.assertEquals(self.finder.find_names(['foobar'], skip_integrationtests=True, find_all=True),
-            ['foobar'])
+        self.check_names(['foobar'], ['foobar'], skip_integrationtests=True, find_all=False)
 
+    def test_paths(self):
+        self.fs.chdir('/foo/bar')
+        self.check_names(['baz_unittest.py'], ['bar.baz_unittest'])
+        self.check_names(['./baz_unittest.py'], ['bar.baz_unittest'])
+        self.check_names(['/foo/bar/baz_unittest.py'], ['bar.baz_unittest'])
+        self.check_names(['.'], ['bar.baz_unittest'])
+        self.check_names(['../../foo2/bar2'], ['bar2.baz2_integrationtest'])
 
+        self.fs.chdir('/')
+        self.check_names(['bar'], ['bar.baz_unittest'])
+        self.check_names(['/foo/bar/'], ['bar.baz_unittest'])
+
+        # This works 'by accident' since it maps onto a package.
+        self.check_names(['bar/'], ['bar.baz_unittest'])
+
+        # This should log an error, since it's outside the trees.
+        oc = OutputCapture()
+        oc.set_log_level(logging.ERROR)
+        oc.capture_output()
+        try:
+            self.check_names(['/tmp/another_unittest.py'], [])
+        finally:
+            _, _, logs = oc.restore_output()
+            self.assertTrue('another_unittest.py' in logs)
+
+        # Paths that don't exist are errors.
+        oc.capture_output()
+        try:
+            self.check_names(['/foo/bar/notexist_unittest.py'], [])
+        finally:
+            _, _, logs = oc.restore_output()
+            self.assertTrue('notexist_unittest.py' in logs)
+
+        # Names that don't exist are caught later, at load time.
+        self.check_names(['bar.notexist_unittest'], ['bar.notexist_unittest'])
+
 if __name__ == '__main__':
     unittest.main()
_______________________________________________
webkit-changes mailing list
[email protected]
http://lists.webkit.org/mailman/listinfo.cgi/webkit-changes

Reply via email to