summaryrefslogtreecommitdiff
path: root/tests/test_plugins.py
diff options
context:
space:
mode:
Diffstat (limited to 'tests/test_plugins.py')
-rw-r--r--tests/test_plugins.py178
1 files changed, 149 insertions, 29 deletions
diff --git a/tests/test_plugins.py b/tests/test_plugins.py
index 69e7b42b..558c0436 100644
--- a/tests/test_plugins.py
+++ b/tests/test_plugins.py
@@ -1,3 +1,6 @@
+# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
+# For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt
+
"""Tests for plugins."""
import os.path
@@ -40,6 +43,9 @@ class LoadPluginsTest(CoverageTest):
class Plugin(CoveragePlugin):
pass
+
+ def coverage_init(reg, options):
+ reg.add_file_tracer(Plugin())
""")
config = FakeConfig("plugin1", {})
@@ -55,8 +61,11 @@ class LoadPluginsTest(CoverageTest):
class Plugin(CoveragePlugin):
def __init__(self, options):
- super(Plugin, self).__init__(options)
+ self.options = options
self.this_is = "me"
+
+ def coverage_init(reg, options):
+ reg.add_file_tracer(Plugin(options))
""")
config = FakeConfig("plugin1", {'a': 'hello'})
@@ -73,14 +82,21 @@ class LoadPluginsTest(CoverageTest):
class Plugin(CoveragePlugin):
def __init__(self, options):
- super(Plugin, self).__init__(options)
+ self.options = options
self.this_is = "me"
+
+ def coverage_init(reg, options):
+ reg.add_file_tracer(Plugin(options))
""")
self.make_file("plugin2.py", """\
from coverage import CoveragePlugin
class Plugin(CoveragePlugin):
- pass
+ def __init__(self, options):
+ self.options = options
+
+ def coverage_init(reg, options):
+ reg.add_file_tracer(Plugin(options))
""")
config = FakeConfig("plugin1", {'a': 'hello'})
@@ -105,12 +121,12 @@ class LoadPluginsTest(CoverageTest):
with self.assertRaises(ImportError):
_ = Plugins.load_plugins(["plugin_not_there"], None)
- def test_plugin_must_define_plugin_class(self):
+ def test_plugin_must_define_coverage_init(self):
self.make_file("no_plugin.py", """\
from coverage import CoveragePlugin
Nothing = 0
""")
- msg_pat = "Plugin module 'no_plugin' didn't define a Plugin class"
+ msg_pat = "Plugin module 'no_plugin' didn't define a coverage_init function"
with self.assertRaisesRegex(CoverageException, msg_pat):
list(Plugins.load_plugins(["no_plugin"], None))
@@ -124,6 +140,8 @@ class PluginTest(CoverageTest):
from coverage import CoveragePlugin
class Plugin(CoveragePlugin):
pass
+ def coverage_init(reg, options):
+ reg.add_noop(Plugin())
with open("evidence.out", "w") as f:
f.write("we are here!")
""")
@@ -163,6 +181,9 @@ class PluginTest(CoverageTest):
class Plugin(coverage.CoveragePlugin):
def sys_info(self):
return [("hello", "world")]
+
+ def coverage_init(reg, options):
+ reg.add_noop(Plugin())
""")
debug_out = StringIO()
cov = coverage.Coverage(debug=["sys"])
@@ -172,7 +193,7 @@ class PluginTest(CoverageTest):
out_lines = debug_out.getvalue().splitlines()
expected_end = [
- "-- sys: plugin_sys_info --------------------------------------",
+ "-- sys: plugin_sys_info.Plugin -------------------------------",
" hello: world",
"-- end -------------------------------------------------------",
]
@@ -184,6 +205,9 @@ class PluginTest(CoverageTest):
class Plugin(coverage.CoveragePlugin):
pass
+
+ def coverage_init(reg, options):
+ reg.add_noop(Plugin())
""")
debug_out = StringIO()
cov = coverage.Coverage(debug=["sys"])
@@ -193,7 +217,7 @@ class PluginTest(CoverageTest):
out_lines = debug_out.getvalue().splitlines()
expected_end = [
- "-- sys: plugin_no_sys_info -----------------------------------",
+ "-- sys: plugin_no_sys_info.Plugin ----------------------------",
"-- end -------------------------------------------------------",
]
self.assertEqual(expected_end, out_lines[-len(expected_end):])
@@ -202,8 +226,10 @@ class PluginTest(CoverageTest):
self.make_file("importing_plugin.py", """\
from coverage import CoveragePlugin
import local_module
- class Plugin(CoveragePlugin):
+ class MyPlugin(CoveragePlugin):
pass
+ def coverage_init(reg, options):
+ reg.add_noop(MyPlugin())
""")
self.make_file("local_module.py", "CONST = 1")
self.make_file(".coveragerc", """\
@@ -220,12 +246,10 @@ class PluginTest(CoverageTest):
class PluginWarningOnPyTracer(CoverageTest):
"""Test that we get a controlled exception with plugins on PyTracer."""
- def setUp(self):
- super(PluginWarningOnPyTracer, self).setUp()
+ def test_exception_if_plugins_on_pytracer(self):
if env.C_TRACER:
self.skip("This test is only about PyTracer.")
- def test_exception_if_plugins_on_pytracer(self):
self.make_file("simple.py", """a = 1""")
cov = coverage.Coverage()
@@ -233,13 +257,13 @@ class PluginWarningOnPyTracer(CoverageTest):
warnings = []
def capture_warning(msg):
+ """A fake implementation of Coverage._warn, to capture warnings."""
warnings.append(msg)
cov._warn = capture_warning
self.start_import_stop(cov, "simple")
self.assertIn(
- "Plugin file tracers (tests.plugin1) "
- "aren't supported with PyTracer",
+ "Plugin file tracers (tests.plugin1.Plugin) aren't supported with PyTracer",
warnings
)
@@ -283,6 +307,7 @@ class GoodPluginTest(FileTracerTest):
self.assertEqual(statements, [105, 106, 107, 205, 206, 207])
def make_render_and_caller(self):
+ """Make the render.py and caller.py files we need."""
# plugin2 emulates a dynamic tracing plugin: the caller's locals
# are examined to determine the source file and line number.
# The plugin is in tests/plugin2.py.
@@ -344,20 +369,20 @@ class GoodPluginTest(FileTracerTest):
_, statements, missing, _ = cov.analysis("foo_7.html")
self.assertEqual(statements, [1, 2, 3, 4, 5, 6, 7])
self.assertEqual(missing, [1, 2, 3, 6, 7])
- self.assertIn("foo_7.html", cov.data.summary())
+ self.assertIn("foo_7.html", cov.data.line_counts())
_, statements, missing, _ = cov.analysis("bar_4.html")
self.assertEqual(statements, [1, 2, 3, 4])
self.assertEqual(missing, [1, 4])
- self.assertIn("bar_4.html", cov.data.summary())
+ self.assertIn("bar_4.html", cov.data.line_counts())
- self.assertNotIn("quux_5.html", cov.data.summary())
+ self.assertNotIn("quux_5.html", cov.data.line_counts())
if env.PY2:
_, statements, missing, _ = cov.analysis("uni_3.html")
self.assertEqual(statements, [1, 2, 3])
self.assertEqual(missing, [1])
- self.assertIn("uni_3.html", cov.data.summary())
+ self.assertIn("uni_3.html", cov.data.line_counts())
def test_plugin2_with_branch(self):
self.make_render_and_caller()
@@ -437,11 +462,62 @@ class GoodPluginTest(FileTracerTest):
]:
self.assertIn(snip, xml)
+ def test_defer_to_python(self):
+ # A plugin that measures, but then wants built-in python reporting.
+ self.make_file("fairly_odd_plugin.py", """\
+ # A plugin that claims all the odd lines are executed, and none of
+ # the even lines, and then punts reporting off to the built-in
+ # Python reporting.
+ import coverage.plugin
+ class Plugin(coverage.CoveragePlugin):
+ def file_tracer(self, filename):
+ return OddTracer(filename)
+ def file_reporter(self, filename):
+ return "python"
+
+ class OddTracer(coverage.plugin.FileTracer):
+ def __init__(self, filename):
+ self.filename = filename
+ def source_filename(self):
+ return self.filename
+ def line_number_range(self, frame):
+ lineno = frame.f_lineno
+ if lineno % 2:
+ return (lineno, lineno)
+ else:
+ return (-1, -1)
+
+ def coverage_init(reg, options):
+ reg.add_file_tracer(Plugin())
+ """)
+ self.make_file("unsuspecting.py", """\
+ a = 1
+ b = 2
+ c = 3
+ d = 4
+ e = 5
+ f = 6
+ """)
+ cov = coverage.Coverage(include=["unsuspecting.py"])
+ cov.config["run:plugins"] = ["fairly_odd_plugin"]
+ self.start_import_stop(cov, "unsuspecting")
+
+ repout = StringIO()
+ total = cov.report(file=repout)
+ report = repout.getvalue().splitlines()
+ expected = [
+ 'Name Stmts Miss Cover Missing',
+ '-----------------------------------------------',
+ 'unsuspecting.py 6 3 50% 2, 4, 6',
+ ]
+ self.assertEqual(report, expected)
+ self.assertEqual(total, 50)
+
class BadPluginTest(FileTracerTest):
"""Test error handling around plugins."""
- def run_bad_plugin(self, plugin_name, our_error=True):
+ def run_bad_plugin(self, module_name, plugin_name, our_error=True):
"""Run a file, and see that the plugin failed.
`plugin_name` is the name of the plugin to use.
@@ -449,6 +525,9 @@ class BadPluginTest(FileTracerTest):
`our_error` is True if the error reported to the user will be an
explicit error in our test code, marked with an # Oh noes! comment.
+ The plugin will be disabled, and we check that a warning is output
+ explaining why.
+
"""
self.make_file("simple.py", """\
import other, another
@@ -469,7 +548,7 @@ class BadPluginTest(FileTracerTest):
""")
cov = coverage.Coverage()
- cov.config["run:plugins"] = [plugin_name]
+ cov.config["run:plugins"] = [module_name]
self.start_import_stop(cov, "simple")
stderr = self.stderr()
@@ -481,18 +560,35 @@ class BadPluginTest(FileTracerTest):
self.assertEqual(errors, 1)
# There should be a warning explaining what's happening, but only one.
- msg = "Disabling plugin %r due to an exception:" % plugin_name
+ # The message can be in two forms:
+ # Disabling plugin '...' due to previous exception
+ # or:
+ # Disabling plugin '...' due to an exception:
+ msg = "Disabling plugin '%s.%s' due to " % (module_name, plugin_name)
warnings = stderr.count(msg)
self.assertEqual(warnings, 1)
+ def test_file_tracer_has_no_file_tracer_method(self):
+ self.make_file("bad_plugin.py", """\
+ class Plugin(object):
+ pass
+
+ def coverage_init(reg, options):
+ reg.add_file_tracer(Plugin())
+ """)
+ self.run_bad_plugin("bad_plugin", "Plugin", our_error=False)
+
def test_file_tracer_fails(self):
self.make_file("bad_plugin.py", """\
import coverage.plugin
class Plugin(coverage.plugin.CoveragePlugin):
def file_tracer(self, filename):
17/0 # Oh noes!
+
+ def coverage_init(reg, options):
+ reg.add_file_tracer(Plugin())
""")
- self.run_bad_plugin("bad_plugin")
+ self.run_bad_plugin("bad_plugin", "Plugin")
def test_file_tracer_returns_wrong(self):
self.make_file("bad_plugin.py", """\
@@ -500,8 +596,11 @@ class BadPluginTest(FileTracerTest):
class Plugin(coverage.plugin.CoveragePlugin):
def file_tracer(self, filename):
return 3.14159
+
+ def coverage_init(reg, options):
+ reg.add_file_tracer(Plugin())
""")
- self.run_bad_plugin("bad_plugin", our_error=False)
+ self.run_bad_plugin("bad_plugin", "Plugin", our_error=False)
def test_has_dynamic_source_filename_fails(self):
self.make_file("bad_plugin.py", """\
@@ -513,8 +612,11 @@ class BadPluginTest(FileTracerTest):
class BadFileTracer(coverage.plugin.FileTracer):
def has_dynamic_source_filename(self):
23/0 # Oh noes!
+
+ def coverage_init(reg, options):
+ reg.add_file_tracer(Plugin())
""")
- self.run_bad_plugin("bad_plugin")
+ self.run_bad_plugin("bad_plugin", "Plugin")
def test_source_filename_fails(self):
self.make_file("bad_plugin.py", """\
@@ -526,8 +628,11 @@ class BadPluginTest(FileTracerTest):
class BadFileTracer(coverage.plugin.FileTracer):
def source_filename(self):
42/0 # Oh noes!
+
+ def coverage_init(reg, options):
+ reg.add_file_tracer(Plugin())
""")
- self.run_bad_plugin("bad_plugin")
+ self.run_bad_plugin("bad_plugin", "Plugin")
def test_source_filename_returns_wrong(self):
self.make_file("bad_plugin.py", """\
@@ -539,8 +644,11 @@ class BadPluginTest(FileTracerTest):
class BadFileTracer(coverage.plugin.FileTracer):
def source_filename(self):
return 17.3
+
+ def coverage_init(reg, options):
+ reg.add_file_tracer(Plugin())
""")
- self.run_bad_plugin("bad_plugin", our_error=False)
+ self.run_bad_plugin("bad_plugin", "Plugin", our_error=False)
def test_dynamic_source_filename_fails(self):
self.make_file("bad_plugin.py", """\
@@ -555,8 +663,11 @@ class BadPluginTest(FileTracerTest):
return True
def dynamic_source_filename(self, filename, frame):
101/0 # Oh noes!
+
+ def coverage_init(reg, options):
+ reg.add_file_tracer(Plugin())
""")
- self.run_bad_plugin("bad_plugin")
+ self.run_bad_plugin("bad_plugin", "Plugin")
def test_line_number_range_returns_non_tuple(self):
self.make_file("bad_plugin.py", """\
@@ -572,8 +683,11 @@ class BadPluginTest(FileTracerTest):
def line_number_range(self, frame):
return 42.23
+
+ def coverage_init(reg, options):
+ reg.add_file_tracer(Plugin())
""")
- self.run_bad_plugin("bad_plugin", our_error=False)
+ self.run_bad_plugin("bad_plugin", "Plugin", our_error=False)
def test_line_number_range_returns_triple(self):
self.make_file("bad_plugin.py", """\
@@ -589,8 +703,11 @@ class BadPluginTest(FileTracerTest):
def line_number_range(self, frame):
return (1, 2, 3)
+
+ def coverage_init(reg, options):
+ reg.add_file_tracer(Plugin())
""")
- self.run_bad_plugin("bad_plugin", our_error=False)
+ self.run_bad_plugin("bad_plugin", "Plugin", our_error=False)
def test_line_number_range_returns_pair_of_strings(self):
self.make_file("bad_plugin.py", """\
@@ -606,5 +723,8 @@ class BadPluginTest(FileTracerTest):
def line_number_range(self, frame):
return ("5", "7")
+
+ def coverage_init(reg, options):
+ reg.add_file_tracer(Plugin())
""")
- self.run_bad_plugin("bad_plugin", our_error=False)
+ self.run_bad_plugin("bad_plugin", "Plugin", our_error=False)