Commits

Ned Batchelder committed ea7c007

Some prep work for finding completely uncovered files.

Comments (0)

Files changed (8)

         # A map from canonical Python source file name to a dictionary with an
         # entry for each pair of line numbers forming an arc:
         #
-        # { filename: { (l1,l2): None, ... }, ...}
+        #   {
+        #       'filename1.py': { (12,14): None, (47,48): None, ... },
+        #       ...
+        #       }
         #
         self.arcs = {}
 

coverage/files.py

                     return True
         return False
 
+
 class FnmatchMatcher(object):
     """A matcher for files by filename pattern."""
     def __init__(self, pats):
             if fnmatch.fnmatch(fpath, pat):
                 return True
         return False
+
+
+def find_python_files(dirname):
+    """Yield all of the importable Python files in `dirname`, recursively."""
+    for dirpath, dirnames, filenames in os.walk(dirname, topdown=True):
+        if '__init__.py' not in filenames:
+            # If a directory doesn't have __init__.py, then it isn't
+            # importable and neither are its files
+            del dirnames[:]
+            continue
+        for filename in filenames:
+            if fnmatch.fnmatch(filename, "*.py"):
+                yield os.path.join(dirpath, filename)

test/coveragetest.py

         fname = os.path.join(*fparts)
         return os.path.normcase(os.path.abspath(os.path.realpath(fname)))
 
+    def assert_same_files(self, flist1, flist2):
+        """Assert that `flist1` and `flist2` are the same set of file names."""
+        flist1_nice = [self.nice_file(f) for f in flist1]
+        flist2_nice = [self.nice_file(f) for f in flist2]
+        self.assertSameElements(flist1_nice, flist2_nice)
+
     def command_line(self, args, ret=OK, _covpkg=None):
         """Run `args` through the command line.
 

test/modules/pkg1/p1c.py

+a = 1
+b = 2
+c = 3

test/modules/pkg1/sub/__init__.py

Empty file added.

test/modules/pkg1/sub/ps1a.py

+d = 1
+e = 2
+f = 3
         self.assertEqual(lines['p1b.py'], 3)
         self.assertEqual(lines['p2a.py'], 3)
         self.assertEqual(lines['p2b.py'], 3)
+        self.assert_('p1c.py' not in lines)
 
     def test_source_package(self):
         lines = self.coverage_usepkgs_summary(source=["pkg1"])

test/test_files.py

 import os, sys
 
 from coverage.files import FileLocator, TreeMatcher, FnmatchMatcher
+from coverage.files import find_python_files
 
 sys.path.insert(0, os.path.split(__file__)[0]) # Force relative import for Py3k
 from coveragetest import CoverageTest
         self.assertEqual(fl.relative_filename(a1), "file1.py")
         self.assertEqual(fl.relative_filename(a2), a2)
 
+
+class MatcherTest(CoverageTest):
+    """Tests of file matchers."""
+
     def test_tree_matcher(self):
         file1 = self.make_file("sub/file1.py")
         file2 = self.make_file("sub/file2.c")
         self.assertTrue(fnm.match(fl.canonical_filename(file3)))
         self.assertTrue(fnm.match(fl.canonical_filename(file4)))
         self.assertFalse(fnm.match(fl.canonical_filename(file5)))
+
+
+class FindPythonFilesTest(CoverageTest):
+    """Tests of `find_python_files`."""
+
+    def test_find_python_files(self):
+        self.make_file("sub/a.py")
+        self.make_file("sub/b.py")
+        self.make_file("sub/__init__.py")
+        self.make_file("sub/x.c")                   # nope: not .py
+        self.make_file("sub/ssub/__init__.py")
+        self.make_file("sub/ssub/s.py")
+        self.make_file("sub/lab/exp.py")            # nope: no __init__.py
+        py_files = set(find_python_files("sub"))
+        self.assert_same_files(py_files, [
+            "sub/__init__.py", "sub/a.py", "sub/b.py",
+            "sub/ssub/__init__.py", "sub/ssub/s.py",
+            ])
+