summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--coverage/cmdline.py79
-rw-r--r--test/test_cmdline.py219
2 files changed, 147 insertions, 151 deletions
diff --git a/coverage/cmdline.py b/coverage/cmdline.py
index 6481f229..9ea72614 100644
--- a/coverage/cmdline.py
+++ b/coverage/cmdline.py
@@ -5,7 +5,9 @@ import optparse, sys
from coverage.execfile import run_python_file
-class opts:
+class Opts:
+ """A namespace class for individual options we'll build parsers from."""
+
directory = optparse.Option(
'-d', '--directory', action='store', dest='directory',
)
@@ -17,7 +19,8 @@ class opts:
)
pylib = optparse.Option(
'-L', '--pylib', action='store_true',
- help="Measure coverage even inside the Python installed library, which isn't done by default."
+ help="Measure coverage even inside the Python installed library, "
+ "which isn't done by default."
)
show_missing = optparse.Option(
'-m', '--show-missing', action='store_true',
@@ -27,13 +30,19 @@ class opts:
)
parallel_mode = optparse.Option(
'-p', '--parallel-mode', action='store_true',
- help="Include the machine name and process id in the .coverage data file name."
+ help="Include the machine name and process id in the .coverage "
+ "data file name."
)
timid = optparse.Option(
'', '--timid', action='store_true',
- help="Use a simpler but slower trace method. Use this if you get seemingly impossible results!"
+ help="Use a simpler but slower trace method. Use this if you get "
+ "seemingly impossible results!"
+ )
+ append = optparse.Option(
+ '-a', '--append', action='store_false', dest="erase_first",
+ help="Append coverage data to .coverage, otherwise it is started "
+ "clean with each run."
)
-
class CoverageOptionParser(optparse.OptionParser, object):
"""Base OptionParser for coverage.
@@ -57,10 +66,11 @@ class CoverageOptionParser(optparse.OptionParser, object):
pylib=None,
show_missing=None,
timid=None,
+ erase_first=None,
)
self.disable_interspersed_args()
- self.help_fn = None
+ self.help_fn = lambda: None
class OptionParserError(Exception):
"""Used to stop the optparse error handler ending the process."""
@@ -73,7 +83,8 @@ class CoverageOptionParser(optparse.OptionParser, object):
"""
try:
- options, args = super(CoverageOptionParser, self).parse_args(args, options)
+ options, args = \
+ super(CoverageOptionParser, self).parse_args(args, options)
except self.OptionParserError:
return False, None, None
return True, options, args
@@ -98,14 +109,14 @@ class ClassicOptionParser(CoverageOptionParser):
self.add_action('-x', '--execute', 'execute')
self.add_options([
- opts.directory,
- opts.help,
- opts.ignore_errors,
- opts.pylib,
- opts.show_missing,
- opts.omit,
- opts.parallel_mode,
- opts.timid,
+ Opts.directory,
+ Opts.help,
+ Opts.ignore_errors,
+ Opts.pylib,
+ Opts.show_missing,
+ Opts.omit,
+ Opts.parallel_mode,
+ Opts.timid,
])
def add_action(self, dash, dashdash, action_code):
@@ -123,26 +134,24 @@ class ClassicOptionParser(CoverageOptionParser):
class NewOptionParser(CoverageOptionParser):
"""Parse one of the new-style commands for coverage.py."""
- def __init__(self, action):
+ def __init__(self, action, options, defaults={}):
super(NewOptionParser, self).__init__(
usage="coverage %s [blah]" % action
)
- self.set_defaults(actions=[action])
-
-
-class RunOptionParser(NewOptionParser):
- def __init__(self):
- super(RunOptionParser, self).__init__("execute")
- self.add_options([
- opts.pylib,
- opts.parallel_mode,
- opts.timid,
- ])
-
+ self.set_defaults(actions=[action], **defaults)
+ self.add_options(options)
CMDS = {
- 'run': RunOptionParser(),
-}
+ 'run': NewOptionParser("execute",
+ [
+ Opts.append,
+ Opts.pylib,
+ Opts.parallel_mode,
+ Opts.timid
+ ],
+ defaults={'erase_first':True}
+ ),
+ }
class CoverageScript:
@@ -233,6 +242,10 @@ class CoverageScript:
self.help_fn("Unexpected arguments: %s" % " ".join(args))
return ERR
+ if 'execute' in options.actions and not args:
+ self.help_fn("Nothing to do.")
+ return ERR
+
# Do something.
self.coverage = self.covpkg.coverage(
data_suffix = bool(options.parallel_mode),
@@ -240,16 +253,12 @@ class CoverageScript:
timid = options.timid,
)
- if 'erase' in options.actions:
+ if 'erase' in options.actions or options.erase_first:
self.coverage.erase()
else:
self.coverage.load()
if 'execute' in options.actions:
- if not args:
- self.help_fn("Nothing to do.")
- return ERR
-
# Run the script.
self.coverage.start()
try:
diff --git a/test/test_cmdline.py b/test/test_cmdline.py
index 875692cd..d51553f0 100644
--- a/test/test_cmdline.py
+++ b/test/test_cmdline.py
@@ -6,104 +6,9 @@ import coverage
from coveragetest import CoverageTest
-class CmdLineParserTest(CoverageTest):
- """Tests of command-line processing for Coverage."""
+OK, ERR = 0, 1
- def setUp(self):
- super(CmdLineParserTest, self).setUp()
- self.help_out = None
-
- def help_fn(self, error=None, topic=None):
- """A mock help_fn to capture the error messages for tests."""
- assert error or topic
- self.help_out = error or ("topic:"+topic)
-
- def command_line(self, args, ret=0, help_out=""):
- """Run a Coverage command line, with `args` as arguments.
-
- The return code must be `ret`, and the help messages written must be
- `help_out`.
-
- """
- self.help_out = ""
- argv = shlex.split(args)
- script = coverage.CoverageScript(_help_fn=self.help_fn)
- ret_code = script.command_line(argv)
- self.assertEqual(ret_code, ret)
- self.assertEqual(self.help_out, help_out)
-
-
-class ClassicCmdLineParserTest(CmdLineParserTest):
-
- def testNoArgumentsAtAll(self):
- self.command_line('',
- help_out="Code coverage for Python. Use -h for help."
- )
-
- def testHelp(self):
- self.command_line('-h', help_out="topic:usage")
- self.command_line('--help', help_out="topic:usage")
-
- def testUnknownOption(self):
- self.command_line('-z', ret=1,
- help_out="no such option: -z"
- )
-
- def testBadActionCombinations(self):
- self.command_line('-e -a', ret=1,
- help_out="You can't specify the 'erase' and 'annotate' "
- "options at the same time."
- )
- self.command_line('-e -r', ret=1,
- help_out="You can't specify the 'erase' and 'report' "
- "options at the same time."
- )
- self.command_line('-e -b', ret=1,
- help_out="You can't specify the 'erase' and 'html' "
- "options at the same time."
- )
- self.command_line('-e -c', ret=1,
- help_out="You can't specify the 'erase' and 'combine' "
- "options at the same time."
- )
- self.command_line('-x -a', ret=1,
- help_out="You can't specify the 'execute' and 'annotate' "
- "options at the same time."
- )
- self.command_line('-x -r', ret=1,
- help_out="You can't specify the 'execute' and 'report' "
- "options at the same time."
- )
- self.command_line('-x -b', ret=1,
- help_out="You can't specify the 'execute' and 'html' "
- "options at the same time."
- )
- self.command_line('-x -c', ret=1,
- help_out="You can't specify the 'execute' and 'combine' "
- "options at the same time."
- )
-
- def testNeedAction(self):
- self.command_line('-p', ret=1,
- help_out="You must specify at least one of "
- "-e, -x, -c, -r, -a, or -b."
- )
-
- def testArglessActions(self):
- self.command_line('-e foo bar', ret=1,
- help_out="Unexpected arguments: foo bar"
- )
- self.command_line('-c baz quux', ret=1,
- help_out="Unexpected arguments: baz quux"
- )
-
- def testNothingToDo(self):
- self.command_line('-x', ret=1,
- help_out="Nothing to do."
- )
-
-
-class CmdLineActionTest(CoverageTest):
+class CmdLineTest(CoverageTest):
"""Tests of execution paths through the command line interpreter."""
def model_object(self):
@@ -112,20 +17,24 @@ class CmdLineActionTest(CoverageTest):
mk.coverage.return_value = mk
return mk
- def run_command_line(self, args, ret):
- """Run `args` through command_line, returning the Mock it used."""
+ def run_command_line(self, args):
+ """Run `args` through command_line.
+
+ Returns the Mock it used and the status code returned.
+
+ """
m = self.model_object()
- ret_actual = coverage.CoverageScript(
+ ret = coverage.CoverageScript(
_covpkg=m, _run_python_file=m.run_python_file, _help_fn=m.help_fn
).command_line(shlex.split(args))
- self.assertEqual(ret_actual, ret,
- "Wrong status: got %s, wanted %s" % (ret_actual, ret)
- )
- return m
+ return m, ret
- def cmd_executes(self, args, code, ret=0):
+ def cmd_executes(self, args, code, ret=OK):
"""Assert that the `args` end up executing the sequence in `code`."""
- m1 = self.run_command_line(args, ret)
+ m1, r1 = self.run_command_line(args)
+ self.assertEqual(r1, ret,
+ "Wrong status: got %s, wanted %s" % (r1, ret)
+ )
code = textwrap.dedent(code)
code = re.sub(r"(?m)^\.", "m2.", code)
@@ -136,19 +45,28 @@ class CmdLineActionTest(CoverageTest):
def cmd_executes_same(self, args1, args2):
"""Assert that the `args1` executes the same as `args2`."""
- m1 = self.run_command_line(args1, ret=0)
- m2 = self.run_command_line(args2, ret=0)
+ m1, r1 = self.run_command_line(args1)
+ m2, r2 = self.run_command_line(args2)
+ self.assertEqual(r1, r2)
self.assertEqual(m1.method_calls, m2.method_calls)
- def cmd_help(self, args, help):
+ def cmd_help(self, args, help_msg=None, topic=None, ret=ERR):
"""Run a command line, and check that it prints the right help."""
- m = self.run_command_line(args, ret=1)
- self.assertEqual(m.method_calls,
- [('help_fn', (help,), {})]
- )
-
+ m, r = self.run_command_line(args)
+ self.assertEqual(r, ret,
+ "Wrong status: got %s, wanted %s" % (r, ret)
+ )
+ if help_msg:
+ self.assertEqual(m.method_calls,
+ [('help_fn', (help_msg,), {})]
+ )
+ else:
+ self.assertEqual(m.method_calls,
+ [('help_fn', (), {'topic':topic})]
+ )
+
-class ClassicCmdLineActionTest(CmdLineActionTest):
+class ClassicCmdLineTest(CmdLineTest):
"""Tests of the classic coverage.py command line."""
def testErase(self):
@@ -348,10 +266,79 @@ class ClassicCmdLineActionTest(CmdLineActionTest):
self.cmd_executes_same("-b -of", "-b --omit=f")
self.cmd_executes_same("-b -of,b", "-b --omit=f,b")
+ def testHelp(self):
+ # coverage -h
+ self.cmd_help("-h", topic="usage", ret=OK)
+ self.cmd_help("--help", topic="usage", ret=OK)
+
+ ## Error cases
+
+ def testArglessActions(self):
+ self.cmd_help("-e foo bar", "Unexpected arguments: foo bar")
+ self.cmd_help("-c baz quux", "Unexpected arguments: baz quux")
+
+ def testNeedAction(self):
+ self.cmd_help("-p", "You must specify at least one of "
+ "-e, -x, -c, -r, -a, or -b.")
+
+ def testBadActionCombinations(self):
+ self.cmd_help('-e -a',
+ "You can't specify the 'erase' and 'annotate' "
+ "options at the same time."
+ )
+ self.cmd_help('-e -r',
+ "You can't specify the 'erase' and 'report' "
+ "options at the same time."
+ )
+ self.cmd_help('-e -b',
+ "You can't specify the 'erase' and 'html' "
+ "options at the same time."
+ )
+ self.cmd_help('-e -c',
+ "You can't specify the 'erase' and 'combine' "
+ "options at the same time."
+ )
+ self.cmd_help('-x -a',
+ "You can't specify the 'execute' and 'annotate' "
+ "options at the same time."
+ )
+ self.cmd_help('-x -r',
+ "You can't specify the 'execute' and 'report' "
+ "options at the same time."
+ )
+ self.cmd_help('-x -b',
+ "You can't specify the 'execute' and 'html' "
+ "options at the same time."
+ )
+ self.cmd_help('-x -c',
+ "You can't specify the 'execute' and 'combine' "
+ "options at the same time."
+ )
+
+ def testNothingToDo(self):
+ self.cmd_help("-x", "Nothing to do.")
+
+ def testUnknownOption(self):
+ self.cmd_help("-z", "no such option: -z")
-class NewCmdLineActionTest(CmdLineActionTest):
+
+class NewCmdLineTest(CmdLineTest):
"""Tests of the coverage.py command line."""
+ def testRun(self):
+ self.cmd_executes_same("run f.py", "-e -x f.py")
+ self.cmd_executes_same("run f.py -a -z a1 a2", "-e -x f.py -a -z a1 a2")
+ self.cmd_executes_same("run -a f.py", "-x f.py")
+ self.cmd_executes_same("run -p f.py", "-e -x -p f.py")
+ self.cmd_executes_same("run -L f.py", "-e -x -L f.py")
+ self.cmd_executes_same("run --timid f.py", "-e -x --timid f.py")
+ self.cmd_executes_same("run", "-x")
+
+ def testNoArgumentsAtAll(self):
+ self.cmd_help("",
+ "Code coverage for Python. Use -h for help.", ret=OK
+ )
+
def testBadCommand(self):
self.cmd_help("xyzzy", "Unknown command: 'xyzzy'")