diff options
-rw-r--r-- | TODO.txt | 5 | ||||
-rw-r--r-- | coverage/cmdline.py | 16 | ||||
-rw-r--r-- | coverage/config.py | 3 | ||||
-rw-r--r-- | coverage/control.py | 129 | ||||
-rw-r--r-- | coverage/files.py | 36 | ||||
-rw-r--r-- | coverage/runners/plugin.py | 6 | ||||
-rw-r--r-- | test/coveragetest.py | 6 | ||||
-rw-r--r-- | test/test_cmdline.py | 40 | ||||
-rw-r--r-- | test/test_files.py | 39 | ||||
-rw-r--r-- | test/test_testing.py | 4 | ||||
-rw-r--r-- | test/test_testplugin.py | 2 |
11 files changed, 221 insertions, 65 deletions
@@ -1,5 +1,10 @@ Coverage TODO +* Cleanups discovered while doing --source: + - PyTracer doesn't use should_trace_cache!! + - optparse recommends using make_option, not Option constructor + + * plugin work + add --cover-include. + add --cover-rcfile. diff --git a/coverage/cmdline.py b/coverage/cmdline.py index d8738db7..8bf90e21 100644 --- a/coverage/cmdline.py +++ b/coverage/cmdline.py @@ -75,6 +75,10 @@ class Opts(object): '', '--rcfile', action='store', help="Specify configuration file. Defaults to '.coveragerc'" ) + source = optparse.Option( + '', '--source', action='store', metavar="SRC1,SRC2,...", + help="A list of packages or directories of code to be measured." + ) timid = optparse.Option( '', '--timid', action='store_true', help="Use a simpler but slower trace method. Try this if you get " @@ -110,6 +114,7 @@ class CoverageOptionParser(optparse.OptionParser, object): pylib=None, rcfile=True, show_missing=None, + source=None, timid=None, erase_first=None, version=None, @@ -290,6 +295,7 @@ CMDS = { Opts.pylib, Opts.parallel_mode, Opts.timid, + Opts.source, Opts.omit, Opts.include, ] + GLOBAL_ARGS, @@ -440,8 +446,9 @@ class CoverageScript(object): return ERR # Listify the list options. - omit = pattern_list(options.omit) - include = pattern_list(options.include) + source = unshell_list(options.source) + omit = unshell_list(options.omit) + include = unshell_list(options.include) # Do something. self.coverage = self.covpkg.coverage( @@ -450,6 +457,7 @@ class CoverageScript(object): timid = options.timid, branch = options.branch, config_file = options.rcfile, + source = source, omit = omit, include = include, ) @@ -528,8 +536,8 @@ class CoverageScript(object): return OK -def pattern_list(s): - """Turn an argument into a list of patterns.""" +def unshell_list(s): + """Turn a command-line argument into a list.""" if not s: return None if sys.platform == 'win32': diff --git a/coverage/config.py b/coverage/config.py index 9f52ecb1..7c22f64b 100644 --- a/coverage/config.py +++ b/coverage/config.py @@ -20,6 +20,7 @@ class CoverageConfig(object): self.data_file = ".coverage" self.parallel = False self.timid = False + self.source = None # Defaults for [report] self.exclude_list = ['(?i)# *pragma[: ]*no *cover'] @@ -68,6 +69,8 @@ class CoverageConfig(object): self.parallel = cp.getboolean('run', 'parallel') if cp.has_option('run', 'timid'): self.timid = cp.getboolean('run', 'timid') + if cp.has_option('run', 'source'): + self.source = self.get_list(cp, 'run', 'source') if cp.has_option('run', 'omit'): self.omit = self.get_list(cp, 'run', 'omit') if cp.has_option('run', 'include'): diff --git a/coverage/control.py b/coverage/control.py index 432a7c27..dafd4930 100644 --- a/coverage/control.py +++ b/coverage/control.py @@ -1,6 +1,6 @@ """Core control stuff for Coverage.""" -import atexit, fnmatch, os, random, socket, sys +import atexit, os, random, socket, sys from coverage.annotate import AnnotateReporter from coverage.backward import string_class @@ -8,7 +8,7 @@ from coverage.codeunit import code_unit_factory, CodeUnit from coverage.collector import Collector from coverage.config import CoverageConfig from coverage.data import CoverageData -from coverage.files import FileLocator +from coverage.files import FileLocator, TreeMatcher, FnmatchMatcher from coverage.html import HtmlReporter from coverage.misc import bool_or_none from coverage.results import Analysis @@ -32,7 +32,7 @@ class coverage(object): def __init__(self, data_file=None, data_suffix=None, cover_pylib=None, auto_data=False, timid=None, branch=None, config_file=True, - omit=None, include=None): + source=None, omit=None, include=None): """ `data_file` is the base name of the data file to use, defaulting to ".coverage". `data_suffix` is appended (with a dot) to `data_file` to @@ -59,6 +59,10 @@ class coverage(object): standard file is read (".coveragerc"). If it is False, then no file is read. + `source` is a list of file paths or package names. Only code located + in the trees indicated by the file paths or package names will be + measured. + `include` and `omit` are lists of filename patterns. Files that match `include` will be measured, files that match `omit` will not. @@ -85,7 +89,7 @@ class coverage(object): self.config.from_args( data_file=data_file, cover_pylib=cover_pylib, timid=timid, branch=branch, parallel=bool_or_none(data_suffix), - omit=omit, include=include + source=source, omit=omit, include=include ) self.auto_data = auto_data @@ -96,6 +100,15 @@ class coverage(object): self.file_locator = FileLocator() + # The source argument can be directories or package names. + self.source = [] + self.source_pkgs = [] + for src in self.config.source or []: + if os.path.exists(src): + self.source.append(self.file_locator.canonical_filename(src)) + else: + self.source_pkgs.append(src) + self.omit = self._abs_files(self.config.omit) self.include = self._abs_files(self.config.include) @@ -126,11 +139,12 @@ class coverage(object): ) # The dirs for files considered "installed with the interpreter". + self.pylib_dirs = [] if not self.config.cover_pylib: # Look at where the "os" module is located. That's the indication # for "installed with the interpreter". os_dir = self.canonical_dir(os.__file__) - self.pylib_dirs = [os_dir] + self.pylib_dirs.append(os_dir) # In a virtualenv, there're actually two lib directories. Find the # other one. This is kind of ad-hoc, but it works. @@ -142,10 +156,22 @@ class coverage(object): # where we are. self.cover_dir = self.canonical_dir(__file__) + # The matchers for _should_trace, created when tracing starts. + self.source_match = None + self.pylib_match = self.cover_match = None + self.include_match = self.omit_match = None + def canonical_dir(self, f): """Return the canonical directory of the file `f`.""" return os.path.split(self.file_locator.canonical_filename(f))[0] + def _source_for_file(self, filename): + """Return the source file for `filename`.""" + if not filename.endswith(".py"): + if filename[-4:-1] == ".py": + filename = filename[:-1] + return filename + def _should_trace(self, filename, frame): """Decide whether to trace execution in `filename` @@ -156,13 +182,15 @@ class coverage(object): should not. """ - if filename[0] == '<': + if filename.startswith('<'): # Lots of non-file execution is represented with artificial # filenames like "<string>", "<doctest readme.txt[0]>", or # "<exec_function>". Don't ever trace these executions, since we # can't do anything with the data later anyway. return False + self._check_for_packages() + # Compiled Python files have two filenames: frame.f_code.co_filename is # the filename at the time the .pyc was compiled. The second name # is __file__, which is where the .pyc was actually loaded from. Since @@ -171,35 +199,31 @@ class coverage(object): # co_filename value. dunder_file = frame.f_globals.get('__file__') if dunder_file: - if not dunder_file.endswith(".py"): - if dunder_file[-4:-1] == ".py": - dunder_file = dunder_file[:-1] - filename = dunder_file - + filename = self._source_for_file(dunder_file) canonical = self.file_locator.canonical_filename(filename) - canon_dir = os.path.split(canonical)[0] - # If we aren't supposed to trace installed code, then check if this is - # near the Python standard library and skip it if so. - if not self.config.cover_pylib: - if canon_dir in self.pylib_dirs: + # If the user specified source, then that's authoritative about what to + # measure. If they didn't, then we have to exclude the stdlib and + # coverage.py directories. + if self.source_match: + if not self.source_match.match(canonical): + return False + else: + # If we aren't supposed to trace installed code, then check if this + # is near the Python standard library and skip it if so. + if self.pylib_match and self.pylib_match.match(canonical): return False - # We exclude the coverage code itself, since a little of it will be - # measured otherwise. - if canon_dir == self.cover_dir: - return False + # We exclude the coverage code itself, since a little of it will be + # measured otherwise. + if self.cover_match and self.cover_match.match(canonical): + return False # Check the file against the include and omit patterns. - if self.include: - for pattern in self.include: - if fnmatch.fnmatch(canonical, pattern): - break - else: - return False - for pattern in self.omit: - if fnmatch.fnmatch(canonical, pattern): - return False + if self.include_match and not self.include_match.match(canonical): + return False + if self.omit_match and self.omit_match.match(canonical): + return False return canonical @@ -217,6 +241,39 @@ class coverage(object): files = files or [] return [self.file_locator.abs_file(f) for f in files] + def _check_for_packages(self): + """Update the source_match matcher with latest imported packages.""" + # Our self.source_pkgs attribute is a list of package names we want to + # measure. Each time through here, we see if we've imported any of + # them yet. If so, we add its file to source_match, and we don't have + # to look for that package any more. + if self.source_pkgs: + found = [] + for pkg in self.source_pkgs: + try: + mod = sys.modules[pkg] + except KeyError: + continue + + found.append(pkg) + + try: + pkg_file = mod.__file__ + except AttributeError: + print "WHOA! No file for module %s" % pkg + else: + d, f = os.path.split(pkg_file) + if f.startswith('__init__.'): + # This is actually a package, return the directory. + pkg_file = d + else: + pkg_file = self._source_for_file(pkg_file) + pkg_file = self.file_locator.canonical_filename(pkg_file) + self.source_match.add(pkg_file) + + for pkg in found: + self.source_pkgs.remove(pkg) + def use_cache(self, usecache): """Control the use of a data file (incorrectly called a cache). @@ -242,6 +299,20 @@ class coverage(object): if not self.atexit_registered: atexit.register(self.save) self.atexit_registered = True + + # Create the matchers we need for _should_trace + if self.source or self.source_pkgs: + self.source_match = TreeMatcher(self.source) + else: + if self.cover_dir: + self.cover_match = TreeMatcher([self.cover_dir]) + if self.pylib_dirs: + self.pylib_match = TreeMatcher(self.pylib_dirs) + if self.include: + self.include_match = FnmatchMatcher(self.include) + if self.omit: + self.omit_match = FnmatchMatcher(self.omit) + self.collector.start() def stop(self): diff --git a/coverage/files.py b/coverage/files.py index 5690679f..d74b4d79 100644 --- a/coverage/files.py +++ b/coverage/files.py @@ -1,6 +1,6 @@ """File wrangling.""" -import os, sys +import fnmatch, os, sys class FileLocator(object): """Understand how filenames work.""" @@ -76,3 +76,37 @@ class FileLocator(object): data = data.decode('utf8') # TODO: How to do this properly? return data return None + + +class TreeMatcher(object): + """A matcher for files in a tree.""" + def __init__(self, directories): + self.dirs = directories[:] + + def add(self, directory): + """Add another directory to the list we match for.""" + self.dirs.append(directory) + + def match(self, fpath): + """Does `fpath` indicate a file in one of our trees?""" + for d in self.dirs: + if fpath.startswith(d): + if fpath == d: + # This is the same file! + return True + if fpath[len(d)] == os.sep: + # This is a file in the directory + return True + return False + +class FnmatchMatcher(object): + """A matcher for files by filename pattern.""" + def __init__(self, pats): + self.pats = pats[:] + + def match(self, fpath): + """Does `fpath` match one of our filename patterns?""" + for pat in self.pats: + if fnmatch.fnmatch(fpath, pat): + return True + return False diff --git a/coverage/runners/plugin.py b/coverage/runners/plugin.py index fd2a3a62..cf059c57 100644 --- a/coverage/runners/plugin.py +++ b/coverage/runners/plugin.py @@ -2,7 +2,7 @@ import optparse, sys import coverage -from coverage.cmdline import pattern_list +from coverage.cmdline import unshell_list class CoverageTestWrapper(object): @@ -34,8 +34,8 @@ class CoverageTestWrapper(object): def start(self): """Start coverage before the test suite.""" # cover_omit is a ',' separated list if provided - self.omit = pattern_list(self.options.cover_omit) - self.include = pattern_list(self.options.cover_omit) + self.omit = unshell_list(self.options.cover_omit) + self.include = unshell_list(self.options.cover_omit) self.coverage = self.covpkg.coverage( config_file = self.options.cover_rcfile, diff --git a/test/coveragetest.py b/test/coveragetest.py index 9b85b034..f09afc1f 100644 --- a/test/coveragetest.py +++ b/test/coveragetest.py @@ -121,12 +121,14 @@ class CoverageTest(TestCase): """Return the data written to stderr during the test.""" return self.captured_stderr.getvalue() - def make_file(self, filename, text): + def make_file(self, filename, text=""): """Create a temp file. `filename` is the path to the file, including directories if desired, and `text` is the content. + Returns the path to the file. + """ # Tests that call `make_file` should be run in a temp environment. assert self.run_in_temp_dir @@ -142,6 +144,8 @@ class CoverageTest(TestCase): f.write(text) f.close() + return filename + def import_module(self, modname): """Import the module named modname, and return the module object.""" modfile = modname + '.py' diff --git a/test/test_cmdline.py b/test/test_cmdline.py index cf20ba7f..9c0e1154 100644 --- a/test/test_cmdline.py +++ b/test/test_cmdline.py @@ -16,7 +16,7 @@ class CmdLineTest(CoverageTest): run_in_temp_dir = False INIT_LOAD = """\ - .coverage(cover_pylib=None, data_suffix=None, timid=None, branch=None, config_file=True, include=None, omit=None) + .coverage(cover_pylib=None, data_suffix=None, timid=None, branch=None, config_file=True, source=None, include=None, omit=None) .load()\n""" def model_object(self): @@ -96,7 +96,7 @@ class ClassicCmdLineTest(CmdLineTest): def testErase(self): # coverage -e self.cmd_executes("-e", """\ - .coverage(cover_pylib=None, data_suffix=None, timid=None, branch=None, config_file=True, include=None, omit=None) + .coverage(cover_pylib=None, data_suffix=None, timid=None, branch=None, config_file=True, source=None, include=None, omit=None) .erase() """) self.cmd_executes_same("-e", "--erase") @@ -106,7 +106,7 @@ class ClassicCmdLineTest(CmdLineTest): # -x calls coverage.load first. self.cmd_executes("-x foo.py", """\ - .coverage(cover_pylib=None, data_suffix=None, timid=None, branch=None, config_file=True, include=None, omit=None) + .coverage(cover_pylib=None, data_suffix=None, timid=None, branch=None, config_file=True, source=None, include=None, omit=None) .load() .start() .run_python_file('foo.py', ['foo.py']) @@ -115,7 +115,7 @@ class ClassicCmdLineTest(CmdLineTest): """) # -e -x calls coverage.erase first. self.cmd_executes("-e -x foo.py", """\ - .coverage(cover_pylib=None, data_suffix=None, timid=None, branch=None, config_file=True, include=None, omit=None) + .coverage(cover_pylib=None, data_suffix=None, timid=None, branch=None, config_file=True, source=None, include=None, omit=None) .erase() .start() .run_python_file('foo.py', ['foo.py']) @@ -124,7 +124,7 @@ class ClassicCmdLineTest(CmdLineTest): """) # --timid sets a flag, and program arguments get passed through. self.cmd_executes("-x --timid foo.py abc 123", """\ - .coverage(cover_pylib=None, data_suffix=None, timid=True, branch=None, config_file=True, include=None, omit=None) + .coverage(cover_pylib=None, data_suffix=None, timid=True, branch=None, config_file=True, source=None, include=None, omit=None) .load() .start() .run_python_file('foo.py', ['foo.py', 'abc', '123']) @@ -133,7 +133,7 @@ class ClassicCmdLineTest(CmdLineTest): """) # -L sets a flag, and flags for the program don't confuse us. self.cmd_executes("-x -p -L foo.py -a -b", """\ - .coverage(cover_pylib=True, data_suffix=True, timid=None, branch=None, config_file=True, include=None, omit=None) + .coverage(cover_pylib=True, data_suffix=True, timid=None, branch=None, config_file=True, source=None, include=None, omit=None) .load() .start() .run_python_file('foo.py', ['foo.py', '-a', '-b']) @@ -150,7 +150,7 @@ class ClassicCmdLineTest(CmdLineTest): def testCombine(self): # coverage -c self.cmd_executes("-c", """\ - .coverage(cover_pylib=None, data_suffix=None, timid=None, branch=None, config_file=True, include=None, omit=None) + .coverage(cover_pylib=None, data_suffix=None, timid=None, branch=None, config_file=True, source=None, include=None, omit=None) .load() .combine() .save() @@ -172,13 +172,13 @@ class ClassicCmdLineTest(CmdLineTest): show_missing=True) """) self.cmd_executes("-r -o fooey", """\ - .coverage(cover_pylib=None, data_suffix=None, timid=None, branch=None, config_file=True, include=None, omit=["fooey"]) + .coverage(cover_pylib=None, data_suffix=None, timid=None, branch=None, config_file=True, source=None, include=None, omit=["fooey"]) .load() .report(ignore_errors=None, omit=["fooey"], include=None, morfs=[], show_missing=None) """) self.cmd_executes("-r -o fooey,booey", """\ - .coverage(cover_pylib=None, data_suffix=None, timid=None, branch=None, config_file=True, include=None, omit=["fooey", "booey"]) + .coverage(cover_pylib=None, data_suffix=None, timid=None, branch=None, config_file=True, source=None, include=None, omit=["fooey", "booey"]) .load() .report(ignore_errors=None, omit=["fooey", "booey"], include=None, morfs=[], show_missing=None) @@ -217,13 +217,13 @@ class ClassicCmdLineTest(CmdLineTest): omit=None, include=None, morfs=[]) """) self.cmd_executes("-a -o fooey", """\ - .coverage(cover_pylib=None, data_suffix=None, timid=None, branch=None, config_file=True, include=None, omit=["fooey"]) + .coverage(cover_pylib=None, data_suffix=None, timid=None, branch=None, config_file=True, source=None, include=None, omit=["fooey"]) .load() .annotate(directory=None, ignore_errors=None, omit=["fooey"], include=None, morfs=[]) """) self.cmd_executes("-a -o fooey,booey", """\ - .coverage(cover_pylib=None, data_suffix=None, timid=None, branch=None, config_file=True, include=None, omit=["fooey", "booey"]) + .coverage(cover_pylib=None, data_suffix=None, timid=None, branch=None, config_file=True, source=None, include=None, omit=["fooey", "booey"]) .load() .annotate(directory=None, ignore_errors=None, omit=["fooey", "booey"], include=None, morfs=[]) @@ -262,13 +262,13 @@ class ClassicCmdLineTest(CmdLineTest): omit=None, include=None, morfs=[]) """) self.cmd_executes("-b -o fooey", """\ - .coverage(cover_pylib=None, data_suffix=None, timid=None, branch=None, config_file=True, include=None, omit=["fooey"]) + .coverage(cover_pylib=None, data_suffix=None, timid=None, branch=None, config_file=True, source=None, include=None, omit=["fooey"]) .load() .html_report(directory=None, ignore_errors=None, omit=["fooey"], include=None, morfs=[]) """) self.cmd_executes("-b -o fooey,booey", """\ - .coverage(cover_pylib=None, data_suffix=None, timid=None, branch=None, config_file=True, include=None, omit=["fooey", "booey"]) + .coverage(cover_pylib=None, data_suffix=None, timid=None, branch=None, config_file=True, source=None, include=None, omit=["fooey", "booey"]) .load() .html_report(directory=None, ignore_errors=None, omit=["fooey", "booey"], include=None, morfs=[]) @@ -465,7 +465,7 @@ class NewCmdLineTest(CmdLineTest): self.cmd_executes_same("run --timid f.py", "-e -x --timid f.py") self.cmd_executes_same("run", "-x") self.cmd_executes("run --branch foo.py", """\ - .coverage(cover_pylib=None, data_suffix=None, timid=None, branch=True, config_file=True, include=None, omit=None) + .coverage(cover_pylib=None, data_suffix=None, timid=None, branch=True, config_file=True, source=None, include=None, omit=None) .erase() .start() .run_python_file('foo.py', ['foo.py']) @@ -473,7 +473,7 @@ class NewCmdLineTest(CmdLineTest): .save() """) self.cmd_executes("run --rcfile=myrc.rc foo.py", """\ - .coverage(cover_pylib=None, data_suffix=None, timid=None, branch=None, config_file="myrc.rc", include=None, omit=None) + .coverage(cover_pylib=None, data_suffix=None, timid=None, branch=None, config_file="myrc.rc", source=None, include=None, omit=None) .erase() .start() .run_python_file('foo.py', ['foo.py']) @@ -481,7 +481,7 @@ class NewCmdLineTest(CmdLineTest): .save() """) self.cmd_executes("run --include=pre1,pre2 foo.py", """\ - .coverage(cover_pylib=None, data_suffix=None, timid=None, branch=None, config_file=True, include=["pre1", "pre2"], omit=None) + .coverage(cover_pylib=None, data_suffix=None, timid=None, branch=None, config_file=True, source=None, include=["pre1", "pre2"], omit=None) .erase() .start() .run_python_file('foo.py', ['foo.py']) @@ -489,7 +489,7 @@ class NewCmdLineTest(CmdLineTest): .save() """) self.cmd_executes("run --omit=opre1,opre2 foo.py", """\ - .coverage(cover_pylib=None, data_suffix=None, timid=None, branch=None, config_file=True, include=None, omit=["opre1", "opre2"]) + .coverage(cover_pylib=None, data_suffix=None, timid=None, branch=None, config_file=True, source=None, include=None, omit=["opre1", "opre2"]) .erase() .start() .run_python_file('foo.py', ['foo.py']) @@ -499,7 +499,7 @@ class NewCmdLineTest(CmdLineTest): self.cmd_executes("run --include=pre1,pre2 --omit=opre1,opre2 foo.py", """\ .coverage(cover_pylib=None, data_suffix=None, timid=None, - branch=None, config_file=True, + branch=None, config_file=True, source=None, include=["pre1", "pre2"], omit=["opre1", "opre2"]) .erase() @@ -528,13 +528,13 @@ class NewCmdLineTest(CmdLineTest): outfile="-") """) self.cmd_executes("xml --omit fooey", """\ - .coverage(cover_pylib=None, data_suffix=None, timid=None, branch=None, config_file=True, include=None, omit=["fooey"]) + .coverage(cover_pylib=None, data_suffix=None, timid=None, branch=None, config_file=True, source=None, include=None, omit=["fooey"]) .load() .xml_report(ignore_errors=None, omit=["fooey"], include=None, morfs=[], outfile="coverage.xml") """) self.cmd_executes("xml --omit fooey,booey", """\ - .coverage(cover_pylib=None, data_suffix=None, timid=None, branch=None, config_file=True, include=None, omit=["fooey", "booey"]) + .coverage(cover_pylib=None, data_suffix=None, timid=None, branch=None, config_file=True, source=None, include=None, omit=["fooey", "booey"]) .load() .xml_report(ignore_errors=None, omit=["fooey", "booey"], include=None, morfs=[], outfile="coverage.xml") diff --git a/test/test_files.py b/test/test_files.py index ca9b4e0d..db835e5e 100644 --- a/test/test_files.py +++ b/test/test_files.py @@ -2,7 +2,7 @@ import os, sys -from coverage.files import FileLocator +from coverage.files import FileLocator, TreeMatcher, FnmatchMatcher sys.path.insert(0, os.path.split(__file__)[0]) # Force relative import for Py3k from coveragetest import CoverageTest @@ -16,7 +16,7 @@ class FileLocatorTest(CoverageTest): return os.path.join(os.getcwd(), os.path.normpath(p)) def test_simple(self): - self.make_file("hello.py", "#hello") + self.make_file("hello.py") fl = FileLocator() self.assertEqual(fl.relative_filename("hello.py"), "hello.py") a = self.abs_path("hello.py") @@ -24,8 +24,8 @@ class FileLocatorTest(CoverageTest): self.assertEqual(fl.relative_filename(a), "hello.py") def test_peer_directories(self): - self.make_file("sub/proj1/file1.py", "file1") - self.make_file("sub/proj2/file2.py", "file2") + self.make_file("sub/proj1/file1.py") + self.make_file("sub/proj2/file2.py") a1 = self.abs_path("sub/proj1/file1.py") a2 = self.abs_path("sub/proj2/file2.py") d = os.path.normpath("sub/proj1") @@ -33,3 +33,34 @@ class FileLocatorTest(CoverageTest): fl = FileLocator() self.assertEqual(fl.relative_filename(a1), "file1.py") self.assertEqual(fl.relative_filename(a2), a2) + + def test_tree_matcher(self): + file1 = self.make_file("sub/file1.py") + file2 = self.make_file("sub/file2.c") + file3 = self.make_file("sub2/file3.h") + file4 = self.make_file("sub3/file4.py") + file5 = self.make_file("sub3/file5.c") + fl = FileLocator() + tm = TreeMatcher([ + fl.canonical_filename("sub"), + fl.canonical_filename(file4), + ]) + self.assertTrue(tm.match(fl.canonical_filename(file1))) + self.assertTrue(tm.match(fl.canonical_filename(file2))) + self.assertFalse(tm.match(fl.canonical_filename(file3))) + self.assertTrue(tm.match(fl.canonical_filename(file4))) + self.assertFalse(tm.match(fl.canonical_filename(file5))) + + def test_fnmatch_matcher(self): + file1 = self.make_file("sub/file1.py") + file2 = self.make_file("sub/file2.c") + file3 = self.make_file("sub2/file3.h") + file4 = self.make_file("sub3/file4.py") + file5 = self.make_file("sub3/file5.c") + fl = FileLocator() + fnm = FnmatchMatcher(["*.py", "*/sub2/*"]) + self.assertTrue(fnm.match(fl.canonical_filename(file1))) + self.assertFalse(fnm.match(fl.canonical_filename(file2))) + self.assertTrue(fnm.match(fl.canonical_filename(file3))) + self.assertTrue(fnm.match(fl.canonical_filename(file4))) + self.assertFalse(fnm.match(fl.canonical_filename(file5))) diff --git a/test/test_testing.py b/test/test_testing.py index 9bbb7cca..1cae9310 100644 --- a/test/test_testing.py +++ b/test/test_testing.py @@ -107,5 +107,5 @@ class CoverageTestTest(CoverageTest): self.make_file("sub/second.txt", "Second") self.assertEqual(open("sub/second.txt").read(), "Second") # A deeper directory - self.make_file("sub/deeper/evenmore/third.txt", "Third") - self.assertEqual(open("sub/deeper/evenmore/third.txt").read(), "Third") + self.make_file("sub/deeper/evenmore/third.txt") + self.assertEqual(open("sub/deeper/evenmore/third.txt").read(), "") diff --git a/test/test_testplugin.py b/test/test_testplugin.py index c5bcca01..b012ca6a 100644 --- a/test/test_testplugin.py +++ b/test/test_testplugin.py @@ -14,7 +14,7 @@ class TestCoverage(PluginTester, unittest.TestCase): @py.test.mark.skipif(True) # "requires nose test runner" def test_output(self): - assert "Processing Coverage..." in self.output, ( + assert "Processing coverage..." in self.output, ( "got: %s" % self.output) def makeSuite(self): class TC(unittest.TestCase): |