summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--coverage/plugin.py6
-rw-r--r--tests/plugin1.py6
-rw-r--r--tests/plugin2.py38
-rw-r--r--tests/test_plugins.py126
4 files changed, 109 insertions, 67 deletions
diff --git a/coverage/plugin.py b/coverage/plugin.py
index 24a2b9a3..6c7de669 100644
--- a/coverage/plugin.py
+++ b/coverage/plugin.py
@@ -2,8 +2,6 @@
import sys
-from coverage.misc import CoverageException
-
class CoveragePlugin(object):
"""Base class for coverage.py plugins."""
@@ -41,7 +39,9 @@ class CoveragePlugin(object):
`file_tracer`. It's an error to return None.
"""
- raise NotImplementedError("Plugin %r needs to implement file_reporter" % self.plugin_name)
+ raise NotImplementedError(
+ "Plugin %r needs to implement file_reporter" % self.plugin_name
+ )
class FileTracer(object):
diff --git a/tests/plugin1.py b/tests/plugin1.py
index 21e64aeb..c7660024 100644
--- a/tests/plugin1.py
+++ b/tests/plugin1.py
@@ -19,8 +19,10 @@ class Plugin(coverage.CoveragePlugin):
class FileTracer(coverage.plugin.FileTracer):
+ """A FileTracer emulating a simple static plugin."""
+
def __init__(self, filename):
- """xyz.py was actually sourced from ABC.zz"""
+ """Claim that xyz.py was actually sourced from ABC.zz"""
self._filename = filename
self._source_filename = os.path.join(
"/src",
@@ -37,9 +39,11 @@ class FileTracer(coverage.plugin.FileTracer):
class FileReporter(coverage.plugin.FileReporter):
+ """Dead-simple FileReporter."""
def get_parser(self, exclude=None):
return PluginParser()
class PluginParser(CodeParser):
+ """CodeParser hard-coded for a test in test_plugins.py."""
def parse_source(self):
return set([105, 106, 107, 205, 206, 207]), set([])
diff --git a/tests/plugin2.py b/tests/plugin2.py
index 1fa66cb2..4fb3d057 100644
--- a/tests/plugin2.py
+++ b/tests/plugin2.py
@@ -1,24 +1,48 @@
"""A plugin for test_plugins.py to import."""
import coverage
+from coverage.parser import CodeParser
+
class Plugin(coverage.CoveragePlugin):
def file_tracer(self, filename):
if "render.py" in filename:
- return RenderFileTracer(filename)
+ return RenderFileTracer()
+
+ def file_reporter(self, filename):
+ return FileReporter(filename)
class RenderFileTracer(coverage.plugin.FileTracer):
- def __init__(self, filename):
- pass
+ """A FileTracer using information from the caller."""
def has_dynamic_source_filename(self):
return True
def dynamic_source_filename(self, filename, frame):
- filename = "fake%d.html" % frame.f_lineno
- print("dynamic filename: %r" % filename)
- return filename
+ if frame.f_code.co_name != "render":
+ return None
+ return frame.f_locals['filename']
def line_number_range(self, frame):
- return 17,19
+ lineno = frame.f_locals['linenum']
+ return lineno,lineno+1
+
+
+class FileReporter(coverage.plugin.FileReporter):
+ # TODO: Why do I have to make a FileReporter just to make a CodeParser??
+ def __init__(self, filename):
+ self.filename = filename
+
+ def get_parser(self, exclude=None):
+ # Goofy test arrangement: claim that the file has as many lines as the
+ # number in its name.
+ num = self.filename.split(".")[0].split("_")[1]
+ return PluginParser(int(num))
+
+class PluginParser(CodeParser):
+ def __init__(self, num_lines):
+ self.num_lines = num_lines
+
+ def parse_source(self):
+ return set(range(1, self.num_lines+1)), set([])
diff --git a/tests/test_plugins.py b/tests/test_plugins.py
index f2658998..b94d0776 100644
--- a/tests/test_plugins.py
+++ b/tests/test_plugins.py
@@ -1,6 +1,6 @@
"""Tests for plugins."""
-import sys
+import os, sys
from nose.plugins.skip import SkipTest
@@ -11,6 +11,9 @@ import coverage.plugin
from tests.coveragetest import CoverageTest
+# Are we running with the C tracer or not?
+C_TRACER = os.getenv('COVERAGE_TEST_TRACER', 'c') == 'c'
+
class FakeConfig(object):
"""A fake config for use in tests."""
@@ -138,62 +141,73 @@ class PluginTest(CoverageTest):
cov.stop()
-class FileTracerTest(CoverageTest):
- """Tests of plugins that implement file_tracer."""
-
- def test_plugin1(self):
- if sys.platform == 'win32':
- raise SkipTest("Plugin stuff is jank on windows.. fixing soon...")
-
- self.make_file("simple.py", """\
- import try_xyz
- a = 1
- b = 2
- """)
- self.make_file("try_xyz.py", """\
- c = 3
- d = 4
- """)
-
- cov = coverage.Coverage()
- cov.config["run:plugins"] = ["tests.plugin1"]
-
- # Import the python file, executing it.
- self.start_import_stop(cov, "simple")
-
- _, statements, missing, _ = cov.analysis("simple.py")
- self.assertEqual(statements, [1,2,3])
- self.assertEqual(missing, [])
- _, statements, _, _ = cov.analysis("/src/try_ABC.zz")
- self.assertEqual(statements, [105, 106, 107, 205, 206, 207])
-
- def test_plugin2(self):
- self.make_file("render.py", """\
- def render(filename, linenum):
- fiddle_around = 1 # vamp until ready
- return "[{0} @ {1}]".format(filename, linenum)
- """)
- self.make_file("caller.py", """\
- from render import render
-
- assert render("foo.html", 17) == "[foo.html @ 17]"
- assert render("bar.html", 23) == "[bar.html @ 23]"
- """)
+if not C_TRACER:
+ class FileTracerTest(CoverageTest):
+ """Tests of plugins that implement file_tracer."""
- cov = coverage.Coverage()
- cov.config["run:plugins"] = ["tests.plugin2"]
- cov.config["run:debug"] = ["trace"]
+ def test_plugin1(self):
+ if sys.platform == 'win32':
+ raise SkipTest("Plugin stuff is jank on windows.. fixing soon...")
- self.start_import_stop(cov, "caller")
+ self.make_file("simple.py", """\
+ import try_xyz
+ a = 1
+ b = 2
+ """)
+ self.make_file("try_xyz.py", """\
+ c = 3
+ d = 4
+ """)
- print(self.stderr())
- cov._harvest_data()
- print(cov.data.line_data())
-
- return # TODO: finish this test
+ cov = coverage.Coverage()
+ cov.config["run:plugins"] = ["tests.plugin1"]
+
+ # Import the python file, executing it.
+ self.start_import_stop(cov, "simple")
+
+ _, statements, missing, _ = cov.analysis("simple.py")
+ self.assertEqual(statements, [1,2,3])
+ self.assertEqual(missing, [])
+ _, statements, _, _ = cov.analysis("/src/try_ABC.zz")
+ self.assertEqual(statements, [105, 106, 107, 205, 206, 207])
+
+ def test_plugin2(self):
+ # 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.
+ self.make_file("render.py", """\
+ def render(filename, linenum):
+ # This function emulates a template renderer. The plugin
+ # will examine the `filename` and `linenum` locals to
+ # determine the source file and line number.
+ fiddle_around = 1 # not used, just chaff.
+ return "[{0} @ {1}]".format(filename, linenum)
+
+ def helper(x):
+ # This function is here just to show that not all code in
+ # this file will be part of the dynamic tracing.
+ return x+1
+ """)
+ self.make_file("caller.py", """\
+ from render import helper, render
+
+ assert render("foo_7.html", 4) == "[foo_7.html @ 4]"
+ assert helper(42) == 43
+ assert render("bar_4.html", 2) == "[bar_4.html @ 2]"
+ assert helper(76) == 77
+ """)
- _, statements, missing, _ = cov.analysis("simple.py")
- self.assertEqual(statements, [1,2,3])
- self.assertEqual(missing, [])
- _, statements, _, _ = cov.analysis("/src/try_ABC.zz")
- self.assertEqual(statements, [105, 106, 107, 205, 206, 207])
+ cov = coverage.Coverage()
+ cov.config["run:plugins"] = ["tests.plugin2"]
+
+ self.start_import_stop(cov, "caller")
+
+ # The way plugin2 works, a file named foo_7.html will be claimed to
+ # have 7 lines in it. If render() was called with line number 4,
+ # then the plugin will claim that lines 4 and 5 were executed.
+ _, 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])
+ _, statements, missing, _ = cov.analysis("bar_4.html")
+ self.assertEqual(statements, [1,2,3,4])
+ self.assertEqual(missing, [1,4])