123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781 |
- # 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
- import coverage
- from coverage import env
- from coverage.backward import StringIO
- from coverage.control import Plugins
- from coverage.misc import CoverageException
- import coverage.plugin
- from tests.coveragetest import CoverageTest
- from tests.helpers import CheckUniqueFilenames
- class FakeConfig(object):
- """A fake config for use in tests."""
- def __init__(self, plugin, options):
- self.plugin = plugin
- self.options = options
- self.asked_for = []
- def get_plugin_options(self, module):
- """Just return the options for `module` if this is the right module."""
- self.asked_for.append(module)
- if module == self.plugin:
- return self.options
- else:
- return {}
- class LoadPluginsTest(CoverageTest):
- """Test Plugins.load_plugins directly."""
- def test_implicit_boolean(self):
- self.make_file("plugin1.py", """\
- from coverage import CoveragePlugin
- class Plugin(CoveragePlugin):
- pass
- def coverage_init(reg, options):
- reg.add_file_tracer(Plugin())
- """)
- config = FakeConfig("plugin1", {})
- plugins = Plugins.load_plugins([], config)
- self.assertFalse(plugins)
- plugins = Plugins.load_plugins(["plugin1"], config)
- self.assertTrue(plugins)
- def test_importing_and_configuring(self):
- self.make_file("plugin1.py", """\
- from coverage import CoveragePlugin
- class Plugin(CoveragePlugin):
- def __init__(self, options):
- self.options = options
- self.this_is = "me"
- def coverage_init(reg, options):
- reg.add_file_tracer(Plugin(options))
- """)
- config = FakeConfig("plugin1", {'a': 'hello'})
- plugins = list(Plugins.load_plugins(["plugin1"], config))
- self.assertEqual(len(plugins), 1)
- self.assertEqual(plugins[0].this_is, "me")
- self.assertEqual(plugins[0].options, {'a': 'hello'})
- self.assertEqual(config.asked_for, ['plugin1'])
- def test_importing_and_configuring_more_than_one(self):
- self.make_file("plugin1.py", """\
- from coverage import CoveragePlugin
- class Plugin(CoveragePlugin):
- def __init__(self, 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):
- def __init__(self, options):
- self.options = options
- def coverage_init(reg, options):
- reg.add_file_tracer(Plugin(options))
- """)
- config = FakeConfig("plugin1", {'a': 'hello'})
- plugins = list(Plugins.load_plugins(["plugin1", "plugin2"], config))
- self.assertEqual(len(plugins), 2)
- self.assertEqual(plugins[0].this_is, "me")
- self.assertEqual(plugins[0].options, {'a': 'hello'})
- self.assertEqual(plugins[1].options, {})
- self.assertEqual(config.asked_for, ['plugin1', 'plugin2'])
- # The order matters...
- config = FakeConfig("plugin1", {'a': 'second'})
- plugins = list(Plugins.load_plugins(["plugin2", "plugin1"], config))
- self.assertEqual(len(plugins), 2)
- self.assertEqual(plugins[0].options, {})
- self.assertEqual(plugins[1].this_is, "me")
- self.assertEqual(plugins[1].options, {'a': 'second'})
- def test_cant_import(self):
- with self.assertRaises(ImportError):
- _ = Plugins.load_plugins(["plugin_not_there"], None)
- 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 coverage_init function"
- with self.assertRaisesRegex(CoverageException, msg_pat):
- list(Plugins.load_plugins(["no_plugin"], None))
- class PluginTest(CoverageTest):
- """Test plugins through the Coverage class."""
- def test_plugin_imported(self):
- # Prove that a plugin will be imported.
- self.make_file("my_plugin.py", """\
- 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!")
- """)
- self.assert_doesnt_exist("evidence.out")
- cov = coverage.Coverage()
- cov.set_option("run:plugins", ["my_plugin"])
- cov.start()
- cov.stop() # pragma: nested
- with open("evidence.out") as f:
- self.assertEqual(f.read(), "we are here!")
- def test_missing_plugin_raises_import_error(self):
- # Prove that a missing plugin will raise an ImportError.
- with self.assertRaises(ImportError):
- cov = coverage.Coverage()
- cov.set_option("run:plugins", ["does_not_exist_woijwoicweo"])
- cov.start()
- cov.stop()
- def test_bad_plugin_isnt_hidden(self):
- # Prove that a plugin with an error in it will raise the error.
- self.make_file("plugin_over_zero.py", """\
- 1/0
- """)
- with self.assertRaises(ZeroDivisionError):
- cov = coverage.Coverage()
- cov.set_option("run:plugins", ["plugin_over_zero"])
- cov.start()
- cov.stop()
- def test_plugin_sys_info(self):
- self.make_file("plugin_sys_info.py", """\
- import coverage
- 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"])
- cov._debug_file = debug_out
- cov.set_option("run:plugins", ["plugin_sys_info"])
- cov.load()
- out_lines = debug_out.getvalue().splitlines()
- expected_end = [
- "-- sys: plugin_sys_info.Plugin -------------------------------",
- " hello: world",
- "-- end -------------------------------------------------------",
- ]
- self.assertEqual(expected_end, out_lines[-len(expected_end):])
- def test_plugin_with_no_sys_info(self):
- self.make_file("plugin_no_sys_info.py", """\
- import coverage
- class Plugin(coverage.CoveragePlugin):
- pass
- def coverage_init(reg, options):
- reg.add_noop(Plugin())
- """)
- debug_out = StringIO()
- cov = coverage.Coverage(debug=["sys"])
- cov._debug_file = debug_out
- cov.set_option("run:plugins", ["plugin_no_sys_info"])
- cov.load()
- out_lines = debug_out.getvalue().splitlines()
- expected_end = [
- "-- sys: plugin_no_sys_info.Plugin ----------------------------",
- "-- end -------------------------------------------------------",
- ]
- self.assertEqual(expected_end, out_lines[-len(expected_end):])
- def test_local_files_are_importable(self):
- self.make_file("importing_plugin.py", """\
- from coverage import CoveragePlugin
- import local_module
- 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", """\
- [run]
- plugins = importing_plugin
- """)
- self.make_file("main_file.py", "print('MAIN')")
- out = self.run_command("coverage run main_file.py")
- self.assertEqual(out, "MAIN\n")
- out = self.run_command("coverage html")
- self.assertEqual(out, "")
- class PluginWarningOnPyTracer(CoverageTest):
- """Test that we get a controlled exception with plugins on PyTracer."""
- def test_exception_if_plugins_on_pytracer(self):
- if env.C_TRACER:
- self.skipTest("This test is only about PyTracer.")
- self.make_file("simple.py", """a = 1""")
- cov = coverage.Coverage()
- cov.set_option("run:plugins", ["tests.plugin1"])
- expected_warnings = [
- r"Plugin file tracers \(tests.plugin1.Plugin\) aren't supported with PyTracer",
- ]
- with self.assert_warnings(cov, expected_warnings):
- self.start_import_stop(cov, "simple")
- class FileTracerTest(CoverageTest):
- """Tests of plugins that implement file_tracer."""
- def setUp(self):
- super(FileTracerTest, self).setUp()
- if not env.C_TRACER:
- self.skipTest("Plugins are only supported with the C tracer.")
- class GoodPluginTest(FileTracerTest):
- """Tests of plugin happy paths."""
- def test_plugin1(self):
- 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()
- CheckUniqueFilenames.hook(cov, '_should_trace')
- CheckUniqueFilenames.hook(cov, '_check_include_omit_etc')
- cov.set_option("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, [])
- zzfile = os.path.abspath(os.path.join("/src", "try_ABC.zz"))
- _, statements, _, _ = cov.analysis(zzfile)
- 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.
- 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", """\
- import sys
- from render import helper, render
- assert render("foo_7.html", 4) == "[foo_7.html @ 4]"
- # Render foo_7.html again to try the CheckUniqueFilenames asserts.
- render("foo_7.html", 4)
- assert helper(42) == 43
- assert render("bar_4.html", 2) == "[bar_4.html @ 2]"
- assert helper(76) == 77
- # quux_5.html will be omitted from the results.
- assert render("quux_5.html", 3) == "[quux_5.html @ 3]"
- # In Python 2, either kind of string should be OK.
- if sys.version_info[0] == 2:
- assert render(u"uni_3.html", 2) == "[uni_3.html @ 2]"
- """)
- # will try to read the actual source files, so make some
- # source files.
- def lines(n):
- """Make a string with n lines of text."""
- return "".join("line %d\n" % i for i in range(n))
- self.make_file("bar_4.html", lines(4))
- self.make_file("foo_7.html", lines(7))
- def test_plugin2(self):
- self.make_render_and_caller()
- cov = coverage.Coverage(omit=["*quux*"])
- CheckUniqueFilenames.hook(cov, '_should_trace')
- CheckUniqueFilenames.hook(cov, '_check_include_omit_etc')
- cov.set_option("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])
- 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.line_counts())
- 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.line_counts())
- def test_plugin2_with_branch(self):
- self.make_render_and_caller()
- cov = coverage.Coverage(branch=True, omit=["*quux*"])
- CheckUniqueFilenames.hook(cov, '_should_trace')
- CheckUniqueFilenames.hook(cov, '_check_include_omit_etc')
- cov.set_option("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.
- analysis = cov._analyze("foo_7.html")
- self.assertEqual(analysis.statements, set([1, 2, 3, 4, 5, 6, 7]))
- # Plugins don't do branch coverage yet.
- self.assertEqual(analysis.has_arcs(), True)
- self.assertEqual(analysis.arc_possibilities(), [])
- self.assertEqual(analysis.missing, set([1, 2, 3, 6, 7]))
- def test_plugin2_with_text_report(self):
- self.make_render_and_caller()
- cov = coverage.Coverage(branch=True, omit=["*quux*"])
- cov.set_option("run:plugins", ["tests.plugin2"])
- self.start_import_stop(cov, "caller")
- repout = StringIO()
- total = cov.report(file=repout, include=["*.html"], omit=["uni*.html"], show_missing=True)
- report = repout.getvalue().splitlines()
- expected = [
- 'Name Stmts Miss Branch BrPart Cover Missing',
- '--------------------------------------------------------',
- 'bar_4.html 4 2 0 0 50% 1, 4',
- 'foo_7.html 7 5 0 0 29% 1-3, 6-7',
- '--------------------------------------------------------',
- 'TOTAL 11 7 0 0 36%',
- ]
- self.assertEqual(report, expected)
- self.assertAlmostEqual(total, 36.36, places=2)
- def test_plugin2_with_html_report(self):
- self.make_render_and_caller()
- cov = coverage.Coverage(branch=True, omit=["*quux*"])
- cov.set_option("run:plugins", ["tests.plugin2"])
- self.start_import_stop(cov, "caller")
- total = cov.html_report(include=["*.html"], omit=["uni*.html"])
- self.assertAlmostEqual(total, 36.36, places=2)
- self.assert_exists("htmlcov/index.html")
- self.assert_exists("htmlcov/bar_4_html.html")
- self.assert_exists("htmlcov/foo_7_html.html")
- def test_plugin2_with_xml_report(self):
- self.make_render_and_caller()
- cov = coverage.Coverage(branch=True, omit=["*quux*"])
- cov.set_option("run:plugins", ["tests.plugin2"])
- self.start_import_stop(cov, "caller")
- total = cov.xml_report(include=["*.html"], omit=["uni*.html"])
- self.assertAlmostEqual(total, 36.36, places=2)
- with open("coverage.xml") as fxml:
- xml = fxml.read()
- for snip in [
- 'filename="bar_4.html" line-rate="0.5" name="bar_4.html"',
- 'filename="foo_7.html" line-rate="0.2857" name="foo_7.html"',
- ]:
- 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.set_option("run:plugins", ["fairly_odd_plugin"])
- self.start_import_stop(cov, "unsuspecting")
- repout = StringIO()
- total = cov.report(file=repout, show_missing=True)
- 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_plugin(self, module_name):
- """Run a plugin with the given module_name.
- Uses a few fixed Python files.
- Returns the Coverage object.
- """
- self.make_file("simple.py", """\
- import other, another
- a = other.f(2)
- b = other.f(3)
- c = another.g(4)
- d = another.g(5)
- """)
- # The names of these files are important: some plugins apply themselves
- # to "*other.py".
- self.make_file("other.py", """\
- def f(x):
- return x+1
- """)
- self.make_file("another.py", """\
- def g(x):
- return x-1
- """)
- cov = coverage.Coverage()
- cov.set_option("run:plugins", [module_name])
- self.start_import_stop(cov, "simple")
- return cov
- def run_bad_plugin(self, module_name, plugin_name, our_error=True, excmsg=None):
- """Run a file, and see that the plugin failed.
- `module_name` and `plugin_name` is the module and name of the plugin to
- use.
- `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.
- `excmsg`, if provided, is text that should appear in the stderr.
- The plugin will be disabled, and we check that a warning is output
- explaining why.
- """
- self.run_plugin(module_name)
- stderr = self.stderr()
- print(stderr) # for diagnosing test failures.
- if our_error:
- errors = stderr.count("# Oh noes!")
- # The exception we're causing should only appear once.
- self.assertEqual(errors, 1)
- # There should be a warning explaining what's happening, but only one.
- # 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)
- if excmsg:
- self.assertIn(excmsg, stderr)
- 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_has_inherited_sourcefilename_method(self):
- self.make_file("bad_plugin.py", """\
- import coverage
- class Plugin(coverage.CoveragePlugin):
- def file_tracer(self, filename):
- # Just grab everything.
- return FileTracer()
- class FileTracer(coverage.FileTracer):
- pass
- def coverage_init(reg, options):
- reg.add_file_tracer(Plugin())
- """)
- self.run_bad_plugin(
- "bad_plugin", "Plugin", our_error=False,
- excmsg="Class 'bad_plugin.FileTracer' needs to implement source_filename()",
- )
- def test_plugin_has_inherited_filereporter_method(self):
- self.make_file("bad_plugin.py", """\
- import coverage
- class Plugin(coverage.CoveragePlugin):
- def file_tracer(self, filename):
- # Just grab everything.
- return FileTracer()
- class FileTracer(coverage.FileTracer):
- def source_filename(self):
- return "foo.xxx"
- def coverage_init(reg, options):
- reg.add_file_tracer(Plugin())
- """)
- cov = self.run_plugin("bad_plugin")
- expected_msg = "Plugin 'bad_plugin.Plugin' needs to implement file_reporter()"
- with self.assertRaisesRegex(NotImplementedError, expected_msg):
- cov.report()
- 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", "Plugin")
- def test_file_tracer_returns_wrong(self):
- self.make_file("bad_plugin.py", """\
- import coverage.plugin
- 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", "Plugin", our_error=False)
- def test_has_dynamic_source_filename_fails(self):
- self.make_file("bad_plugin.py", """\
- import coverage.plugin
- class Plugin(coverage.plugin.CoveragePlugin):
- def file_tracer(self, filename):
- return BadFileTracer()
- 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", "Plugin")
- def test_source_filename_fails(self):
- self.make_file("bad_plugin.py", """\
- import coverage.plugin
- class Plugin(coverage.plugin.CoveragePlugin):
- def file_tracer(self, filename):
- return BadFileTracer()
- 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", "Plugin")
- def test_source_filename_returns_wrong(self):
- self.make_file("bad_plugin.py", """\
- import coverage.plugin
- class Plugin(coverage.plugin.CoveragePlugin):
- def file_tracer(self, filename):
- return BadFileTracer()
- 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", "Plugin", our_error=False)
- def test_dynamic_source_filename_fails(self):
- self.make_file("bad_plugin.py", """\
- import coverage.plugin
- class Plugin(coverage.plugin.CoveragePlugin):
- def file_tracer(self, filename):
- if filename.endswith("other.py"):
- return BadFileTracer()
- class BadFileTracer(coverage.plugin.FileTracer):
- def has_dynamic_source_filename(self):
- 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", "Plugin")
- def test_line_number_range_returns_non_tuple(self):
- self.make_file("bad_plugin.py", """\
- import coverage.plugin
- class Plugin(coverage.plugin.CoveragePlugin):
- def file_tracer(self, filename):
- if filename.endswith("other.py"):
- return BadFileTracer()
- class BadFileTracer(coverage.plugin.FileTracer):
- def source_filename(self):
- return "something.foo"
- 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", "Plugin", our_error=False)
- def test_line_number_range_returns_triple(self):
- self.make_file("bad_plugin.py", """\
- import coverage.plugin
- class Plugin(coverage.plugin.CoveragePlugin):
- def file_tracer(self, filename):
- if filename.endswith("other.py"):
- return BadFileTracer()
- class BadFileTracer(coverage.plugin.FileTracer):
- def source_filename(self):
- return "something.foo"
- 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", "Plugin", our_error=False)
- def test_line_number_range_returns_pair_of_strings(self):
- self.make_file("bad_plugin.py", """\
- import coverage.plugin
- class Plugin(coverage.plugin.CoveragePlugin):
- def file_tracer(self, filename):
- if filename.endswith("other.py"):
- return BadFileTracer()
- class BadFileTracer(coverage.plugin.FileTracer):
- def source_filename(self):
- return "something.foo"
- 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", "Plugin", our_error=False)
|