summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.hgtags1
-rw-r--r--.travis.yml3
-rw-r--r--AUTHORS.txt10
-rw-r--r--CHANGES.txt176
-rw-r--r--LICENSE.txt177
-rw-r--r--MANIFEST.in6
-rw-r--r--Makefile27
-rw-r--r--NOTICE.txt14
-rw-r--r--README.txt3
-rw-r--r--TODO.txt28
-rw-r--r--__main__.py3
-rw-r--r--coverage/__init__.py96
-rw-r--r--coverage/__main__.py4
-rw-r--r--coverage/annotate.py28
-rw-r--r--coverage/backunittest.py14
-rw-r--r--coverage/backward.py16
-rw-r--r--coverage/bytecode.py3
-rw-r--r--coverage/cmdline.py101
-rw-r--r--coverage/collector.py100
-rw-r--r--coverage/config.py80
-rw-r--r--coverage/control.py328
-rw-r--r--coverage/ctracer/datastack.c42
-rw-r--r--coverage/ctracer/datastack.h45
-rw-r--r--coverage/ctracer/filedisp.c85
-rw-r--r--coverage/ctracer/filedisp.h26
-rw-r--r--coverage/ctracer/module.c108
-rw-r--r--coverage/ctracer/stats.h30
-rw-r--r--coverage/ctracer/tracer.c (renamed from coverage/tracer.c)410
-rw-r--r--coverage/ctracer/tracer.h68
-rw-r--r--coverage/ctracer/util.h52
-rw-r--r--coverage/data.py780
-rw-r--r--coverage/debug.py46
-rw-r--r--coverage/env.py13
-rw-r--r--coverage/execfile.py6
-rw-r--r--coverage/files.py113
-rw-r--r--coverage/fullcoverage/encodings.py9
-rw-r--r--coverage/html.py16
-rw-r--r--coverage/htmlfiles/coverage_html.js3
-rw-r--r--coverage/htmlfiles/index.html94
-rw-r--r--coverage/htmlfiles/pyfile.html92
-rw-r--r--coverage/htmlfiles/style.css5
-rw-r--r--coverage/misc.py61
-rw-r--r--coverage/monkey.py12
-rw-r--r--coverage/parser.py60
-rw-r--r--coverage/phystokens.py66
-rw-r--r--coverage/plugin.py79
-rw-r--r--coverage/plugin_support.py249
-rw-r--r--coverage/python.py51
-rw-r--r--coverage/pytracer.py29
-rw-r--r--coverage/report.py5
-rw-r--r--coverage/results.py18
-rw-r--r--coverage/summary.py16
-rw-r--r--coverage/templite.py5
-rw-r--r--coverage/test_helpers.py19
-rw-r--r--coverage/version.py7
-rw-r--r--coverage/xmlreport.py31
-rw-r--r--doc/api.rst27
-rw-r--r--doc/branch.rst5
-rw-r--r--doc/changes.rst86
-rw-r--r--doc/cmd.rst41
-rw-r--r--doc/conf.py46
-rw-r--r--doc/config.rst7
-rw-r--r--doc/contributing.rst3
-rw-r--r--doc/dict.txt14
-rw-r--r--doc/excluding.rst25
-rw-r--r--doc/faq.rst29
-rw-r--r--doc/index.rst29
-rw-r--r--doc/install.rst15
-rw-r--r--doc/plugins.rst12
-rw-r--r--doc/python-coverage.1.txt8
-rw-r--r--doc/requirements.txt1
-rw-r--r--doc/source.rst3
-rw-r--r--doc/subprocess.rst5
-rw-r--r--doc/trouble.rst13
-rw-r--r--howto.txt25
-rw-r--r--igor.py69
-rw-r--r--lab/branches.py3
-rw-r--r--lab/hack_pyc.py3
-rw-r--r--lab/lnotab.py122
-rw-r--r--lab/new-data.js74
-rw-r--r--lab/parser.py21
-rw-r--r--lab/platform_info.py26
-rw-r--r--lab/run_trace.py35
-rw-r--r--lab/sample.py5
-rw-r--r--lab/show_pyc.py3
-rw-r--r--lab/trace_sample.py57
-rw-r--r--metacov.ini5
-rw-r--r--pylintrc5
-rw-r--r--requirements.txt12
-rw-r--r--setup.py76
-rw-r--r--test_old.sh3
-rw-r--r--tests/backtest.py3
-rw-r--r--tests/coveragetest.py39
-rw-r--r--tests/covmodzip1.py3
-rw-r--r--tests/eggsrc/egg1/egg1.py3
-rw-r--r--tests/eggsrc/setup.py3
-rw-r--r--tests/farm/annotate/annotate_dir.py3
-rw-r--r--tests/farm/annotate/gold/white.py,cover3
-rw-r--r--tests/farm/annotate/gold_anno_dir/a_a.py,cover3
-rw-r--r--tests/farm/annotate/gold_anno_dir/b_b.py,cover3
-rw-r--r--tests/farm/annotate/gold_anno_dir/multi.py,cover3
-rw-r--r--tests/farm/annotate/gold_encodings/utf8.py,cover7
-rw-r--r--tests/farm/annotate/gold_multi/a/a.py,cover3
-rw-r--r--tests/farm/annotate/gold_multi/b/b.py,cover3
-rw-r--r--tests/farm/annotate/gold_multi/multi.py,cover3
-rw-r--r--tests/farm/annotate/gold_v24/white.py,cover3
-rw-r--r--tests/farm/annotate/run.py3
-rw-r--r--tests/farm/annotate/run_encodings.py10
-rw-r--r--tests/farm/annotate/run_multi.py3
-rw-r--r--tests/farm/annotate/src/a/a.py3
-rw-r--r--tests/farm/annotate/src/b/b.py3
-rw-r--r--tests/farm/annotate/src/multi.py3
-rw-r--r--tests/farm/annotate/src/utf8.py7
-rw-r--r--tests/farm/annotate/src/white.py3
-rw-r--r--tests/farm/html/gold_a/a_py.html118
-rw-r--r--tests/farm/html/gold_a/index.html82
-rw-r--r--tests/farm/html/gold_b_branch/b_py.html208
-rw-r--r--tests/farm/html/gold_b_branch/index.html86
-rw-r--r--tests/farm/html/gold_bom/bom_py.html120
-rw-r--r--tests/farm/html/gold_bom/index.html78
-rw-r--r--tests/farm/html/gold_isolatin1/index.html82
-rw-r--r--tests/farm/html/gold_isolatin1/isolatin1_py.html110
-rw-r--r--tests/farm/html/gold_omit_1/index.html100
-rw-r--r--tests/farm/html/gold_omit_1/m1_py.html98
-rw-r--r--tests/farm/html/gold_omit_1/m2_py.html98
-rw-r--r--tests/farm/html/gold_omit_1/m3_py.html98
-rw-r--r--tests/farm/html/gold_omit_1/main_py.html130
-rw-r--r--tests/farm/html/gold_omit_2/index.html94
-rw-r--r--tests/farm/html/gold_omit_2/m2_py.html98
-rw-r--r--tests/farm/html/gold_omit_2/m3_py.html98
-rw-r--r--tests/farm/html/gold_omit_2/main_py.html130
-rw-r--r--tests/farm/html/gold_omit_3/index.html88
-rw-r--r--tests/farm/html/gold_omit_3/m3_py.html98
-rw-r--r--tests/farm/html/gold_omit_3/main_py.html130
-rw-r--r--tests/farm/html/gold_omit_4/index.html94
-rw-r--r--tests/farm/html/gold_omit_4/m1_py.html98
-rw-r--r--tests/farm/html/gold_omit_4/m3_py.html98
-rw-r--r--tests/farm/html/gold_omit_4/main_py.html130
-rw-r--r--tests/farm/html/gold_omit_5/index.html88
-rw-r--r--tests/farm/html/gold_omit_5/m1_py.html98
-rw-r--r--tests/farm/html/gold_omit_5/main_py.html130
-rw-r--r--tests/farm/html/gold_other/blah_blah_other_py.html106
-rw-r--r--tests/farm/html/gold_other/here_py.html122
-rw-r--r--tests/farm/html/gold_other/index.html88
-rw-r--r--tests/farm/html/gold_partial/index.html90
-rw-r--r--tests/farm/html/gold_partial/partial_py.html164
-rw-r--r--tests/farm/html/gold_styled/a_py.html120
-rw-r--r--tests/farm/html/gold_styled/index.html84
-rw-r--r--tests/farm/html/gold_styled/style.css5
-rw-r--r--tests/farm/html/gold_unicode/index.html82
-rw-r--r--tests/farm/html/gold_unicode/unicode_py.html110
-rw-r--r--tests/farm/html/gold_x_xml/coverage.xml45
-rw-r--r--tests/farm/html/gold_y_xml_branch/coverage.xml49
-rw-r--r--tests/farm/html/othersrc/other.py3
-rw-r--r--tests/farm/html/run_a.py19
-rw-r--r--tests/farm/html/run_a_xml_1.py11
-rw-r--r--tests/farm/html/run_a_xml_2.py11
-rw-r--r--tests/farm/html/run_b_branch.py25
-rw-r--r--tests/farm/html/run_bom.py9
-rw-r--r--tests/farm/html/run_isolatin1.py9
-rw-r--r--tests/farm/html/run_omit_1.py7
-rw-r--r--tests/farm/html/run_omit_2.py7
-rw-r--r--tests/farm/html/run_omit_3.py7
-rw-r--r--tests/farm/html/run_omit_4.py7
-rw-r--r--tests/farm/html/run_omit_5.py7
-rw-r--r--tests/farm/html/run_other.py11
-rw-r--r--tests/farm/html/run_partial.py19
-rw-r--r--tests/farm/html/run_styled.py21
-rw-r--r--tests/farm/html/run_tabbed.py17
-rw-r--r--tests/farm/html/run_unicode.py13
-rw-r--r--tests/farm/html/run_y_xml_branch.py11
-rw-r--r--tests/farm/html/src/a.py5
-rw-r--r--tests/farm/html/src/b.py5
-rw-r--r--tests/farm/html/src/bom.py23
-rw-r--r--tests/farm/html/src/coverage.xml2
-rw-r--r--tests/farm/html/src/here.py5
-rw-r--r--tests/farm/html/src/isolatin1.py5
-rw-r--r--tests/farm/html/src/m1.py3
-rw-r--r--tests/farm/html/src/m2.py3
-rw-r--r--tests/farm/html/src/m3.py3
-rw-r--r--tests/farm/html/src/main.py3
-rw-r--r--tests/farm/html/src/omit4.ini3
-rw-r--r--tests/farm/html/src/omit5.ini3
-rw-r--r--tests/farm/html/src/partial.py3
-rw-r--r--tests/farm/html/src/run_a_xml_2.ini3
-rw-r--r--tests/farm/html/src/tabbed.py3
-rw-r--r--tests/farm/html/src/unicode.py5
-rw-r--r--tests/farm/html/src/y.py5
-rw-r--r--tests/farm/run/run_chdir.py3
-rw-r--r--tests/farm/run/run_timid.py3
-rw-r--r--tests/farm/run/run_xxx.py3
-rw-r--r--tests/farm/run/src/chdir.py3
-rw-r--r--tests/farm/run/src/showtrace.py3
-rw-r--r--tests/farm/run/src/xxx3
-rw-r--r--tests/helpers.py5
-rw-r--r--tests/js/tests.js3
-rw-r--r--tests/modules/covmod1.py3
-rw-r--r--tests/modules/pkg1/p1a.py3
-rw-r--r--tests/modules/pkg1/p1b.py3
-rw-r--r--tests/modules/pkg1/p1c.py3
-rw-r--r--tests/modules/pkg1/runmod2.py3
-rw-r--r--tests/modules/pkg1/sub/ps1a.py3
-rw-r--r--tests/modules/pkg1/sub/runmod3.py3
-rw-r--r--tests/modules/pkg2/p2a.py3
-rw-r--r--tests/modules/pkg2/p2b.py3
-rw-r--r--tests/modules/plugins/a_plugin.py3
-rw-r--r--tests/modules/plugins/another.py6
-rw-r--r--tests/modules/runmod1.py3
-rw-r--r--tests/modules/usepkgs.py3
-rw-r--r--tests/moremodules/othermods/othera.py3
-rw-r--r--tests/moremodules/othermods/otherb.py3
-rw-r--r--tests/moremodules/othermods/sub/osa.py3
-rw-r--r--tests/moremodules/othermods/sub/osb.py3
-rw-r--r--tests/osinfo.py3
-rw-r--r--tests/plugin1.py8
-rw-r--r--tests/plugin2.py15
-rw-r--r--tests/stress_phystoken.tok3
-rw-r--r--tests/stress_phystoken_dos.tok3
-rw-r--r--tests/test_api.py135
-rw-r--r--tests/test_arcs.py128
-rw-r--r--tests/test_backward.py3
-rw-r--r--tests/test_cmdline.py143
-rw-r--r--tests/test_collector.py5
-rw-r--r--tests/test_concurrency.py12
-rw-r--r--tests/test_config.py119
-rw-r--r--tests/test_coverage.py21
-rw-r--r--tests/test_data.py704
-rw-r--r--tests/test_debug.py7
-rw-r--r--tests/test_execfile.py3
-rw-r--r--tests/test_farm.py11
-rw-r--r--tests/test_filereporter.py29
-rw-r--r--tests/test_files.py102
-rw-r--r--tests/test_html.py131
-rw-r--r--tests/test_misc.py54
-rw-r--r--tests/test_oddball.py61
-rw-r--r--tests/test_parser.py37
-rw-r--r--tests/test_phystokens.py57
-rw-r--r--tests/test_plugins.py178
-rw-r--r--tests/test_process.py205
-rw-r--r--tests/test_python.py3
-rw-r--r--tests/test_results.py7
-rw-r--r--tests/test_summary.py51
-rw-r--r--tests/test_templite.py3
-rw-r--r--tests/test_testing.py37
-rw-r--r--tests/test_xml.py15
-rw-r--r--tests/try_execfile.py3
-rw-r--r--tox.ini17
-rw-r--r--tox_wheels.ini14
-rw-r--r--tox_winkits.ini3
249 files changed, 7365 insertions, 4288 deletions
diff --git a/.hgtags b/.hgtags
index 7af832c5..c00f17e3 100644
--- a/.hgtags
+++ b/.hgtags
@@ -41,3 +41,4 @@ af20c6543c226fbc9deeba4a6d4114641374734a coverage-3.6
eec8e928880df1beafdf7d4bea87f784375b35d7 coverage-4.0a3
989f42e9eb11b5864e5746cea675d95ff4cf645d coverage-4.0a4
00a61f028fcb000ae2cbc77bfbe0ac4cfacfab65 coverage-4.0a5
+90debbdf56495e6c0422ceb5f53f8550d2ad86bf coverage-4.0a6
diff --git a/.travis.yml b/.travis.yml
index aa6cf5c5..e73d7394 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -12,11 +12,10 @@ python:
install:
- if [[ $TRAVIS_PYTHON_VERSION == '2.6' ]]; then pip install unittest2; fi
+ - pip install PyContracts
- python setup.py clean develop
before_script:
- - pwd
- - ls -l
- coverage debug sys
- if [[ $TRAVIS_PYTHON_VERSION == 'pypy' ]]; then export COVERAGE_NO_EXTENSION=1; fi
diff --git a/AUTHORS.txt b/AUTHORS.txt
index fcd128e4..d484edf1 100644
--- a/AUTHORS.txt
+++ b/AUTHORS.txt
@@ -5,6 +5,7 @@ Other contributions have been made by:
Adi Roiban
Alex Gaynor
+Alexander Todorov
Anthony Sottile
Ben Finney
Bill Hart
@@ -16,7 +17,9 @@ Catherine Proulx
Chris Adams
Chris Rose
Christian Heimes
+Christine Lytwynec
Christoph Zwerschke
+Conrad Ho
Danek Duvall
Danny Allen
David Christian
@@ -31,17 +34,21 @@ George Song
Greg Rogers
Guillaume Chazarain
Imri Goldberg
+Ionel Cristian Mărieș
JT Olds
Jessamyn Smith
+Jon Chappell
Joseph Tate
Julian Berman
Krystian Kichewko
+Leonardo Pistone
Lex Berezhny
Marc Abramowitz
Marcus Cobden
Mark van der Wal
Martin Fuzzey
Matthew Desmarais
+Mickie Betz
Noel O'Boyle
Pablo Carballo
Patrick Mezard
@@ -51,6 +58,9 @@ Ross Lawley
Sandra Martocchia
Sigve Tjora
Stan Hu
+Stefan Behnel
Steve Leonard
+Steve Peak
+Ted Wexler
Titus Brown
Zooko Wilcox-O'Hearn
diff --git a/CHANGES.txt b/CHANGES.txt
index 45c279c1..492e831c 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -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
+
------------------------------
Change history for Coverage.py
------------------------------
@@ -6,15 +9,127 @@ Change history for Coverage.py
Latest
------
+- Coverage.py is now licensed under the Apache 2.0 license. See NOTICE.txt for
+ details. Closes `issue 313`_.
+
+- The data storage has been completely revamped. The data file is now
+ almost-JSON instead of a pickle, closing `issue 236`_. The `CoverageData`
+ class is now a public supported API to the data file.
+
+- A new configuration option, ``[run] note``, lets you set a note that will be
+ stored in the `runs` section of the data file. You can use this to annotate
+ the data file with any information you like.
+
+- Unrecognized configuration options will now print an error message and stop
+ coverage.py. This should help prevent configuration mistakes from passing
+ silently. Finishes `issue 386`_.
+
+- In parallel mode, ``coverage erase`` will now delete all of the data files,
+ fixing `issue 262`_.
+
+- The XML report now includes a ``missing-branches`` attribute. Thanks, Steve
+ Peak. This is not a part of the Cobertura DTD, so the XML report no longer
+ references the DTD.
+
+- All the reporting functions now behave the same if no data had been
+ collected, exiting with a status code of 1. Fixed ``fail_under`` to be
+ applied even when the report is empty. Thanks, Ionel Cristian Mărieș.
+
+- Plugins are now initialized differently. Instead of looking for a class
+ called ``Plugin``, coverage.py looks for a function called ``coverage_init``.
+
+- A file-tracing plugin can now ask to have built-in Python reporting by
+ returning `"python"` from its `file_reporter()` method.
+
+- Code that was executed with `exec` would be mis-attributed to the file that
+ called it. This is now fixed, closing `issue 380`_.
+
+- The ``use_cache`` method is no longer supported.
+
+- The private method ``Coverage._harvest_data`` is now called
+ ``Coverage.get_data``, and returns the ``CoverageData`` containing the
+ collected data.
+
+- The project is consistently referred to as "coverage.py" throughout the code
+ and the documentation, closing `issue 275`_.
+
+- Combining data files with an explicit configuration file was broken in 4.0a6,
+ but now works again, closing `issue 385`_.
+
+- The speed is back to 3.7.1 levels, after having slowed down due to plugin
+ support, finishing up `issue 387`_.
+
+.. _issue 236: https://bitbucket.org/ned/coveragepy/issues/236/pickles-are-bad-and-you-should-feel-bad
+.. _issue 262: https://bitbucket.org/ned/coveragepy/issues/262/when-parallel-true-erase-should-erase-all
+.. _issue 275: https://bitbucket.org/ned/coveragepy/issues/275/refer-consistently-to-project-as-coverage
+.. _issue 313: https://bitbucket.org/ned/coveragepy/issues/313/add-license-file-containing-2-3-or-4
+.. _issue 380: https://bitbucket.org/ned/coveragepy/issues/380/code-executed-by-exec-excluded-from
+.. _issue 385: https://bitbucket.org/ned/coveragepy/issues/385/coverage-combine-doesnt-work-with-rcfile
+.. _issue 386: https://bitbucket.org/ned/coveragepy/issues/386/error-on-unrecognised-configuration
+.. _issue 387: https://bitbucket.org/ned/coveragepy/issues/387/performance-degradation-from-371-to-40
+
+.. 41 issues closed in 4.0 below here
+
+
+Version 4.0a6 --- 21 June 2015
+------------------------------
+
+- Python 3.5b2 and PyPy 2.6.0 are supported.
+
+- The original module-level function interface to coverage.py is no longer
+ supported. You must now create a ``coverage.Coverage`` object, and use
+ methods on it.
+
+- The ``coverage combine`` command now accepts any number of directories as
+ arguments, and will combine all the data files from those directories. This
+ means you don't have to copy the files to one directory before combining.
+ Thanks, Christine Lytwynec. Finishes `issue 354`_.
+
- Branch coverage couldn't properly handle certain extremely long files. This
is now fixed (`issue 359`_).
+- Branch coverage didn't understand yield statements properly. Mickie Betz
+ persisted in pursuing this despite Ned's pessimism. Fixes `issue 308`_ and
+ `issue 324`_.
+
+- The COVERAGE_DEBUG environment variable can be used to set the ``[run] debug``
+ configuration option to control what internal operations are logged.
+
- HTML reports were truncated at formfeed characters. This is now fixed
(`issue 360`_). It's always fun when the problem is due to a `bug in the
Python standard library <http://bugs.python.org/issue19035>`_.
+- Files with incorrect encoding declaration comments are no longer ignored by
+ the reporting commands, fixing `issue 351`_.
+
+- HTML reports now include a timestamp in the footer, closing `issue 299`_.
+ Thanks, Conrad Ho.
+
+- HTML reports now begrudgingly use double-quotes rather than single quotes,
+ because there are "software engineers" out there writing tools that read HTML
+ and somehow have no idea that single quotes exist. Capitulates to the absurd
+ `issue 361`_. Thanks, Jon Chappell.
+
+- The ``coverage annotate`` command now handles non-ASCII characters properly,
+ closing `issue 363`_. Thanks, Leonardo Pistone.
+
+- Drive letters on Windows were not normalized correctly, now they are. Thanks,
+ Ionel Cristian Mărieș.
+
+- Plugin support had some bugs fixed, closing `issue 374`_ and `issue 375`_.
+ Thanks, Stefan Behnel.
+
+.. _issue 299: https://bitbucket.org/ned/coveragepy/issue/299/inserted-created-on-yyyy-mm-dd-hh-mm-in
+.. _issue 308: https://bitbucket.org/ned/coveragepy/issue/308/yield-lambda-branch-coverage
+.. _issue 324: https://bitbucket.org/ned/coveragepy/issue/324/yield-in-loop-confuses-branch-coverage
+.. _issue 351: https://bitbucket.org/ned/coveragepy/issue/351/files-with-incorrect-encoding-are-ignored
+.. _issue 354: https://bitbucket.org/ned/coveragepy/issue/354/coverage-combine-should-take-a-list-of
.. _issue 359: https://bitbucket.org/ned/coveragepy/issue/359/xml-report-chunk-error
.. _issue 360: https://bitbucket.org/ned/coveragepy/issue/360/html-reports-get-confused-by-l-in-the-code
+.. _issue 361: https://bitbucket.org/ned/coveragepy/issue/361/use-double-quotes-in-html-output-to
+.. _issue 363: https://bitbucket.org/ned/coveragepy/issue/363/annotate-command-hits-unicode-happy-fun
+.. _issue 374: https://bitbucket.org/ned/coveragepy/issue/374/c-tracer-lookups-fail-in
+.. _issue 375: https://bitbucket.org/ned/coveragepy/issue/375/ctracer_handle_return-reads-byte-code
Version 4.0a5 --- 16 February 2015
@@ -36,7 +151,7 @@ Version 4.0a5 --- 16 February 2015
Eduardo Schettino. Currently, this does not work on Windows.
- A new warning is possible, if a desired file isn't measured because it was
- imported before coverage was started (`issue 353`_).
+ imported before coverage.py was started (`issue 353`_).
- The `coverage.process_startup` function now will start coverage measurement
only once, no matter how many times it is called. This fixes problems due
@@ -133,8 +248,8 @@ Version 4.0a2 --- 14 January 2015
- The ``--debug`` switch can now be used on any command.
-- You can now programmatically adjust the configuration of coverage by setting
- items on `Coverage.config` after construction.
+- You can now programmatically adjust the configuration of coverage.py by
+ setting items on `Coverage.config` after construction.
- A module run with ``-m`` can be used as the argument to ``--source``, fixing
`issue 328`_. Thanks, Buck Evan.
@@ -145,9 +260,15 @@ Version 4.0a2 --- 14 January 2015
- Made some PyPy-specific tweaks to improve speed under PyPy. Thanks, Alex
Gaynor.
-- In some cases, with a source file missing a final newline, coverage would
+- In some cases, with a source file missing a final newline, coverage.py would
count statements incorrectly. This is now fixed, closing `issue 293`_.
+- The status.dat file that HTML reports use to avoid re-creating files that
+ haven't changed is now a JSON file instead of a pickle file. This obviates
+ `issue 287`_ and `issue 237`_.
+
+.. _issue 237: https://bitbucket.org/ned/coveragepy/issue/237/htmlcov-with-corrupt-statusdat
+.. _issue 287: https://bitbucket.org/ned/coveragepy/issue/287/htmlpy-doesnt-specify-pickle-protocol
.. _issue 293: https://bitbucket.org/ned/coveragepy/issue/293/number-of-statement-detection-wrong-if-no
.. _issue 314: https://bitbucket.org/ned/coveragepy/issue/314/fail_under-param-not-working-in-coveragerc
.. _issue 315: https://bitbucket.org/ned/coveragepy/issue/315/option-to-omit-empty-files-eg-__init__py
@@ -231,9 +352,9 @@ Version 3.7 --- 6 October 2013
- Running code with ``coverage run -m`` now behaves more like Python does,
setting sys.path properly, which fixes `issue 207`_ and `issue 242`_.
-- Coverage can now run .pyc files directly, closing `issue 264`_.
+- Coverage.py can now run .pyc files directly, closing `issue 264`_.
-- Coverage properly supports .pyw files, fixing `issue 261`_.
+- Coverage.py properly supports .pyw files, fixing `issue 261`_.
- Omitting files within a tree specified with the ``source`` option would
cause them to be incorrectly marked as unexecuted, as described in
@@ -249,9 +370,10 @@ Version 3.7 --- 6 October 2013
- Trying to create an XML report with no files to report on, would cause a
ZeroDivideError, but no longer does, fixing `issue 250`_.
-- When running a threaded program under the Python tracer, coverage no longer
- issues a spurious warning about the trace function changing: "Trace function
- changed, measurement is likely wrong: None." This fixes `issue 164`_.
+- When running a threaded program under the Python tracer, coverage.py no
+ longer issues a spurious warning about the trace function changing: "Trace
+ function changed, measurement is likely wrong: None." This fixes `issue
+ 164`_.
- Static files necessary for HTML reports are found in system-installed places,
to ease OS-level packaging of coverage.py. Closes `issue 259`_.
@@ -340,7 +462,7 @@ Version 3.6b1 --- 28 November 2012
- Configuration files now support substitution of environment variables, using
syntax like ``${WORD}``. Closes `issue 97`_.
-- Embarrassingly, the `[xml] output=` setting in the .coveragerc file simply
+- Embarrassingly, the ``[xml] output=`` setting in the .coveragerc file simply
didn't work. Now it does.
- The XML report now consistently uses filenames for the filename attribute,
@@ -384,8 +506,9 @@ Version 3.6b1 --- 28 November 2012
- Jython files now work with the ``--source`` option, fixing `issue 100`_.
-- Running coverage under a debugger is unlikely to work, but it shouldn't fail
- with "TypeError: 'NoneType' object is not iterable". Fixes `issue 201`_.
+- Running coverage.py under a debugger is unlikely to work, but it shouldn't
+ fail with "TypeError: 'NoneType' object is not iterable". Fixes `issue
+ 201`_.
- On some Linux distributions, when installed with the OS package manager,
coverage.py would report its own code as part of the results. Now it won't,
@@ -467,7 +590,6 @@ Version 3.5.3 --- 29 September 2012
.. _tox: http://tox.readthedocs.org/
-
Version 3.5.2 --- 4 May 2012
----------------------------
@@ -481,7 +603,7 @@ Version 3.5.2b1 --- 29 April 2012
the page are color-coded to the source lines they affect.
- Custom CSS can be applied to the HTML report by specifying a CSS file as
- the extra_css configuration value in the [html] section.
+ the ``extra_css`` configuration value in the ``[html]`` section.
- Source files with custom encodings declared in a comment at the top are now
properly handled during reporting on Python 2. Python 3 always handled them
@@ -529,10 +651,10 @@ Version 3.5.1 --- 23 September 2011
Version 3.5.1b1 --- 28 August 2011
----------------------------------
-- When combining data files from parallel runs, you can now instruct coverage
- about which directories are equivalent on different machines. A ``[paths]``
- section in the configuration file lists paths that are to be considered
- equivalent. Finishes `issue 17`_.
+- When combining data files from parallel runs, you can now instruct
+ coverage.py about which directories are equivalent on different machines. A
+ ``[paths]`` section in the configuration file lists paths that are to be
+ considered equivalent. Finishes `issue 17`_.
- for-else constructs are understood better, and don't cause erroneous partial
branch warnings. Fixes `issue 122`_.
@@ -554,7 +676,7 @@ Version 3.5.1b1 --- 28 August 2011
- In order to help the core developers measure the test coverage of the
standard library, Brandon Rhodes devised an aggressive hack to trick Python
- into running some coverage code before anything else in the process.
+ into running some coverage.py code before anything else in the process.
See the coverage/fullcoverage directory if you are interested.
.. _issue 17: http://bitbucket.org/ned/coveragepy/issue/17/support-combining-coverage-data-from
@@ -742,7 +864,7 @@ Version 3.4b1 --- 21 August 2010
and parent processes. Use ``coverage run -p`` to get two data files that can
be combined with ``coverage combine``. Fixes `issue 56`_.
-- Coverage is now runnable as a module: ``python -m coverage``. Thanks,
+- Coverage.py is now runnable as a module: ``python -m coverage``. Thanks,
Brett Cannon.
- When measuring code running in a virtualenv, most of the system library was
@@ -963,8 +1085,8 @@ Version 3.0.1 --- 7 July 2009
raised. This is now fixed.
- The coverage.py code itself will now not be measured by coverage.py, and no
- coverage modules will be mentioned in the nose --with-cover plug-in. Fixed
- `issue 8`_.
+ coverage.py modules will be mentioned in the nose --with-cover plug-in.
+ Fixed `issue 8`_.
- When running source files, coverage.py now opens them in universal newline
mode just like Python does. This lets it run Windows files on Mac, for
@@ -1043,10 +1165,10 @@ Version 3.0b1 --- 7 March 2009
Major overhaul.
-- Coverage is now a package rather than a module. Functionality has been split
- into classes.
+- Coverage.py is now a package rather than a module. Functionality has been
+ split into classes.
-- The trace function is implemented in C for speed. Coverage runs are now
+- The trace function is implemented in C for speed. Coverage.py runs are now
much faster. Thanks to David Christian for productive micro-sprints and
other encouragement.
@@ -1058,7 +1180,7 @@ Major overhaul.
- The singleton coverage object is only created if the module-level functions
are used. This maintains the old interface while allowing better
- programmatic use of Coverage.
+ programmatic use of Coverage.py.
- The minimum supported Python version is 2.3.
@@ -1156,7 +1278,7 @@ Version 2.5 --- 4 December 2005
- Add a file argument to report so that reports can be captured to a different
destination.
-- coverage.py can now measure itself.
+- Coverage.py can now measure itself.
- Adapted Greg Rogers' patch for using relative file names, and sorting and
omitting files to report on.
diff --git a/LICENSE.txt b/LICENSE.txt
new file mode 100644
index 00000000..f433b1a5
--- /dev/null
+++ b/LICENSE.txt
@@ -0,0 +1,177 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
diff --git a/MANIFEST.in b/MANIFEST.in
index 72246745..727c85b0 100644
--- a/MANIFEST.in
+++ b/MANIFEST.in
@@ -1,6 +1,10 @@
+# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
+# For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt
+
# MANIFEST.in file for coverage.py
-recursive-include coverage/htmlfiles *
+recursive-include coverage/ctracer *
recursive-include coverage/fullcoverage *
+recursive-include coverage/htmlfiles *
include coverage.egg-info/*.*
include setup.py
diff --git a/Makefile b/Makefile
index b755d07e..67fd6f00 100644
--- a/Makefile
+++ b/Makefile
@@ -1,4 +1,7 @@
-# Makefile for utility work on Coverage.
+# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
+# For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt
+
+# Makefile for utility work on coverage.py.
default:
@echo "* No default action *"
@@ -35,7 +38,7 @@ spell:
-pylint --disable=all --enable=spelling $(LINTABLE)
pep8:
- pep8 --filename=*.py --ignore=E401,E301 --repeat coverage
+ pep8 --filename=*.py --ignore=E401,E301 --repeat $(LINTABLE)
test:
tox -e py27 $(ARGS)
@@ -48,15 +51,17 @@ metahtml:
# Kitting
-# For kitting on Windows:
-# SDIST_CMD = python setup.py sdist --keep-temp --formats=gztar fixtar --owner=ned --group=coverage --clean
-SDIST_CMD = python setup.py sdist --formats=gztar
-
kit:
- $(SDIST_CMD)
+ python setup.py sdist --formats=gztar,zip
+
+wheel:
+ tox -c tox_wheels.ini
kit_upload:
- $(SDIST_CMD) upload
+ twine upload dist/*
+
+winkit:
+ tox -c tox_winkits.ini
kit_local:
cp -v dist/* `awk -F "=" '/find-links/ {print $$2}' ~/.pip/pip.conf`
@@ -86,17 +91,17 @@ WEBHOME = ~/web/stellated/pages/code/coverage
docreqs:
pip install -r doc/requirements.txt
-px: docreqs
+px:
$(SPHINXBUILD) -b px $(SPHINXOPTS) doc/_build/px
rm doc/_build/px/search.px
python doc/_ext/px_cleaner.py doc/_build/px/*.px
-dochtml: docreqs
+dochtml:
$(SPHINXBUILD) -b html $(SPHINXOPTS) doc/_build/html
@echo
@echo "Build finished. The HTML pages are in doc/_build/html."
-docspell: docreqs
+docspell:
$(SPHINXBUILD) -b spelling $(SPHINXOPTS) doc/_spell
publish: px
diff --git a/NOTICE.txt b/NOTICE.txt
new file mode 100644
index 00000000..ff68ab80
--- /dev/null
+++ b/NOTICE.txt
@@ -0,0 +1,14 @@
+Copyright 2001 Gareth Rees. All rights reserved.
+Copyright 2004-2015 Ned Batchelder. All rights reserved.
+
+Except where noted otherwise, this software is licensed under the Apache
+License, Version 2.0 (the "License"); you may not use this work except in
+compliance with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
diff --git a/README.txt b/README.txt
index 2c6fe3d6..6b35f348 100644
--- a/README.txt
+++ b/README.txt
@@ -7,3 +7,6 @@ library to determine which lines are executable, and which have been executed.
For more information, see http://nedbatchelder.com/code/coverage
Code repo and issue tracking are at http://bitbucket.org/ned/coveragepy
+
+Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
+For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt
diff --git a/TODO.txt b/TODO.txt
index 0062dbe6..18e530b1 100644
--- a/TODO.txt
+++ b/TODO.txt
@@ -1,4 +1,4 @@
-Coverage TODO
+Coverage.py TODO
Key:
* Heading
@@ -9,7 +9,7 @@ Key:
* 4.0
- What defaults should change?
- - --source = . ?
+ x --source = . ?
x --branch = True ?
- Remove 2.3, 2.4, 2.5 limitations
@@ -43,8 +43,8 @@ Key:
- cov.config["run:branch"] api
- "added in 4.0"
+ Remove code only run on <2.6
-- Change data file to json
-- Create data api
++ Change data file to json
++ Create data api
+ gevent, etc.
+ Remove the old command-line syntax
+ A pain, b/c of the structure of the tests.
@@ -126,7 +126,7 @@ x Tricky swapping of collector like figleaf, pycov, et al. (Don't need to do
3: c = 3
if the coverage data shows 1,2,3, it was if-then. if it's 1,3, then the
missing else was executed.
-- API for getting coverage data.
++ API for getting coverage data.
- Instruction tracing instead of line tracing.
- Path tracing (how does this even work?)
- Count execution of lines
@@ -143,7 +143,7 @@ x Tricky swapping of collector like figleaf, pycov, et al. (Don't need to do
directory.
- Why can't a morf also be a string, the name of a module?
- ignore by module as well as file?
-+ Use a .coveragerc file to control Coverage without the programmatic API.
++ Use a .coveragerc file to control coverage.py without the programmatic API.
- Add a --data switch to explicitly control the data file on the command line.
x Why can't you specify execute (-x) and report (-r) in the same invocation?
Maybe just because -x needs the rest of the command line?
@@ -156,7 +156,7 @@ x Why can't you specify execute (-x) and report (-r) in the same invocation?
class definitions, etc, when coverage is started after the class is defined.
- Different categories of exclude pragma? So you can enable and disable them
from the command line, to reconsider exclusions.
-+ Reporting on files never touched by coverage (package completeness)
++ Reporting on files never touched by coverage.py (package completeness)
- A setup.py command? http://jeetworks.org/node/50
- Deltas: indicate the change in coverage percentage from the last run.
+ Show lines missing rather than lines run in the reporting, since that's what
@@ -177,7 +177,7 @@ x Why can't you specify execute (-x) and report (-r) in the same invocation?
+ Clickable column headers on the index page.
+ Syntax coloring in HTML report.
+ Dynamic effects in HTML report.
- + Footer in reports pointing to coverage home page.
+ + Footer in reports pointing to coverage.py home page.
+ Baseline grid for linenumber font.
+ Separate out css and HTML.
+ Does it work right with utf-8 source files? http://www.python.org/dev/peps/pep-0263/
@@ -196,10 +196,10 @@ x Why can't you specify execute (-x) and report (-r) in the same invocation?
- ignore (files not to collect)
- exclude (lines not to report as missed)
- omit (files not to report)
- - Changes from coverage 2.x:
+ - Changes from coverage.py 2.x:
- Bare "except:" lines now count as executable code.
- Double function decorators: all decorator lines count as executable code.
- - Document the .coverage file format.
+ x Document the .coverage file format.
+ HTML reporting.
- Much more detail about what's in the report.
- References between pages are off:
@@ -220,8 +220,8 @@ x Why can't you specify execute (-x) and report (-r) in the same invocation?
* Installation
-x How will Coverage package install over coverage.py module?
-x pip can't install it: it reads the coverage html page, and finds the kit link,
+x How will coverage.py package install over coverage.py module?
+x pip can't install it: it reads the coverage.py html page, and finds the kit link,
but then can't handle the root-relative link.
@@ -263,13 +263,13 @@ x pip can't install it: it reads the coverage html page, and finds the kit link,
+ Switch to a real test runner, like nose.
+ Test both the C trace function and the Python trace function.
+ parser.py has no direct tests.
-- Tests about the .coverage file.
++ Tests about the .coverage file.
+ Tests about the --long-form of arguments.
+ Tests about overriding the .coverage filename.
- Tests about parallel mode.
+ Tests about assigning a multi-line string.
- Tests about tricky docstrings.
-+ Coverage test Coverage!
++ Coverage test coverage.py!
- Tests that tracing stops after calling stop()
- More intensive thread testing.
x Tests about the "import __main__" in cmdline.py
diff --git a/__main__.py b/__main__.py
index b11dafce..a44b820a 100644
--- a/__main__.py
+++ b/__main__.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
+
"""Be able to execute coverage.py by pointing Python at a working tree."""
import runpy, os
diff --git a/coverage/__init__.py b/coverage/__init__.py
index 0aa1d45c..f9e8a04b 100644
--- a/coverage/__init__.py
+++ b/coverage/__init__.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
+
"""Code coverage measurement for Python.
Ned Batchelder
@@ -9,74 +12,12 @@ from coverage.version import __version__, __url__
from coverage.control import Coverage, process_startup
from coverage.data import CoverageData
-from coverage.cmdline import main, CoverageScript
from coverage.misc import CoverageException
from coverage.plugin import CoveragePlugin
# Backward compatibility.
coverage = Coverage
-# Module-level functions. The original API to this module was based on
-# functions defined directly in the module, with a singleton of the Coverage()
-# class. That design hampered programmability, so the current API uses
-# explicitly-created Coverage objects. But for backward compatibility, here we
-# define the top-level functions to create the singleton when they are first
-# called.
-
-# Singleton object for use with module-level functions. The singleton is
-# created as needed when one of the module-level functions is called.
-_the_coverage = None
-
-def _singleton_method(name):
- """Return a function to the `name` method on a singleton `Coverage` object.
-
- The singleton object is created the first time one of these functions is
- called.
-
- """
- # Disable pylint message, because a bunch of variables look unused, but
- # they're accessed via locals().
- # pylint: disable=unused-variable
-
- def wrapper(*args, **kwargs):
- """Singleton wrapper around a coverage method."""
- global _the_coverage
- if not _the_coverage:
- _the_coverage = Coverage(auto_data=True)
- return getattr(_the_coverage, name)(*args, **kwargs)
-
- import inspect
- meth = getattr(Coverage, name)
- args, varargs, kw, defaults = inspect.getargspec(meth)
- argspec = inspect.formatargspec(args[1:], varargs, kw, defaults)
- docstring = meth.__doc__
- wrapper.__doc__ = ("""\
- A first-use-singleton wrapper around Coverage.%(name)s.
-
- This wrapper is provided for backward compatibility with legacy code.
- New code should use Coverage.%(name)s directly.
-
- %(name)s%(argspec)s:
-
- %(docstring)s
- """ % locals()
- )
-
- return wrapper
-
-
-# Define the module-level functions.
-use_cache = _singleton_method('use_cache')
-start = _singleton_method('start')
-stop = _singleton_method('stop')
-erase = _singleton_method('erase')
-exclude = _singleton_method('exclude')
-analysis = _singleton_method('analysis')
-analysis2 = _singleton_method('analysis2')
-report = _singleton_method('report')
-annotate = _singleton_method('annotate')
-
-
# On Windows, we encode and decode deep enough that something goes wrong and
# the encodings.utf_8 module is loaded and then unloaded, I don't know why.
# Adding a reference here prevents it from being unloaded. Yuk.
@@ -91,34 +32,3 @@ try:
del sys.modules['coverage.coverage']
except KeyError:
pass
-
-
-# COPYRIGHT AND LICENSE
-#
-# Copyright 2001 Gareth Rees. All rights reserved.
-# Copyright 2004-2015 Ned Batchelder. All rights reserved.
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions are
-# met:
-#
-# 1. Redistributions of source code must retain the above copyright
-# notice, this list of conditions and the following disclaimer.
-#
-# 2. Redistributions in binary form must reproduce the above copyright
-# notice, this list of conditions and the following disclaimer in the
-# documentation and/or other materials provided with the
-# distribution.
-#
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-# HOLDERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
-# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
-# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
-# OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
-# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
-# TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
-# USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
-# DAMAGE.
diff --git a/coverage/__main__.py b/coverage/__main__.py
index 55e0d259..35ab87a5 100644
--- a/coverage/__main__.py
+++ b/coverage/__main__.py
@@ -1,4 +1,8 @@
+# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
+# For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt
+
"""Coverage.py's main entry point."""
+
import sys
from coverage.cmdline import main
sys.exit(main())
diff --git a/coverage/annotate.py b/coverage/annotate.py
index b77df4ec..4b4966e9 100644
--- a/coverage/annotate.py
+++ b/coverage/annotate.py
@@ -1,6 +1,11 @@
-"""Source file annotation for Coverage."""
+# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
+# For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt
-import os, re
+"""Source file annotation for coverage.py."""
+
+import io
+import os
+import re
from coverage.report import Reporter
@@ -59,7 +64,7 @@ class AnnotateReporter(Reporter):
else:
dest_file = fr.filename + ",cover"
- with open(dest_file, 'w') as dest:
+ with io.open(dest_file, 'w', encoding='utf8') as dest:
i = 0
j = 0
covered = True
@@ -72,21 +77,22 @@ class AnnotateReporter(Reporter):
if i < len(statements) and statements[i] == lineno:
covered = j >= len(missing) or missing[j] > lineno
if self.blank_re.match(line):
- dest.write(' ')
+ dest.write(u' ')
elif self.else_re.match(line):
# Special logic for lines containing only 'else:'.
if i >= len(statements) and j >= len(missing):
- dest.write('! ')
+ dest.write(u'! ')
elif i >= len(statements) or j >= len(missing):
- dest.write('> ')
+ dest.write(u'> ')
elif statements[i] == missing[j]:
- dest.write('! ')
+ dest.write(u'! ')
else:
- dest.write('> ')
+ dest.write(u'> ')
elif lineno in excluded:
- dest.write('- ')
+ dest.write(u'- ')
elif covered:
- dest.write('> ')
+ dest.write(u'> ')
else:
- dest.write('! ')
+ dest.write(u'! ')
+
dest.write(line)
diff --git a/coverage/backunittest.py b/coverage/backunittest.py
index 95b6fcc6..09574ccb 100644
--- a/coverage/backunittest.py
+++ b/coverage/backunittest.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
+
"""Implementations of unittest features from the future."""
# Use unittest2 if it's available, otherwise unittest. This gives us
@@ -22,10 +25,13 @@ class TestCase(unittest.TestCase):
"""
# pylint: disable=missing-docstring
- if not unittest_has('assertCountEqual'):
- def assertCountEqual(self, s1, s2):
- """Assert these have the same elements, regardless of order."""
- self.assertEqual(set(s1), set(s2))
+ # Many Pythons have this method defined. But PyPy3 has a bug with it
+ # somehow (https://bitbucket.org/pypy/pypy/issues/2092), so always use our
+ # own implementation that works everywhere, at least for the ways we're
+ # calling it.
+ def assertCountEqual(self, s1, s2):
+ """Assert these have the same elements, regardless of order."""
+ self.assertEqual(sorted(s1), sorted(s2))
if not unittest_has('assertRaisesRegex'):
def assertRaisesRegex(self, *args, **kwargs):
diff --git a/coverage/backward.py b/coverage/backward.py
index 58d9cfea..340dc130 100644
--- a/coverage/backward.py
+++ b/coverage/backward.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
+
"""Add things to old Pythons so I can pretend they are newer."""
# This file does lots of tricky stuff, so disable a bunch of pylint warnings.
@@ -16,6 +19,12 @@ try:
except ImportError:
from io import StringIO
+# In py3, ConfigParser was renamed to the more-standard configparser
+try:
+ import configparser
+except ImportError:
+ import ConfigParser as configparser
+
# What's a string called?
try:
string_class = basestring
@@ -28,12 +37,6 @@ try:
except NameError:
unicode_class = str
-# Where do pickles come from?
-try:
- import cPickle as pickle
-except ImportError:
- import pickle
-
# range or xrange?
try:
range = xrange
@@ -155,7 +158,6 @@ def import_local_file(modname):
with open(modfile, 'r') as f:
# pylint: disable=undefined-loop-variable
- # (Using possibly undefined loop variable 'suff')
mod = imp.load_module(modname, f, modfile, suff)
return mod
diff --git a/coverage/bytecode.py b/coverage/bytecode.py
index d7304936..82929cef 100644
--- a/coverage/bytecode.py
+++ b/coverage/bytecode.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
+
"""Bytecode manipulation for coverage.py"""
import opcode
diff --git a/coverage/cmdline.py b/coverage/cmdline.py
index 2be32947..fc40e619 100644
--- a/coverage/cmdline.py
+++ b/coverage/cmdline.py
@@ -1,4 +1,7 @@
-"""Command-line support for Coverage."""
+# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
+# For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt
+
+"""Command-line support for coverage.py."""
import glob
import optparse
@@ -16,7 +19,7 @@ class Opts(object):
"""A namespace class for individual options we'll build parsers from."""
append = optparse.make_option(
- '-a', '--append', action='store_false', dest="erase_first",
+ '-a', '--append', action='store_true',
help="Append coverage data to .coverage, otherwise it is started "
"clean with each run."
)
@@ -56,7 +59,7 @@ class Opts(object):
include = optparse.make_option(
'', '--include', action='store',
metavar="PAT1,PAT2,...",
- help="Include only files whose paths match one of these patterns."
+ help="Include only files whose paths match one of these patterns. "
"Accepts shell-style wildcards, which must be quoted."
)
pylib = optparse.make_option(
@@ -119,7 +122,7 @@ class Opts(object):
class CoverageOptionParser(optparse.OptionParser, object):
- """Base OptionParser for coverage.
+ """Base OptionParser for coverage.py.
Problems don't exit the program.
Defaults are initialized for all options.
@@ -132,6 +135,7 @@ class CoverageOptionParser(optparse.OptionParser, object):
)
self.set_defaults(
action=None,
+ append=None,
branch=None,
concurrency=None,
debug=None,
@@ -140,9 +144,9 @@ class CoverageOptionParser(optparse.OptionParser, object):
help=None,
ignore_errors=None,
include=None,
+ module=None,
omit=None,
parallel_mode=None,
- module=None,
pylib=None,
rcfile=True,
show_missing=None,
@@ -150,7 +154,6 @@ class CoverageOptionParser(optparse.OptionParser, object):
source=None,
timid=None,
title=None,
- erase_first=None,
version=None,
)
@@ -202,7 +205,7 @@ class CmdOptionParser(CoverageOptionParser):
def __init__(self, action, options=None, defaults=None, usage=None,
description=None
):
- """Create an OptionParser for a coverage command.
+ """Create an OptionParser for a coverage.py command.
`action` is the slug to put into `options.action`.
`options` is a list of Option's for the command.
@@ -239,8 +242,8 @@ CMDS = {
[
Opts.directory,
Opts.ignore_errors,
- Opts.omit,
Opts.include,
+ Opts.omit,
] + GLOBAL_ARGS,
usage = "[options] [modules]",
description = "Make annotated copies of the given files, marking "
@@ -249,10 +252,13 @@ CMDS = {
),
'combine': CmdOptionParser("combine", GLOBAL_ARGS,
- usage = " ",
+ usage = "<path1> <path2> ... <pathN>",
description = "Combine data from multiple coverage files collected "
"with 'run -p'. The combined results are written to a single "
- "file representing the union of the data."
+ "file representing the union of the data. The positional "
+ "arguments are data files or directories containing data files. "
+ "If no paths are provided, data files in the default data file's "
+ "directory are combined."
),
'debug': CmdOptionParser("debug", GLOBAL_ARGS,
@@ -278,8 +284,8 @@ CMDS = {
Opts.directory,
Opts.fail_under,
Opts.ignore_errors,
- Opts.omit,
Opts.include,
+ Opts.omit,
Opts.title,
] + GLOBAL_ARGS,
usage = "[options] [modules]",
@@ -292,10 +298,10 @@ CMDS = {
[
Opts.fail_under,
Opts.ignore_errors,
- Opts.omit,
Opts.include,
+ Opts.omit,
Opts.show_missing,
- Opts.skip_covered
+ Opts.skip_covered,
] + GLOBAL_ARGS,
usage = "[options] [modules]",
description = "Report coverage statistics on modules."
@@ -306,15 +312,14 @@ CMDS = {
Opts.append,
Opts.branch,
Opts.concurrency,
+ Opts.include,
+ Opts.module,
+ Opts.omit,
Opts.pylib,
Opts.parallel_mode,
- Opts.module,
- Opts.timid,
Opts.source,
- Opts.omit,
- Opts.include,
+ Opts.timid,
] + GLOBAL_ARGS,
- defaults = {'erase_first': True},
usage = "[options] <pyfile> [program options]",
description = "Run a Python program, measuring code execution."
),
@@ -323,8 +328,8 @@ CMDS = {
[
Opts.fail_under,
Opts.ignore_errors,
- Opts.omit,
Opts.include,
+ Opts.omit,
Opts.output_xml,
] + GLOBAL_ARGS,
usage = "[options] [modules]",
@@ -337,7 +342,7 @@ OK, ERR, FAIL_UNDER = 0, 1, 2
class CoverageScript(object):
- """The command-line interface to Coverage."""
+ """The command-line interface to coverage.py."""
def __init__(self, _covpkg=None, _run_python_file=None,
_run_python_module=None, _help_fn=None):
@@ -357,7 +362,7 @@ class CoverageScript(object):
self.coverage = None
def command_line(self, argv):
- """The bulk of the command line interface to Coverage.
+ """The bulk of the command line interface to coverage.py.
`argv` is the argument list to process.
@@ -421,17 +426,19 @@ class CoverageScript(object):
if options.action == "debug":
return self.do_debug(args)
- if options.action == "erase" or options.erase_first:
+ elif options.action == "erase":
self.coverage.erase()
- else:
- self.coverage.load()
+ return OK
- if options.action == "run":
- self.do_run(options, args)
+ elif options.action == "run":
+ return self.do_run(options, args)
- if options.action == "combine":
- self.coverage.combine()
+ elif options.action == "combine":
+ self.coverage.load()
+ data_dirs = args or None
+ self.coverage.combine(data_dirs)
self.coverage.save()
+ return OK
# Remaining actions are reporting, with some common options.
report_args = dict(
@@ -441,19 +448,21 @@ class CoverageScript(object):
include = include,
)
+ self.coverage.load()
+
total = None
if options.action == "report":
total = self.coverage.report(
show_missing=options.show_missing,
skip_covered=options.skip_covered, **report_args)
- if options.action == "annotate":
+ elif options.action == "annotate":
self.coverage.annotate(
directory=options.directory, **report_args)
- if options.action == "html":
+ elif options.action == "html":
total = self.coverage.html_report(
directory=options.directory, title=options.title,
**report_args)
- if options.action == "xml":
+ elif options.action == "xml":
outfile = options.outfile
total = self.coverage.xml_report(outfile=outfile, **report_args)
@@ -538,11 +547,19 @@ class CoverageScript(object):
self.help_fn("Nothing to do.")
return False
+ if options.append and options.parallel_mode:
+ self.help_fn("Can't append to data files in parallel mode.")
+ return False
+
return True
def do_run(self, options, args):
"""Implementation of 'coverage run'."""
+ if not self.coverage.config.parallel:
+ if not options.append:
+ self.coverage.erase()
+
# Set the first path element properly.
old_path0 = sys.path[0]
@@ -563,17 +580,22 @@ class CoverageScript(object):
finally:
self.coverage.stop()
if code_ran:
+ if options.append:
+ self.coverage.combine(data_paths=[self.coverage.config.data_file])
self.coverage.save()
# Restore the old path
sys.path[0] = old_path0
+ return OK
+
def do_debug(self, args):
"""Implementation of 'coverage debug'."""
if not args:
self.help_fn("What information would you like: data, sys?")
return ERR
+
for info in args:
if info == 'sys':
sys_info = self.coverage.sys_info()
@@ -582,17 +604,17 @@ class CoverageScript(object):
print(" %s" % line)
elif info == 'data':
self.coverage.load()
+ data = self.coverage.data
print(info_header("data"))
- print("path: %s" % self.coverage.data.filename)
- print("has_arcs: %r" % self.coverage.data.has_arcs())
- summary = self.coverage.data.summary(fullpath=True)
- if summary:
- plugins = self.coverage.data.plugin_data()
+ print("path: %s" % self.coverage.data_files.filename)
+ if data:
+ print("has_arcs: %r" % data.has_arcs())
+ summary = data.line_counts(fullpath=True)
filenames = sorted(summary.keys())
print("\n%d files:" % len(filenames))
for f in filenames:
line = "%s: %d lines" % (f, summary[f])
- plugin = plugins.get(f)
+ plugin = data.file_tracer(f)
if plugin:
line += " [%s]" % plugin
print(line)
@@ -601,6 +623,7 @@ class CoverageScript(object):
else:
self.help_fn("Don't know what you mean by %r" % info)
return ERR
+
return OK
@@ -609,7 +632,7 @@ def unshell_list(s):
if not s:
return None
if env.WINDOWS:
- # When running coverage as coverage.exe, some of the behavior
+ # When running coverage.py as coverage.exe, some of the behavior
# of the shell is emulated: wildcards are expanded into a list of
# filenames. So you have to single-quote patterns on the command
# line, but (not) helpfully, the single quotes are included in the
@@ -665,7 +688,7 @@ Documentation at %(__url__)s
def main(argv=None):
- """The main entry point to Coverage.
+ """The main entry point to coverage.py.
This is installed as the script entry point.
diff --git a/coverage/collector.py b/coverage/collector.py
index 948cbbb0..ae5f6c8b 100644
--- a/coverage/collector.py
+++ b/coverage/collector.py
@@ -1,14 +1,19 @@
-"""Raw data collector for Coverage."""
+# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
+# For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt
+
+"""Raw data collector for coverage.py."""
import os, sys
from coverage import env
+from coverage.backward import iitems
+from coverage.files import abs_file
from coverage.misc import CoverageException
from coverage.pytracer import PyTracer
try:
# Use the C extension code when we can, for speed.
- from coverage.tracer import CTracer # pylint: disable=no-name-in-module
+ from coverage.tracer import CTracer, CFileDisposition # pylint: disable=no-name-in-module
except ImportError:
# Couldn't import the C extension, maybe it isn't built.
if os.getenv('COVERAGE_TEST_TRACER') == 'c':
@@ -20,11 +25,16 @@ except ImportError:
# exception here causes all sorts of other noise in unittest.
sys.stderr.write(
"*** COVERAGE_TEST_TRACER is 'c' but can't import CTracer!\n"
- )
+ )
sys.exit(1)
CTracer = None
+class FileDisposition(object):
+ """A simple value type for recording what to do with a file."""
+ pass
+
+
class Collector(object):
"""Collects trace data.
@@ -46,14 +56,14 @@ class Collector(object):
# the top, and resumed when they become the top again.
_collectors = []
- def __init__(self,
+ def __init__(
+ self,
should_trace, check_include, timid, branch, warn, concurrency,
):
"""Create a collector.
`should_trace` is a function, taking a filename, and returning a
- canonicalized filename, or None depending on whether the file should
- be traced or not.
+ `coverage.FileDisposition object`.
TODO: `check_include`
@@ -85,13 +95,13 @@ class Collector(object):
try:
if concurrency == "greenlet":
- import greenlet # pylint: disable=import-error
+ import greenlet # pylint: disable=import-error,useless-suppression
self.concur_id_func = greenlet.getcurrent
elif concurrency == "eventlet":
- import eventlet.greenthread # pylint: disable=import-error
+ import eventlet.greenthread # pylint: disable=import-error,useless-suppression
self.concur_id_func = eventlet.greenthread.getcurrent
elif concurrency == "gevent":
- import gevent # pylint: disable=import-error
+ import gevent # pylint: disable=import-error,useless-suppression
self.concur_id_func = gevent.getcurrent
elif concurrency == "thread" or not concurrency:
# It's important to import threading only if we need it. If
@@ -119,7 +129,12 @@ class Collector(object):
# trace function.
self._trace_class = CTracer or PyTracer
- self.supports_plugins = self._trace_class is CTracer
+ if self._trace_class is CTracer:
+ self.file_disposition_class = CFileDisposition
+ self.supports_plugins = True
+ else:
+ self.file_disposition_class = FileDisposition
+ self.supports_plugins = False
def __repr__(self):
return "<Collector at 0x%x>" % id(self)
@@ -130,15 +145,23 @@ class Collector(object):
def reset(self):
"""Clear collected data, and prepare to collect more."""
- # A dictionary mapping filenames to dicts with line number keys,
- # or mapping filenames to dicts with line number pairs as keys.
+ # A dictionary mapping filenames to dicts with line number keys (if not
+ # branch coverage), or mapping filenames to dicts with line number
+ # pairs as keys (if branch coverage).
self.data = {}
- self.plugin_data = {}
-
- # A cache of the results from should_trace, the decision about whether
- # to trace execution in a file. A dict of filename to (filename or
- # None).
+ # A dictionary mapping filenames to file tracer plugin names that will
+ # handle them.
+ self.file_tracers = {}
+
+ # The .should_trace_cache attribute is a cache from filenames to
+ # coverage.FileDisposition objects, or None. When a file is first
+ # considered for tracing, a FileDisposition is obtained from
+ # Coverage.should_trace. Its .trace attribute indicates whether the
+ # file should be traced or not. If it should be, a plugin with dynamic
+ # filenames can decide not to trace it based on the dynamic filename
+ # being excluded by the inclusion rules, in which case the
+ # FileDisposition will be replaced by None in the cache.
if env.PYPY:
import __pypy__ # pylint: disable=import-error
# Alex Gaynor said:
@@ -181,8 +204,8 @@ class Collector(object):
)
)
- if hasattr(tracer, 'plugin_data'):
- tracer.plugin_data = self.plugin_data
+ if hasattr(tracer, 'file_tracers'):
+ tracer.file_tracers = self.file_tracers
if hasattr(tracer, 'threading'):
tracer.threading = self.threading
if hasattr(tracer, 'check_include'):
@@ -283,39 +306,20 @@ class Collector(object):
else:
self._start_tracer()
- def get_line_data(self):
- """Return the line data collected.
+ def save_data(self, covdata):
+ """Save the collected data to a `CoverageData`.
- Data is { filename: { lineno: None, ...}, ...}
+ Also resets the collector.
"""
- if self.branch:
- # If we were measuring branches, then we have to re-build the dict
- # to show line data. We'll use the first lines of all the arcs,
- # if they are actual lines. We don't need the second lines, because
- # the second lines will also be first lines, sometimes to exits.
- line_data = {}
- for f, arcs in self.data.items():
- line_data[f] = dict(
- (l1, None) for l1, _ in arcs.keys() if l1 > 0
- )
- return line_data
- else:
- return self.data
-
- def get_arc_data(self):
- """Return the arc data collected.
-
- Data is { filename: { (l1, l2): None, ...}, ...}
+ def abs_file_dict(d):
+ """Return a dict like d, but with keys modified by `abs_file`."""
+ return dict((abs_file(k), v) for k, v in iitems(d))
- Note that no data is collected or returned if the Collector wasn't
- created with `branch` true.
-
- """
if self.branch:
- return self.data
+ covdata.set_arcs(abs_file_dict(self.data))
else:
- return {}
+ covdata.set_lines(abs_file_dict(self.data))
+ covdata.set_file_tracers(abs_file_dict(self.file_tracers))
- def get_plugin_data(self):
- return self.plugin_data
+ self.reset()
diff --git a/coverage/config.py b/coverage/config.py
index 7b142671..9939d6c0 100644
--- a/coverage/config.py
+++ b/coverage/config.py
@@ -1,22 +1,21 @@
-"""Config file for coverage.py"""
+# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
+# For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt
-import os, re, sys
-from coverage.backward import string_class, iitems
-from coverage.misc import CoverageException
+"""Config file for coverage.py"""
+import collections
+import os
+import re
+import sys
-# In py3, ConfigParser was renamed to the more-standard configparser
-try:
- import configparser
-except ImportError:
- import ConfigParser as configparser
+from coverage.backward import configparser, iitems, string_class
+from coverage.misc import CoverageException
class HandyConfigParser(configparser.RawConfigParser):
"""Our specialization of ConfigParser."""
def __init__(self, section_prefix):
- # pylint: disable=super-init-not-called
configparser.RawConfigParser.__init__(self)
self.section_prefix = section_prefix
@@ -61,7 +60,7 @@ class HandyConfigParser(configparser.RawConfigParser):
def dollar_replace(m):
"""Called for each $replacement."""
# Only one of the groups will have matched, just get its text.
- word = next(w for w in m.groups() if w is not None)
+ word = next(w for w in m.groups() if w is not None) # pragma: part covered
if word == "$":
return "$"
else:
@@ -112,10 +111,8 @@ class HandyConfigParser(configparser.RawConfigParser):
re.compile(value)
except re.error as e:
raise CoverageException(
- "Invalid [%s].%s value %r: %s" % (
- section, option, value, e
- )
- )
+ "Invalid [%s].%s value %r: %s" % (section, option, value, e)
+ )
if value:
value_list.append(value)
return value_list
@@ -124,12 +121,12 @@ class HandyConfigParser(configparser.RawConfigParser):
# The default line exclusion regexes.
DEFAULT_EXCLUDE = [
r'(?i)#\s*pragma[:\s]?\s*no\s*cover',
- ]
+]
# The default partial branch regexes, to be modified by the user.
DEFAULT_PARTIAL = [
r'(?i)#\s*pragma[:\s]?\s*no\s*branch',
- ]
+]
# The default partial branch regexes, based on Python semantics.
# These are any Python branching constructs that can't actually execute all
@@ -137,7 +134,7 @@ DEFAULT_PARTIAL = [
DEFAULT_PARTIAL_ALWAYS = [
'while (True|1|False|0):',
'if (True|1|False|0):',
- ]
+]
class CoverageConfig(object):
@@ -158,11 +155,12 @@ class CoverageConfig(object):
self.concurrency = None
self.cover_pylib = False
self.data_file = ".coverage"
- self.parallel = False
- self.timid = False
- self.source = None
self.debug = []
+ self.note = None
+ self.parallel = False
self.plugins = []
+ self.source = None
+ self.timid = False
# Defaults for [report]
self.exclude_list = DEFAULT_EXCLUDE[:]
@@ -170,15 +168,15 @@ class CoverageConfig(object):
self.ignore_errors = False
self.include = None
self.omit = None
- self.partial_list = DEFAULT_PARTIAL[:]
self.partial_always_list = DEFAULT_PARTIAL_ALWAYS[:]
+ self.partial_list = DEFAULT_PARTIAL[:]
self.precision = 0
self.show_missing = False
self.skip_covered = False
# Defaults for [html]
- self.html_dir = "htmlcov"
self.extra_css = None
+ self.html_dir = "htmlcov"
self.html_title = "Coverage report"
# Defaults for [xml]
@@ -215,9 +213,7 @@ class CoverageConfig(object):
try:
files_read = cp.read(filename)
except configparser.Error as err:
- raise CoverageException(
- "Couldn't read config file %s: %s" % (filename, err)
- )
+ raise CoverageException("Couldn't read config file %s: %s" % (filename, err))
if not files_read:
return False
@@ -227,9 +223,24 @@ class CoverageConfig(object):
for option_spec in self.CONFIG_FILE_OPTIONS:
self._set_attr_from_config_option(cp, *option_spec)
except ValueError as err:
- raise CoverageException(
- "Couldn't read config file %s: %s" % (filename, err)
- )
+ raise CoverageException("Couldn't read config file %s: %s" % (filename, err))
+
+ # Check that there are no unrecognized options.
+ all_options = collections.defaultdict(set)
+ for option_spec in self.CONFIG_FILE_OPTIONS:
+ section, option = option_spec[1].split(":")
+ all_options[section].add(option)
+
+ for section, options in iitems(all_options):
+ if cp.has_section(section):
+ for unknown in set(cp.options(section)) - options:
+ if section_prefix:
+ section = section_prefix + section
+ raise CoverageException(
+ "Unrecognized option '[%s] %s=' in config file %s" % (
+ section, unknown, filename
+ )
+ )
# [paths] is special
if cp.has_section('paths'):
@@ -258,10 +269,11 @@ class CoverageConfig(object):
('cover_pylib', 'run:cover_pylib', 'boolean'),
('data_file', 'run:data_file'),
('debug', 'run:debug', 'list'),
- ('plugins', 'run:plugins', 'list'),
('include', 'run:include', 'list'),
+ ('note', 'run:note'),
('omit', 'run:omit', 'list'),
('parallel', 'run:parallel', 'boolean'),
+ ('plugins', 'run:plugins', 'list'),
('source', 'run:source', 'list'),
('timid', 'run:timid', 'boolean'),
@@ -271,27 +283,27 @@ class CoverageConfig(object):
('ignore_errors', 'report:ignore_errors', 'boolean'),
('include', 'report:include', 'list'),
('omit', 'report:omit', 'list'),
- ('partial_list', 'report:partial_branches', 'regexlist'),
('partial_always_list', 'report:partial_branches_always', 'regexlist'),
+ ('partial_list', 'report:partial_branches', 'regexlist'),
('precision', 'report:precision', 'int'),
('show_missing', 'report:show_missing', 'boolean'),
('skip_covered', 'report:skip_covered', 'boolean'),
# [html]
- ('html_dir', 'html:directory'),
('extra_css', 'html:extra_css'),
+ ('html_dir', 'html:directory'),
('html_title', 'html:title'),
# [xml]
('xml_output', 'xml:output'),
('xml_package_depth', 'xml:package_depth', 'int'),
- ]
+ ]
def _set_attr_from_config_option(self, cp, attr, where, type_=''):
"""Set an attribute on self if it exists in the ConfigParser."""
section, option = where.split(":")
if cp.has_option(section, option):
- method = getattr(cp, 'get'+type_)
+ method = getattr(cp, 'get' + type_)
setattr(self, attr, method(section, option))
def get_plugin_options(self, plugin):
diff --git a/coverage/control.py b/coverage/control.py
index 563925ef..4837356d 100644
--- a/coverage/control.py
+++ b/coverage/control.py
@@ -1,29 +1,31 @@
-"""Core control stuff for Coverage."""
+# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
+# For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt
+
+"""Core control stuff for coverage.py."""
import atexit
import inspect
import os
import platform
-import random
-import socket
import sys
import traceback
-from coverage import env
+from coverage import env, files
from coverage.annotate import AnnotateReporter
from coverage.backward import string_class, iitems
from coverage.collector import Collector
from coverage.config import CoverageConfig
-from coverage.data import CoverageData
+from coverage.data import CoverageData, CoverageDataFiles
from coverage.debug import DebugControl
-from coverage.files import FileLocator, TreeMatcher, FnmatchMatcher
+from coverage.files import TreeMatcher, FnmatchMatcher
from coverage.files import PathAliases, find_python_files, prep_patterns
from coverage.files import ModuleMatcher, abs_file
from coverage.html import HtmlReporter
from coverage.misc import CoverageException, bool_or_none, join_regex
-from coverage.misc import file_be_gone, overrides
+from coverage.misc import file_be_gone
from coverage.monkey import patch_multiprocessing
-from coverage.plugin import CoveragePlugin, FileReporter
+from coverage.plugin import FileReporter
+from coverage.plugin_support import Plugins
from coverage.python import PythonFileReporter
from coverage.results import Analysis, Numbers
from coverage.summary import SummaryReporter
@@ -138,6 +140,9 @@ class Coverage(object):
env_data_file = os.environ.get('COVERAGE_FILE')
if env_data_file:
self.config.data_file = env_data_file
+ debugs = os.environ.get('COVERAGE_DEBUG')
+ if debugs:
+ self.config.debug.extend(debugs.split(","))
# 4: from constructor arguments:
self.config.from_args(
@@ -166,10 +171,10 @@ class Coverage(object):
# Other instance attributes, set later.
self.omit = self.include = self.source = None
- self.source_pkgs = self.file_locator = None
- self.data = self.collector = None
- self.plugins = self.file_tracing_plugins = None
- self.pylib_dirs = self.cover_dir = None
+ self.source_pkgs = None
+ self.data = self.data_files = self.collector = None
+ self.plugins = None
+ self.pylib_dirs = self.cover_dirs = None
self.data_suffix = self.run_suffix = None
self._exclude_re = None
self.debug = None
@@ -190,8 +195,6 @@ class Coverage(object):
is called.
"""
- from coverage import __version__
-
if self._inited:
return
@@ -201,26 +204,21 @@ class Coverage(object):
self.debug = DebugControl(self.config.debug, self._debug_file)
# Load plugins
- self.plugins = Plugins.load_plugins(self.config.plugins, self.config)
-
- self.file_tracing_plugins = []
- for plugin in self.plugins:
- if overrides(plugin, "file_tracer", CoveragePlugin):
- self.file_tracing_plugins.append(plugin)
+ self.plugins = Plugins.load_plugins(self.config.plugins, self.config, self.debug)
# _exclude_re is a dict that maps exclusion list names to compiled
# regexes.
self._exclude_re = {}
self._exclude_regex_stale()
- self.file_locator = FileLocator()
+ files.set_relative_directory()
# 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))
+ self.source.append(files.canonical_filename(src))
else:
self.source_pkgs.append(src)
@@ -242,17 +240,17 @@ class Coverage(object):
)
# Early warning if we aren't going to be able to support plugins.
- if self.file_tracing_plugins and not self.collector.supports_plugins:
+ if self.plugins.file_tracers and not self.collector.supports_plugins:
self._warn(
"Plugin file tracers (%s) aren't supported with %s" % (
", ".join(
plugin._coverage_plugin_name
- for plugin in self.file_tracing_plugins
+ for plugin in self.plugins.file_tracers
),
self.collector.tracer_name(),
)
)
- for plugin in self.file_tracing_plugins:
+ for plugin in self.plugins.file_tracers:
plugin._coverage_enabled = False
# Suffixes are a bit tricky. We want to use the data suffix only when
@@ -271,11 +269,8 @@ class Coverage(object):
# Create the data file. We do this at construction time so that the
# data file will be written into the directory where the process
# started rather than wherever the process eventually chdir'd to.
- self.data = CoverageData(
- basename=self.config.data_file,
- collector="coverage v%s" % __version__,
- debug=self.debug,
- )
+ self.data = CoverageData(debug=self.debug)
+ self.data_files = CoverageDataFiles(basename=self.config.data_file)
# The dirs for files considered "installed with the interpreter".
self.pylib_dirs = set()
@@ -285,7 +280,7 @@ class Coverage(object):
# environments (virtualenv, for example), these modules may be
# spread across a few locations. Look at all the candidate modules
# we've imported, and take all the different ones.
- for m in (atexit, os, platform, random, socket, _structseq):
+ for m in (atexit, inspect, os, platform, _structseq, traceback):
if m is not None and hasattr(m, "__file__"):
self.pylib_dirs.add(self._canonical_dir(m))
if _structseq and not hasattr(_structseq, '__file__'):
@@ -299,9 +294,16 @@ class Coverage(object):
structseq_file = structseq_new.__code__.co_filename
self.pylib_dirs.add(self._canonical_dir(structseq_file))
- # To avoid tracing the coverage code itself, we skip anything located
- # where we are.
- self.cover_dir = self._canonical_dir(__file__)
+ # To avoid tracing the coverage.py code itself, we skip anything
+ # located where we are.
+ self.cover_dirs = [self._canonical_dir(__file__)]
+ if env.TESTING:
+ # When testing, we use PyContracts, which should be considered
+ # part of coverage.py, and it uses six. Exclude those directories
+ # just as we exclude ourselves.
+ import contracts, six
+ for mod in [contracts, six]:
+ self.cover_dirs.append(self._canonical_dir(mod))
# Set the reporting precision.
Numbers.set_precision(self.config.precision)
@@ -315,8 +317,8 @@ class Coverage(object):
self.source_match = TreeMatcher(self.source)
self.source_pkgs_match = ModuleMatcher(self.source_pkgs)
else:
- if self.cover_dir:
- self.cover_match = TreeMatcher([self.cover_dir])
+ if self.cover_dirs:
+ self.cover_match = TreeMatcher(self.cover_dirs)
if self.pylib_dirs:
self.pylib_match = TreeMatcher(self.pylib_dirs)
if self.include:
@@ -424,7 +426,8 @@ class Coverage(object):
Returns a FileDisposition object.
"""
- disp = FileDisposition(filename)
+ original_filename = filename
+ disp = _disposition_init(self.collector.file_disposition_class, filename)
def nope(disp, reason):
"""Simple helper to make it easy to return NO."""
@@ -441,6 +444,13 @@ class Coverage(object):
dunder_file = frame.f_globals.get('__file__')
if dunder_file:
filename = self._source_for_file(dunder_file)
+ if original_filename and not original_filename.startswith('<'):
+ orig = os.path.basename(original_filename)
+ if orig != os.path.basename(filename):
+ # Files shouldn't be renamed when moved. This happens when
+ # exec'ing code. If it seems like something is wrong with
+ # the frame's filename, then just use the original.
+ filename = original_filename
if not filename:
# Empty string is pretty useless.
@@ -460,12 +470,12 @@ class Coverage(object):
if filename.endswith("$py.class"):
filename = filename[:-9] + ".py"
- canonical = self.file_locator.canonical_filename(filename)
+ canonical = files.canonical_filename(filename)
disp.canonical_filename = canonical
# Try the plugins, see if they have an opinion about the file.
plugin = None
- for plugin in self.file_tracing_plugins:
+ for plugin in self.plugins.file_tracers:
if not plugin._coverage_enabled:
continue
@@ -478,10 +488,9 @@ class Coverage(object):
if file_tracer.has_dynamic_source_filename():
disp.has_dynamic_filename = True
else:
- disp.source_filename = \
- self.file_locator.canonical_filename(
- file_tracer.source_filename()
- )
+ disp.source_filename = files.canonical_filename(
+ file_tracer.source_filename()
+ )
break
except Exception:
self._warn(
@@ -541,8 +550,8 @@ class Coverage(object):
if self.pylib_match and self.pylib_match.match(filename):
return "is in the stdlib"
- # We exclude the coverage code itself, since a little of it will be
- # measured otherwise.
+ # We exclude the coverage.py code itself, since a little of it
+ # will be measured otherwise.
if self.cover_match and self.cover_match.match(filename):
return "is part of coverage.py"
@@ -561,7 +570,7 @@ class Coverage(object):
"""
disp = self._should_trace_internal(filename, frame)
if self.debug.should('trace'):
- self.debug.write(disp.debug_message())
+ self.debug.write(_disposition_debug_msg(disp))
return disp
def _check_include_omit_etc(self, filename, frame):
@@ -588,19 +597,16 @@ class Coverage(object):
sys.stderr.write("Coverage.py warning: %s\n" % msg)
def use_cache(self, usecache):
- """Control the use of a data file (incorrectly called a cache).
-
- `usecache` is true or false, whether to read and write data on disk.
-
- """
+ """Obsolete method."""
self._init()
- self.data.usefile(usecache)
+ if not usecache:
+ self._warn("use_cache(False) is no longer supported.")
def load(self):
"""Load previously-collected coverage data from the data file."""
self._init()
self.collector.reset()
- self.data.read()
+ self.data_files.read(self.data)
def start(self):
"""Start measuring code coverage.
@@ -647,6 +653,7 @@ class Coverage(object):
self._init()
self.collector.reset()
self.data.erase()
+ self.data_files.erase(parallel=self.config.parallel)
def clear_exclude(self, which='exclude'):
"""Clear the exclude list."""
@@ -698,58 +705,48 @@ class Coverage(object):
def save(self):
"""Save the collected coverage data to the data file."""
self._init()
- data_suffix = self.data_suffix
- if data_suffix is True:
- # If data_suffix was a simple true value, then make a suffix with
- # plenty of distinguishing information. We do this here in
- # `save()` at the last minute so that the pid will be correct even
- # if the process forks.
- extra = ""
- if _TEST_NAME_FILE: # pragma: debugging
- with open(_TEST_NAME_FILE) as f:
- test_name = f.read()
- extra = "." + test_name
- data_suffix = "%s%s.%s.%06d" % (
- socket.gethostname(), extra, os.getpid(),
- random.randint(0, 999999)
- )
+ self.get_data()
+ self.data_files.write(self.data, suffix=self.data_suffix)
- self._harvest_data()
- self.data.write(suffix=data_suffix)
-
- def combine(self):
+ def combine(self, data_paths=None):
"""Combine together a number of similarly-named coverage data files.
All coverage data files whose name starts with `data_file` (from the
coverage() constructor) will be read, and combined together into the
current measurements.
+ `data_paths` is a list of files or directories from which data should
+ be combined. If no list is passed, then the data files from the
+ directory indicated by the current data file (probably the current
+ directory) will be combined.
+
"""
self._init()
+ self.get_data()
+
aliases = None
if self.config.paths:
- aliases = PathAliases(self.file_locator)
+ aliases = PathAliases()
for paths in self.config.paths.values():
result = paths[0]
for pattern in paths[1:]:
aliases.add(pattern, result)
- self.data.combine_parallel_data(aliases=aliases)
- def _harvest_data(self):
+ self.data_files.combine_parallel_data(self.data, aliases=aliases, data_paths=data_paths)
+
+ def get_data(self):
"""Get the collected data and reset the collector.
Also warn about various problems collecting data.
+ Returns a :class:`CoverageData`, the collected coverage data.
+
"""
self._init()
if not self._measured:
return
- # TODO: seems like this parallel structure is getting kinda old...
- self.data.add_line_data(self.collector.get_line_data())
- self.data.add_arc_data(self.collector.get_arc_data())
- self.data.add_plugin_data(self.collector.get_plugin_data())
- self.collector.reset()
+ self.collector.save_data(self.data)
# If there are still entries in the source_pkgs list, then we never
# encountered those packages.
@@ -769,14 +766,13 @@ class Coverage(object):
)
# Find out if we got any data.
- summary = self.data.summary()
- if not summary and self._warn_no_data:
+ if not self.data and self._warn_no_data:
self._warn("No data was collected.")
# Find files that were never executed at all.
for src in self.source:
for py_file in find_python_files(src):
- py_file = self.file_locator.canonical_filename(py_file)
+ py_file = files.canonical_filename(py_file)
if self.omit_match and self.omit_match.match(py_file):
# Turns out this file was omitted, so don't pull it back
@@ -785,7 +781,20 @@ class Coverage(object):
self.data.touch_file(py_file)
+ # Add run information.
+ self.data.add_run_info(
+ brief_sys=" ".join([
+ platform.python_implementation(),
+ platform.python_version(),
+ platform.system(),
+ ])
+ )
+
+ if self.config.note:
+ self.data.add_run_info(note=self.config.note)
+
self._measured = False
+ return self.data
# Backward compatibility with version 1.
def analysis(self, morf):
@@ -826,19 +835,20 @@ class Coverage(object):
Returns an `Analysis` object.
"""
- self._harvest_data()
+ self.get_data()
if not isinstance(it, FileReporter):
it = self._get_file_reporter(it)
- return Analysis(self, it)
+ return Analysis(self.data, it)
def _get_file_reporter(self, morf):
"""Get a FileReporter for a module or filename."""
plugin = None
+ file_reporter = "python"
if isinstance(morf, string_class):
abs_morf = abs_file(morf)
- plugin_name = self.data.plugin_data().get(abs_morf)
+ plugin_name = self.data.file_tracer(abs_morf)
if plugin_name:
plugin = self.plugins.get(plugin_name)
@@ -850,15 +860,9 @@ class Coverage(object):
plugin._coverage_plugin_name, morf
)
)
- else:
- file_reporter = PythonFileReporter(morf, self)
- # The FileReporter can have a name attribute, but if it doesn't, we'll
- # supply it as the relative path to self.filename.
- if not hasattr(file_reporter, "name"):
- file_reporter.name = self.file_locator.relative_filename(
- file_reporter.filename
- )
+ if file_reporter == "python":
+ file_reporter = PythonFileReporter(morf, self)
return file_reporter
@@ -904,7 +908,7 @@ class Coverage(object):
Returns a float, the total percentage covered.
"""
- self._harvest_data()
+ self.get_data()
self.config.from_args(
ignore_errors=ignore_errors, omit=omit, include=include,
show_missing=show_missing, skip_covered=skip_covered,
@@ -926,7 +930,7 @@ class Coverage(object):
See `coverage.report()` for other arguments.
"""
- self._harvest_data()
+ self.get_data()
self.config.from_args(
ignore_errors=ignore_errors, omit=omit, include=include
)
@@ -952,7 +956,7 @@ class Coverage(object):
Returns a float, the total percentage covered.
"""
- self._harvest_data()
+ self.get_data()
self.config.from_args(
ignore_errors=ignore_errors, omit=omit, include=include,
html_dir=directory, extra_css=extra_css, html_title=title,
@@ -976,7 +980,7 @@ class Coverage(object):
Returns a float, the total percentage covered.
"""
- self._harvest_data()
+ self.get_data()
self.config.from_args(
ignore_errors=ignore_errors, omit=omit, include=include,
xml_output=outfile,
@@ -997,7 +1001,7 @@ class Coverage(object):
outfile = open(self.config.xml_output, "w")
file_to_close = outfile
try:
- reporter = XmlReporter(self, self.config, self.file_locator)
+ reporter = XmlReporter(self, self.config)
return reporter.report(morfs, outfile=outfile)
except CoverageException:
delete_file = True
@@ -1014,13 +1018,9 @@ class Coverage(object):
import coverage as covmod
self._init()
- try:
- implementation = platform.python_implementation()
- except AttributeError:
- implementation = "unknown"
ft_plugins = []
- for ft in self.file_tracing_plugins:
+ for ft in self.plugins.file_tracers:
ft_name = ft._coverage_plugin_name
if not ft._coverage_enabled:
ft_name += " (disabled)"
@@ -1029,16 +1029,16 @@ class Coverage(object):
info = [
('version', covmod.__version__),
('coverage', covmod.__file__),
- ('cover_dir', self.cover_dir),
+ ('cover_dirs', self.cover_dirs),
('pylib_dirs', self.pylib_dirs),
('tracer', self.collector.tracer_name()),
- ('file_tracing_plugins', ft_plugins),
+ ('plugins.file_tracers', ft_plugins),
('config_files', self.config.attempted_config_files),
('configs_read', self.config.config_files),
- ('data_path', self.data.filename),
+ ('data_path', self.data_files.filename),
('python', sys.version.replace('\n', '')),
('platform', platform.platform()),
- ('implementation', implementation),
+ ('implementation', platform.python_implementation()),
('executable', sys.executable),
('cwd', os.getcwd()),
('path', sys.path),
@@ -1067,36 +1067,32 @@ class Coverage(object):
return info
-class FileDisposition(object):
- """A simple object for noting a number of details of files to trace."""
- def __init__(self, original_filename):
- self.original_filename = original_filename
- self.canonical_filename = original_filename
- self.source_filename = None
- self.trace = False
- self.reason = ""
- self.file_tracer = None
- self.has_dynamic_filename = False
-
- def __repr__(self):
- ret = "FileDisposition %r" % (self.original_filename,)
- if self.trace:
- ret += " trace"
- else:
- ret += " notrace=%r" % (self.reason,)
- if self.file_tracer:
- ret += " file_tracer=%r" % (self.file_tracer,)
- return "<" + ret + ">"
-
- def debug_message(self):
- """Produce a debugging message explaining the outcome."""
- if self.trace:
- msg = "Tracing %r" % (self.original_filename,)
- if self.file_tracer:
- msg += ": will be traced by %r" % self.file_tracer
- else:
- msg = "Not tracing %r: %s" % (self.original_filename, self.reason)
- return msg
+# FileDisposition "methods": FileDisposition is a pure value object, so it can
+# be implemented in either C or Python. Acting on them is done with these
+# functions.
+
+def _disposition_init(cls, original_filename):
+ """Construct and initialize a new FileDisposition object."""
+ disp = cls()
+ disp.original_filename = original_filename
+ disp.canonical_filename = original_filename
+ disp.source_filename = None
+ disp.trace = False
+ disp.reason = ""
+ disp.file_tracer = None
+ disp.has_dynamic_filename = False
+ return disp
+
+
+def _disposition_debug_msg(disp):
+ """Make a nice debug message of what the FileDisposition is doing."""
+ if disp.trace:
+ msg = "Tracing %r" % (disp.original_filename,)
+ if disp.file_tracer:
+ msg += ": will be traced by %r" % disp.file_tracer
+ else:
+ msg = "Not tracing %r: %s" % (disp.original_filename, disp.reason)
+ return msg
def process_startup():
@@ -1128,7 +1124,7 @@ def process_startup():
# because some virtualenv configurations make the same directory visible
# twice in sys.path. This means that the .pth file will be found twice,
# and executed twice, executing this function twice. We set a global
- # flag (an attribute on this function) to indicate that coverage has
+ # flag (an attribute on this function) to indicate that coverage.py has
# already been started, so we can avoid doing it twice.
#
# https://bitbucket.org/ned/coveragepy/issue/340/keyerror-subpy has more
@@ -1136,7 +1132,7 @@ def process_startup():
if hasattr(process_startup, "done"):
# We've annotated this function before, so we must have already
- # started coverage in this process. Nothing to do.
+ # started coverage.py in this process. Nothing to do.
return
process_startup.done = True
@@ -1144,55 +1140,3 @@ def process_startup():
cov.start()
cov._warn_no_data = False
cov._warn_unimported_source = False
-
-
-# A hack for debugging testing in sub-processes.
-_TEST_NAME_FILE = "" # "/tmp/covtest.txt"
-
-
-class Plugins(object):
- """The currently loaded collection of coverage.py plugins."""
-
- def __init__(self):
- self.order = []
- self.names = {}
-
- @classmethod
- def load_plugins(cls, modules, config):
- """Load plugins from `modules`.
-
- Returns a list of loaded and configured plugins.
-
- """
- plugins = cls()
-
- for module in modules:
- __import__(module)
- mod = sys.modules[module]
-
- plugin_class = getattr(mod, "Plugin", None)
- if plugin_class:
- options = config.get_plugin_options(module)
- plugin = plugin_class(options)
- plugin._coverage_plugin_name = module
- plugin._coverage_enabled = True
- plugins.order.append(plugin)
- plugins.names[module] = plugin
- else:
- raise CoverageException(
- "Plugin module %r didn't define a Plugin class" % module
- )
-
- return plugins
-
- def __nonzero__(self):
- return bool(self.order)
-
- __bool__ = __nonzero__
-
- def __iter__(self):
- return iter(self.order)
-
- def get(self, plugin_name):
- """Return a plugin by name."""
- return self.names[plugin_name]
diff --git a/coverage/ctracer/datastack.c b/coverage/ctracer/datastack.c
new file mode 100644
index 00000000..1d925ad7
--- /dev/null
+++ b/coverage/ctracer/datastack.c
@@ -0,0 +1,42 @@
+/* Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 */
+/* For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt */
+
+#include "util.h"
+#include "datastack.h"
+
+#define STACK_DELTA 100
+
+int
+DataStack_init(Stats *pstats, DataStack *pdata_stack)
+{
+ pdata_stack->depth = -1;
+ pdata_stack->stack = NULL;
+ pdata_stack->alloc = 0;
+ return RET_OK;
+}
+
+void
+DataStack_dealloc(Stats *pstats, DataStack *pdata_stack)
+{
+ PyMem_Free(pdata_stack->stack);
+}
+
+int
+DataStack_grow(Stats *pstats, DataStack *pdata_stack)
+{
+ pdata_stack->depth++;
+ if (pdata_stack->depth >= pdata_stack->alloc) {
+ STATS( pstats->stack_reallocs++; )
+ /* We've outgrown our data_stack array: make it bigger. */
+ int bigger = pdata_stack->alloc + STACK_DELTA;
+ DataStackEntry * bigger_data_stack = PyMem_Realloc(pdata_stack->stack, bigger * sizeof(DataStackEntry));
+ if (bigger_data_stack == NULL) {
+ PyErr_NoMemory();
+ pdata_stack->depth--;
+ return RET_ERROR;
+ }
+ pdata_stack->stack = bigger_data_stack;
+ pdata_stack->alloc = bigger;
+ }
+ return RET_OK;
+}
diff --git a/coverage/ctracer/datastack.h b/coverage/ctracer/datastack.h
new file mode 100644
index 00000000..78f85f7e
--- /dev/null
+++ b/coverage/ctracer/datastack.h
@@ -0,0 +1,45 @@
+/* Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 */
+/* For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt */
+
+#ifndef _COVERAGE_DATASTACK_H
+#define _COVERAGE_DATASTACK_H
+
+#include "util.h"
+#include "stats.h"
+
+/* An entry on the data stack. For each call frame, we need to record all
+ * the information needed for CTracer_handle_line to operate as quickly as
+ * possible. All PyObject* here are borrowed references.
+ */
+typedef struct DataStackEntry {
+ /* The current file_data dictionary. Borrowed, owned by self->data. */
+ PyObject * file_data;
+
+ /* The disposition object for this frame. If collector.py and control.py
+ * are working properly, this will be an instance of CFileDisposition.
+ */
+ PyObject * disposition;
+
+ /* The FileTracer handling this frame, or None if it's Python. */
+ PyObject * file_tracer;
+
+ /* The line number of the last line recorded, for tracing arcs.
+ -1 means there was no previous line, as when entering a code object.
+ */
+ int last_line;
+} DataStackEntry;
+
+/* A data stack is a dynamically allocated vector of DataStackEntry's. */
+typedef struct DataStack {
+ int depth; /* The index of the last-used entry in stack. */
+ int alloc; /* number of entries allocated at stack. */
+ /* The file data at each level, or NULL if not recording. */
+ DataStackEntry * stack;
+} DataStack;
+
+
+int DataStack_init(Stats * pstats, DataStack *pdata_stack);
+void DataStack_dealloc(Stats * pstats, DataStack *pdata_stack);
+int DataStack_grow(Stats * pstats, DataStack *pdata_stack);
+
+#endif /* _COVERAGE_DATASTACK_H */
diff --git a/coverage/ctracer/filedisp.c b/coverage/ctracer/filedisp.c
new file mode 100644
index 00000000..479a2c9f
--- /dev/null
+++ b/coverage/ctracer/filedisp.c
@@ -0,0 +1,85 @@
+/* Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 */
+/* For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt */
+
+#include "util.h"
+#include "filedisp.h"
+
+void
+CFileDisposition_dealloc(CFileDisposition *self)
+{
+ Py_XDECREF(self->original_filename);
+ Py_XDECREF(self->canonical_filename);
+ Py_XDECREF(self->source_filename);
+ Py_XDECREF(self->trace);
+ Py_XDECREF(self->reason);
+ Py_XDECREF(self->file_tracer);
+ Py_XDECREF(self->has_dynamic_filename);
+}
+
+static PyMemberDef
+CFileDisposition_members[] = {
+ { "original_filename", T_OBJECT, offsetof(CFileDisposition, original_filename), 0,
+ PyDoc_STR("") },
+
+ { "canonical_filename", T_OBJECT, offsetof(CFileDisposition, canonical_filename), 0,
+ PyDoc_STR("") },
+
+ { "source_filename", T_OBJECT, offsetof(CFileDisposition, source_filename), 0,
+ PyDoc_STR("") },
+
+ { "trace", T_OBJECT, offsetof(CFileDisposition, trace), 0,
+ PyDoc_STR("") },
+
+ { "reason", T_OBJECT, offsetof(CFileDisposition, reason), 0,
+ PyDoc_STR("") },
+
+ { "file_tracer", T_OBJECT, offsetof(CFileDisposition, file_tracer), 0,
+ PyDoc_STR("") },
+
+ { "has_dynamic_filename", T_OBJECT, offsetof(CFileDisposition, has_dynamic_filename), 0,
+ PyDoc_STR("") },
+
+ { NULL }
+};
+
+PyTypeObject
+CFileDispositionType = {
+ MyType_HEAD_INIT
+ "coverage.CFileDispositionType", /*tp_name*/
+ sizeof(CFileDisposition), /*tp_basicsize*/
+ 0, /*tp_itemsize*/
+ (destructor)CFileDisposition_dealloc, /*tp_dealloc*/
+ 0, /*tp_print*/
+ 0, /*tp_getattr*/
+ 0, /*tp_setattr*/
+ 0, /*tp_compare*/
+ 0, /*tp_repr*/
+ 0, /*tp_as_number*/
+ 0, /*tp_as_sequence*/
+ 0, /*tp_as_mapping*/
+ 0, /*tp_hash */
+ 0, /*tp_call*/
+ 0, /*tp_str*/
+ 0, /*tp_getattro*/
+ 0, /*tp_setattro*/
+ 0, /*tp_as_buffer*/
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
+ "CFileDisposition objects", /* tp_doc */
+ 0, /* tp_traverse */
+ 0, /* tp_clear */
+ 0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ 0, /* tp_iter */
+ 0, /* tp_iternext */
+ 0, /* tp_methods */
+ CFileDisposition_members, /* tp_members */
+ 0, /* tp_getset */
+ 0, /* tp_base */
+ 0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ 0, /* tp_init */
+ 0, /* tp_alloc */
+ 0, /* tp_new */
+};
diff --git a/coverage/ctracer/filedisp.h b/coverage/ctracer/filedisp.h
new file mode 100644
index 00000000..e46d2266
--- /dev/null
+++ b/coverage/ctracer/filedisp.h
@@ -0,0 +1,26 @@
+/* Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 */
+/* For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt */
+
+#ifndef _COVERAGE_FILEDISP_H
+#define _COVERAGE_FILEDISP_H
+
+#include "util.h"
+#include "structmember.h"
+
+typedef struct CFileDisposition {
+ PyObject_HEAD
+
+ PyObject * original_filename;
+ PyObject * canonical_filename;
+ PyObject * source_filename;
+ PyObject * trace;
+ PyObject * reason;
+ PyObject * file_tracer;
+ PyObject * has_dynamic_filename;
+} CFileDisposition;
+
+void CFileDisposition_dealloc(CFileDisposition *self);
+
+PyTypeObject CFileDispositionType;
+
+#endif /* _COVERAGE_FILEDISP_H */
diff --git a/coverage/ctracer/module.c b/coverage/ctracer/module.c
new file mode 100644
index 00000000..76231859
--- /dev/null
+++ b/coverage/ctracer/module.c
@@ -0,0 +1,108 @@
+/* Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 */
+/* For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt */
+
+#include "util.h"
+#include "tracer.h"
+#include "filedisp.h"
+
+/* Module definition */
+
+#define MODULE_DOC PyDoc_STR("Fast coverage tracer.")
+
+#if PY_MAJOR_VERSION >= 3
+
+static PyModuleDef
+moduledef = {
+ PyModuleDef_HEAD_INIT,
+ "coverage.tracer",
+ MODULE_DOC,
+ -1,
+ NULL, /* methods */
+ NULL,
+ NULL, /* traverse */
+ NULL, /* clear */
+ NULL
+};
+
+
+PyObject *
+PyInit_tracer(void)
+{
+ PyObject * mod = PyModule_Create(&moduledef);
+ if (mod == NULL) {
+ return NULL;
+ }
+
+ if (CTracer_intern_strings() < 0) {
+ return NULL;
+ }
+
+ /* Initialize CTracer */
+ CTracerType.tp_new = PyType_GenericNew;
+ if (PyType_Ready(&CTracerType) < 0) {
+ Py_DECREF(mod);
+ return NULL;
+ }
+
+ Py_INCREF(&CTracerType);
+ if (PyModule_AddObject(mod, "CTracer", (PyObject *)&CTracerType) < 0) {
+ Py_DECREF(mod);
+ Py_DECREF(&CTracerType);
+ return NULL;
+ }
+
+ /* Initialize CFileDisposition */
+ CFileDispositionType.tp_new = PyType_GenericNew;
+ if (PyType_Ready(&CFileDispositionType) < 0) {
+ Py_DECREF(mod);
+ Py_DECREF(&CTracerType);
+ return NULL;
+ }
+
+ Py_INCREF(&CFileDispositionType);
+ if (PyModule_AddObject(mod, "CFileDisposition", (PyObject *)&CFileDispositionType) < 0) {
+ Py_DECREF(mod);
+ Py_DECREF(&CTracerType);
+ Py_DECREF(&CFileDispositionType);
+ return NULL;
+ }
+
+ return mod;
+}
+
+#else
+
+void
+inittracer(void)
+{
+ PyObject * mod;
+
+ mod = Py_InitModule3("coverage.tracer", NULL, MODULE_DOC);
+ if (mod == NULL) {
+ return;
+ }
+
+ if (CTracer_intern_strings() < 0) {
+ return;
+ }
+
+ /* Initialize CTracer */
+ CTracerType.tp_new = PyType_GenericNew;
+ if (PyType_Ready(&CTracerType) < 0) {
+ return;
+ }
+
+ Py_INCREF(&CTracerType);
+ PyModule_AddObject(mod, "CTracer", (PyObject *)&CTracerType);
+
+ /* Initialize CFileDisposition */
+ CFileDispositionType.tp_new = PyType_GenericNew;
+ if (PyType_Ready(&CFileDispositionType) < 0) {
+ return;
+ }
+
+ Py_INCREF(&CFileDispositionType);
+ PyModule_AddObject(mod, "CFileDisposition", (PyObject *)&CFileDispositionType);
+}
+
+#endif /* Py3k */
diff --git a/coverage/ctracer/stats.h b/coverage/ctracer/stats.h
new file mode 100644
index 00000000..06b9e85f
--- /dev/null
+++ b/coverage/ctracer/stats.h
@@ -0,0 +1,30 @@
+/* Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 */
+/* For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt */
+
+#ifndef _COVERAGE_STATS_H
+#define _COVERAGE_STATS_H
+
+#include "util.h"
+
+#if COLLECT_STATS
+#define STATS(x) x
+#else
+#define STATS(x)
+#endif
+
+typedef struct Stats {
+#if COLLECT_STATS
+ unsigned int calls;
+ unsigned int lines;
+ unsigned int returns;
+ unsigned int exceptions;
+ unsigned int others;
+ unsigned int new_files;
+ unsigned int missed_returns;
+ unsigned int stack_reallocs;
+ unsigned int errors;
+ unsigned int pycalls;
+#endif
+} Stats;
+
+#endif /* _COVERAGE_STATS_H */
diff --git a/coverage/tracer.c b/coverage/ctracer/tracer.c
index 52543b81..586bddc0 100644
--- a/coverage/tracer.c
+++ b/coverage/ctracer/tracer.c
@@ -1,51 +1,12 @@
-/* C-based Tracer for Coverage. */
+/* Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 */
+/* For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt */
-#include "Python.h"
-#include "structmember.h"
-#include "frameobject.h"
+/* C-based Tracer for coverage.py. */
-/* Compile-time debugging helpers */
-#undef WHAT_LOG /* Define to log the WHAT params in the trace function. */
-#undef TRACE_LOG /* Define to log our bookkeeping. */
-#undef COLLECT_STATS /* Collect counters: stats are printed when tracer is stopped. */
-
-#if COLLECT_STATS
-#define STATS(x) x
-#else
-#define STATS(x)
-#endif
-
-/* Py 2.x and 3.x compatibility */
-
-#if PY_MAJOR_VERSION >= 3
-
-#define MyText_Type PyUnicode_Type
-#define MyText_AS_BYTES(o) PyUnicode_AsASCIIString(o)
-#define MyBytes_AS_STRING(o) PyBytes_AS_STRING(o)
-#define MyText_AsString(o) PyUnicode_AsUTF8(o)
-#define MyText_FromFormat PyUnicode_FromFormat
-#define MyInt_FromInt(i) PyLong_FromLong((long)i)
-#define MyInt_AsInt(o) (int)PyLong_AsLong(o)
-
-#define MyType_HEAD_INIT PyVarObject_HEAD_INIT(NULL, 0)
-
-#else
-
-#define MyText_Type PyString_Type
-#define MyText_AS_BYTES(o) (Py_INCREF(o), o)
-#define MyBytes_AS_STRING(o) PyString_AS_STRING(o)
-#define MyText_AsString(o) PyString_AsString(o)
-#define MyText_FromFormat PyUnicode_FromFormat
-#define MyInt_FromInt(i) PyInt_FromLong((long)i)
-#define MyInt_AsInt(o) (int)PyInt_AsLong(o)
-
-#define MyType_HEAD_INIT PyObject_HEAD_INIT(NULL) 0,
-
-#endif /* Py3k */
-
-/* The values returned to indicate ok or error. */
-#define RET_OK 0
-#define RET_ERROR -1
+#include "util.h"
+#include "datastack.h"
+#include "filedisp.h"
+#include "tracer.h"
/* Python C API helpers. */
@@ -62,135 +23,41 @@ pyint_as_int(PyObject * pyint, int *pint)
}
-/* An entry on the data stack. For each call frame, we need to record all
- * the information needed for CTracer_handle_line to operate as quickly as
- * possible.
- */
-typedef struct {
- /* The current file_data dictionary. Borrowed, owned by self->data. */
- PyObject * file_data;
-
- /* The disposition object for this frame. */
- PyObject * disposition;
-
- /* The FileTracer handling this frame, or None if it's Python. */
- PyObject * file_tracer;
-
- /* The line number of the last line recorded, for tracing arcs.
- -1 means there was no previous line, as when entering a code object.
- */
- int last_line;
-} DataStackEntry;
-
-/* A data stack is a dynamically allocated vector of DataStackEntry's. */
-typedef struct {
- int depth; /* The index of the last-used entry in stack. */
- int alloc; /* number of entries allocated at stack. */
- /* The file data at each level, or NULL if not recording. */
- DataStackEntry * stack;
-} DataStack;
-
-/* The CTracer type. */
-
-typedef struct {
- PyObject_HEAD
-
- /* Python objects manipulated directly by the Collector class. */
- PyObject * should_trace;
- PyObject * check_include;
- PyObject * warn;
- PyObject * concur_id_func;
- PyObject * data;
- PyObject * plugin_data;
- PyObject * should_trace_cache;
- PyObject * arcs;
-
- /* Has the tracer been started? */
- int started;
- /* Are we tracing arcs, or just lines? */
- int tracing_arcs;
-
- /*
- The data stack is a stack of dictionaries. Each dictionary collects
- data for a single source file. The data stack parallels the call stack:
- each call pushes the new frame's file data onto the data stack, and each
- return pops file data off.
-
- The file data is a dictionary whose form depends on the tracing options.
- If tracing arcs, the keys are line number pairs. If not tracing arcs,
- the keys are line numbers. In both cases, the value is irrelevant
- (None).
- */
-
- DataStack data_stack; /* Used if we aren't doing concurrency. */
-
- PyObject * data_stack_index; /* Used if we are doing concurrency. */
- DataStack * data_stacks;
- int data_stacks_alloc;
- int data_stacks_used;
- DataStack * pdata_stack;
+/* Interned strings to speed GetAttr etc. */
- /* The current file's data stack entry, copied from the stack. */
- DataStackEntry cur_entry;
-
- /* The parent frame for the last exception event, to fix missing returns. */
- PyFrameObject * last_exc_back;
- int last_exc_firstlineno;
-
-#if COLLECT_STATS
- struct {
- unsigned int calls;
- unsigned int lines;
- unsigned int returns;
- unsigned int exceptions;
- unsigned int others;
- unsigned int new_files;
- unsigned int missed_returns;
- unsigned int stack_reallocs;
- unsigned int errors;
- } stats;
-#endif /* COLLECT_STATS */
-} CTracer;
+static PyObject *str_trace;
+static PyObject *str_file_tracer;
+static PyObject *str__coverage_enabled;
+static PyObject *str__coverage_plugin;
+static PyObject *str__coverage_plugin_name;
+static PyObject *str_dynamic_source_filename;
+static PyObject *str_line_number_range;
+int
+CTracer_intern_strings()
+{
+ int ret = RET_ERROR;
-#define STACK_DELTA 100
+#define INTERN_STRING(v, s) \
+ v = MyText_InternFromString(s); \
+ if (v == NULL) { \
+ goto error; \
+ }
-static int
-DataStack_init(CTracer *self, DataStack *pdata_stack)
-{
- pdata_stack->depth = -1;
- pdata_stack->stack = NULL;
- pdata_stack->alloc = 0;
- return RET_OK;
-}
+ INTERN_STRING(str_trace, "trace")
+ INTERN_STRING(str_file_tracer, "file_tracer")
+ INTERN_STRING(str__coverage_enabled, "_coverage_enabled")
+ INTERN_STRING(str__coverage_plugin, "_coverage_plugin")
+ INTERN_STRING(str__coverage_plugin_name, "_coverage_plugin_name")
+ INTERN_STRING(str_dynamic_source_filename, "dynamic_source_filename")
+ INTERN_STRING(str_line_number_range, "line_number_range")
-static void
-DataStack_dealloc(CTracer *self, DataStack *pdata_stack)
-{
- PyMem_Free(pdata_stack->stack);
-}
+ ret = RET_OK;
-static int
-DataStack_grow(CTracer *self, DataStack *pdata_stack)
-{
- pdata_stack->depth++;
- if (pdata_stack->depth >= pdata_stack->alloc) {
- STATS( self->stats.stack_reallocs++; )
- /* We've outgrown our data_stack array: make it bigger. */
- int bigger = pdata_stack->alloc + STACK_DELTA;
- DataStackEntry * bigger_data_stack = PyMem_Realloc(pdata_stack->stack, bigger * sizeof(DataStackEntry));
- if (bigger_data_stack == NULL) {
- PyErr_NoMemory();
- pdata_stack->depth--;
- return RET_ERROR;
- }
- pdata_stack->stack = bigger_data_stack;
- pdata_stack->alloc = bigger;
- }
- return RET_OK;
+error:
+ return ret;
}
-
static void CTracer_disable_plugin(CTracer *self, PyObject * disposition);
static int
@@ -199,7 +66,7 @@ CTracer_init(CTracer *self, PyObject *args_unused, PyObject *kwds_unused)
int ret = RET_ERROR;
PyObject * weakref = NULL;
- if (DataStack_init(self, &self->data_stack) < 0) {
+ if (DataStack_init(&self->stats, &self->data_stack) < 0) {
goto error;
}
@@ -207,6 +74,7 @@ CTracer_init(CTracer *self, PyObject *args_unused, PyObject *kwds_unused)
if (weakref == NULL) {
goto error;
}
+ STATS( self->stats.pycalls++; )
self->data_stack_index = PyObject_CallMethod(weakref, "WeakKeyDictionary", NULL);
Py_XDECREF(weakref);
@@ -242,13 +110,13 @@ CTracer_dealloc(CTracer *self)
Py_XDECREF(self->warn);
Py_XDECREF(self->concur_id_func);
Py_XDECREF(self->data);
- Py_XDECREF(self->plugin_data);
+ Py_XDECREF(self->file_tracers);
Py_XDECREF(self->should_trace_cache);
- DataStack_dealloc(self, &self->data_stack);
+ DataStack_dealloc(&self->stats, &self->data_stack);
if (self->data_stacks) {
for (i = 0; i < self->data_stacks_used; i++) {
- DataStack_dealloc(self, self->data_stacks + i);
+ DataStack_dealloc(&self->stats, self->data_stacks + i);
}
PyMem_Free(self->data_stacks);
}
@@ -344,6 +212,7 @@ CTracer_set_pdata_stack(CTracer *self)
if (self->concur_id_func != Py_None) {
int the_index = 0;
+ STATS( self->stats.pycalls++; )
co_obj = PyObject_CallObject(self->concur_id_func, NULL);
if (co_obj == NULL) {
goto error;
@@ -373,7 +242,7 @@ CTracer_set_pdata_stack(CTracer *self)
self->data_stacks = bigger_stacks;
self->data_stacks_alloc = bigger;
}
- DataStack_init(self, &self->data_stacks[the_index]);
+ DataStack_init(&self->stats, &self->data_stacks[the_index]);
}
else {
if (pyint_as_int(stack_index, &the_index) < 0) {
@@ -450,16 +319,19 @@ CTracer_handle_call(CTracer *self, PyFrameObject *frame)
int ret2;
/* Owned references that we clean up at the very end of the function. */
- PyObject * tracename = NULL;
PyObject * disposition = NULL;
- PyObject * disp_trace = NULL;
- PyObject * file_tracer = NULL;
PyObject * plugin = NULL;
PyObject * plugin_name = NULL;
- PyObject * has_dynamic_filename = NULL;
+ PyObject * next_tracename = NULL;
/* Borrowed references. */
PyObject * filename = NULL;
+ PyObject * disp_trace = NULL;
+ PyObject * tracename = NULL;
+ PyObject * file_tracer = NULL;
+ PyObject * has_dynamic_filename = NULL;
+
+ CFileDisposition * pdisp;
STATS( self->stats.calls++; )
@@ -467,7 +339,7 @@ CTracer_handle_call(CTracer *self, PyFrameObject *frame)
if (CTracer_set_pdata_stack(self) < 0) {
goto error;
}
- if (DataStack_grow(self, self->pdata_stack) < 0) {
+ if (DataStack_grow(&self->stats, self->pdata_stack) < 0) {
goto error;
}
@@ -482,8 +354,10 @@ CTracer_handle_call(CTracer *self, PyFrameObject *frame)
goto error;
}
STATS( self->stats.new_files++; )
+
/* We've never considered this file before. */
/* Ask should_trace about it. */
+ STATS( self->stats.pycalls++; )
disposition = PyObject_CallFunctionObjArgs(self->should_trace, filename, frame, NULL);
if (disposition == NULL) {
/* An error occurred inside should_trace. */
@@ -497,40 +371,48 @@ CTracer_handle_call(CTracer *self, PyFrameObject *frame)
Py_INCREF(disposition);
}
- disp_trace = PyObject_GetAttrString(disposition, "trace");
- if (disp_trace == NULL) {
- goto error;
+ if (disposition == Py_None) {
+ /* A later check_include returned false, so don't trace it. */
+ disp_trace = Py_False;
+ }
+ else {
+ /* The object we got is a CFileDisposition, use it efficiently. */
+ pdisp = (CFileDisposition *) disposition;
+ disp_trace = pdisp->trace;
+ if (disp_trace == NULL) {
+ goto error;
+ }
}
if (disp_trace == Py_True) {
/* If tracename is a string, then we're supposed to trace. */
- tracename = PyObject_GetAttrString(disposition, "source_filename");
+ tracename = pdisp->source_filename;
if (tracename == NULL) {
goto error;
}
- file_tracer = PyObject_GetAttrString(disposition, "file_tracer");
+ file_tracer = pdisp->file_tracer;
if (file_tracer == NULL) {
goto error;
}
if (file_tracer != Py_None) {
- plugin = PyObject_GetAttrString(file_tracer, "_coverage_plugin");
+ plugin = PyObject_GetAttr(file_tracer, str__coverage_plugin);
if (plugin == NULL) {
goto error;
}
- plugin_name = PyObject_GetAttrString(plugin, "_coverage_plugin_name");
+ plugin_name = PyObject_GetAttr(plugin, str__coverage_plugin_name);
if (plugin_name == NULL) {
goto error;
}
}
- has_dynamic_filename = PyObject_GetAttrString(disposition, "has_dynamic_filename");
+ has_dynamic_filename = pdisp->has_dynamic_filename;
if (has_dynamic_filename == NULL) {
goto error;
}
if (has_dynamic_filename == Py_True) {
- PyObject * next_tracename = NULL;
- next_tracename = PyObject_CallMethod(
- file_tracer, "dynamic_source_filename",
- "OO", tracename, frame
+ STATS( self->stats.pycalls++; )
+ next_tracename = PyObject_CallMethodObjArgs(
+ file_tracer, str_dynamic_source_filename,
+ tracename, frame, NULL
);
if (next_tracename == NULL) {
/* An exception from the function. Alert the user with a
@@ -540,37 +422,41 @@ CTracer_handle_call(CTracer *self, PyFrameObject *frame)
/* Because we handled the error, goto ok. */
goto ok;
}
- Py_DECREF(tracename);
tracename = next_tracename;
if (tracename != Py_None) {
/* Check the dynamic source filename against the include rules. */
PyObject * included = NULL;
+ int should_include;
included = PyDict_GetItem(self->should_trace_cache, tracename);
if (included == NULL) {
+ PyObject * should_include_bool;
if (PyErr_Occurred()) {
goto error;
}
STATS( self->stats.new_files++; )
- included = PyObject_CallFunctionObjArgs(self->check_include, tracename, frame, NULL);
- if (included == NULL) {
+ STATS( self->stats.pycalls++; )
+ should_include_bool = PyObject_CallFunctionObjArgs(self->check_include, tracename, frame, NULL);
+ if (should_include_bool == NULL) {
goto error;
}
- if (PyDict_SetItem(self->should_trace_cache, tracename, included) < 0) {
+ should_include = (should_include_bool == Py_True);
+ Py_DECREF(should_include_bool);
+ if (PyDict_SetItem(self->should_trace_cache, tracename, should_include ? disposition : Py_None) < 0) {
goto error;
}
}
- if (included != Py_True) {
- Py_DECREF(tracename);
+ else {
+ should_include = (included != Py_None);
+ }
+ if (!should_include) {
tracename = Py_None;
- Py_INCREF(tracename);
}
}
}
}
else {
tracename = Py_None;
- Py_INCREF(tracename);
}
if (tracename != Py_None) {
@@ -592,7 +478,7 @@ CTracer_handle_call(CTracer *self, PyFrameObject *frame)
/* If the disposition mentions a plugin, record that. */
if (file_tracer != Py_None) {
- ret2 = PyDict_SetItem(self->plugin_data, tracename, plugin_name);
+ ret2 = PyDict_SetItem(self->file_tracers, tracename, plugin_name);
if (ret2 < 0) {
goto error;
}
@@ -614,19 +500,21 @@ CTracer_handle_call(CTracer *self, PyFrameObject *frame)
}
self->cur_entry.disposition = disposition;
- self->cur_entry.last_line = -1;
+
+ /* A call event is really a "start frame" event, and can happen for
+ * re-entering a generator also. f_lasti is -1 for a true call, and a
+ * real byte offset for a generator re-entry.
+ */
+ self->cur_entry.last_line = (frame->f_lasti < 0) ? -1 : frame->f_lineno;
ok:
ret = RET_OK;
error:
- Py_XDECREF(tracename);
+ Py_XDECREF(next_tracename);
Py_XDECREF(disposition);
- Py_XDECREF(disp_trace);
- Py_XDECREF(file_tracer);
Py_XDECREF(plugin);
Py_XDECREF(plugin_name);
- Py_XDECREF(has_dynamic_filename);
return ret;
}
@@ -641,7 +529,9 @@ CTracer_disable_plugin(CTracer *self, PyObject * disposition)
PyObject * msg = NULL;
PyObject * ignored = NULL;
- file_tracer = PyObject_GetAttrString(disposition, "file_tracer");
+ PyErr_Print();
+
+ file_tracer = PyObject_GetAttr(disposition, str_file_tracer);
if (file_tracer == NULL) {
goto error;
}
@@ -649,33 +539,32 @@ CTracer_disable_plugin(CTracer *self, PyObject * disposition)
/* This shouldn't happen... */
goto ok;
}
- plugin = PyObject_GetAttrString(file_tracer, "_coverage_plugin");
+ plugin = PyObject_GetAttr(file_tracer, str__coverage_plugin);
if (plugin == NULL) {
goto error;
}
- plugin_name = PyObject_GetAttrString(plugin, "_coverage_plugin_name");
+ plugin_name = PyObject_GetAttr(plugin, str__coverage_plugin_name);
if (plugin_name == NULL) {
goto error;
}
msg = MyText_FromFormat(
- "Disabling plugin '%s' due to an exception:",
+ "Disabling plugin '%s' due to previous exception",
MyText_AsString(plugin_name)
);
if (msg == NULL) {
goto error;
}
+ STATS( self->stats.pycalls++; )
ignored = PyObject_CallFunctionObjArgs(self->warn, msg, NULL);
if (ignored == NULL) {
goto error;
}
- PyErr_Print();
-
/* Disable the plugin for future files, and stop tracing this file. */
- if (PyObject_SetAttrString(plugin, "_coverage_enabled", Py_False) < 0) {
+ if (PyObject_SetAttr(plugin, str__coverage_enabled, Py_False) < 0) {
goto error;
}
- if (PyObject_SetAttrString(disposition, "trace", Py_False) < 0) {
+ if (PyObject_SetAttr(disposition, str_trace, Py_False) < 0) {
goto error;
}
@@ -745,7 +634,8 @@ CTracer_handle_line(CTracer *self, PyFrameObject *frame)
/* We're tracing in this frame: record something. */
if (self->cur_entry.file_tracer != Py_None) {
PyObject * from_to = NULL;
- from_to = PyObject_CallMethod(self->cur_entry.file_tracer, "line_number_range", "O", frame);
+ STATS( self->stats.pycalls++; )
+ from_to = PyObject_CallMethodObjArgs(self->cur_entry.file_tracer, str_line_number_range, frame, NULL);
if (from_to == NULL) {
goto error;
}
@@ -808,9 +698,23 @@ CTracer_handle_return(CTracer *self, PyFrameObject *frame)
}
if (self->pdata_stack->depth >= 0) {
if (self->tracing_arcs && self->cur_entry.file_data) {
- int first = frame->f_code->co_firstlineno;
- if (CTracer_record_pair(self, self->cur_entry.last_line, -first) < 0) {
- goto error;
+ /* Need to distinguish between RETURN_VALUE and YIELD_VALUE. Read
+ * the current bytecode to see what it is. In unusual circumstances
+ * (Cython code), co_code can be the empty string, so range-check
+ * f_lasti before reading the byte.
+ */
+ int bytecode = RETURN_VALUE;
+ PyObject * pCode = frame->f_code->co_code;
+ int lasti = frame->f_lasti;
+
+ if (lasti < MyBytes_GET_SIZE(pCode)) {
+ bytecode = MyBytes_AS_STRING(pCode)[lasti];
+ }
+ if (bytecode != YIELD_VALUE) {
+ int first = frame->f_code->co_firstlineno;
+ if (CTracer_record_pair(self, self->cur_entry.last_line, -first) < 0) {
+ goto error;
+ }
}
}
@@ -1031,7 +935,7 @@ CTracer_get_stats(CTracer *self)
{
#if COLLECT_STATS
return Py_BuildValue(
- "{sI,sI,sI,sI,sI,sI,sI,sI,si,sI}",
+ "{sI,sI,sI,sI,sI,sI,sI,sI,si,sI,sI}",
"calls", self->stats.calls,
"lines", self->stats.lines,
"returns", self->stats.returns,
@@ -1041,7 +945,8 @@ CTracer_get_stats(CTracer *self)
"missed_returns", self->stats.missed_returns,
"stack_reallocs", self->stats.stack_reallocs,
"stack_alloc", self->pdata_stack->alloc,
- "errors", self->stats.errors
+ "errors", self->stats.errors,
+ "pycalls", self->stats.pycalls
);
#else
Py_RETURN_NONE;
@@ -1065,7 +970,7 @@ CTracer_members[] = {
{ "data", T_OBJECT, offsetof(CTracer, data), 0,
PyDoc_STR("The raw dictionary of trace data.") },
- { "plugin_data", T_OBJECT, offsetof(CTracer, plugin_data), 0,
+ { "file_tracers", T_OBJECT, offsetof(CTracer, file_tracers), 0,
PyDoc_STR("Mapping from filename to plugin name.") },
{ "should_trace_cache", T_OBJECT, offsetof(CTracer, should_trace_cache), 0,
@@ -1091,7 +996,7 @@ CTracer_methods[] = {
{ NULL }
};
-static PyTypeObject
+PyTypeObject
CTracerType = {
MyType_HEAD_INIT
"coverage.CTracer", /*tp_name*/
@@ -1132,70 +1037,3 @@ CTracerType = {
0, /* tp_alloc */
0, /* tp_new */
};
-
-/* Module definition */
-
-#define MODULE_DOC PyDoc_STR("Fast coverage tracer.")
-
-#if PY_MAJOR_VERSION >= 3
-
-static PyModuleDef
-moduledef = {
- PyModuleDef_HEAD_INIT,
- "coverage.tracer",
- MODULE_DOC,
- -1,
- NULL, /* methods */
- NULL,
- NULL, /* traverse */
- NULL, /* clear */
- NULL
-};
-
-
-PyObject *
-PyInit_tracer(void)
-{
- PyObject * mod = PyModule_Create(&moduledef);
- if (mod == NULL) {
- return NULL;
- }
-
- CTracerType.tp_new = PyType_GenericNew;
- if (PyType_Ready(&CTracerType) < 0) {
- Py_DECREF(mod);
- return NULL;
- }
-
- Py_INCREF(&CTracerType);
- if (PyModule_AddObject(mod, "CTracer", (PyObject *)&CTracerType) < 0) {
- Py_DECREF(mod);
- Py_DECREF(&CTracerType);
- return NULL;
- }
-
- return mod;
-}
-
-#else
-
-void
-inittracer(void)
-{
- PyObject * mod;
-
- mod = Py_InitModule3("coverage.tracer", NULL, MODULE_DOC);
- if (mod == NULL) {
- return;
- }
-
- CTracerType.tp_new = PyType_GenericNew;
- if (PyType_Ready(&CTracerType) < 0) {
- return;
- }
-
- Py_INCREF(&CTracerType);
- PyModule_AddObject(mod, "CTracer", (PyObject *)&CTracerType);
-}
-
-#endif /* Py3k */
diff --git a/coverage/ctracer/tracer.h b/coverage/ctracer/tracer.h
new file mode 100644
index 00000000..a5b80cd7
--- /dev/null
+++ b/coverage/ctracer/tracer.h
@@ -0,0 +1,68 @@
+/* Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 */
+/* For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt */
+
+#ifndef _COVERAGE_TRACER_H
+#define _COVERAGE_TRACER_H
+
+#include "util.h"
+#include "structmember.h"
+#include "frameobject.h"
+#include "opcode.h"
+
+#include "datastack.h"
+
+/* The CTracer type. */
+
+typedef struct CTracer {
+ PyObject_HEAD
+
+ /* Python objects manipulated directly by the Collector class. */
+ PyObject * should_trace;
+ PyObject * check_include;
+ PyObject * warn;
+ PyObject * concur_id_func;
+ PyObject * data;
+ PyObject * file_tracers;
+ PyObject * should_trace_cache;
+ PyObject * arcs;
+
+ /* Has the tracer been started? */
+ int started;
+ /* Are we tracing arcs, or just lines? */
+ int tracing_arcs;
+
+ /*
+ The data stack is a stack of dictionaries. Each dictionary collects
+ data for a single source file. The data stack parallels the call stack:
+ each call pushes the new frame's file data onto the data stack, and each
+ return pops file data off.
+
+ The file data is a dictionary whose form depends on the tracing options.
+ If tracing arcs, the keys are line number pairs. If not tracing arcs,
+ the keys are line numbers. In both cases, the value is irrelevant
+ (None).
+ */
+
+ DataStack data_stack; /* Used if we aren't doing concurrency. */
+
+ PyObject * data_stack_index; /* Used if we are doing concurrency. */
+ DataStack * data_stacks;
+ int data_stacks_alloc;
+ int data_stacks_used;
+ DataStack * pdata_stack;
+
+ /* The current file's data stack entry, copied from the stack. */
+ DataStackEntry cur_entry;
+
+ /* The parent frame for the last exception event, to fix missing returns. */
+ PyFrameObject * last_exc_back;
+ int last_exc_firstlineno;
+
+ Stats stats;
+} CTracer;
+
+int CTracer_intern_strings();
+
+PyTypeObject CTracerType;
+
+#endif /* _COVERAGE_TRACER_H */
diff --git a/coverage/ctracer/util.h b/coverage/ctracer/util.h
new file mode 100644
index 00000000..bb3ad5a3
--- /dev/null
+++ b/coverage/ctracer/util.h
@@ -0,0 +1,52 @@
+/* Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 */
+/* For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt */
+
+#ifndef _COVERAGE_UTIL_H
+#define _COVERAGE_UTIL_H
+
+#include <Python.h>
+
+/* Compile-time debugging helpers */
+#undef WHAT_LOG /* Define to log the WHAT params in the trace function. */
+#undef TRACE_LOG /* Define to log our bookkeeping. */
+#undef COLLECT_STATS /* Collect counters: stats are printed when tracer is stopped. */
+
+/* Py 2.x and 3.x compatibility */
+
+#if PY_MAJOR_VERSION >= 3
+
+#define MyText_Type PyUnicode_Type
+#define MyText_AS_BYTES(o) PyUnicode_AsASCIIString(o)
+#define MyBytes_GET_SIZE(o) PyBytes_GET_SIZE(o)
+#define MyBytes_AS_STRING(o) PyBytes_AS_STRING(o)
+#define MyText_AsString(o) PyUnicode_AsUTF8(o)
+#define MyText_FromFormat PyUnicode_FromFormat
+#define MyInt_FromInt(i) PyLong_FromLong((long)i)
+#define MyInt_AsInt(o) (int)PyLong_AsLong(o)
+#define MyText_InternFromString(s) \
+ PyUnicode_InternFromString(s)
+
+#define MyType_HEAD_INIT PyVarObject_HEAD_INIT(NULL, 0)
+
+#else
+
+#define MyText_Type PyString_Type
+#define MyText_AS_BYTES(o) (Py_INCREF(o), o)
+#define MyBytes_GET_SIZE(o) PyString_GET_SIZE(o)
+#define MyBytes_AS_STRING(o) PyString_AS_STRING(o)
+#define MyText_AsString(o) PyString_AsString(o)
+#define MyText_FromFormat PyUnicode_FromFormat
+#define MyInt_FromInt(i) PyInt_FromLong((long)i)
+#define MyInt_AsInt(o) (int)PyInt_AsLong(o)
+#define MyText_InternFromString(s) \
+ PyString_InternFromString(s)
+
+#define MyType_HEAD_INIT PyObject_HEAD_INIT(NULL) 0,
+
+#endif /* Py3k */
+
+/* The values returned to indicate ok or error. */
+#define RET_OK 0
+#define RET_ERROR -1
+
+#endif /* _COVERAGE_UTIL_H */
diff --git a/coverage/data.py b/coverage/data.py
index bfe4823a..0fa2c878 100644
--- a/coverage/data.py
+++ b/coverage/data.py
@@ -1,298 +1,664 @@
-"""Coverage data for Coverage."""
+# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
+# For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt
+"""Coverage data for coverage.py."""
+
+import glob
+import json
import os
+import os.path
+import random
+import socket
-from coverage.backward import iitems, pickle
-from coverage.files import PathAliases, abs_file
-from coverage.misc import file_be_gone
+from coverage import env
+from coverage.backward import iitems, string_class
+from coverage.debug import _TEST_NAME_FILE, pretty_data
+from coverage.files import PathAliases
+from coverage.misc import CoverageException, file_be_gone
class CoverageData(object):
"""Manages collected coverage data, including file storage.
- The data file format is a pickled dict, with these keys:
+ This class is the public supported API to the data coverage.py collects
+ during program execution. It includes information about what code was
+ executed.
- * collector: a string identifying the collecting software
+ .. note::
- * lines: a dict mapping filenames to sorted lists of line numbers
- executed:
- { 'file1': [17,23,45], 'file2': [1,2,3], ... }
+ The file format is not documented or guaranteed. It will change in
+ the future, in possibly complicated ways. Use this API to avoid
+ disruption.
- * arcs: a dict mapping filenames to sorted lists of line number pairs:
- { 'file1': [(17,23), (17,25), (25,26)], ... }
+ There are a number of kinds of data that can be collected:
- * plugins: a dict mapping filenames to plugin names:
- { 'file1': "django.coverage", ... }
- # TODO: how to handle the difference between a plugin module
- # name, and the class in the module?
+ * **lines**: the line numbers of source lines that were executed.
+ These are always available.
- """
+ * **arcs**: pairs of source and destination line numbers for transitions
+ between source lines. These are only available if branch coverage was
+ used.
- def __init__(self, basename=None, collector=None, debug=None):
- """Create a CoverageData.
+ * **file tracer names**: the module names of the file tracer plugins that
+ handled each file in the data.
- `basename` is the name of the file to use for storing data.
+ * **run information**: information about the program execution. This is
+ written during "coverage run", and then accumulated during "coverage
+ combine".
- `collector` is a string describing the coverage measurement software.
+ To read a coverage.py data file, use :meth:`read_file`, or :meth:`read` if
+ you have an already-opened file. You can then access the line, arc, or
+ file tracer data with :meth:`lines`, :meth:`arcs`, or :meth:`file_tracer`.
+ Run information is available with :meth:`run_infos`.
- `debug` is a `DebugControl` object for writing debug messages.
+ The :meth:`has_arcs` method indicates whether arc data is available. You
+ can get a list of the files in the data with :meth:`measured_files`.
+ A summary of the line data is available from :meth:`line_counts`. As with
+ most Python containers, you can determine if there is any data at all by
+ using this object as a boolean value.
- """
- self.collector = collector or 'unknown'
- self.debug = debug
- self.use_file = True
+ Most data files will be created by coverage.py itself, but you can use
+ methods here to create data files if you like. The :meth:`set_lines`,
+ :meth:`set_arcs`, and :meth:`set_file_tracers` methods add data, in ways
+ that are convenient for coverage.py. The :meth:`add_run_info` method adds
+ key-value pairs to the run information.
- # Construct the filename that will be used for data file storage, if we
- # ever do any file storage.
- self.filename = basename or ".coverage"
- self.filename = os.path.abspath(self.filename)
+ To add a file without any measured data, use :meth:`touch_file`.
+
+ You write to a named file with :meth:`write_file`, or to an already opened
+ file with :meth:`write`.
+
+ You can clear the data in memory with :meth:`erase`. Two data collections
+ can be combined by using :meth:`update` on one `CoverageData`, passing it
+ the other.
+
+ """
+
+ # TODO: case-sensitivity in file names in these methods.
+
+ # The data file format is JSON, with these keys:
+ #
+ # * lines: a dict mapping filenames to lists of line numbers
+ # executed::
+ #
+ # { "file1": [17,23,45], "file2": [1,2,3], ... }
+ #
+ # * arcs: a dict mapping filenames to lists of line number pairs::
+ #
+ # { "file1": [[17,23], [17,25], [25,26]], ... }
+ #
+ # * file_tracers: a dict mapping filenames to plugin names::
+ #
+ # { "file1": "django.coverage", ... }
+ #
+ # * runs: a list of dicts of information about the coverage.py runs
+ # contributing to the data::
+ #
+ # [ { "briefsys": "CPython 2.7.10 Darwin" }, ... ]
+ #
+ # Only one of `lines` or `arcs` will be present: with branch coverage, data
+ # is stored as arcs. Without branch coverage, it is stored as lines. The
+ # line data is easily recovered from the arcs: it is all the first elements
+ # of the pairs that are greater than zero.
+
+ def __init__(self, debug=None):
+ """Create a CoverageData.
+
+ `debug` is a `DebugControl` object for writing debug messages.
+
+ """
+ self._debug = debug
# A map from canonical Python source file name to a dictionary in
# which there's an entry for each line number that has been
# executed:
#
- # {
- # 'filename1.py': { 12: None, 47: None, ... },
- # ...
- # }
+ # { 'filename1.py': [12, 47, 1001], ... }
#
- self.lines = {}
+ self._lines = {}
# A map from canonical Python source file name to a dictionary with an
# entry for each pair of line numbers forming an arc:
#
- # {
- # 'filename1.py': { (12,14): None, (47,48): None, ... },
- # ...
- # }
+ # { 'filename1.py': [(12,14), (47,48), ... ], ... }
#
- self.arcs = {}
+ self._arcs = {}
# A map from canonical source file name to a plugin module name:
#
- # {
- # 'filename1.py': 'django.coverage',
- # ...
- # }
- self.plugins = {}
-
- def usefile(self, use_file=True):
- """Set whether or not to use a disk file for data."""
- self.use_file = use_file
-
- def read(self):
- """Read coverage data from the coverage data file (if it exists)."""
- if self.use_file:
- self.lines, self.arcs, self.plugins = self._read_file(self.filename)
- else:
- self.lines, self.arcs, self.plugins = {}, {}, {}
+ # { 'filename1.py': 'django.coverage', ... }
+ #
+ self._file_tracers = {}
- def write(self, suffix=None):
- """Write the collected coverage data to a file.
+ # A list of dicts of information about the coverage.py runs.
+ self._runs = []
- `suffix` is a suffix to append to the base file name. This can be used
- for multiple or parallel execution, so that many coverage data files
- can exist simultaneously. A dot will be used to join the base name and
- the suffix.
+ ##
+ ## Reading data
+ ##
+
+ def has_arcs(self):
+ """Does this data have arcs?
+
+ Arc data is only available if branch coverage was used during
+ collection.
+
+ Returns a boolean.
"""
- if self.use_file:
- filename = self.filename
- if suffix:
- filename += "." + suffix
- self.write_file(filename)
+ return self._has_arcs()
- def erase(self):
- """Erase the data, both in this object, and from its file storage."""
- if self.use_file:
- if self.filename:
- file_be_gone(self.filename)
- self.lines = {}
- self.arcs = {}
- self.plugins = {}
-
- def line_data(self):
- """Return the map from filenames to lists of line numbers executed."""
- return dict(
- (f, sorted(lmap.keys())) for f, lmap in iitems(self.lines)
- )
+ def lines(self, filename):
+ """Get the list of lines executed for a file.
- def arc_data(self):
- """Return the map from filenames to lists of line number pairs."""
- return dict(
- (f, sorted(amap.keys())) for f, amap in iitems(self.arcs)
- )
+ If the file was not measured, returns None. A file might be measured,
+ and have no lines executed, in which case an empty list is returned.
- def plugin_data(self):
- return self.plugins
+ If the file was executed, returns a list of integers, the line numbers
+ executed in the file. The list is in no particular order.
- def write_file(self, filename):
- """Write the coverage data to `filename`."""
+ """
+ if self._arcs:
+ if filename in self._arcs:
+ return [s for s, __ in self._arcs[filename] if s > 0]
+ else:
+ if filename in self._lines:
+ return self._lines[filename]
+ return None
- # Create the file data.
- data = {}
+ def arcs(self, filename):
+ """Get the list of arcs executed for a file.
- data['lines'] = self.line_data()
- arcs = self.arc_data()
- if arcs:
- data['arcs'] = arcs
+ If the file was not measured, returns None. A file might be measured,
+ and have no arcs executed, in which case an empty list is returned.
- if self.collector:
- data['collector'] = self.collector
+ If the file was executed, returns a list of 2-tuples of integers. Each
+ pair is a starting line number and an ending line number for a
+ transition from one line to another. The list is in no particular
+ order.
- data['plugins'] = self.plugins
+ Negative numbers have special meaning. If the starting line number is
+ -N, it represents an entry to the code object that starts at line N.
+ If the ending ling number is -N, it's an exit from the code object that
+ starts at line N.
- if self.debug and self.debug.should('dataio'):
- self.debug.write("Writing data to %r" % (filename,))
+ """
+ if filename in self._arcs:
+ return self._arcs[filename]
+ return None
- # Write the pickle to the file.
- with open(filename, 'wb') as fdata:
- pickle.dump(data, fdata, 2)
+ def file_tracer(self, filename):
+ """Get the plugin name of the file tracer for a file.
- def read_file(self, filename):
- """Read the coverage data from `filename`."""
- self.lines, self.arcs, self.plugins = self._read_file(filename)
+ Returns the name of the plugin that handles this file. If the file was
+ measured, but didn't use a plugin, then "" is returned. If the file
+ was not measured, then None is returned.
- def raw_data(self, filename):
- """Return the raw pickled data from `filename`."""
- if self.debug and self.debug.should('dataio'):
- self.debug.write("Reading data from %r" % (filename,))
- with open(filename, 'rb') as fdata:
- data = pickle.load(fdata)
- return data
+ """
+ # Because the vast majority of files involve no plugin, we don't store
+ # them explicitly in self._file_tracers. Check the measured data
+ # instead to see if it was a known file with no plugin.
+ if filename in (self._arcs or self._lines):
+ return self._file_tracers.get(filename, "")
+ return None
- def _read_file(self, filename):
- """Return the stored coverage data from the given file.
+ def run_infos(self):
+ """Return the list of dicts of run information.
- Returns three values, suitable for assigning to `self.lines`,
- `self.arcs`, and `self.plugins`.
+ For data collected during a single run, this will be a one-element
+ list. If data has been combined, there will be one element for each
+ original data file.
"""
- lines = {}
- arcs = {}
- plugins = {}
- try:
- data = self.raw_data(filename)
- if isinstance(data, dict):
- # Unpack the 'lines' item.
- lines = dict([
- (f, dict.fromkeys(linenos, None))
- for f, linenos in iitems(data.get('lines', {}))
- ])
- # Unpack the 'arcs' item.
- arcs = dict([
- (f, dict.fromkeys(arcpairs, None))
- for f, arcpairs in iitems(data.get('arcs', {}))
- ])
- plugins = data.get('plugins', {})
- except Exception:
- pass
- return lines, arcs, plugins
+ return self._runs
- def combine_parallel_data(self, aliases=None):
- """Combine a number of data files together.
+ def measured_files(self):
+ """A list of all files that had been measured."""
+ return list(self._arcs or self._lines)
- Treat `self.filename` as a file prefix, and combine the data from all
- of the data files starting with that prefix plus a dot.
+ def line_counts(self, fullpath=False):
+ """Return a dict summarizing the line coverage data.
- If `aliases` is provided, it's a `PathAliases` object that is used to
- re-map paths to match the local machine's.
+ Keys are based on the filenames, and values are the number of executed
+ lines. If `fullpath` is true, then the keys are the full pathnames of
+ the files, otherwise they are the basenames of the files.
+
+ Returns a dict mapping filenames to counts of lines.
"""
- aliases = aliases or PathAliases()
- data_dir, local = os.path.split(self.filename)
- localdot = local + '.'
- for f in os.listdir(data_dir or '.'):
- if f.startswith(localdot):
- full_path = os.path.join(data_dir, f)
- new_lines, new_arcs, new_plugins = self._read_file(full_path)
- for filename, file_data in iitems(new_lines):
- filename = aliases.map(filename)
- self.lines.setdefault(filename, {}).update(file_data)
- for filename, file_data in iitems(new_arcs):
- filename = aliases.map(filename)
- self.arcs.setdefault(filename, {}).update(file_data)
- self.plugins.update(new_plugins)
- if f != local:
- os.remove(full_path)
-
- def add_line_data(self, line_data):
+ summ = {}
+ if fullpath:
+ filename_fn = lambda f: f
+ else:
+ filename_fn = os.path.basename
+ for filename in self.measured_files():
+ summ[filename_fn(filename)] = len(self.lines(filename))
+ return summ
+
+ def __nonzero__(self):
+ return bool(self._lines) or bool(self._arcs)
+
+ __bool__ = __nonzero__
+
+ def read(self, file_obj):
+ """Read the coverage data from the given file object.
+
+ Should only be used on an empty CoverageData object.
+
+ """
+ data = self._read_raw_data(file_obj)
+
+ self._lines = data.get('lines', {})
+ self._arcs = dict(
+ (fname, [tuple(pair) for pair in arcs])
+ for fname, arcs in iitems(data.get('arcs', {}))
+ )
+ self._file_tracers = data.get('file_tracers', {})
+ self._runs = data.get('runs', [])
+
+ self._validate()
+
+ def read_file(self, filename):
+ """Read the coverage data from `filename` into this object."""
+ if self._debug and self._debug.should('dataio'):
+ self._debug.write("Reading data from %r" % (filename,))
+ try:
+ with self._open_for_reading(filename) as f:
+ self.read(f)
+ except Exception as exc:
+ raise CoverageException(
+ "Couldn't read data from '%s': %s: %s" % (
+ filename, exc.__class__.__name__, exc,
+ )
+ )
+
+ _GO_AWAY = "!coverage.py: This is a private format, don't read it directly!"
+
+ @classmethod
+ def _open_for_reading(cls, filename):
+ """Open a file appropriately for reading data."""
+ f = open(filename, "r")
+ try:
+ go_away = f.read(len(cls._GO_AWAY))
+ if go_away != cls._GO_AWAY:
+ raise CoverageException("Doesn't seem to be a coverage.py data file")
+ except Exception:
+ f.close()
+ raise
+ return f
+
+ @classmethod
+ def _read_raw_data(cls, file_obj):
+ """Read the raw data from a file object."""
+ return json.load(file_obj)
+
+ @classmethod
+ def _read_raw_data_file(cls, filename):
+ """Read the raw data from a file, for debugging."""
+ with cls._open_for_reading(filename) as f:
+ return cls._read_raw_data(f)
+
+ ##
+ ## Writing data
+ ##
+
+ def set_lines(self, line_data):
"""Add executed line data.
- `line_data` is { filename: { lineno: None, ... }, ...}
+ `line_data` is a dictionary mapping filenames to dictionaries::
+
+ { filename: { lineno: None, ... }, ...}
+
+ Do not call this more than once, it will not update data, it only sets
+ data.
"""
+ if self._has_arcs():
+ raise CoverageException("Can't add lines to existing arc data")
+
for filename, linenos in iitems(line_data):
- self.lines.setdefault(abs_file(filename), {}).update(linenos)
+ self._lines[filename] = list(linenos)
+
+ self._validate()
- def add_arc_data(self, arc_data):
+ def set_arcs(self, arc_data):
"""Add measured arc data.
`arc_data` is { filename: { (l1,l2): None, ... }, ...}
+ Do not call this more than once, it will not update data, it only sets
+ data.
+
"""
+ if self._has_lines():
+ raise CoverageException("Can't add arcs to existing line data")
+
for filename, arcs in iitems(arc_data):
- self.arcs.setdefault(abs_file(filename), {}).update(arcs)
+ self._arcs[filename] = list(arcs)
+
+ self._validate()
- def add_plugin_data(self, plugin_data):
- for filename, plugin_name in iitems(plugin_data):
- self.plugins[abs_file(filename)] = plugin_name
+ def set_file_tracers(self, file_tracers):
+ """Add per-file plugin information.
+
+ `file_tracers` is { filename: plugin_name, ... }
+
+ """
+ existing_files = self._arcs or self._lines
+ for filename, plugin_name in iitems(file_tracers):
+ if filename not in existing_files:
+ raise CoverageException(
+ "Can't add file tracer data for unmeasured file '%s'" % (filename,)
+ )
+ existing_plugin = self._file_tracers.get(filename)
+ if existing_plugin is not None and plugin_name != existing_plugin:
+ raise CoverageException(
+ "Conflicting file tracer name for '%s': %r vs %r" % (
+ filename, existing_plugin, plugin_name,
+ )
+ )
+ self._file_tracers[filename] = plugin_name
+
+ self._validate()
+
+ def add_run_info(self, **kwargs):
+ """Add information about the run.
+
+ Keywords are arbitrary, and are stored in the run dictionary. Values
+ must be JSON serializable. You may use this function more than once,
+ but repeated keywords overwrite each other.
+
+ """
+ if not self._runs:
+ self._runs = [{}]
+ self._runs[0].update(kwargs)
+ self._validate()
def touch_file(self, filename):
"""Ensure that `filename` appears in the data, empty if needed."""
- self.lines.setdefault(abs_file(filename), {})
+ (self._arcs or self._lines).setdefault(filename, [])
+ self._validate()
- def measured_files(self):
- """A list of all files that had been measured."""
- return list(self.lines.keys())
+ def write(self, file_obj):
+ """Write the coverage data to `file_obj`."""
- def executed_lines(self, filename):
- """A map containing all the line numbers executed in `filename`.
+ # Create the file data.
+ file_data = {}
- If `filename` hasn't been collected at all (because it wasn't executed)
- then return an empty map.
+ if self._arcs:
+ file_data['arcs'] = self._arcs
+ else:
+ file_data['lines'] = self._lines
+
+ if self._file_tracers:
+ file_data['file_tracers'] = self._file_tracers
+
+ if self._runs:
+ file_data['runs'] = self._runs
+
+ # Write the data to the file.
+ file_obj.write(self._GO_AWAY)
+ json.dump(file_data, file_obj)
+
+ def write_file(self, filename):
+ """Write the coverage data to `filename`."""
+ if self._debug and self._debug.should('dataio'):
+ self._debug.write("Writing data to %r" % (filename,))
+ with open(filename, 'w') as fdata:
+ self.write(fdata)
+
+ def erase(self):
+ """Erase the data in this object."""
+ self._lines = {}
+ self._arcs = {}
+ self._file_tracers = {}
+ self._runs = []
+ self._validate()
+
+ def update(self, other_data, aliases=None):
+ """Update this data with data from another `CoverageData`.
+
+ If `aliases` is provided, it's a `PathAliases` object that is used to
+ re-map paths to match the local machine's.
"""
- return self.lines.get(filename) or {}
+ if self._has_lines() and other_data._has_arcs():
+ raise CoverageException("Can't combine arc data with line data")
+ if self._has_arcs() and other_data._has_lines():
+ raise CoverageException("Can't combine line data with arc data")
- def executed_arcs(self, filename):
- """A map containing all the arcs executed in `filename`."""
- return self.arcs.get(filename) or {}
+ aliases = aliases or PathAliases()
- def add_to_hash(self, filename, hasher):
- """Contribute `filename`'s data to the Md5Hash `hasher`."""
- hasher.update(self.executed_lines(filename))
- hasher.update(self.executed_arcs(filename))
+ # _file_tracers: only have a string, so they have to agree.
+ # Have to do these first, so that our examination of self._arcs and
+ # self._lines won't be confused by data updated from other_data.
+ for filename in other_data.measured_files():
+ other_plugin = other_data.file_tracer(filename)
+ filename = aliases.map(filename)
+ this_plugin = self.file_tracer(filename)
+ if this_plugin is None:
+ if other_plugin:
+ self._file_tracers[filename] = other_plugin
+ elif this_plugin != other_plugin:
+ raise CoverageException(
+ "Conflicting file tracer name for '%s': %r vs %r" % (
+ filename, this_plugin, other_plugin,
+ )
+ )
+
+ # _runs: add the new runs to these runs.
+ self._runs.extend(other_data._runs)
+
+ # _lines: merge dicts.
+ for filename, file_lines in iitems(other_data._lines):
+ filename = aliases.map(filename)
+ if filename in self._lines:
+ lines = set(self._lines[filename])
+ lines.update(file_lines)
+ file_lines = list(lines)
+ self._lines[filename] = file_lines
+
+ # _arcs: merge dicts.
+ for filename, file_arcs in iitems(other_data._arcs):
+ filename = aliases.map(filename)
+ if filename in self._arcs:
+ arcs = set(self._arcs[filename])
+ arcs.update(file_arcs)
+ file_arcs = list(arcs)
+ self._arcs[filename] = file_arcs
+
+ self._validate()
+
+ ##
+ ## Miscellaneous
+ ##
+
+ def _validate(self):
+ """If we are in paranoid mode, validate that everything is right."""
+ if env.TESTING:
+ self._validate_invariants()
+
+ def _validate_invariants(self):
+ """Validate internal invariants."""
+ # Only one of _lines or _arcs should exist.
+ assert not(self._has_lines() and self._has_arcs()), (
+ "Shouldn't have both _lines and _arcs"
+ )
+
+ # _lines should be a dict of lists of ints.
+ for fname, lines in iitems(self._lines):
+ assert isinstance(fname, string_class), "Key in _lines shouldn't be %r" % (fname,)
+ assert all(isinstance(x, int) for x in lines), (
+ "_lines[%r] shouldn't be %r" % (fname, lines)
+ )
- def summary(self, fullpath=False):
- """Return a dict summarizing the coverage data.
+ # _arcs should be a dict of lists of pairs of ints.
+ for fname, arcs in iitems(self._arcs):
+ assert isinstance(fname, string_class), "Key in _arcs shouldn't be %r" % (fname,)
+ assert all(isinstance(x, int) and isinstance(y, int) for x, y in arcs), (
+ "_arcs[%r] shouldn't be %r" % (fname, arcs)
+ )
- Keys are based on the filenames, and values are the number of executed
- lines. If `fullpath` is true, then the keys are the full pathnames of
- the files, otherwise they are the basenames of the files.
+ # _file_tracers should have only non-empty strings as values.
+ for fname, plugin in iitems(self._file_tracers):
+ assert isinstance(fname, string_class), (
+ "Key in _file_tracers shouldn't be %r" % (fname,)
+ )
+ assert plugin and isinstance(plugin, string_class), (
+ "_file_tracers[%r] shoudn't be %r" % (fname, plugin)
+ )
+
+ # _runs should be a list of dicts.
+ for val in self._runs:
+ assert isinstance(val, dict)
+ for key in val:
+ assert isinstance(key, string_class), "Key in _runs shouldn't be %r" % (key,)
+
+ def add_to_hash(self, filename, hasher):
+ """Contribute `filename`'s data to the `hasher`.
+
+ `hasher` is a :class:`coverage.misc.Hasher` instance to be updated with
+ the file's data. It should only get the results data, not the run
+ data.
"""
- summ = {}
- if fullpath:
- filename_fn = lambda f: f
+ if self._arcs:
+ hasher.update(sorted(self.arcs(filename)))
else:
- filename_fn = os.path.basename
- for filename, lines in iitems(self.lines):
- summ[filename_fn(filename)] = len(lines)
- return summ
+ hasher.update(sorted(self.lines(filename)))
+ hasher.update(self.file_tracer(filename))
- def has_arcs(self):
- """Does this data have arcs?"""
- return bool(self.arcs)
+ ##
+ ## Internal
+ ##
+
+ def _has_lines(self):
+ """Do we have data in self._lines?"""
+ return bool(self._lines)
+
+ def _has_arcs(self):
+ """Do we have data in self._arcs?"""
+ return bool(self._arcs)
+
+
+class CoverageDataFiles(object):
+ """Manage the use of coverage data files."""
+
+ def __init__(self, basename=None):
+ """Create a CoverageDataFiles to manage data files.
+
+ `basename` is the name of the file to use for storing data.
+
+ """
+ # Construct the filename that will be used for data storage.
+ self.filename = os.path.abspath(basename or ".coverage")
+
+ def erase(self, parallel=False):
+ """Erase the data from the file storage.
+
+ If `parallel` is true, then also deletes data files created from the
+ basename by parallel-mode.
+
+ """
+ file_be_gone(self.filename)
+ if parallel:
+ data_dir, local = os.path.split(self.filename)
+ localdot = local + '.*'
+ pattern = os.path.join(os.path.abspath(data_dir), localdot)
+ for filename in glob.glob(pattern):
+ file_be_gone(filename)
+
+ def read(self, data):
+ """Read the coverage data."""
+ if os.path.exists(self.filename):
+ data.read_file(self.filename)
+
+ def write(self, data, suffix=None):
+ """Write the collected coverage data to a file.
+
+ `suffix` is a suffix to append to the base file name. This can be used
+ for multiple or parallel execution, so that many coverage data files
+ can exist simultaneously. A dot will be used to join the base name and
+ the suffix.
+
+ """
+ filename = self.filename
+ if suffix is True:
+ # If data_suffix was a simple true value, then make a suffix with
+ # plenty of distinguishing information. We do this here in
+ # `save()` at the last minute so that the pid will be correct even
+ # if the process forks.
+ extra = ""
+ if _TEST_NAME_FILE: # pragma: debugging
+ with open(_TEST_NAME_FILE) as f:
+ test_name = f.read()
+ extra = "." + test_name
+ suffix = "%s%s.%s.%06d" % (
+ socket.gethostname(), extra, os.getpid(),
+ random.randint(0, 999999)
+ )
+
+ if suffix:
+ filename += "." + suffix
+ data.write_file(filename)
+
+ def combine_parallel_data(self, data, aliases=None, data_paths=None):
+ """Combine a number of data files together.
+
+ Treat `self.filename` as a file prefix, and combine the data from all
+ of the data files starting with that prefix plus a dot.
+
+ If `aliases` is provided, it's a `PathAliases` object that is used to
+ re-map paths to match the local machine's.
+
+ If `data_paths` is provided, it is a list of directories or files to
+ combine. Directories are searched for files that start with
+ `self.filename` plus dot as a prefix, and those files are combined.
+
+ If `data_dirs` is not provided, then the directory portion of
+ `self.filename` is used as the directory to search for data files.
+
+ Every data file found and combined is then deleted from disk.
+
+ """
+ # Because of the os.path.abspath in the constructor, data_dir will
+ # never be an empty string.
+ data_dir, local = os.path.split(self.filename)
+ localdot = local + '.*'
+
+ data_paths = data_paths or [data_dir]
+ files_to_combine = []
+ for p in data_paths:
+ if os.path.isfile(p):
+ files_to_combine.append(os.path.abspath(p))
+ elif os.path.isdir(p):
+ pattern = os.path.join(os.path.abspath(p), localdot)
+ files_to_combine.extend(glob.glob(pattern))
+ else:
+ raise CoverageException("Couldn't combine from non-existent path '%s'" % (p,))
+
+ for f in files_to_combine:
+ new_data = CoverageData()
+ new_data.read_file(f)
+ data.update(new_data, aliases=aliases)
+ file_be_gone(f)
+
+
+def debug_main(args):
+ """Dump the raw data from data files.
+
+ Run this as::
+
+ $ python -m coverage.data [FILE]
+
+ """
+ for filename in (args or [".coverage"]):
+ print("--- {0} ------------------------------".format(filename))
+ data = CoverageData._read_raw_data_file(filename)
+ print(pretty_data(data))
if __name__ == '__main__':
- # Ad-hoc: show the raw data in a data file.
- import pprint, sys
- covdata = CoverageData()
- if sys.argv[1:]:
- fname = sys.argv[1]
- else:
- fname = covdata.filename
- pprint.pprint(covdata.raw_data(fname))
+ import sys
+ debug_main(sys.argv[1:])
diff --git a/coverage/debug.py b/coverage/debug.py
index 5b41bc40..8d36c1cd 100644
--- a/coverage/debug.py
+++ b/coverage/debug.py
@@ -1,6 +1,12 @@
+# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
+# For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt
+
"""Control of and utilities for debugging."""
+import inspect
+import json
import os
+import re
# When debugging, it can be helpful to force some options, especially when
@@ -8,6 +14,9 @@ import os
# This is a list of forced debugging options.
FORCED_DEBUG = []
+# A hack for debugging testing in sub-processes.
+_TEST_NAME_FILE = "" # "/tmp/covtest.txt"
+
class DebugControl(object):
"""Control and output for debugging."""
@@ -66,3 +75,40 @@ def info_formatter(info):
prefix = ""
else:
yield "%*s: %s" % (label_len, label, data)
+
+
+def short_stack(): # pragma: debugging
+ """Return a string summarizing the call stack.
+
+ The string is multi-line, with one line per stack frame. Each line shows
+ the function name, the file name, and the line number:
+
+ ...
+ start_import_stop : /Users/ned/coverage/trunk/tests/coveragetest.py @95
+ import_local_file : /Users/ned/coverage/trunk/tests/coveragetest.py @81
+ import_local_file : /Users/ned/coverage/trunk/coverage/backward.py @159
+ ...
+
+ """
+ stack = inspect.stack()[:0:-1]
+ return "\n".join("%30s : %s @%d" % (t[3], t[1], t[2]) for t in stack)
+
+
+def dump_stack_frames(): # pragma: debugging
+ """Print a summary of the stack to stdout."""
+ print(short_stack())
+
+
+def pretty_data(data):
+ """Format data as JSON, but as nicely as possible.
+
+ Returns a string.
+
+ """
+ # Start with a basic JSON dump.
+ out = json.dumps(data, indent=4, sort_keys=True)
+ # But pairs of numbers shouldn't be split across lines...
+ out = re.sub(r"\[\s+(-?\d+),\s+(-?\d+)\s+]", r"[\1, \2]", out)
+ # Trailing spaces mess with tests, get rid of them.
+ out = re.sub(r"(?m)\s+$", "", out)
+ return out
diff --git a/coverage/env.py b/coverage/env.py
index 85ffa5ff..1d2846c6 100644
--- a/coverage/env.py
+++ b/coverage/env.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
+
"""Determine facts about the environment."""
import os
@@ -11,11 +14,17 @@ LINUX = sys.platform == "linux2"
PYPY = '__pypy__' in sys.builtin_module_names
# Python versions.
-PY2 = sys.version_info < (3, 0)
-PY3 = sys.version_info >= (3, 0)
+PYVERSION = sys.version_info
+PY2 = PYVERSION < (3, 0)
+PY3 = PYVERSION >= (3, 0)
# Coverage.py specifics.
+
# Are we using the C-implemented trace function?
C_TRACER = os.getenv('COVERAGE_TEST_TRACER', 'c') == 'c'
+
# Are we coverage-measuring ourselves?
METACOV = os.getenv('COVERAGE_COVERAGE', '') != ''
+
+# Are we running our test suite?
+TESTING = os.getenv('COVERAGE_TESTING', '') != ''
diff --git a/coverage/execfile.py b/coverage/execfile.py
index 71bdb8db..d1158b51 100644
--- a/coverage/execfile.py
+++ b/coverage/execfile.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
+
"""Execute files of Python code."""
import marshal
@@ -8,6 +11,7 @@ import types
from coverage.backward import BUILTINS
from coverage.backward import PYC_MAGIC_NUMBER, imp, importlib_util_find_spec
from coverage.misc import ExceptionDuringRun, NoCode, NoSource
+from coverage.phystokens import compile_unicode
from coverage.python import get_python_source
@@ -192,7 +196,7 @@ def make_code_from_py(filename):
except (IOError, NoSource):
raise NoSource("No file to run: '%s'" % filename)
- code = compile(source, filename, "exec")
+ code = compile_unicode(source, filename, "exec")
return code
diff --git a/coverage/files.py b/coverage/files.py
index f7fc9693..e3ebd6ce 100644
--- a/coverage/files.py
+++ b/coverage/files.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
+
"""File wrangling."""
import fnmatch
@@ -9,86 +12,98 @@ import re
import sys
from coverage import env
+from coverage.backward import unicode_class
from coverage.misc import CoverageException, join_regex
-class FileLocator(object):
- """Understand how filenames work."""
+RELATIVE_DIR = None
+CANONICAL_FILENAME_CACHE = {}
- def __init__(self):
- # The absolute path to our current directory.
- self.relative_dir = os.path.normcase(abs_file(os.curdir) + os.sep)
- # Cache of results of calling the canonical_filename() method, to
- # avoid duplicating work.
- self.canonical_filename_cache = {}
+def set_relative_directory():
+ """Set the directory that `relative_filename` will be relative to."""
+ global RELATIVE_DIR, CANONICAL_FILENAME_CACHE
- def relative_filename(self, filename):
- """Return the relative form of `filename`.
+ # The absolute path to our current directory.
+ RELATIVE_DIR = os.path.normcase(abs_file(os.curdir) + os.sep)
- The filename will be relative to the current directory when the
- `FileLocator` was constructed.
+ # Cache of results of calling the canonical_filename() method, to
+ # avoid duplicating work.
+ CANONICAL_FILENAME_CACHE = {}
- """
- fnorm = os.path.normcase(filename)
- if fnorm.startswith(self.relative_dir):
- filename = filename[len(self.relative_dir):]
- return filename
+def relative_directory():
+ """Return the directory that `relative_filename` is relative to."""
+ return RELATIVE_DIR
- def canonical_filename(self, filename):
- """Return a canonical filename for `filename`.
+def relative_filename(filename):
+ """Return the relative form of `filename`.
- An absolute path with no redundant components and normalized case.
+ The filename will be relative to the current directory when the
+ `set_relative_directory` was called.
- """
- if filename not in self.canonical_filename_cache:
- if not os.path.isabs(filename):
- for path in [os.curdir] + sys.path:
- if path is None:
- continue
- f = os.path.join(path, filename)
- if os.path.exists(f):
- filename = f
- break
- cf = abs_file(filename)
- self.canonical_filename_cache[filename] = cf
- return self.canonical_filename_cache[filename]
+ """
+ fnorm = os.path.normcase(filename)
+ if fnorm.startswith(RELATIVE_DIR):
+ filename = filename[len(RELATIVE_DIR):]
+ return filename
+
+def canonical_filename(filename):
+ """Return a canonical filename for `filename`.
+
+ An absolute path with no redundant components and normalized case.
+
+ """
+ if filename not in CANONICAL_FILENAME_CACHE:
+ if not os.path.isabs(filename):
+ for path in [os.curdir] + sys.path:
+ if path is None:
+ continue
+ f = os.path.join(path, filename)
+ if os.path.exists(f):
+ filename = f
+ break
+ cf = abs_file(filename)
+ CANONICAL_FILENAME_CACHE[filename] = cf
+ return CANONICAL_FILENAME_CACHE[filename]
if env.WINDOWS:
+ _ACTUAL_PATH_CACHE = {}
+ _ACTUAL_PATH_LIST_CACHE = {}
+
def actual_path(path):
"""Get the actual path of `path`, including the correct case."""
- if path in actual_path.cache:
- return actual_path.cache[path]
+ if env.PY2 and isinstance(path, unicode_class):
+ path = path.encode(sys.getfilesystemencoding())
+ if path in _ACTUAL_PATH_CACHE:
+ return _ACTUAL_PATH_CACHE[path]
head, tail = os.path.split(path)
if not tail:
- actpath = head
+ # This means head is the drive spec: normalize it.
+ actpath = head.upper()
elif not head:
actpath = tail
else:
head = actual_path(head)
- if head in actual_path.list_cache:
- files = actual_path.list_cache[head]
+ if head in _ACTUAL_PATH_LIST_CACHE:
+ files = _ACTUAL_PATH_LIST_CACHE[head]
else:
try:
files = os.listdir(head)
except OSError:
files = []
- actual_path.list_cache[head] = files
+ _ACTUAL_PATH_LIST_CACHE[head] = files
normtail = os.path.normcase(tail)
for f in files:
if os.path.normcase(f) == normtail:
tail = f
break
actpath = os.path.join(head, tail)
- actual_path.cache[path] = actpath
+ _ACTUAL_PATH_CACHE[path] = actpath
return actpath
- actual_path.cache = {}
- actual_path.list_cache = {}
-
else:
def actual_path(filename):
"""The actual path for non-Windows platforms."""
@@ -228,12 +243,9 @@ class PathAliases(object):
A `PathAliases` object tracks a list of pattern/result pairs, and can
map a path through those aliases to produce a unified path.
- `locator` is a FileLocator that is used to canonicalize the results.
-
"""
- def __init__(self, locator=None):
+ def __init__(self):
self.aliases = []
- self.locator = locator
def add(self, pattern, result):
"""Add the `pattern`/`result` pair to the list of aliases.
@@ -286,6 +298,10 @@ class PathAliases(object):
The separator style in the result is made to match that of the result
in the alias.
+ Returns the mapped path. If a mapping has happened, this is a
+ canonical path. If no mapping has happened, it is the original value
+ of `path` unchanged.
+
"""
for regex, result, pattern_sep, result_sep in self.aliases:
m = regex.match(path)
@@ -293,8 +309,7 @@ class PathAliases(object):
new = path.replace(m.group(0), result)
if pattern_sep != result_sep:
new = new.replace(pattern_sep, result_sep)
- if self.locator:
- new = self.locator.canonical_filename(new)
+ new = canonical_filename(new)
return new
return path
diff --git a/coverage/fullcoverage/encodings.py b/coverage/fullcoverage/encodings.py
index 6a258d67..699f3863 100644
--- a/coverage/fullcoverage/encodings.py
+++ b/coverage/fullcoverage/encodings.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
+
"""Imposter encodings module that installs a coverage-style tracer.
This is NOT the encodings module; it is an imposter that sets up tracing
@@ -6,10 +9,10 @@ instrumentation and then replaces itself with the real encodings module.
If the directory that holds this file is placed first in the PYTHONPATH when
using "coverage" to run Python's tests, then this file will become the very
first module imported by the internals of Python 3. It installs a
-coverage-compatible trace function that can watch Standard Library modules
+coverage.py-compatible trace function that can watch Standard Library modules
execute from the very earliest stages of Python's own boot process. This fixes
-a problem with coverage - that it starts too late to trace the coverage of many
-of the most fundamental modules in the Standard Library.
+a problem with coverage.py - that it starts too late to trace the coverage of
+many of the most fundamental modules in the Standard Library.
"""
diff --git a/coverage/html.py b/coverage/html.py
index 96282161..9022ac4f 100644
--- a/coverage/html.py
+++ b/coverage/html.py
@@ -1,7 +1,9 @@
-"""HTML reporting for Coverage."""
+# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
+# For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt
-from __future__ import unicode_literals
+"""HTML reporting for coverage.py."""
+import datetime
import json
import os
import re
@@ -92,6 +94,7 @@ class HtmlReporter(Reporter):
self.status = HtmlStatus()
self.extra_css = None
self.totals = Numbers()
+ self.time_stamp = datetime.datetime.now().strftime('%Y-%m-%d %H:%M')
def report(self, morfs):
"""Generate an HTML report for `morfs`.
@@ -126,8 +129,7 @@ class HtmlReporter(Reporter):
self.index_file()
self.make_local_static_report_files()
-
- return self.totals.pc_covered
+ return self.totals.n_statements and self.totals.pc_covered
def make_local_static_report_files(self):
"""Make local instances of static files for HTML report."""
@@ -221,7 +223,7 @@ class HtmlReporter(Reporter):
else:
tok_html = escape(tok_text) or '&nbsp;'
html.append(
- "<span class='%s'>%s</span>" % (tok_type, tok_html)
+ '<span class="%s">%s</span>' % (tok_type, tok_html)
)
lines.append({
@@ -237,6 +239,7 @@ class HtmlReporter(Reporter):
'c_exc': c_exc, 'c_mis': c_mis, 'c_par': c_par, 'c_run': c_run,
'arcs': self.arcs, 'extra_css': self.extra_css,
'fr': fr, 'nums': nums, 'lines': lines,
+ 'time_stamp': self.time_stamp,
}
html = spaceless(self.source_tmpl.render(template_values))
@@ -248,7 +251,7 @@ class HtmlReporter(Reporter):
index_info = {
'nums': nums,
'html_filename': html_filename,
- 'name': fr.name,
+ 'relative_filename': fr.relative_filename(),
}
self.files.append(index_info)
self.status.set_index_info(flat_rootname, index_info)
@@ -266,6 +269,7 @@ class HtmlReporter(Reporter):
'extra_css': self.extra_css,
'files': self.files,
'totals': self.totals,
+ 'time_stamp': self.time_stamp,
})
self.write_html(
diff --git a/coverage/htmlfiles/coverage_html.js b/coverage/htmlfiles/coverage_html.js
index 5ef0ece5..bd6a8753 100644
--- a/coverage/htmlfiles/coverage_html.js
+++ b/coverage/htmlfiles/coverage_html.js
@@ -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
+
// Coverage.py HTML report browser code.
/*jslint browser: true, sloppy: true, vars: true, plusplus: true, maxerr: 50, indent: 4 */
/*global coverage: true, document, window, $ */
diff --git a/coverage/htmlfiles/index.html b/coverage/htmlfiles/index.html
index 90802c81..25ced0eb 100644
--- a/coverage/htmlfiles/index.html
+++ b/coverage/htmlfiles/index.html
@@ -1,30 +1,33 @@
+{# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 #}
+{# For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt #}
+
<!DOCTYPE html>
<html>
<head>
- <meta http-equiv='Content-Type' content='text/html; charset=utf-8'>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>{{ title|escape }}</title>
- <link rel='stylesheet' href='style.css' type='text/css'>
+ <link rel="stylesheet" href="style.css" type="text/css">
{% if extra_css %}
- <link rel='stylesheet' href='{{ extra_css }}' type='text/css'>
+ <link rel="stylesheet" href="{{ extra_css }}" type="text/css">
{% endif %}
- <script type='text/javascript' src='jquery.min.js'></script>
- <script type='text/javascript' src='jquery.debounce.min.js'></script>
- <script type='text/javascript' src='jquery.tablesorter.min.js'></script>
- <script type='text/javascript' src='jquery.hotkeys.js'></script>
- <script type='text/javascript' src='coverage_html.js'></script>
- <script type='text/javascript'>
+ <script type="text/javascript" src="jquery.min.js"></script>
+ <script type="text/javascript" src="jquery.debounce.min.js"></script>
+ <script type="text/javascript" src="jquery.tablesorter.min.js"></script>
+ <script type="text/javascript" src="jquery.hotkeys.js"></script>
+ <script type="text/javascript" src="coverage_html.js"></script>
+ <script type="text/javascript">
jQuery(document).ready(coverage.index_ready);
</script>
</head>
-<body class='indexfile'>
+<body class="indexfile">
-<div id='header'>
- <div class='content'>
+<div id="header">
+ <div class="content">
<h1>{{ title|escape }}:
- <span class='pc_cov'>{{totals.pc_covered_str}}%</span>
+ <span class="pc_cov">{{totals.pc_covered_str}}%</span>
</h1>
- <img id='keyboard_icon' src='keybd_closed.png' alt='Show keyboard shortcuts' />
+ <img id="keyboard_icon" src="keybd_closed.png" alt="Show keyboard shortcuts" />
<form id="filter_container">
<input id="filter" type="text" value="" placeholder="filter..." />
@@ -32,44 +35,44 @@
</div>
</div>
-<div class='help_panel'>
- <img id='panel_icon' src='keybd_open.png' alt='Hide keyboard shortcuts' />
- <p class='legend'>Hot-keys on this page</p>
+<div class="help_panel">
+ <img id="panel_icon" src="keybd_open.png" alt="Hide keyboard shortcuts" />
+ <p class="legend">Hot-keys on this page</p>
<div>
- <p class='keyhelp'>
- <span class='key'>n</span>
- <span class='key'>s</span>
- <span class='key'>m</span>
- <span class='key'>x</span>
+ <p class="keyhelp">
+ <span class="key">n</span>
+ <span class="key">s</span>
+ <span class="key">m</span>
+ <span class="key">x</span>
{% if arcs %}
- <span class='key'>b</span>
- <span class='key'>p</span>
+ <span class="key">b</span>
+ <span class="key">p</span>
{% endif %}
- <span class='key'>c</span> &nbsp; change column sorting
+ <span class="key">c</span> &nbsp; change column sorting
</p>
</div>
</div>
-<div id='index'>
- <table class='index'>
+<div id="index">
+ <table class="index">
<thead>
- {# The title='' attr doesn't work in Safari. #}
- <tr class='tablehead' title='Click to sort'>
- <th class='name left headerSortDown shortkey_n'>Module</th>
- <th class='shortkey_s'>statements</th>
- <th class='shortkey_m'>missing</th>
- <th class='shortkey_x'>excluded</th>
+ {# The title="" attr doesn"t work in Safari. #}
+ <tr class="tablehead" title="Click to sort">
+ <th class="name left headerSortDown shortkey_n">Module</th>
+ <th class="shortkey_s">statements</th>
+ <th class="shortkey_m">missing</th>
+ <th class="shortkey_x">excluded</th>
{% if arcs %}
- <th class='shortkey_b'>branches</th>
- <th class='shortkey_p'>partial</th>
+ <th class="shortkey_b">branches</th>
+ <th class="shortkey_p">partial</th>
{% endif %}
- <th class='right shortkey_c'>coverage</th>
+ <th class="right shortkey_c">coverage</th>
</tr>
</thead>
{# HTML syntax requires thead, tfoot, tbody #}
<tfoot>
- <tr class='total'>
- <td class='name left'>Total</td>
+ <tr class="total">
+ <td class="name left">Total</td>
<td>{{totals.n_statements}}</td>
<td>{{totals.n_missing}}</td>
<td>{{totals.n_excluded}}</td>
@@ -77,13 +80,13 @@
<td>{{totals.n_branches}}</td>
<td>{{totals.n_partial_branches}}</td>
{% endif %}
- <td class='right' data-ratio='{{totals.ratio_covered|pair}}'>{{totals.pc_covered_str}}%</td>
+ <td class="right" data-ratio="{{totals.ratio_covered|pair}}">{{totals.pc_covered_str}}%</td>
</tr>
</tfoot>
<tbody>
{% for file in files %}
- <tr class='file'>
- <td class='name left'><a href='{{file.html_filename}}'>{{file.name}}</a></td>
+ <tr class="file">
+ <td class="name left"><a href="{{file.html_filename}}">{{file.relative_filename}}</a></td>
<td>{{file.nums.n_statements}}</td>
<td>{{file.nums.n_missing}}</td>
<td>{{file.nums.n_excluded}}</td>
@@ -91,7 +94,7 @@
<td>{{file.nums.n_branches}}</td>
<td>{{file.nums.n_partial_branches}}</td>
{% endif %}
- <td class='right' data-ratio='{{file.nums.ratio_covered|pair}}'>{{file.nums.pc_covered_str}}%</td>
+ <td class="right" data-ratio="{{file.nums.ratio_covered|pair}}">{{file.nums.pc_covered_str}}%</td>
</tr>
{% endfor %}
</tbody>
@@ -102,10 +105,11 @@
</p>
</div>
-<div id='footer'>
- <div class='content'>
+<div id="footer">
+ <div class="content">
<p>
- <a class='nav' href='{{__url__}}'>coverage.py v{{__version__}}</a>
+ <a class="nav" href="{{__url__}}">coverage.py v{{__version__}}</a>,
+ created at {{ time_stamp }}
</p>
</div>
</div>
diff --git a/coverage/htmlfiles/pyfile.html b/coverage/htmlfiles/pyfile.html
index f9d898aa..7bf9f554 100644
--- a/coverage/htmlfiles/pyfile.html
+++ b/coverage/htmlfiles/pyfile.html
@@ -1,90 +1,94 @@
+{# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 #}
+{# For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt #}
+
<!DOCTYPE html>
<html>
<head>
- <meta http-equiv='Content-Type' content='text/html; charset=utf-8'>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
{# IE8 rounds line-height incorrectly, and adding this emulateIE7 line makes it right! #}
{# http://social.msdn.microsoft.com/Forums/en-US/iewebdevelopment/thread/7684445e-f080-4d8f-8529-132763348e21 #}
- <meta http-equiv='X-UA-Compatible' content='IE=emulateIE7' />
- <title>Coverage for {{fr.name|escape}}: {{nums.pc_covered_str}}%</title>
- <link rel='stylesheet' href='style.css' type='text/css'>
+ <meta http-equiv="X-UA-Compatible" content="IE=emulateIE7" />
+ <title>Coverage for {{fr.relative_filename|escape}}: {{nums.pc_covered_str}}%</title>
+ <link rel="stylesheet" href="style.css" type="text/css">
{% if extra_css %}
- <link rel='stylesheet' href='{{ extra_css }}' type='text/css'>
+ <link rel="stylesheet" href="{{ extra_css }}" type="text/css">
{% endif %}
- <script type='text/javascript' src='jquery.min.js'></script>
- <script type='text/javascript' src='jquery.hotkeys.js'></script>
- <script type='text/javascript' src='jquery.isonscreen.js'></script>
- <script type='text/javascript' src='coverage_html.js'></script>
- <script type='text/javascript'>
+ <script type="text/javascript" src="jquery.min.js"></script>
+ <script type="text/javascript" src="jquery.hotkeys.js"></script>
+ <script type="text/javascript" src="jquery.isonscreen.js"></script>
+ <script type="text/javascript" src="coverage_html.js"></script>
+ <script type="text/javascript">
jQuery(document).ready(coverage.pyfile_ready);
</script>
</head>
-<body class='pyfile'>
+<body class="pyfile">
-<div id='header'>
- <div class='content'>
- <h1>Coverage for <b>{{fr.name|escape}}</b> :
- <span class='pc_cov'>{{nums.pc_covered_str}}%</span>
+<div id="header">
+ <div class="content">
+ <h1>Coverage for <b>{{fr.relative_filename|escape}}</b> :
+ <span class="pc_cov">{{nums.pc_covered_str}}%</span>
</h1>
- <img id='keyboard_icon' src='keybd_closed.png' alt='Show keyboard shortcuts' />
+ <img id="keyboard_icon" src="keybd_closed.png" alt="Show keyboard shortcuts" />
- <h2 class='stats'>
+ <h2 class="stats">
{{nums.n_statements}} statements &nbsp;
- <span class='{{c_run}} shortkey_r button_toggle_run'>{{nums.n_executed}} run</span>
- <span class='{{c_mis}} shortkey_m button_toggle_mis'>{{nums.n_missing}} missing</span>
- <span class='{{c_exc}} shortkey_x button_toggle_exc'>{{nums.n_excluded}} excluded</span>
+ <span class="{{c_run}} shortkey_r button_toggle_run">{{nums.n_executed}} run</span>
+ <span class="{{c_mis}} shortkey_m button_toggle_mis">{{nums.n_missing}} missing</span>
+ <span class="{{c_exc}} shortkey_x button_toggle_exc">{{nums.n_excluded}} excluded</span>
{% if arcs %}
- <span class='{{c_par}} shortkey_p button_toggle_par'>{{nums.n_partial_branches}} partial</span>
+ <span class="{{c_par}} shortkey_p button_toggle_par">{{nums.n_partial_branches}} partial</span>
{% endif %}
</h2>
</div>
</div>
-<div class='help_panel'>
- <img id='panel_icon' src='keybd_open.png' alt='Hide keyboard shortcuts' />
- <p class='legend'>Hot-keys on this page</p>
+<div class="help_panel">
+ <img id="panel_icon" src="keybd_open.png" alt="Hide keyboard shortcuts" />
+ <p class="legend">Hot-keys on this page</p>
<div>
- <p class='keyhelp'>
- <span class='key'>r</span>
- <span class='key'>m</span>
- <span class='key'>x</span>
- <span class='key'>p</span> &nbsp; toggle line displays
+ <p class="keyhelp">
+ <span class="key">r</span>
+ <span class="key">m</span>
+ <span class="key">x</span>
+ <span class="key">p</span> &nbsp; toggle line displays
</p>
- <p class='keyhelp'>
- <span class='key'>j</span>
- <span class='key'>k</span> &nbsp; next/prev highlighted chunk
+ <p class="keyhelp">
+ <span class="key">j</span>
+ <span class="key">k</span> &nbsp; next/prev highlighted chunk
</p>
- <p class='keyhelp'>
- <span class='key'>0</span> &nbsp; (zero) top of page
+ <p class="keyhelp">
+ <span class="key">0</span> &nbsp; (zero) top of page
</p>
- <p class='keyhelp'>
- <span class='key'>1</span> &nbsp; (one) first highlighted chunk
+ <p class="keyhelp">
+ <span class="key">1</span> &nbsp; (one) first highlighted chunk
</p>
</div>
</div>
-<div id='source'>
+<div id="source">
<table>
<tr>
- <td class='linenos'>
+ <td class="linenos">
{% for line in lines %}
- <p id='n{{line.number}}' class='{{line.class}}'><a href='#n{{line.number}}'>{{line.number}}</a></p>
+ <p id="n{{line.number}}" class="{{line.class}}"><a href="#n{{line.number}}">{{line.number}}</a></p>
{% endfor %}
</td>
- <td class='text'>
+ <td class="text">
{% for line in lines %}
- <p id='t{{line.number}}' class='{{line.class}}'>{% if line.annotate %}<span class='annotate' title='{{line.annotate_title}}'>{{line.annotate}}</span>{% endif %}{{line.html}}<span class='strut'>&nbsp;</span></p>
+ <p id="t{{line.number}}" class="{{line.class}}">{% if line.annotate %}<span class="annotate" title="{{line.annotate_title}}">{{line.annotate}}</span>{% endif %}{{line.html}}<span class="strut">&nbsp;</span></p>
{% endfor %}
</td>
</tr>
</table>
</div>
-<div id='footer'>
- <div class='content'>
+<div id="footer">
+ <div class="content">
<p>
- <a class='nav' href='index.html'>&#xab; index</a> &nbsp; &nbsp; <a class='nav' href='{{__url__}}'>coverage.py v{{__version__}}</a>
+ <a class="nav" href="index.html">&#xab; index</a> &nbsp; &nbsp; <a class="nav" href="{{__url__}}">coverage.py v{{__version__}}</a>,
+ created at {{ time_stamp }}
</p>
</div>
</div>
diff --git a/coverage/htmlfiles/style.css b/coverage/htmlfiles/style.css
index 038335c1..2dfb8f65 100644
--- a/coverage/htmlfiles/style.css
+++ b/coverage/htmlfiles/style.css
@@ -1,4 +1,7 @@
-/* CSS styles for Coverage. */
+/* Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 */
+/* For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt */
+
+/* CSS styles for coverage.py. */
/* Page-wide styles */
html, body, h1, h2, h3, p, table, td, th {
margin: 0;
diff --git a/coverage/misc.py b/coverage/misc.py
index d5197ea3..d11ebccb 100644
--- a/coverage/misc.py
+++ b/coverage/misc.py
@@ -1,4 +1,7 @@
-"""Miscellaneous stuff for Coverage."""
+# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
+# For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt
+
+"""Miscellaneous stuff for coverage.py."""
import errno
import hashlib
@@ -6,7 +9,30 @@ import inspect
import os
from coverage import env
-from coverage.backward import string_class, to_bytes
+from coverage.backward import string_class, to_bytes, unicode_class
+
+
+# Use PyContracts for assertion testing on parameters and returns, but only if
+# we are running our own test suite.
+if env.TESTING:
+ from contracts import contract # pylint: disable=unused-import
+ from contracts import new_contract
+
+ try:
+ # Define contract words that PyContract doesn't have.
+ new_contract('bytes', lambda v: isinstance(v, bytes))
+ if env.PY3:
+ new_contract('unicode', lambda v: isinstance(v, unicode_class))
+ except ValueError:
+ # During meta-coverage, this module is imported twice, and PyContracts
+ # doesn't like redefining contracts. It's OK.
+ pass
+else: # pragma: not covered
+ # We aren't using real PyContracts, so just define a no-op decorator as a
+ # stunt double.
+ def contract(**unused):
+ """Dummy no-op implementation of `contract`."""
+ return lambda func: func
def nice_pair(pair):
@@ -56,12 +82,6 @@ def format_lines(statements, lines):
return ret
-def short_stack(): # pragma: debugging
- """Return a string summarizing the call stack."""
- stack = inspect.stack()[:0:-1]
- return "\n".join("%30s : %s @%d" % (t[3], t[1], t[2]) for t in stack)
-
-
def expensive(fn):
"""A decorator to cache the result of an expensive operation.
@@ -139,29 +159,6 @@ class Hasher(object):
return self.md5.hexdigest()
-def overrides(obj, method_name, base_class):
- """Does `obj` override the `method_name` it got from `base_class`?
-
- Determine if `obj` implements the method called `method_name`, which it
- inherited from `base_class`.
-
- Returns a boolean.
-
- """
- klass = obj.__class__
- klass_func = getattr(klass, method_name)
- base_func = getattr(base_class, method_name)
-
- # Python 2/3 compatibility: Python 2 returns an instancemethod object, the
- # function is the .im_func attribute. Python 3 returns a plain function
- # object already.
- if env.PY2:
- klass_func = klass_func.im_func
- base_func = base_func.im_func
-
- return klass_func is not base_func
-
-
# TODO: abc?
def _needs_to_implement(that, func_name):
"""Helper to raise NotImplementedError in interface stubs."""
@@ -181,7 +178,7 @@ def _needs_to_implement(that, func_name):
class CoverageException(Exception):
- """An exception specific to Coverage."""
+ """An exception specific to coverage.py."""
pass
diff --git a/coverage/monkey.py b/coverage/monkey.py
index ee84d992..c4ec68c6 100644
--- a/coverage/monkey.py
+++ b/coverage/monkey.py
@@ -1,4 +1,7 @@
-"""Monkey-patching to make coverage work right in some cases."""
+# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
+# For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt
+
+"""Monkey-patching to make coverage.py work right in some cases."""
import multiprocessing
import multiprocessing.process
@@ -6,7 +9,7 @@ import sys
# An attribute that will be set on modules to indicate that they have been
# monkey-patched.
-MARKER = "_coverage$patched"
+PATCHED_MARKER = "_coverage$patched"
def patch_multiprocessing():
@@ -16,7 +19,7 @@ def patch_multiprocessing():
This is wildly experimental!
"""
- if hasattr(multiprocessing, MARKER):
+ if hasattr(multiprocessing, PATCHED_MARKER):
return
if sys.version_info >= (3, 4):
@@ -29,6 +32,7 @@ def patch_multiprocessing():
class ProcessWithCoverage(klass):
"""A replacement for multiprocess.Process that starts coverage."""
def _bootstrap(self):
+ """Wrapper around _bootstrap to start coverage."""
from coverage import Coverage
cov = Coverage(data_suffix=True)
cov.start()
@@ -43,4 +47,4 @@ def patch_multiprocessing():
else:
multiprocessing.Process = ProcessWithCoverage
- setattr(multiprocessing, MARKER, 1)
+ setattr(multiprocessing, PATCHED_MARKER, True)
diff --git a/coverage/parser.py b/coverage/parser.py
index f488367d..a2b1b610 100644
--- a/coverage/parser.py
+++ b/coverage/parser.py
@@ -1,4 +1,7 @@
-"""Code parsing for Coverage."""
+# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
+# For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt
+
+"""Code parsing for coverage.py."""
import collections
import dis
@@ -9,9 +12,9 @@ import tokenize
from coverage.backward import range # pylint: disable=redefined-builtin
from coverage.backward import bytes_to_ints
from coverage.bytecode import ByteCodes, CodeObjects
-from coverage.misc import nice_pair, expensive, join_regex
+from coverage.misc import contract, nice_pair, expensive, join_regex
from coverage.misc import CoverageException, NoSource, NotPython
-from coverage.phystokens import generate_tokens
+from coverage.phystokens import compile_unicode, generate_tokens
class CodeParser(object):
@@ -34,6 +37,7 @@ class CodeParser(object):
class PythonParser(CodeParser):
"""Parse code to find executable lines, excluded lines, etc."""
+ @contract(text='unicode|None')
def __init__(self, text=None, filename=None, exclude=None):
"""
Source can be provided as `text`, the text itself, or `filename`, from
@@ -53,14 +57,6 @@ class PythonParser(CodeParser):
"No source for code: '%s': %s" % (self.filename, err)
)
- if self.text:
- assert isinstance(self.text, str)
- # Scrap the BOM if it exists.
- # (Used to do this, but no longer. Not sure what bad will happen
- # if we don't do it.)
- # if ord(self.text[0]) == 0xfeff:
- # self.text = self.text[1:]
-
self.exclude = exclude
self.show_tokens = False
@@ -334,7 +330,7 @@ OPS_NO_JUMP = OPS_PUSH_BLOCK
OP_BREAK_LOOP = _opcode('BREAK_LOOP')
OP_END_FINALLY = _opcode('END_FINALLY')
OP_COMPARE_OP = _opcode('COMPARE_OP')
-COMPARE_EXCEPTION = 10 # just have to get this const from the code.
+COMPARE_EXCEPTION = 10 # just have to get this constant from the code.
OP_LOAD_CONST = _opcode('LOAD_CONST')
OP_RETURN_VALUE = _opcode('RETURN_VALUE')
@@ -342,13 +338,14 @@ OP_RETURN_VALUE = _opcode('RETURN_VALUE')
class ByteParser(object):
"""Parse byte codes to understand the structure of code."""
+ @contract(text='unicode')
def __init__(self, text, code=None, filename=None):
self.text = text
if code:
self.code = code
else:
try:
- self.code = compile(text, filename, "exec")
+ self.code = compile_unicode(text, filename, "exec")
except SyntaxError as synerr:
raise NotPython(
"Couldn't parse '%s' as Python source: '%s' at line %d" % (
@@ -460,7 +457,7 @@ class ByteParser(object):
# Walk the byte codes building chunks.
for bc in bytecodes:
- # Maybe have to start a new chunk
+ # Maybe have to start a new chunk.
start_new_chunk = False
first_chunk = False
if bc.offset in bytes_lines_map:
@@ -481,9 +478,13 @@ class ByteParser(object):
if chunk:
chunk.exits.add(bc.offset)
chunk = Chunk(bc.offset, chunk_lineno, first_chunk)
+ if not chunks:
+ # The very first chunk of a code object is always an
+ # entrance.
+ chunk.entrance = True
chunks.append(chunk)
- # Look at the opcode
+ # Look at the opcode.
if bc.jump_to >= 0 and bc.op not in OPS_NO_JUMP:
if ignore_branch:
# Someone earlier wanted us to ignore this branch.
@@ -570,15 +571,15 @@ class ByteParser(object):
"""
chunks = self._split_into_chunks()
- # A map from byte offsets to chunks jumped into.
+ # A map from byte offsets to the chunk starting at that offset.
byte_chunks = dict((c.byte, c) for c in chunks)
- # There's always an entrance at the first chunk.
- yield (-1, byte_chunks[0].line)
-
# Traverse from the first chunk in each line, and yield arcs where
# the trace function will be invoked.
for chunk in chunks:
+ if chunk.entrance:
+ yield (-1, chunk.line)
+
if not chunk.first:
continue
@@ -586,7 +587,7 @@ class ByteParser(object):
chunks_to_consider = [chunk]
while chunks_to_consider:
# Get the chunk we're considering, and make sure we don't
- # consider it again
+ # consider it again.
this_chunk = chunks_to_consider.pop()
chunks_considered.add(this_chunk)
@@ -649,6 +650,8 @@ class Chunk(object):
.. _basic block: http://en.wikipedia.org/wiki/Basic_block
+ `byte` is the offset to the bytecode starting this chunk.
+
`line` is the source line number containing this chunk.
`first` is true if this is the first chunk in the source line.
@@ -656,19 +659,24 @@ class Chunk(object):
An exit < 0 means the chunk can leave the code (return). The exit is
the negative of the starting line number of the code block.
+ The `entrance` attribute is a boolean indicating whether the code object
+ can be entered at this chunk.
+
"""
def __init__(self, byte, line, first):
self.byte = byte
self.line = line
self.first = first
self.length = 0
+ self.entrance = False
self.exits = set()
def __repr__(self):
- if self.first:
- bang = "!"
- else:
- bang = ""
- return "<%d+%d @%d%s %r>" % (
- self.byte, self.length, self.line, bang, list(self.exits)
+ return "<%d+%d @%d%s%s %r>" % (
+ self.byte,
+ self.length,
+ self.line,
+ "!" if self.first else "",
+ "v" if self.entrance else "",
+ list(self.exits),
)
diff --git a/coverage/phystokens.py b/coverage/phystokens.py
index ed6bd238..92da8d32 100644
--- a/coverage/phystokens.py
+++ b/coverage/phystokens.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
+
"""Better tokenizing for coverage.py."""
import codecs
@@ -8,6 +11,7 @@ import tokenize
from coverage import env
from coverage.backward import iternext
+from coverage.misc import contract
def phys_tokens(toks):
@@ -66,6 +70,7 @@ def phys_tokens(toks):
last_lineno = elineno
+@contract(source='unicode')
def source_token_lines(source):
"""Generate a series of lines, one for each line in `source`.
@@ -134,11 +139,10 @@ class CachedTokenizer(object):
self.last_text = None
self.last_tokens = None
+ @contract(text='unicode')
def generate_tokens(self, text):
"""A stand-in for `tokenize.generate_tokens`."""
- # Check the type first so we don't compare bytes to unicode and get
- # warnings.
- if type(text) != type(self.last_text) or text != self.last_text:
+ if text != self.last_text:
self.last_text = text
readline = iternext(text.splitlines(True))
self.last_tokens = list(tokenize.generate_tokens(readline))
@@ -148,14 +152,15 @@ class CachedTokenizer(object):
generate_tokens = CachedTokenizer().generate_tokens
+COOKIE_RE = re.compile(r"^\s*#.*coding[:=]\s*([-\w.]+)", flags=re.MULTILINE)
+
+@contract(source='bytes')
def _source_encoding_py2(source):
"""Determine the encoding for `source`, according to PEP 263.
- Arguments:
- source (byte string): the text of the program.
+ `source` is a byte string, the text of the program.
- Returns:
- string: the name of the encoding.
+ Returns a string, the name of the encoding.
"""
assert isinstance(source, bytes)
@@ -165,8 +170,6 @@ def _source_encoding_py2(source):
# This is mostly code adapted from Py3.2's tokenize module.
- cookie_re = re.compile(r"^\s*#.*coding[:=]\s*([-\w.]+)")
-
def _get_normal_name(orig_enc):
"""Imitates get_normal_name in tokenizer.c."""
# Only care about the first 12 characters.
@@ -204,7 +207,7 @@ def _source_encoding_py2(source):
except UnicodeDecodeError:
return None
- matches = cookie_re.findall(line_string)
+ matches = COOKIE_RE.findall(line_string)
if not matches:
return None
encoding = _get_normal_name(matches[0])
@@ -246,17 +249,15 @@ def _source_encoding_py2(source):
return default
+@contract(source='bytes')
def _source_encoding_py3(source):
"""Determine the encoding for `source`, according to PEP 263.
- Arguments:
- source (byte string): the text of the program.
+ `source` is a byte string: the text of the program.
- Returns:
- string: the name of the encoding.
+ Returns a string, the name of the encoding.
"""
- assert isinstance(source, bytes)
readline = iternext(source.splitlines(True))
return tokenize.detect_encoding(readline)[0]
@@ -265,3 +266,38 @@ if env.PY3:
source_encoding = _source_encoding_py3
else:
source_encoding = _source_encoding_py2
+
+
+@contract(source='unicode')
+def compile_unicode(source, filename, mode):
+ """Just like the `compile` builtin, but works on any Unicode string.
+
+ Python 2's compile() builtin has a stupid restriction: if the source string
+ is Unicode, then it may not have a encoding declaration in it. Why not?
+ Who knows!
+
+ This function catches that exception, neuters the coding declaration, and
+ compiles it anyway.
+
+ """
+ try:
+ code = compile(source, filename, mode)
+ except SyntaxError as synerr:
+ if "coding declaration in unicode string" not in synerr.args[0].lower():
+ raise
+ source = neuter_encoding_declaration(source)
+ code = compile(source, filename, mode)
+
+ return code
+
+
+@contract(source='unicode', returns='unicode')
+def neuter_encoding_declaration(source):
+ """Return `source`, with any encoding declaration neutered.
+
+ This function will only ever be called on `source` that has an encoding
+ declaration, so some edge cases can be ignored.
+
+ """
+ source = COOKIE_RE.sub("# (deleted declaration)", source)
+ return source
diff --git a/coverage/plugin.py b/coverage/plugin.py
index 6648d7a6..5b0479c3 100644
--- a/coverage/plugin.py
+++ b/coverage/plugin.py
@@ -1,10 +1,15 @@
+# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
+# For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt
+
"""Plugin interfaces for coverage.py"""
import os
import re
+from coverage import files
from coverage.misc import _needs_to_implement
+
# TODO: document that the plugin objects may be decorated with attributes with
# named "_coverage_*".
@@ -25,19 +30,6 @@ class CoveragePlugin(object):
"""
- def __init__(self, options):
- """
- When the plugin is constructed, it will be passed a dictionary of
- plugin-specific options read from the .coveragerc configuration file.
- The base class stores these on the `self.options` attribute.
-
- Arguments:
- options (dict): The plugin-specific options read from the
- .coveragerc configuration file.
-
- """
- self.options = options
-
def file_tracer(self, filename): # pylint: disable=unused-argument
"""Return a FileTracer object for a file.
@@ -50,14 +42,12 @@ class CoveragePlugin(object):
trace the file or not. Be prepared for `filename` to refer to all
kinds of files that have nothing to do with your plugin.
- Arguments:
- filename (str): The path to the file being considered. This is the
- absolute real path to the file. If you are comparing to other
- paths, be sure to take this into account.
+ `filename` is a string, the path to the file being considered. This is
+ the absolute real path to the file. If you are comparing to other
+ paths, be sure to take this into account.
- Returns:
- FileTracer: the :class:`FileTracer` object to use to trace
- `filename`, or None if this plugin cannot trace this file.
+ Returns a :class:`FileTracer` object to use to trace `filename`, or
+ None if this plugin cannot trace this file.
"""
return None
@@ -99,8 +89,7 @@ class FileTracer(object):
to own the mapping from Python execution back to whatever source
filename was originally the source of the code.
- Returns:
- The filename to credit with this execution.
+ Returns the filename to credit with this execution.
"""
_needs_to_implement(self, "source_filename")
@@ -110,13 +99,12 @@ class FileTracer(object):
FileTracers can provide dynamically determined filenames by
implementing dynamic_source_filename. Invoking that function is
- expensive. To determine whether to invoke it, coverage.py uses
- the result of this function to know if it needs to bother invoking
+ expensive. To determine whether to invoke it, coverage.py uses the
+ result of this function to know if it needs to bother invoking
:meth:`dynamic_source_filename`.
- Returns:
- boolean: True if :meth:`dynamic_source_filename` should be called
- to get dynamic source filenames.
+ Returns true if :meth:`dynamic_source_filename` should be called to get
+ dynamic source filenames.
"""
return False
@@ -130,9 +118,8 @@ class FileTracer(object):
This function will not be invoked if
:meth:`has_dynamic_source_filename` returns False.
- Returns:
- The source filename for this frame, or None if this frame shouldn't
- be measured.
+ Returns the source filename for this frame, or None if this frame
+ shouldn't be measured.
"""
return None
@@ -150,13 +137,6 @@ class FileTracer(object):
from the source file were executed. Return (-1, -1) in this case to
tell coverage.py that no lines should be recorded for this frame.
- Arguments:
- frame: the call frame to examine.
-
- Returns:
- int, int: a pair of line numbers, the start and end lines
- executed in the source, inclusive.
-
"""
lineno = frame.f_lineno
return lineno, lineno
@@ -169,27 +149,26 @@ class FileReporter(object):
self.filename = filename
def __repr__(self):
- return (
- # pylint: disable=redundant-keyword-arg
- "<{self.__class__.__name__}"
- " filename={self.filename!r}>".format(self=self)
- )
+ return "<{0.__class__.__name__} filename={0.filename!r}>".format(self)
+
+ def relative_filename(self):
+ return files.relative_filename(self.filename)
# Annoying comparison operators. Py3k wants __lt__ etc, and Py2k needs all
# of them defined.
+ def __eq__(self, other):
+ return isinstance(other, FileReporter) and self.filename == other.filename
+
+ def __ne__(self, other):
+ return not (self == other)
+
def __lt__(self, other):
return self.filename < other.filename
def __le__(self, other):
return self.filename <= other.filename
- def __eq__(self, other):
- return self.filename == other.filename
-
- def __ne__(self, other):
- return self.filename != other.filename
-
def __gt__(self, other):
return self.filename > other.filename
@@ -247,6 +226,8 @@ class FileReporter(object):
For example, the file a/b/c.py will return 'a_b_c_py'
+ You should not need to override this method.
+
"""
- name = os.path.splitdrive(self.name)[1]
+ name = os.path.splitdrive(self.relative_filename())[1]
return re.sub(r"[\\/.:]", "_", name)
diff --git a/coverage/plugin_support.py b/coverage/plugin_support.py
new file mode 100644
index 00000000..23c1bc1a
--- /dev/null
+++ b/coverage/plugin_support.py
@@ -0,0 +1,249 @@
+# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
+# For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt
+
+"""Support for plugins."""
+
+import os.path
+import sys
+
+from coverage.misc import CoverageException
+from coverage.plugin import CoveragePlugin, FileTracer, FileReporter
+
+
+class Plugins(object):
+ """The currently loaded collection of coverage.py plugins."""
+
+ def __init__(self):
+ self.order = []
+ self.names = {}
+ self.file_tracers = []
+
+ self.current_module = None
+ self.debug = None
+
+ @classmethod
+ def load_plugins(cls, modules, config, debug=None):
+ """Load plugins from `modules`.
+
+ Returns a list of loaded and configured plugins.
+
+ """
+ plugins = cls()
+ plugins.debug = debug
+
+ for module in modules:
+ plugins.current_module = module
+ __import__(module)
+ mod = sys.modules[module]
+
+ coverage_init = getattr(mod, "coverage_init", None)
+ if not coverage_init:
+ raise CoverageException(
+ "Plugin module %r didn't define a coverage_init function" % module
+ )
+
+ options = config.get_plugin_options(module)
+ coverage_init(plugins, options)
+
+ plugins.current_module = None
+ return plugins
+
+ def add_file_tracer(self, plugin):
+ """Add a file tracer plugin.
+
+ `plugin` is an instance of a third-party plugin class. It must
+ implement the :meth:`CoveragePlugin.file_tracer` method.
+
+ """
+ self._add_plugin(plugin, self.file_tracers)
+
+ def add_noop(self, plugin):
+ """Add a plugin that does nothing.
+
+ This is only useful for testing the plugin support.
+
+ """
+ self._add_plugin(plugin, None)
+
+ def _add_plugin(self, plugin, specialized):
+ """Add a plugin object.
+
+ `plugin` is a :class:`CoveragePlugin` instance to add. `specialized`
+ is a list to append the plugin to.
+
+ """
+ plugin_name = "%s.%s" % (self.current_module, plugin.__class__.__name__)
+ if self.debug and self.debug.should('plugin'):
+ self.debug.write("Loaded plugin %r: %r" % (self.current_module, plugin))
+ labelled = LabelledDebug("plugin %r" % (self.current_module,), self.debug)
+ plugin = DebugPluginWrapper(plugin, labelled)
+
+ # pylint: disable=attribute-defined-outside-init
+ plugin._coverage_plugin_name = plugin_name
+ plugin._coverage_enabled = True
+ self.order.append(plugin)
+ self.names[plugin_name] = plugin
+ if specialized is not None:
+ specialized.append(plugin)
+
+ def __nonzero__(self):
+ return bool(self.order)
+
+ __bool__ = __nonzero__
+
+ def __iter__(self):
+ return iter(self.order)
+
+ def get(self, plugin_name):
+ """Return a plugin by name."""
+ return self.names[plugin_name]
+
+
+class LabelledDebug(object):
+ """A Debug writer, but with labels for prepending to the messages."""
+
+ def __init__(self, label, debug, prev_labels=()):
+ self.labels = list(prev_labels) + [label]
+ self.debug = debug
+
+ def add_label(self, label):
+ """Add a label to the writer, and return a new `LabelledDebug`."""
+ return LabelledDebug(label, self.debug, self.labels)
+
+ def message_prefix(self):
+ """The prefix to use on messages, combining the labels."""
+ prefixes = self.labels + ['']
+ return ":\n".join(" "*i+label for i, label in enumerate(prefixes))
+
+ def write(self, message):
+ """Write `message`, but with the labels prepended."""
+ self.debug.write("%s%s" % (self.message_prefix(), message))
+
+
+class DebugPluginWrapper(CoveragePlugin):
+ """Wrap a plugin, and use debug to report on what it's doing."""
+
+ def __init__(self, plugin, debug):
+ super(DebugPluginWrapper, self).__init__()
+ self.plugin = plugin
+ self.debug = debug
+
+ def file_tracer(self, filename):
+ tracer = self.plugin.file_tracer(filename)
+ self.debug.write("file_tracer(%r) --> %r" % (filename, tracer))
+ if tracer:
+ debug = self.debug.add_label("file %r" % (filename,))
+ tracer = DebugFileTracerWrapper(tracer, debug)
+ return tracer
+
+ def file_reporter(self, filename):
+ reporter = self.plugin.file_reporter(filename)
+ self.debug.write("file_reporter(%r) --> %r" % (filename, reporter))
+ if reporter:
+ debug = self.debug.add_label("file %r" % (filename,))
+ reporter = DebugFileReporterWrapper(filename, reporter, debug)
+ return reporter
+
+ def sys_info(self):
+ return self.plugin.sys_info()
+
+
+class DebugFileTracerWrapper(FileTracer):
+ """A debugging `FileTracer`."""
+
+ def __init__(self, tracer, debug):
+ self.tracer = tracer
+ self.debug = debug
+
+ def _show_frame(self, frame):
+ """A short string identifying a frame, for debug messages."""
+ return "%s@%d" % (
+ os.path.basename(frame.f_code.co_filename),
+ frame.f_lineno,
+ )
+
+ def source_filename(self):
+ sfilename = self.tracer.source_filename()
+ self.debug.write("source_filename() --> %r" % (sfilename,))
+ return sfilename
+
+ def has_dynamic_source_filename(self):
+ has = self.tracer.has_dynamic_source_filename()
+ self.debug.write("has_dynamic_source_filename() --> %r" % (has,))
+ return has
+
+ def dynamic_source_filename(self, filename, frame):
+ dyn = self.tracer.dynamic_source_filename(filename, frame)
+ self.debug.write("dynamic_source_filename(%r, %s) --> %r" % (
+ filename, self._show_frame(frame), dyn,
+ ))
+ return dyn
+
+ def line_number_range(self, frame):
+ pair = self.tracer.line_number_range(frame)
+ self.debug.write("line_number_range(%s) --> %r" % (self._show_frame(frame), pair))
+ return pair
+
+
+class DebugFileReporterWrapper(FileReporter):
+ """A debugging `FileReporter`."""
+
+ def __init__(self, filename, reporter, debug):
+ super(DebugFileReporterWrapper, self).__init__(filename)
+ self.reporter = reporter
+ self.debug = debug
+
+ def relative_filename(self):
+ ret = self.reporter.relative_filename()
+ self.debug.write("relative_filename() --> %r" % (ret,))
+ return ret
+
+ def statements(self):
+ ret = self.reporter.statements()
+ self.debug.write("statements() --> %r" % (ret,))
+ return ret
+
+ def excluded_statements(self):
+ ret = self.reporter.excluded_statements()
+ self.debug.write("excluded_statements() --> %r" % (ret,))
+ return ret
+
+ def translate_lines(self, lines):
+ ret = self.reporter.translate_lines(lines)
+ self.debug.write("translate_lines(%r) --> %r" % (lines, ret))
+ return ret
+
+ def translate_arcs(self, arcs):
+ ret = self.reporter.translate_arcs(arcs)
+ self.debug.write("translate_arcs(%r) --> %r" % (arcs, ret))
+ return ret
+
+ def no_branch_lines(self):
+ ret = self.reporter.no_branch_lines()
+ self.debug.write("no_branch_lines() --> %r" % (ret,))
+ return ret
+
+ def exit_counts(self):
+ ret = self.reporter.exit_counts()
+ self.debug.write("exit_counts() --> %r" % (ret,))
+ return ret
+
+ def arcs(self):
+ ret = self.reporter.arcs()
+ self.debug.write("arcs() --> %r" % (ret,))
+ return ret
+
+ def source(self):
+ ret = self.reporter.source()
+ self.debug.write("source() --> %d chars" % (len(ret),))
+ return ret
+
+ def source_token_lines(self):
+ ret = list(self.reporter.source_token_lines())
+ self.debug.write("source_token_lines() --> %d tokens" % (len(ret),))
+ return ret
+
+ def should_be_python(self):
+ ret = self.reporter.should_be_python()
+ self.debug.write("should_be_python() --> %r" % (ret,))
+ return ret
diff --git a/coverage/python.py b/coverage/python.py
index 19212a5b..94d20fd8 100644
--- a/coverage/python.py
+++ b/coverage/python.py
@@ -1,37 +1,32 @@
+# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
+# For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt
+
"""Python source expertise for coverage.py"""
import os.path
-import sys
-import tokenize
import zipimport
-from coverage import env
-from coverage.backward import unicode_class
-from coverage.files import FileLocator
-from coverage.misc import NoSource, join_regex
+from coverage import env, files
+from coverage.misc import contract, NoSource, join_regex
from coverage.parser import PythonParser
from coverage.phystokens import source_token_lines, source_encoding
from coverage.plugin import FileReporter
+@contract(returns='bytes')
def read_python_source(filename):
"""Read the Python source text from `filename`.
- Returns a str: unicode on Python 3, bytes on Python 2.
+ Returns bytes.
"""
- # Python 3.2 provides `tokenize.open`, the best way to open source files.
- if sys.version_info >= (3, 2):
- f = tokenize.open(filename)
- else:
- f = open(filename, "rU")
-
- with f:
- return f.read()
+ with open(filename, "rb") as f:
+ return f.read().replace(b"\r\n", b"\n").replace(b"\r", b"\n")
+@contract(returns='unicode')
def get_python_source(filename):
- """Return the source code, as a str."""
+ """Return the source code, as unicode."""
base, ext = os.path.splitext(filename)
if ext == ".py" and env.WINDOWS:
exts = [".py", ".pyw"]
@@ -48,13 +43,13 @@ def get_python_source(filename):
# Maybe it's in a zip file?
source = get_zip_bytes(try_filename)
if source is not None:
- if env.PY3:
- source = source.decode(source_encoding(source))
break
else:
# Couldn't find source.
raise NoSource("No source for code: '%s'." % filename)
+ source = source.decode(source_encoding(source), "replace")
+
# Python code should always end with a line with a newline.
if source and source[-1] != '\n':
source += '\n'
@@ -62,6 +57,7 @@ def get_python_source(filename):
return source
+@contract(returns='bytes|None')
def get_zip_bytes(filename):
"""Get data from `filename` if it is a zip file path.
@@ -82,7 +78,6 @@ def get_zip_bytes(filename):
data = zi.get_data(parts[1])
except IOError:
continue
- assert isinstance(data, bytes)
return data
return None
@@ -92,7 +87,6 @@ class PythonFileReporter(FileReporter):
def __init__(self, morf, coverage=None):
self.coverage = coverage
- file_locator = coverage.file_locator if coverage else FileLocator()
if hasattr(morf, '__file__'):
filename = morf.__file__
@@ -105,24 +99,26 @@ class PythonFileReporter(FileReporter):
elif filename.endswith('$py.class'): # Jython
filename = filename[:-9] + ".py"
- super(PythonFileReporter, self).__init__(
- file_locator.canonical_filename(filename)
- )
+ super(PythonFileReporter, self).__init__(files.canonical_filename(filename))
if hasattr(morf, '__name__'):
name = morf.__name__
name = name.replace(".", os.sep) + ".py"
else:
- name = file_locator.relative_filename(filename)
- self.name = name
+ name = files.relative_filename(filename)
+ self.relname = name
self._source = None
self._parser = None
self._statements = None
self._excluded = None
+ def relative_filename(self):
+ return self.relname
+
@property
def parser(self):
+ """Lazily create a :class:`PythonParser`."""
if self._parser is None:
self._parser = PythonParser(
filename=self.filename,
@@ -161,13 +157,10 @@ class PythonFileReporter(FileReporter):
def exit_counts(self):
return self.parser.exit_counts()
+ @contract(returns='unicode')
def source(self):
if self._source is None:
self._source = get_python_source(self.filename)
- if env.PY2:
- encoding = source_encoding(self._source)
- self._source = self._source.decode(encoding, "replace")
- assert isinstance(self._source, unicode_class)
return self._source
def should_be_python(self):
diff --git a/coverage/pytracer.py b/coverage/pytracer.py
index 0eafbef0..c657ad01 100644
--- a/coverage/pytracer.py
+++ b/coverage/pytracer.py
@@ -1,7 +1,18 @@
-"""Raw data collector for Coverage."""
+# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
+# For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt
+"""Raw data collector for coverage.py."""
+
+import dis
import sys
+from coverage import env
+
+# We need the YIELD_VALUE opcode below, in a comparison-friendly form.
+YIELD_VALUE = dis.opmap['YIELD_VALUE']
+if env.PY2:
+ YIELD_VALUE = chr(YIELD_VALUE)
+
class PyTracer(object):
"""Python implementation of the raw data tracer."""
@@ -79,9 +90,11 @@ class PyTracer(object):
if tracename not in self.data:
self.data[tracename] = {}
self.cur_file_dict = self.data[tracename]
- # Set the last_line to -1 because the next arc will be entering a
- # code block, indicated by (-1, n).
- self.last_line = -1
+ # The call event is really a "start frame" event, and happens for
+ # function calls and re-entering generators. The f_lasti field is
+ # -1 for calls, and a real offset for generators. Use -1 as the
+ # line number for calls, and the real line number for generators.
+ self.last_line = -1 if (frame.f_lasti < 0) else frame.f_lineno
elif event == 'line':
# Record an executed line.
if self.cur_file_dict is not None:
@@ -93,8 +106,12 @@ class PyTracer(object):
self.last_line = lineno
elif event == 'return':
if self.arcs and self.cur_file_dict:
- first = frame.f_code.co_firstlineno
- self.cur_file_dict[(self.last_line, -first)] = None
+ # Record an arc leaving the function, but beware that a
+ # "return" event might just mean yielding from a generator.
+ bytecode = frame.f_code.co_code[frame.f_lasti]
+ if bytecode != YIELD_VALUE:
+ first = frame.f_code.co_firstlineno
+ self.cur_file_dict[(self.last_line, -first)] = None
# Leaving this function, pop the filename stack.
self.cur_file_dict, self.last_line = self.data_stack.pop()
elif event == 'exception':
diff --git a/coverage/report.py b/coverage/report.py
index 33a46070..fa081862 100644
--- a/coverage/report.py
+++ b/coverage/report.py
@@ -1,4 +1,7 @@
-"""Reporter foundation for Coverage."""
+# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
+# For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt
+
+"""Reporter foundation for coverage.py."""
import os
diff --git a/coverage/results.py b/coverage/results.py
index c1718d46..f15fae74 100644
--- a/coverage/results.py
+++ b/coverage/results.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
+
"""Results of coverage measurement."""
import collections
@@ -9,19 +12,19 @@ from coverage.misc import format_lines
class Analysis(object):
"""The results of analyzing a FileReporter."""
- def __init__(self, cov, file_reporters):
- self.coverage = cov
+ def __init__(self, data, file_reporters):
+ self.data = data
self.file_reporter = file_reporters
self.filename = self.file_reporter.filename
self.statements = self.file_reporter.statements()
self.excluded = self.file_reporter.excluded_statements()
# Identify missing statements.
- executed = self.coverage.data.executed_lines(self.filename)
+ executed = self.data.lines(self.filename) or []
executed = self.file_reporter.translate_lines(executed)
self.missing = self.statements - executed
- if self.coverage.data.has_arcs():
+ if self.data.has_arcs():
self.no_branch = self.file_reporter.no_branch_lines()
n_branches = self.total_branches()
mba = self.missing_branch_arcs()
@@ -53,7 +56,7 @@ class Analysis(object):
def has_arcs(self):
"""Were arcs measured in this result?"""
- return self.coverage.data.has_arcs()
+ return self.data.has_arcs()
def arc_possibilities(self):
"""Returns a sorted list of the arcs in the code."""
@@ -61,7 +64,7 @@ class Analysis(object):
def arcs_executed(self):
"""Returns a sorted list of the arcs actually executed in the code."""
- executed = self.coverage.data.executed_arcs(self.filename)
+ executed = self.data.arcs(self.filename) or []
executed = self.file_reporter.translate_arcs(executed)
return sorted(executed)
@@ -101,10 +104,13 @@ class Analysis(object):
# Exclude arcs here which connect a line to itself. They can occur
# in executed data in some cases. This is where they can cause
# trouble, and here is where it's the least burden to remove them.
+ # Also, generators can somehow cause arcs from "enter" to "exit", so
+ # make sure we have at least one positive value.
unpredicted = (
e for e in executed
if e not in possible
and e[0] != e[1]
+ and (e[0] > 0 or e[1] > 0)
)
return sorted(unpredicted)
diff --git a/coverage/summary.py b/coverage/summary.py
index 5b8c903f..03270c04 100644
--- a/coverage/summary.py
+++ b/coverage/summary.py
@@ -1,10 +1,13 @@
+# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
+# For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt
+
"""Summary reporting"""
import sys
from coverage.report import Reporter
from coverage.results import Numbers
-from coverage.misc import NotPython
+from coverage.misc import NotPython, CoverageException
class SummaryReporter(Reporter):
@@ -23,7 +26,7 @@ class SummaryReporter(Reporter):
self.find_file_reporters(morfs)
# Prepare the formatting strings
- max_name = max([len(fr.name) for fr in self.file_reporters] + [5])
+ max_name = max([len(fr.relative_filename()) for fr in self.file_reporters] + [5])
fmt_name = "%%- %ds " % max_name
fmt_err = "%s %s: %s\n"
header = (fmt_name % "Name") + " Stmts Miss"
@@ -65,7 +68,7 @@ class SummaryReporter(Reporter):
if no_missing_lines and no_missing_branches:
continue
- args = (fr.name, nums.n_statements, nums.n_missing)
+ args = (fr.relative_filename(), nums.n_statements, nums.n_missing)
if self.branches:
args += (nums.n_branches, nums.n_partial_branches)
args += (nums.pc_covered_str,)
@@ -87,7 +90,7 @@ class SummaryReporter(Reporter):
if typ is NotPython and not fr.should_be_python():
report_it = False
if report_it:
- outfile.write(fmt_err % (fr.name, typ.__name__, msg))
+ outfile.write(fmt_err % (fr.relative_filename(), typ.__name__, msg))
if total.n_files > 1:
outfile.write(rule)
@@ -98,5 +101,6 @@ class SummaryReporter(Reporter):
if self.config.show_missing:
args += ("",)
outfile.write(fmt_coverage % args)
-
- return total.pc_covered
+ if not total.n_files:
+ raise CoverageException("No data to report.")
+ return total.n_statements and total.pc_covered
diff --git a/coverage/templite.py b/coverage/templite.py
index c102a8f2..8595b868 100644
--- a/coverage/templite.py
+++ b/coverage/templite.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
+
"""A simple Python template renderer, for a nano-subset of Django syntax."""
# Coincidentally named the same as http://code.activestate.com/recipes/496702/
@@ -201,7 +204,7 @@ class Templite(object):
for var_name in self.all_vars - self.loop_vars:
vars_code.add_line("c_%s = context[%r]" % (var_name, var_name))
- code.add_line("return ''.join(result)")
+ code.add_line('return "".join(result)')
code.dedent()
self._render_function = code.get_globals()['render_function']
diff --git a/coverage/test_helpers.py b/coverage/test_helpers.py
index 55a67a0c..3ddb48ba 100644
--- a/coverage/test_helpers.py
+++ b/coverage/test_helpers.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
+
"""Mixin classes to help make good tests."""
import atexit
@@ -147,8 +150,15 @@ class TempDirMixin(SysPathAwareMixin, ModuleAwareMixin, TestCase):
"""
# Our own setting: most of these tests run in their own temp directory.
+ # Set this to False in your subclass if you don't want a temp directory
+ # created.
run_in_temp_dir = True
+ # Set this if you aren't creating any files with make_file, but still want
+ # the temp directory. This will stop the test behavior checker from
+ # complaining.
+ no_files_in_temp_dir = False
+
def setUp(self):
super(TempDirMixin, self).setUp()
@@ -165,8 +175,8 @@ class TempDirMixin(SysPathAwareMixin, ModuleAwareMixin, TestCase):
class_behavior = self.class_behavior()
class_behavior.tests += 1
- class_behavior.test_method_made_any_files = False
class_behavior.temp_dir = self.run_in_temp_dir
+ class_behavior.no_files_ok = self.no_files_in_temp_dir
self.addCleanup(self.check_behavior)
@@ -239,6 +249,7 @@ class TempDirMixin(SysPathAwareMixin, ModuleAwareMixin, TestCase):
self.tests = 0
self.skipped = 0
self.temp_dir = True
+ self.no_files_ok = False
self.tests_making_files = 0
self.test_method_made_any_files = False
@@ -249,14 +260,14 @@ class TempDirMixin(SysPathAwareMixin, ModuleAwareMixin, TestCase):
def report_on_class_behavior(cls):
"""Called at process exit to report on class behavior."""
for test_class, behavior in cls.class_behaviors.items():
+ bad = ""
if behavior.tests <= behavior.skipped:
bad = ""
elif behavior.temp_dir and behavior.tests_making_files == 0:
- bad = "Inefficient"
+ if not behavior.no_files_ok:
+ bad = "Inefficient"
elif not behavior.temp_dir and behavior.tests_making_files > 0:
bad = "Unsafe"
- else:
- bad = ""
if bad:
if behavior.temp_dir:
diff --git a/coverage/version.py b/coverage/version.py
index 51e1310f..f95881cc 100644
--- a/coverage/version.py
+++ b/coverage/version.py
@@ -1,9 +1,12 @@
+# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
+# For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt
+
"""The version and URL for coverage.py"""
# This file is exec'ed in setup.py, don't import anything!
-__version__ = "4.0a6" # see detailed history in CHANGES.txt
+__version__ = "4.0a7" # see detailed history in CHANGES.txt
__url__ = "https://coverage.readthedocs.org"
if max(__version__).isalpha():
# For pre-releases, use a version-specific URL.
- __url__ += "/en/" + __version__
+ __url__ += "/en/coverage-" + __version__
diff --git a/coverage/xmlreport.py b/coverage/xmlreport.py
index 996f19a2..b60cecd2 100644
--- a/coverage/xmlreport.py
+++ b/coverage/xmlreport.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
+
"""XML reporting for coverage.py"""
import os
@@ -5,9 +8,15 @@ import sys
import time
import xml.dom.minidom
-from coverage import __url__, __version__
+from coverage import __url__, __version__, files
from coverage.report import Reporter
+DTD_URL = (
+ 'https://raw.githubusercontent.com/cobertura/web/'
+ 'f0366e5e2cf18f111cbd61fc34ef720a6584ba02'
+ '/htdocs/xml/coverage-03.dtd'
+)
+
def rate(hit, num):
"""Return the fraction of `hit`/`num`, as a string."""
@@ -20,10 +29,9 @@ def rate(hit, num):
class XmlReporter(Reporter):
"""A reporter for writing Cobertura-style XML coverage results."""
- def __init__(self, coverage, config, file_locator):
+ def __init__(self, coverage, config):
super(XmlReporter, self).__init__(coverage, config)
- self.file_locator = file_locator
self.source_paths = set()
self.packages = {}
self.xml_out = None
@@ -42,11 +50,7 @@ class XmlReporter(Reporter):
# Create the DOM that will store the data.
impl = xml.dom.minidom.getDOMImplementation()
- docType = impl.createDocumentType(
- "coverage", None,
- "http://cobertura.sourceforge.net/xml/coverage-03.dtd"
- )
- self.xml_out = impl.createDocument(None, "coverage", docType)
+ self.xml_out = impl.createDocument(None, "coverage", None)
# Write header stuff.
xcoverage = self.xml_out.documentElement
@@ -55,6 +59,7 @@ class XmlReporter(Reporter):
xcoverage.appendChild(self.xml_out.createComment(
" Generated by coverage.py: %s " % __url__
))
+ xcoverage.appendChild(self.xml_out.createComment(" Based on %s " % DTD_URL))
# Call xml_file for each file in the data.
self.report_files(self.xml_file, morfs)
@@ -122,15 +127,15 @@ class XmlReporter(Reporter):
# Create the 'lines' and 'package' XML elements, which
# are populated later. Note that a package == a directory.
- filename = self.file_locator.relative_filename(fr.filename)
+ filename = files.relative_filename(fr.filename)
filename = filename.replace("\\", "/")
dirname = os.path.dirname(filename) or "."
parts = dirname.split("/")
dirname = "/".join(parts[:self.config.xml_package_depth])
package_name = dirname.replace("/", ".")
- className = fr.name
+ className = fr.relative_filename()
- self.source_paths.add(self.file_locator.relative_dir.rstrip('/'))
+ self.source_paths.add(files.relative_directory().rstrip('/'))
package = self.packages.setdefault(package_name, [{}, 0, 0, 0, 0])
xclass = self.xml_out.createElement("class")
@@ -145,6 +150,7 @@ class XmlReporter(Reporter):
xclass.setAttribute("complexity", "0")
branch_stats = analysis.branch_stats()
+ missing_branch_arcs = analysis.missing_branch_arcs()
# For each statement, create an XML 'line' element.
for line in sorted(analysis.statements):
@@ -163,6 +169,9 @@ class XmlReporter(Reporter):
"condition-coverage",
"%d%% (%d/%d)" % (100*taken/total, taken, total)
)
+ if line in missing_branch_arcs:
+ annlines = ["exit" if b < 0 else str(b) for b in missing_branch_arcs[line]]
+ xline.setAttribute("missing-branches", ",".join(annlines))
xlines.appendChild(xline)
class_lines = len(analysis.statements)
diff --git a/doc/api.rst b/doc/api.rst
index 815b75e0..2904058b 100644
--- a/doc/api.rst
+++ b/doc/api.rst
@@ -1,8 +1,11 @@
+.. Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
+.. For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt
+
.. _api:
-============
-Coverage API
-============
+===============
+Coverage.py API
+===============
.. :history: 20090524T134300, brand new docs.
.. :history: 20090613T164000, final touches for 3.0
@@ -30,17 +33,27 @@ available in the command line interface. For example, a simple use would be::
cov.html_report()
-The coverage module
--------------------
+The Coverage class
+------------------
.. module:: coverage
.. autoclass:: Coverage
:members:
+ :exclude-members: use_cache
+ :special-members: __init__
+
+
+The CoverageData class
+----------------------
+
+.. autoclass:: CoverageData
+ :members:
+ :special-members: __init__
-Starting coverage automatically
--------------------------------
+Starting coverage.py automatically
+----------------------------------
This function is used to start coverage measurement automatically when Python
starts. See :ref:`subprocess` for details.
diff --git a/doc/branch.rst b/doc/branch.rst
index 2738447e..9cee70bf 100644
--- a/doc/branch.rst
+++ b/doc/branch.rst
@@ -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
+
.. _branch:
===========================
@@ -13,7 +16,7 @@ Branch coverage measurement
:linenothreshold: 5
-In addition to the usual statement coverage, Coverage.py also supports branch
+In addition to the usual statement coverage, coverage.py also supports branch
coverage measurement. Where a line in your program could jump to more than one
next line, coverage.py tracks which of those destinations are actually visited,
and flags lines that haven't visited all of their possible destinations.
diff --git a/doc/changes.rst b/doc/changes.rst
index c19d42f3..64be4f2f 100644
--- a/doc/changes.rst
+++ b/doc/changes.rst
@@ -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
+
.. _changes:
====================================
@@ -37,13 +40,13 @@ history, see the `CHANGES.txt`_ file in the source tree.
.. _changes_40:
-Version 4.0a5 pre-release --- 16 February 2015
-----------------------------------------------
+Version 4.0a6 pre-release --- 21 June 2015
+------------------------------------------
Backward incompatibilities:
-- CPython versions supported are now Python 2.6, 2.7, 3.3, 3.4 and 3.5a1.
- PyPy2 2.4 and PyPy3 2.4 are also supported.
+- CPython versions supported are now Python 2.6, 2.7, 3.3, 3.4 and 3.5b2.
+ PyPy2 2.4, 2.6, and PyPy3 2.4 are also supported.
- The original command line switches (`-x` to run a program, etc) are no
longer supported.
@@ -51,6 +54,10 @@ Backward incompatibilities:
- The ``COVERAGE_OPTIONS`` environment variable is no longer supported. It was
a hack for ``--timid`` before configuration files were available.
+- The original module-level function interface to coverage.py is no longer
+ supported. You must now create a ``coverage.Coverage`` object, and use
+ methods on it.
+
Major new features:
- Gevent, eventlet, and greenlet are now supported, closing `issue 149`_.
@@ -64,7 +71,8 @@ Major new features:
- Plugins: third parties can write plugins to add file support for non-Python
files, such as web application templating engines, or languages that compile
- down to Python.
+ down to Python. A plugin for measuring Django template coverage is
+ available: `django_coverage_plugin`_
- Wildly experimental: support for measuring processes started by the
multiprocessing module. To use, set ``--concurrency=multiprocessing``,
@@ -88,12 +96,20 @@ New features:
- The ``report`` command can now show missing branches when reporting on branch
coverage. Thanks, Steve Leonard. Closes `issue 230`_.
+- The ``coverage combine`` command now accepts any number of directories as
+ arguments, and will combine all the data files from those directories. This
+ means you don't have to copy the files to one directory before combining.
+ Thanks, Christine Lytwynec. Finishes `issue 354`_.
+
- A new configuration option for the XML report: ``[xml] package_depth``
controls which directories are identified as packages in the report.
Directories deeper than this depth are not reported as packages.
The default is that all directories are reported as packages.
Thanks, Lex Berezhny.
+- The COVERAGE_DEBUG environment variable can be used to set the `[run]debug`
+ configuration option to control what internal operations are logged.
+
Improvements:
- Coverage.py now always adds the current directory to sys.path, so that
@@ -113,7 +129,7 @@ Improvements:
fixed.
- A new warning is possible, if a desired file isn't measured because it was
- imported before coverage was started (`issue 353`_).
+ imported before coverage.py was started (`issue 353`_).
- The `coverage.process_startup` function now will start coverage measurement
only once, no matter how many times it is called. This fixes problems due
@@ -124,8 +140,8 @@ API changes:
- The class defined in the coverage module is now called ``Coverage`` instead
of ``coverage``, though the old name still works, for backward compatibility.
-- You can now programmatically adjust the configuration of coverage by setting
- items on `Coverage.config` after construction.
+- You can now programmatically adjust the configuration of coverage.py by
+ setting items on `Coverage.config` after construction.
- If the `config_file` argument to the Coverage constructor is specified as
".coveragerc", it is treated as if it were True. This means setup.cfg is
@@ -141,6 +157,16 @@ Bug fixes:
- The ``fail-under`` value is now rounded the same as reported results,
preventing paradoxical results, fixing `issue 284`_.
+- Branch coverage couldn't properly handle certain extremely long files. This
+ is now fixed (`issue 359`_).
+
+- Branch coverage didn't understand yield statements properly. Mickie Betz
+ persisted in pursuing this despite Ned's pessimism. Fixes `issue 308`_ and
+ `issue 324`_.
+
+- Files with incorrect encoding declaration comments are no longer ignored by
+ the reporting commands, fixing `issue 351`_.
+
- Empty files are now reported as 100% covered in the XML report, not 0%
covered (`issue 345`_).
@@ -153,6 +179,7 @@ Bug fixes:
- The annotate command will now annotate all files, not just ones relative to
the current directory, fixing `issue 57`_.
+.. _django_coverage_plugin: https://pypi.python.org/pypi/django_coverage_plugin
.. _issue 57: https://bitbucket.org/ned/coveragepy/issue/57/annotate-command-fails-to-annotate-many
.. _issue 69: https://bitbucket.org/ned/coveragepy/issue/69/coverage-html-overwrite-files-that-doesnt
.. _issue 94: https://bitbucket.org/ned/coveragepy/issue/94/coverage-xml-doesnt-produce-sources
@@ -164,15 +191,20 @@ Bug fixes:
.. _issue 285: https://bitbucket.org/ned/coveragepy/issue/285/xml-report-fails-if-output-file-directory
.. _issue 303: https://bitbucket.org/ned/coveragepy/issue/303/unicodedecodeerror
.. _issue 304: https://bitbucket.org/ned/coveragepy/issue/304/attempt-to-get-configuration-from-setupcfg
+.. _issue 308: https://bitbucket.org/ned/coveragepy/issue/308/yield-lambda-branch-coverage
.. _issue 314: https://bitbucket.org/ned/coveragepy/issue/314/fail_under-param-not-working-in-coveragerc
.. _issue 315: https://bitbucket.org/ned/coveragepy/issue/315/option-to-omit-empty-files-eg-__init__py
+.. _issue 324: https://bitbucket.org/ned/coveragepy/issue/324/yield-in-loop-confuses-branch-coverage
.. _issue 331: https://bitbucket.org/ned/coveragepy/issue/331/failure-of-encoding-detection-on-python2
.. _issue 340: https://bitbucket.org/ned/coveragepy/issue/340/keyerror-subpy
.. _issue 342: https://bitbucket.org/ned/coveragepy/issue/342/console-and-html-coverage-reports-differ
.. _issue 345: https://bitbucket.org/ned/coveragepy/issue/345/xml-reports-line-rate-0-for-empty-files
+.. _issue 351: https://bitbucket.org/ned/coveragepy/issue/351/files-with-incorrect-encoding-are-ignored
.. _issue 353: https://bitbucket.org/ned/coveragepy/issue/353/40a3-introduces-an-unexpected-third-case
+.. _issue 354: https://bitbucket.org/ned/coveragepy/issue/354/coverage-combine-should-take-a-list-of
.. _issue 357: https://bitbucket.org/ned/coveragepy/issue/357/behavior-changed-when-coveragerc-is
.. _issue 358: https://bitbucket.org/ned/coveragepy/issue/358/all-coverage-commands-should-adjust
+.. _issue 359: https://bitbucket.org/ned/coveragepy/issue/359/xml-report-chunk-error
.. _changes_371:
@@ -200,9 +232,9 @@ Version 3.7 --- 6 October 2013
- Running code with ``coverage run -m`` now behaves more like Python does,
setting sys.path properly, which fixes `issue 207`_ and `issue 242`_.
-- Coverage can now run .pyc files directly, closing `issue 264`_.
+- Coverage.py can now run .pyc files directly, closing `issue 264`_.
-- Coverage properly supports .pyw files, fixing `issue 261`_.
+- Coverage.py properly supports .pyw files, fixing `issue 261`_.
- Omitting files within a tree specified with the ``source`` option would
cause them to be incorrectly marked as unexecuted, as described in
@@ -218,9 +250,10 @@ Version 3.7 --- 6 October 2013
- Trying to create an XML report with no files to report on, would cause a
ZeroDivideError, but no longer does, fixing `issue 250`_.
-- When running a threaded program under the Python tracer, coverage no longer
- issues a spurious warning about the trace function changing: "Trace function
- changed, measurement is likely wrong: None." This fixes `issue 164`_.
+- When running a threaded program under the Python tracer, coverage.py no
+ longer issues a spurious warning about the trace function changing: "Trace
+ function changed, measurement is likely wrong: None." This fixes
+ `issue 164`_.
- Static files necessary for HTML reports are found in system-installed places,
to ease OS-level packaging of coverage.py. Closes `issue 259`_.
@@ -348,8 +381,9 @@ Fixes:
- Jython files now work with the ``--source`` option, fixing `issue 100`_.
-- Running coverage under a debugger is unlikely to work, but it shouldn't fail
- with "TypeError: 'NoneType' object is not iterable". Fixes `issue 201`_.
+- Running coverage.py under a debugger is unlikely to work, but it shouldn't
+ fail with "TypeError: 'NoneType' object is not iterable". Fixes
+ `issue 201`_.
- On some Linux distributions, when installed with the OS package manager,
coverage.py would report its own code as part of the results. Now it won't,
@@ -471,10 +505,10 @@ Version 3.5.2 --- 4 May 2012
Version 3.5.1 --- 23 September 2011
-----------------------------------
-- When combining data files from parallel runs, you can now instruct coverage
- about which directories are equivalent on different machines. A ``[paths]``
- section in the configuration file lists paths that are to be considered
- equivalent. Finishes `issue 17`_.
+- When combining data files from parallel runs, you can now instruct
+ coverage.py about which directories are equivalent on different machines. A
+ ``[paths]`` section in the configuration file lists paths that are to be
+ considered equivalent. Finishes `issue 17`_.
- for-else constructs are understood better, and don't cause erroneous partial
branch warnings. Fixes `issue 122`_.
@@ -496,7 +530,7 @@ Version 3.5.1 --- 23 September 2011
- In order to help the core developers measure the test coverage of the
standard library, Brandon Rhodes devised an aggressive hack to trick Python
- into running some coverage code before anything else in the process.
+ into running some coverage.py code before anything else in the process.
See the coverage/fullcoverage directory if you are interested.
.. _issue 17: http://bitbucket.org/ned/coveragepy/issue/17/support-combining-coverage-data-from
@@ -653,7 +687,7 @@ Execution and measurement:
- When measuring code running in a virtualenv, most of the system library was
being measured when it shouldn't have been. This is now fixed.
-- Coverage can now be run as a module: ``python -m coverage``. Thanks,
+- Coverage.py can now be run as a module: ``python -m coverage``. Thanks,
Brett Cannon.
.. _issue 1: http://bitbucket.org/ned/coveragepy/issue/1/empty-__init__py-files-are-reported-as-1-executable
@@ -731,7 +765,7 @@ Version 3.2 --- 5 December 2009
- Added a ``--version`` option on the command line.
-- Program execution under coverage is a few percent faster.
+- Program execution under coverage.py is a few percent faster.
- Some exceptions reported by the command line interface have been cleaned up
so that tracebacks inside coverage.py aren't shown. Fixes `issue 23`_.
@@ -792,7 +826,7 @@ Version 3.0.1 --- 7 July 2009
raised. This is now fixed.
- The coverage.py code itself will now not be measured by coverage.py, and no
- coverage modules will be mentioned in the nose ``--with-cover`` plugin.
+ coverage.py modules will be mentioned in the nose ``--with-cover`` plugin.
- When running source files, coverage.py now opens them in universal newline
mode just like Python does. This lets it run Windows files on Mac, for
@@ -802,13 +836,13 @@ Version 3.0.1 --- 7 July 2009
Version 3.0 --- 13 June 2009
----------------------------
-- Coverage is now a package rather than a module. Functionality has been split
- into classes.
+- Coverage.py is now a package rather than a module. Functionality has been
+ split into classes.
- HTML reports and annotation of source files: use the new ``-b`` (browser)
switch. Thanks to George Song for code, inspiration and guidance.
-- The trace function is implemented in C for speed. Coverage runs are now
+- The trace function is implemented in C for speed. Coverage.py runs are now
much faster. Thanks to David Christian for productive micro-sprints and
other encouragement.
diff --git a/doc/cmd.rst b/doc/cmd.rst
index b1eeb633..7827a5ed 100644
--- a/doc/cmd.rst
+++ b/doc/cmd.rst
@@ -1,8 +1,11 @@
+.. Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
+.. For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt
+
.. _cmd:
-===========================
-Coverage command line usage
-===========================
+==============================
+Coverage.py command line usage
+==============================
.. :history: 20090524T134300, brand new docs.
.. :history: 20090613T164000, final touches for 3.0
@@ -29,7 +32,7 @@ and a ``coverage-X.Y`` alias, depending on the version of Python you're using.
For example, when installing on Python 2.7, you will be able to use
``coverage``, ``coverage2``, or ``coverage-2.7`` on the command line.
-Coverage has a number of commands which determine the action performed:
+Coverage.py has a number of commands which determine the action performed:
* **run** -- Run a Python program and collect execution data.
@@ -96,9 +99,9 @@ but before the program invocation::
$ coverage run --source=dir1,dir2 my_program.py arg1 arg2
$ coverage run --source=dir1,dir2 -m packagename.modulename arg1 arg2
-Coverage can measure multi-threaded programs by default. If you are using
+Coverage.py can measure multi-threaded programs by default. If you are using
more exotic concurrency, with the `greenlet`_, `eventlet`_, or `gevent`_
-libraries, then coverage will get very confused. Use the ``--concurrency``
+libraries, then coverage.py will get very confused. Use the ``--concurrency``
switch to properly measure programs using these libraries. Give it a value of
``greenlet``, ``eventlet``, or ``gevent``.
@@ -106,12 +109,12 @@ switch to properly measure programs using these libraries. Give it a value of
.. _gevent: http://www.gevent.org/
.. _eventlet: http://eventlet.net/
-By default, coverage does not measure code installed with the Python
+By default, coverage.py does not measure code installed with the Python
interpreter, for example, the standard library. If you want to measure that
code as well as your own, add the ``-L`` flag.
If your coverage results seem to be overlooking code that you know has been
-executed, try running coverage again with the ``--timid`` flag. This uses a
+executed, try running coverage.py again with the ``--timid`` flag. This uses a
simpler but slower trace method. Projects that use DecoratorTools, including
TurboGears, will need to use ``--timid`` to get correct results.
@@ -149,7 +152,7 @@ could affect the measurement process. The possible warnings include:
.. _cmd_run_debug:
-The ``--debug`` option instructs coverage to log internal details of its
+The ``--debug`` option instructs coverage.py to log internal details of its
operation, to help with diagnosing problems. It takes a comma-separated list
of options, each indicating a facet of operation to log to stderr:
@@ -172,8 +175,8 @@ of options, each indicating a facet of operation to log to stderr:
Data file
---------
-Coverage collects execution data in a file called ".coverage". If need be, you
-can set a new file name with the COVERAGE_FILE environment variable.
+Coverage.py collects execution data in a file called ".coverage". If need be,
+you can set a new file name with the COVERAGE_FILE environment variable.
By default,each run of your program starts with an empty data set. If you need
to run your program multiple times to get complete data (for example, because
@@ -191,9 +194,9 @@ Combining data files
--------------------
If you need to collect coverage data from different machines or processes,
-coverage can combine multiple files into one for reporting. Use the ``-p`` flag
-during execution to append distinguishing information to the .coverage data
-file name.
+coverage.py can combine multiple files into one for reporting. Use the ``-p``
+flag during execution to append distinguishing information to the .coverage
+data file name.
Once you have created a number of these files, you can copy them all to a
single directory, and use the **combine** command to combine them into one
@@ -202,9 +205,9 @@ single directory, and use the **combine** command to combine them into one
$ coverage combine
If the different machines run your code from different places in their file
-systems, coverage won't know how to combine the data. You can tell coverage
-how the different locations correlate with a ``[paths]`` section in your
-configuration file. See :ref:`config_paths` for details.
+systems, coverage.py won't know how to combine the data. You can tell
+coverage.py how the different locations correlate with a ``[paths]`` section in
+your configuration file. See :ref:`config_paths` for details.
If you are collecting and renaming your own data files, you'll need to name
them properly for **combine** to find them. It looks for files named after
@@ -222,7 +225,7 @@ Here are some examples of data files that can be combined::
Reporting
---------
-Coverage provides a few styles of reporting, with the **report**, **html**,
+Coverage.py provides a few styles of reporting, with the **report**, **html**,
**annotate**, and **xml** commands. They share a number of common options.
The command-line arguments are module or file names to report on, if you'd like
@@ -307,7 +310,7 @@ Other common reporting options are described above in :ref:`cmd_reporting`.
HTML annotation
---------------
-Coverage can annotate your source code for which lines were executed
+Coverage.py can annotate your source code for which lines were executed
and which were not. The **html** command creates an HTML report similar to the
**report** summary, but as an HTML file. Each module name links to the source
file decorated to show the status of each line.
diff --git a/doc/conf.py b/doc/conf.py
index 49bcf393..de90dd74 100644
--- a/doc/conf.py
+++ b/doc/conf.py
@@ -1,4 +1,7 @@
# -*- coding: utf-8 -*-
+# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
+# For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt
+
#
# coverage.py documentation build configuration file, created by
# sphinx-quickstart on Wed May 13 22:18:33 2009.
@@ -28,7 +31,6 @@ extensions = [
'sphinx.ext.autodoc',
'sphinx.ext.todo',
'sphinx.ext.ifconfig',
- 'sphinxcontrib.napoleon',
'sphinxcontrib.spelling',
'px_xlator',
]
@@ -46,7 +48,7 @@ source_suffix = '.rst'
master_doc = 'index'
# General information about the project.
-project = u'coverage.py'
+project = u'Coverage.py'
copyright = u'2009\N{EN DASH}2015, Ned Batchelder'
# The version info for the project you're documenting, acts as replacement for
@@ -56,7 +58,7 @@ copyright = u'2009\N{EN DASH}2015, Ned Batchelder'
# The short X.Y version.
version = '4.0'
# The full version, including alpha/beta/rc tags.
-release = '4.0a5'
+release = '4.0a6'
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
@@ -101,7 +103,7 @@ pygments_style = 'sphinx'
# The theme to use for HTML and HTML Help pages. Major themes that come with
# Sphinx are currently 'default' and 'sphinxdoc'.
-html_theme = 'default'
+html_theme = 'classic'
# Theme options are theme-specific and customize the look and feel of a theme
# further. For a list of options available for each theme, see the
@@ -110,7 +112,7 @@ html_theme = 'default'
#html_style = "neds.css"
-html_add_permalinks = False
+#html_add_permalinks = ""
# Add any paths that contain custom themes here, relative to this directory.
html_theme_path = ['_templates']
@@ -179,42 +181,10 @@ htmlhelp_basename = 'coveragepydoc'
spelling_word_list_filename = 'dict.txt'
spelling_show_suggestions = False
-# -- Options for LaTeX output --------------------------------------------------
-
-# The paper size ('letter' or 'a4').
-#latex_paper_size = 'letter'
-
-# The font size ('10pt', '11pt' or '12pt').
-#latex_font_size = '10pt'
-
-# Grouping the document tree into LaTeX files. List of tuples
-# (source start file, target name, title, author, documentclass [howto/manual]).
-latex_documents = [
- ('index', 'coveragepy.tex', u'coverage.py Documentation',
- u'Ned Batchelder', 'manual'),
-]
-
-# The name of an image file (relative to this directory) to place at the top of
-# the title page.
-#latex_logo = None
-
-# For "manual" documents, if this is true, then toplevel headings are parts,
-# not chapters.
-#latex_use_parts = False
-
-# Additional stuff for the LaTeX preamble.
-#latex_preamble = ''
-
-# Documents to append as an appendix to all manuals.
-#latex_appendices = []
-
-# If false, no module index is generated.
-#latex_use_modindex = True
-
# When auto-doc'ing a class, write the class' docstring and the __init__ docstring
# into the class docs.
-autoclass_content = "both"
+autoclass_content = "class"
diff --git a/doc/config.rst b/doc/config.rst
index df703ba9..980d0462 100644
--- a/doc/config.rst
+++ b/doc/config.rst
@@ -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
+
.. _config:
===================
@@ -15,7 +18,7 @@ Configuration files
Coverage.py options can be specified in a configuration file. This makes it
-easier to re-run coverage with consistent settings, and also allows for
+easier to re-run coverage.py with consistent settings, and also allows for
specification of options that are otherwise only available in the
:ref:`API <api>`.
@@ -105,7 +108,7 @@ standard library.
``concurrency`` (string, default "thread"): the name of the concurrency
library in use by the product code. If your program uses `gevent`_,
`greenlet`_, or `eventlet`_, you must name that library in this option, or
-coverage will produce very wrong results.
+coverage.py will produce very wrong results.
.. _greenlet: http://greenlet.readthedocs.org/en/latest/
.. _gevent: http://www.gevent.org/
diff --git a/doc/contributing.rst b/doc/contributing.rst
index 32929e0b..99459b1d 100644
--- a/doc/contributing.rst
+++ b/doc/contributing.rst
@@ -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
+
.. _contributing:
===========================
diff --git a/doc/dict.txt b/doc/dict.txt
index db161e03..8bed966a 100644
--- a/doc/dict.txt
+++ b/doc/dict.txt
@@ -1,8 +1,11 @@
activestate
api
+apache
API
args
argv
+basename
+basenames
bitbucket
BOM
bom
@@ -13,6 +16,7 @@ builtin
builtins
bytecode
bytecodes
+bytestring
canonicalize
canonicalized
canonicalizes
@@ -31,6 +35,7 @@ covhtml
CPython
css
CTracer
+datetime
dedent
dict
dict's
@@ -52,6 +57,7 @@ executable's
expr
filename
filenames
+filepath
fname
fnmatch
fpath
@@ -73,8 +79,10 @@ importlib
instancemethod
int
ints
+invariants
iterable
iterables
+json
jython
kwargs
matcher
@@ -88,6 +96,7 @@ morf
morfs
multi
mumbo
+mycode
namespace
namespaces
nano
@@ -108,9 +117,12 @@ outfile
overridable
parsable
parsers
+pathnames
pragma
pragmas
pre
+prepended
+prepending
programmability
programmatically
py
@@ -130,6 +142,7 @@ settrace
setuptools
sitecustomize
sortable
+src
stackoverflow
stderr
stdlib
@@ -168,6 +181,7 @@ whitespace
wikipedia
wildcard
wildcards
+www
xml
XML
xrange
diff --git a/doc/excluding.rst b/doc/excluding.rst
index 44c51f96..465afe16 100644
--- a/doc/excluding.rst
+++ b/doc/excluding.rst
@@ -1,8 +1,11 @@
+.. Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
+.. For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt
+
.. _excluding:
-============================
-Excluding code from coverage
-============================
+===============================
+Excluding code from coverage.py
+===============================
.. :history: 20090613T090500, brand new docs.
.. :history: 20100224T200900, updated for 3.3.
@@ -11,13 +14,13 @@ Excluding code from coverage
You may have code in your project that you know won't be executed, and you want
-to tell coverage to ignore it. For example, you may have debugging-only code
-that won't be executed during your unit tests. You can tell coverage to exclude
-this code during reporting so that it doesn't clutter your reports with noise
-about code that you don't need to hear about.
+to tell coverage.py to ignore it. For example, you may have debugging-only
+code that won't be executed during your unit tests. You can tell coverage.py to
+exclude this code during reporting so that it doesn't clutter your reports with
+noise about code that you don't need to hear about.
-Coverage will look for comments marking clauses for exclusion. In this code,
-the "if debug" clause is excluded from reporting::
+Coverage.py will look for comments marking clauses for exclusion. In this
+code, the "if debug" clause is excluded from reporting::
a = my_function1()
if debug: # pragma: no cover
@@ -39,7 +42,7 @@ function is not reported as missing::
return "<MyObject>"
Excluded code is executed as usual, and its execution is recorded in the
-coverage data as usual. When producing reports though, coverage excludes it
+coverage data as usual. When producing reports though, coverage.py excludes it
from the list of missing code.
@@ -64,7 +67,7 @@ line, so it isn't considered a branch at all.
Advanced exclusion
------------------
-Coverage identifies exclusions by matching lines against a list of regular
+Coverage.py identifies exclusions by matching lines against a list of regular
expressions. Using :ref:`configuration files <config>` or the coverage
:ref:`API <api>`, you can add to that list. This is useful if you have
often-used constructs to exclude that can be matched with a regex. You can
diff --git a/doc/faq.rst b/doc/faq.rst
index 32492532..e8e9c49e 100644
--- a/doc/faq.rst
+++ b/doc/faq.rst
@@ -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
+
.. _faq:
==================
@@ -26,10 +29,10 @@ the :ref:`command line <cmd>` to create the reports you need from that data.
**Q: Why do unexecutable lines show up as executed?**
-Usually this is because you've updated your code and run coverage on it again
-without erasing the old data. Coverage records line numbers executed, so the
-old data may have recorded a line number which has since moved, causing
-coverage to claim a line has been executed which cannot be.
+Usually this is because you've updated your code and run coverage.py on it
+again without erasing the old data. Coverage.py records line numbers executed,
+so the old data may have recorded a line number which has since moved, causing
+coverage.py to claim a line has been executed which cannot be.
If you are using the ``-x`` command line action, it doesn't erase first by
default. Switch to the ``coverage run`` command, or use the ``-e`` switch to
@@ -39,18 +42,18 @@ erase all data before starting the next run.
**Q: Why do the bodies of functions (or classes) show as executed, but the def
lines do not?**
-This happens because coverage is started after the functions are defined. The
-definition lines are executed without coverage measurement, then coverage is
-started, then the function is called. This means the body is measured, but
-the definition of the function itself is not.
+This happens because coverage.py is started after the functions are defined.
+The definition lines are executed without coverage measurement, then
+coverage.py is started, then the function is called. This means the body is
+measured, but the definition of the function itself is not.
-To fix this, start coverage earlier. If you use the :ref:`command line <cmd>`
-to run your program with coverage, then your entire program will be monitored.
-If you are using the :ref:`API <api>`, you need to call coverage.start() before
-importing the modules that define your functions.
+To fix this, start coverage.py earlier. If you use the :ref:`command line
+<cmd>` to run your program with coverage.py, then your entire program will be
+monitored. If you are using the :ref:`API <api>`, you need to call
+coverage.start() before importing the modules that define your functions.
-**Q: Coverage is much slower than I remember, what's going on?**
+**Q: Coverage.py is much slower than I remember, what's going on?**
Make sure you are using the C trace function. Coverage.py provides two
implementations of the trace function. The C implementation runs much faster.
diff --git a/doc/index.rst b/doc/index.rst
index 5603a8ac..299bd883 100644
--- a/doc/index.rst
+++ b/doc/index.rst
@@ -1,5 +1,8 @@
+.. Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
+.. For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt
+
===========
-coverage.py
+Coverage.py
===========
.. :history: 20090524T134300, brand new docs.
@@ -51,9 +54,9 @@ not.
.. ifconfig:: prerelease
- The latest version is coverage.py 4.0a5, released 16 February 2015.
- It is supported on Python versions 2.6, 2.7, 3.3, 3.4, and 3.5a1, as well
- as PyPy 2.4, and PyPy3 2.4.
+ The latest version is coverage.py 4.0a6, released 21 June 2015.
+ It is supported on Python versions 2.6, 2.7, 3.3, 3.4, and 3.5b2, as well
+ as PyPy 2.4 and 2.6, and PyPy3 2.4.
**This is a pre-release build. The usual warnings about possible bugs apply.**
The latest stable version is coverage.py 3.7.1, `described here`_.
@@ -65,7 +68,7 @@ Quick start
Getting started is easy:
-#. Install coverage.py from the `coverage page on the Python Package Index`_,
+#. Install coverage.py from the `coverage.py page on the Python Package Index`_,
or by using "pip install coverage". For a few more details, see
:ref:`install`.
@@ -105,7 +108,7 @@ Getting started is easy:
Then visit htmlcov/index.html in your browser, to see a
`report like this one`_.
-.. _coverage page on the Python Package Index: http://pypi.python.org/pypi/coverage
+.. _coverage.py page on the Python Package Index: http://pypi.python.org/pypi/coverage
.. _report like this: http://nedbatchelder.com/code/coverage/sample_html/index.html
.. _report like this one: http://nedbatchelder.com/code/coverage/sample_html_beta/index.html
@@ -118,12 +121,12 @@ There are a few different ways to use coverage.py. The simplest is the
If you need more control over how your project is measured, you can use the
:ref:`API <api>`.
-Some test runners provide coverage integration to make it easy to use coverage
-while running tests. For example, `nose`_ has a `cover plug-in`_.
+Some test runners provide coverage integration to make it easy to use
+coverage.py while running tests. For example, `nose`_ has a `cover plug-in`_.
-You can fine-tune coverage's view of your code by directing it to ignore parts
-that you know aren't interesting. See :ref:`source` and :ref:`excluding` for
-details.
+You can fine-tune coverage.py's view of your code by directing it to ignore
+parts that you know aren't interesting. See :ref:`source` and :ref:`excluding`
+for details.
.. _nose: http://somethingaboutorange.com/mrl/projects/nose
.. _cover plug-in: https://nose.readthedocs.org/en/latest/plugins/cover.html
@@ -173,7 +176,3 @@ More information
trouble
faq
changes
-
-
-.. How it works
-.. .coverage file format
diff --git a/doc/install.rst b/doc/install.rst
index 757b7757..57fd38e7 100644
--- a/doc/install.rst
+++ b/doc/install.rst
@@ -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
+
.. _install:
============
@@ -47,7 +50,7 @@ The alternate old-school technique is:
#. Install (or already have installed) `setuptools`_ or `Distribute`_.
#. Download the appropriate kit from the
- `coverage page on the Python Package Index`__.
+ `coverage.py page on the Python Package Index`__.
#. Run ``python setup.py install``.
@@ -63,6 +66,10 @@ with::
$ sudo apt-get install python-dev
+or for Python 3.x::
+
+ $ sudo apt-get install python3-dev
+
Installing on Windows
---------------------
@@ -78,8 +85,8 @@ the C extension pre-compiled so there's no need to worry about compilers.
Checking the installation
-------------------------
-If all went well, you should be able to open a command prompt, and see coverage
-installed properly:
+If all went well, you should be able to open a command prompt, and see
+coverage.py installed properly:
.. ifconfig:: not prerelease
@@ -97,7 +104,7 @@ installed properly:
Coverage.py, version |release|.
Documentation at https://coverage.readthedocs.org/en/|release|
-You can also invoke coverage as a module:
+You can also invoke coverage.py as a module:
.. ifconfig:: not prerelease
diff --git a/doc/plugins.rst b/doc/plugins.rst
index 1fc5c10d..30d73396 100644
--- a/doc/plugins.rst
+++ b/doc/plugins.rst
@@ -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
+
.. _plugins:
=======
@@ -38,14 +41,7 @@ example, let's say you want to use one called fred_plugin.
Check the documentation for the plugin to see if it takes any options, and
what they are.
-#. Run your tests as you usually would. NOTE: You will see a warning when
- coverage starts::
-
- Coverage.py warning: Setting timid=True to support plugins.
-
- This means that coverage will run slower than it usually would. This
- limitation is part of the initial alpha release, it will be gone in the
- final version.
+#. Run your tests as you usually would.
Plugin API
diff --git a/doc/python-coverage.1.txt b/doc/python-coverage.1.txt
index f79f33d8..9b298118 100644
--- a/doc/python-coverage.1.txt
+++ b/doc/python-coverage.1.txt
@@ -9,9 +9,9 @@ measure code coverage of Python program execution
:Author: Ned Batchelder <ned@nedbatchelder.com>
:Author: |author|
:Date: 2014-09-27
-:Copyright: BSD license, attribution and disclaimer required.
+:Copyright: Apache 2.0 license, attribution and disclaimer required.
:Manual section: 1
-:Manual group: Coverage
+:Manual group: Coverage.py
.. |command| replace:: **python-coverage**
@@ -66,7 +66,7 @@ GLOBAL OPTIONS
==============
**--help**, **-h**
- Describe how to use Coverage, in general or a command.
+ Describe how to use coverage.py, in general or a command.
**--rcfile** `RCFILE`
Specify configuration file `RCFILE`. Defaults to ``.coveragerc``.
@@ -105,7 +105,7 @@ COMMAND REFERENCE
**help** [ `command` ]
- Describe how to use Coverage.
+ Describe how to use coverage.py.
**help** **classic**
diff --git a/doc/requirements.txt b/doc/requirements.txt
index 3ae49210..81778351 100644
--- a/doc/requirements.txt
+++ b/doc/requirements.txt
@@ -1,5 +1,4 @@
# PyPI requirements for building documentation for coverage.py
pyenchant
sphinx
-sphinxcontrib-napoleon
sphinxcontrib-spelling
diff --git a/doc/source.rst b/doc/source.rst
index 42fd3382..b8dcb7be 100644
--- a/doc/source.rst
+++ b/doc/source.rst
@@ -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
+
.. _source:
=======================
diff --git a/doc/subprocess.rst b/doc/subprocess.rst
index cce2c0bf..5a55dc4a 100644
--- a/doc/subprocess.rst
+++ b/doc/subprocess.rst
@@ -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
+
.. _subprocess:
=======================
@@ -30,7 +33,7 @@ Measuring coverage in sub-processes is a little tricky. When you spawn a
sub-process, you are invoking Python to run your program. Usually, to get
coverage measurement, you have to use coverage.py to run your program. Your
sub-process won't be using coverage.py, so we have to convince Python to use
-coverage even when not explicitly invoked.
+coverage.py even when not explicitly invoked.
To do that, we'll configure Python to run a little coverage.py code when it
starts. That code will look for an environment variable that tells it to start
diff --git a/doc/trouble.rst b/doc/trouble.rst
index bfdf12bb..b7011988 100644
--- a/doc/trouble.rst
+++ b/doc/trouble.rst
@@ -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
+
.. _trouble:
=========================
@@ -8,8 +11,8 @@ Things that cause trouble
.. :history: 20150124T160800, remove obsolete stuff.
-Coverage works well, and I want it to properly measure any Python program, but
-there are some situations it can't cope with. This page details some known
+Coverage.py works well, and I want it to properly measure any Python program,
+but there are some situations it can't cope with. This page details some known
problems, with possible courses of action, and links to coverage.py bug reports
with more information.
@@ -34,10 +37,6 @@ coverage.py from working properly:
program that calls execv will not be fully measured. A patch for coverage.py
is in `issue 43`_.
-* `multiprocessing`_ launches processes to provide parallelism. These
- processes don't get measured by coverage.py. Some possible fixes are
- discussed or linked to in `issue 117`_.
-
* `thread`_, in the Python standard library, is the low-level threading
interface. Threads created with this module will not be traced. Use the
higher-level `threading`_ module instead.
@@ -48,12 +47,10 @@ coverage.py from working properly:
measured properly.
.. _execv: http://docs.python.org/library/os#os.execl
-.. _multiprocessing: http://docs.python.org/library/multiprocessing.html
.. _sys.settrace: http://docs.python.org/library/sys.html#sys.settrace
.. _thread: http://docs.python.org/library/thread.html
.. _threading: http://docs.python.org/library/threading.html
.. _issue 43: https://bitbucket.org/ned/coveragepy/issue/43/coverage-measurement-fails-on-code
-.. _issue 117: https://bitbucket.org/ned/coveragepy/issue/117/enable-coverage-measurement-of-code-run-by
Things that require --timid
diff --git a/howto.txt b/howto.txt
index 9a33d54c..f6c0743f 100644
--- a/howto.txt
+++ b/howto.txt
@@ -19,8 +19,8 @@
pip install -e .
cd ~/cog/trunk
rm -rf htmlcov
- TODO: this isn't right with the new cog
- coverage run --branch --source=cogapp -m cogapp.test_cogapp CogTestsInMemory
+ coverage run --branch --source=cogapp -m nose cogapp/test_cogapp.py:CogTestsInMemory
+ coverage combine
coverage html
- IF BETA:
cp -r htmlcov/ ~/coverage/trunk/doc/sample_html_beta/
@@ -39,15 +39,16 @@
- $ tox -c tox_winkits.ini
- Update PyPi:
- $ make pypi
- - upload the kits:
- - $ make kit_upload
- - $ tox -c tox_winkits.ini upload
- # note: this seems to try to upload each file twice, so you'll have a
- # successful upload, then a failure, but the file gets there.
+ - upload source kits:
+ - $ make kit kit_upload
+ - upload wheels (for each platform):
+ - $ make wheel kit_upload
+ - upload windows kits:
+ - $ make winkit kit_upload
- Visit http://pypi.python.org/pypi?%3Aaction=pkg_edit&name=coverage :
- show/hide the proper versions.
- Tag the tree
- - hg tag -m "Coverage 3.0.1" 3.0.1
+ - hg tag -m "Coverage 3.0.1" coverage-3.0.1
- Update nedbatchelder.com
- Edit webfaction.htaccess to make sure the proper versions are mapped to /beta
- Blog post?
@@ -84,10 +85,10 @@
$ make metacov
- This will run coverage under its own measurement. You can do this in
- different environments (Linux vs. Windows, for example), then copy the
- data files (.coverage.meta.*) to one machine for combination and
- reporting. To combine and report:
+ This will run coverage.py under its own measurement. You can do this in
+ different environments (Linux vs. Windows, for example), then copy the data
+ files (.metacov.*) to one machine for combination and reporting. To
+ combine and report:
$ make metahtml
diff --git a/igor.py b/igor.py
index 17f4114b..9dadf595 100644
--- a/igor.py
+++ b/igor.py
@@ -1,4 +1,7 @@
# coding: utf-8
+# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
+# For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt
+
"""Helper for building, testing, and linting coverage.py.
To get portability, all these operations are written in Python here instead
@@ -6,6 +9,7 @@ of in shell scripts, batch files, or Makefiles.
"""
+import contextlib
import fnmatch
import glob
import inspect
@@ -18,8 +22,17 @@ import warnings
import zipfile
+# We want to see all warnings while we are running tests. But we also need to
+# disable warnings for some of the more complex setting up of tests.
warnings.simplefilter("default")
+@contextlib.contextmanager
+def ignore_warnings():
+ """Context manager to ignore warning within the with statement."""
+ with warnings.catch_warnings():
+ warnings.simplefilter("ignore")
+ yield
+
# Functions named do_* are executable from the command line: do_blah is run
# by "python igor.py blah".
@@ -32,6 +45,7 @@ def do_remove_extension():
tracer.so
tracer.*.so
tracer.pyd
+ tracer.*.pyd
""".split()
for pattern in so_patterns:
@@ -45,13 +59,18 @@ def do_remove_extension():
def run_tests(tracer, *nose_args):
"""The actual running of tests."""
- import nose.core
+ with ignore_warnings():
+ import nose.core
+
if tracer == "py":
label = "with Python tracer"
skipper = os.environ.get("COVERAGE_NO_PYTRACER")
else:
label = "with C tracer"
- skipper = os.environ.get("COVERAGE_NO_EXTENSION")
+ skipper = (
+ os.environ.get("COVERAGE_NO_EXTENSION") or
+ os.environ.get("COVERAGE_NO_CTRACER")
+ )
if skipper:
msg = "Skipping tests " + label
@@ -60,6 +79,7 @@ def run_tests(tracer, *nose_args):
print(msg)
return
+ os.environ['COVERAGE_TESTING'] = "True"
print_banner(label)
nose_args = ["nosetests"] + list(nose_args)
nose.core.main(argv=nose_args)
@@ -67,6 +87,9 @@ def run_tests(tracer, *nose_args):
def run_tests_with_coverage(tracer, *nose_args):
"""Run tests, but with coverage."""
+ # Need to define this early enough that the first import of env.py sees it.
+ os.environ['COVERAGE_TESTING'] = "True"
+
import coverage
os.environ['COVERAGE_PROCESS_START'] = os.path.abspath('metacov.ini')
@@ -84,10 +107,10 @@ def run_tests_with_coverage(tracer, *nose_args):
version = "%s%s" % sys.version_info[:2]
suffix = "%s_%s_%s" % (version, tracer, socket.gethostname())
- cov = coverage.coverage(config_file="metacov.ini", data_suffix=suffix)
- # Cheap trick: the coverage code itself is excluded from measurement, but
- # if we clobber the cover_prefix in the coverage object, we can defeat the
- # self-detection.
+ cov = coverage.Coverage(config_file="metacov.ini", data_suffix=suffix)
+ # Cheap trick: the coverage.py code itself is excluded from measurement,
+ # but if we clobber the cover_prefix in the coverage object, we can defeat
+ # the self-detection.
cov.cover_prefix = "Please measure coverage.py!"
cov._warn_unimported_source = False
cov.erase()
@@ -126,7 +149,7 @@ def do_combine_html():
"""Combine data from a meta-coverage run, and make the HTML report."""
import coverage
os.environ['COVERAGE_HOME'] = os.getcwd()
- cov = coverage.coverage(config_file="metacov.ini")
+ cov = coverage.Coverage(config_file="metacov.ini")
cov.load()
cov.combine()
cov.save()
@@ -176,14 +199,15 @@ def do_install_egg():
"""Install the egg1 egg for tests."""
# I am pretty certain there are easier ways to install eggs...
# pylint: disable=import-error,no-name-in-module
- import distutils.core
cur_dir = os.getcwd()
os.chdir("tests/eggsrc")
- distutils.core.run_setup("setup.py", ["--quiet", "bdist_egg"])
- egg = glob.glob("dist/*.egg")[0]
- distutils.core.run_setup(
- "setup.py", ["--quiet", "easy_install", "--no-deps", "--zip-ok", egg]
- )
+ with ignore_warnings():
+ import distutils.core
+ distutils.core.run_setup("setup.py", ["--quiet", "bdist_egg"])
+ egg = glob.glob("dist/*.egg")[0]
+ distutils.core.run_setup(
+ "setup.py", ["--quiet", "easy_install", "--no-deps", "--zip-ok", egg]
+ )
os.chdir(cur_dir)
@@ -281,6 +305,19 @@ def do_help():
print("%-20s%s" % (name[3:], value.__doc__))
+def analyze_args(function):
+ """What kind of args does `function` expect?
+
+ Returns:
+ star, num_pos:
+ star(boolean): Does `function` accept *args?
+ num_args(int): How many positional arguments does `function` have?
+ """
+ with ignore_warnings():
+ argspec = inspect.getargspec(function)
+ return bool(argspec[1]), len(argspec[0])
+
+
def main(args):
"""Main command-line execution for igor.
@@ -294,14 +331,13 @@ def main(args):
if handler is None:
print("*** No handler for %r" % verb)
return 1
- argspec = inspect.getargspec(handler)
- if argspec[1]:
+ star, num_args = analyze_args(handler)
+ if star:
# Handler has *args, give it all the rest of the command line.
handler_args = args
args = []
else:
# Handler has specific arguments, give it only what it needs.
- num_args = len(argspec[0])
handler_args = args[:num_args]
args = args[num_args:]
ret = handler(*handler_args)
@@ -309,5 +345,6 @@ def main(args):
if ret:
return ret
+
if __name__ == '__main__':
sys.exit(main(sys.argv[1:]))
diff --git a/lab/branches.py b/lab/branches.py
index fbba87eb..275eef4a 100644
--- a/lab/branches.py
+++ b/lab/branches.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
+
# Demonstrate some issues with coverage.py branch testing.
def my_function(x):
diff --git a/lab/hack_pyc.py b/lab/hack_pyc.py
index 1cdc4765..0ebd9948 100644
--- a/lab/hack_pyc.py
+++ b/lab/hack_pyc.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
+
""" Wicked hack to get .pyc files to do bytecode tracing instead of
line tracing.
"""
diff --git a/lab/lnotab.py b/lab/lnotab.py
deleted file mode 100644
index 230e42bb..00000000
--- a/lab/lnotab.py
+++ /dev/null
@@ -1,122 +0,0 @@
-# Comment copied from Python/compile.c:
-#
-# All about a_lnotab.
-#
-# c_lnotab is an array of unsigned bytes disguised as a Python string.
-# It is used to map bytecode offsets to source code line #s (when needed
-# for tracebacks).
-#
-# The array is conceptually a list of
-# (bytecode offset increment, line number increment)
-# pairs. The details are important and delicate, best illustrated by example:
-#
-# byte code offset source code line number
-# 0 1
-# 6 2
-# 50 7
-# 350 307
-# 361 308
-#
-# The first trick is that these numbers aren't stored, only the increments
-# from one row to the next (this doesn't really work, but it's a start):
-#
-# 0, 1, 6, 1, 44, 5, 300, 300, 11, 1
-#
-# The second trick is that an unsigned byte can't hold negative values, or
-# values larger than 255, so (a) there's a deep assumption that byte code
-# offsets and their corresponding line #s both increase monotonically, and (b)
-# if at least one column jumps by more than 255 from one row to the next, more
-# than one pair is written to the table. In case #b, there's no way to know
-# from looking at the table later how many were written. That's the delicate
-# part. A user of c_lnotab desiring to find the source line number
-# corresponding to a bytecode address A should do something like this
-#
-# lineno = addr = 0
-# for addr_incr, line_incr in c_lnotab:
-# addr += addr_incr
-# if addr > A:
-# return lineno
-# lineno += line_incr
-#
-# In order for this to work, when the addr field increments by more than 255,
-# the line # increment in each pair generated must be 0 until the remaining addr
-# increment is < 256. So, in the example above, assemble_lnotab (it used
-# to be called com_set_lineno) should not (as was actually done until 2.2)
-# expand 300, 300 to 255, 255, 45, 45,
-# but to 255, 0, 45, 255, 0, 45.
-#
-
-def lnotab(pairs, first_lineno=0):
- """Yields byte integers representing the pairs of integers passed in."""
- assert first_lineno <= pairs[0][1]
- cur_byte, cur_line = 0, first_lineno
- for byte_off, line_off in pairs:
- byte_delta = byte_off - cur_byte
- line_delta = line_off - cur_line
- assert byte_delta >= 0
- assert line_delta >= 0
- while byte_delta > 255:
- yield 255 # byte
- yield 0 # line
- byte_delta -= 255
- yield byte_delta
- while line_delta > 255:
- yield 255 # line
- yield 0 # byte
- line_delta -= 255
- yield line_delta
- cur_byte, cur_line = byte_off, line_off
-
-def lnotab_string(pairs, first_lineno=0):
- return "".join(chr(b) for b in lnotab(pairs, first_lineno))
-
-def byte_pairs(lnotab):
- """Yield pairs of integers from a string."""
- for i in range(0, len(lnotab), 2):
- yield ord(lnotab[i]), ord(lnotab[i+1])
-
-def lnotab_numbers(lnotab, first_lineno=0):
- """Yields the byte, line offset pairs from a packed lnotab string."""
-
- last_line = None
- cur_byte, cur_line = 0, first_lineno
- for byte_delta, line_delta in byte_pairs(lnotab):
- if byte_delta:
- if cur_line != last_line:
- yield cur_byte, cur_line
- last_line = cur_line
- cur_byte += byte_delta
- cur_line += line_delta
- if cur_line != last_line:
- yield cur_byte, cur_line
-
-
-## Tests
-
-def same_list(a, b):
- a = list(a)
- assert a == b
-
-def test_simple():
- same_list(lnotab([(0,1)]), [0, 1])
- same_list(lnotab([(0,1), (6, 2)]), [0, 1, 6, 1])
-
-def test_starting_above_one():
- same_list(lnotab([(0,100), (6,101)]), [0, 100, 6, 1])
- same_list(lnotab([(0,100), (6,101)], 50), [0, 50, 6, 1])
-
-def test_large_gaps():
- same_list(lnotab([(0,1), (300, 300)]), [0, 1, 255, 0, 45, 255, 0, 44])
- same_list(lnotab([(0,1), (255, 300)]), [0, 1, 255, 255, 0, 44])
- same_list(lnotab([(0,1), (255, 256)]), [0, 1, 255, 255])
-
-def test_strings():
- assert lnotab_string([(0,1), (6, 2)]) == "\x00\x01\x06\x01"
- assert lnotab_string([(0,1), (300, 300)]) == "\x00\x01\xff\x00\x2d\xff\x00\x2c"
-
-def test_numbers():
- same_list(lnotab_numbers("\x00\x01\x06\x01"), [(0,1), (6,2)])
- same_list(lnotab_numbers("\x00\x01\xff\x00\x2d\xff\x00\x2c"), [(0,1), (300, 300)])
-
-def test_numbers_firstlineno():
- same_list(lnotab_numbers("\x00\x01\xff\x00\x2d\xff\x00\x2c", 10), [(0,11), (300, 310)])
diff --git a/lab/new-data.js b/lab/new-data.js
new file mode 100644
index 00000000..973aa116
--- /dev/null
+++ b/lab/new-data.js
@@ -0,0 +1,74 @@
+{
+ "run" {
+ "collector": "coverage.py 4.0",
+ "config": {
+ "branch": true,
+ "source": ".",
+ },
+ "collected": "20150711T090600",
+ },
+
+ // As of now:
+ "lines": {
+ "a/b/c.py": [1, 2, 3, 4, 5],
+ "a/b/d.py": [4, 5, 6, 7, 8],
+ },
+ "arcs": {
+ "a/b/c.py: [[1, 2], [2, 3], [4, 5]],
+ },
+ "plugins: {
+ "a/b/c.py": "fooey.plugin",
+ },
+
+ // Maybe in the future?
+ "files": {
+ "a/b/c.py": {
+ "lines": [1, 2, 3, 4, 5],
+ "arcs": [
+ [1, 2], [3, 4], [5, -1],
+ ],
+
+ "plugin": "django.coverage",
+
+ "lines": {
+ "1": {
+ "tests": [
+ "foo/bar/test.py:TheTest.test_it",
+ "asdasdasd",
+ ],
+ "tests": [17, 34, 23, 12389],
+ },
+ "2": {
+ "count": 23,
+ },
+ "3": {},
+ "4": {},
+ "17": {},
+ },
+
+ "arcs": {
+ "1.2": {},
+ "2.3": {},
+ "3.-1": {},
+ },
+ },
+ },
+
+ "tests": [
+ {
+ "file": "a/b/c.py",
+ "test": "test_it",
+ },
+ {
+ "file": "a/b/d.py",
+ "test": "TheTest.test_it",
+ },
+ ],
+
+ "runs": [
+ {
+ // info about each run?
+ },
+ { ... },
+ ],
+}
diff --git a/lab/parser.py b/lab/parser.py
index 1783468b..1343f4ce 100644
--- a/lab/parser.py
+++ b/lab/parser.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
+
"""Parser.py: a main for invoking code in coverage/parser.py"""
from __future__ import division
@@ -9,8 +12,8 @@ from optparse import OptionParser
import disgen
from coverage.misc import CoverageException
-from coverage.files import get_python_source
from coverage.parser import ByteParser, PythonParser
+from coverage.python import get_python_source
opcode_counts = collections.Counter()
@@ -82,7 +85,7 @@ class ParserMain(object):
self.disassemble(bp, histogram=options.histogram)
arcs = bp._all_arcs()
- if options.chunks and not options.dis:
+ if options.chunks:# and not options.dis:
chunks = bp._all_chunks()
if options.recursive:
print("%6d: %s" % (len(chunks), filename))
@@ -116,7 +119,7 @@ class ParserMain(object):
m2 = 'C'
if lineno in cp.excluded:
m3 = 'x'
- a = arc_chars.get(lineno, '').ljust(arc_width)
+ a = arc_chars[lineno].ljust(arc_width)
print("%4d %s%s%s%s%s %s" %
(lineno, m0, m1, m2, m3, a, ltext)
)
@@ -162,12 +165,12 @@ class ParserMain(object):
dictionary mapping line numbers to ascii strings to draw for that line.
"""
- arc_chars = {}
+ arc_chars = collections.defaultdict(str)
for lfrom, lto in sorted(arcs):
if lfrom < 0:
- arc_chars[lto] = arc_chars.get(lto, '') + 'v'
+ arc_chars[lto] += 'v'
elif lto < 0:
- arc_chars[lfrom] = arc_chars.get(lfrom, '') + '^'
+ arc_chars[lfrom] += '^'
else:
if lfrom == lto - 1:
# Don't show obvious arcs.
@@ -176,7 +179,7 @@ class ParserMain(object):
l1, l2 = lfrom, lto
else:
l1, l2 = lto, lfrom
- w = max([len(arc_chars.get(l, '')) for l in range(l1, l2+1)])
+ w = max(len(arc_chars[l]) for l in range(l1, l2+1))
for l in range(l1, l2+1):
if l == lfrom:
ch = '<'
@@ -184,11 +187,11 @@ class ParserMain(object):
ch = '>'
else:
ch = '|'
- arc_chars[l] = arc_chars.get(l, '').ljust(w) + ch
+ arc_chars[l] = arc_chars[l].ljust(w) + ch
arc_width = 0
if arc_chars:
- arc_width = max([len(a) for a in arc_chars.values()])
+ arc_width = max(len(a) for a in arc_chars.values())
else:
arc_width = 0
diff --git a/lab/platform_info.py b/lab/platform_info.py
new file mode 100644
index 00000000..8caea50f
--- /dev/null
+++ b/lab/platform_info.py
@@ -0,0 +1,26 @@
+# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
+# For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt
+
+"""Dump information so we can get a quick look at what's available."""
+
+import platform
+import sys
+
+
+def whatever(f):
+ try:
+ return f()
+ except:
+ return f
+
+
+def dump_module(mod):
+ print("\n### {0} ---------------------------".format(mod.__name__))
+ for name in dir(mod):
+ if name.startswith("_"):
+ continue
+ print("{0:30s}: {1!r:.100}".format(name, whatever(getattr(mod, name))))
+
+
+for mod in [platform, sys]:
+ dump_module(mod)
diff --git a/lab/run_trace.py b/lab/run_trace.py
new file mode 100644
index 00000000..ea0a6cb7
--- /dev/null
+++ b/lab/run_trace.py
@@ -0,0 +1,35 @@
+# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
+# For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt
+
+"""Run a simple trace function on a file of Python code."""
+
+import os, sys
+
+nest = 0
+
+def trace(frame, event, arg):
+ global nest
+
+ if nest is None:
+ # This can happen when Python is shutting down.
+ return None
+
+ print "%s%s %s %d @%d" % (
+ " " * nest,
+ event,
+ os.path.basename(frame.f_code.co_filename),
+ frame.f_lineno,
+ frame.f_lasti,
+ )
+
+ if event == 'call':
+ nest += 1
+ if event == 'return':
+ nest -= 1
+
+ return trace
+
+the_program = sys.argv[1]
+
+sys.settrace(trace)
+execfile(the_program)
diff --git a/lab/sample.py b/lab/sample.py
deleted file mode 100644
index bb628484..00000000
--- a/lab/sample.py
+++ /dev/null
@@ -1,5 +0,0 @@
-a, b = 1, 0
-if a or b or fn():
- # Hey
- a = 3
-d = 4
diff --git a/lab/show_pyc.py b/lab/show_pyc.py
index d6bbd921..147c6ff8 100644
--- a/lab/show_pyc.py
+++ b/lab/show_pyc.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
+
import dis, marshal, struct, sys, time, types
def show_pyc_file(fname):
diff --git a/lab/trace_sample.py b/lab/trace_sample.py
deleted file mode 100644
index 3f819199..00000000
--- a/lab/trace_sample.py
+++ /dev/null
@@ -1,57 +0,0 @@
-import os, sys
-
-global nest
-nest = 0
-
-def trace(frame, event, arg):
- #if event == 'line':
- global nest
-
- print "%s%s %s %d" % (
- " " * nest,
- event,
- os.path.basename(frame.f_code.co_filename),
- frame.f_lineno,
- )
-
- if event == 'call':
- nest += 1
- if event == 'return':
- nest -= 1
-
- return trace
-
-def trace2(frame, event, arg):
- #if event == 'line':
- global nest
-
- print "2: %s%s %s %d" % (
- " " * nest,
- event,
- os.path.basename(frame.f_code.co_filename),
- frame.f_lineno,
- )
-
- if event == 'call':
- nest += 1
- if event == 'return':
- nest -= 1
-
- return trace2
-
-sys.settrace(trace)
-
-def bar():
- print "nar"
-
-a = 26
-def foo(n):
- a = 28
- sys.settrace(sys.gettrace())
- bar()
- a = 30
- return 2*n
-
-print foo(a)
-#import sample
-#import littleclass
diff --git a/metacov.ini b/metacov.ini
index 3bce01e2..ba8c234a 100644
--- a/metacov.ini
+++ b/metacov.ini
@@ -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
+
# Settings to use when using coverage.py to measure itself.
[run]
branch = true
@@ -20,8 +23,8 @@ exclude_lines =
partial_branches =
# pragma: part covered
+ if env.TESTING:
-omit = mock.py
ignore_errors = true
precision = 1
diff --git a/pylintrc b/pylintrc
index 2ea150b4..fba8b40b 100644
--- a/pylintrc
+++ b/pylintrc
@@ -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
+
# lint Python modules using external checkers.
#
# This is the main checker controling the other ones and the reports
@@ -294,7 +297,7 @@ int-import-graph=
[FORMAT]
# Maximum number of characters on a single line.
-max-line-length=80
+max-line-length=100
# Maximum number of lines in a module
max-module-lines=10000
diff --git a/requirements.txt b/requirements.txt
index ab84656b..e2eede24 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,5 +1,15 @@
+# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
+# For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt
+
# PyPI requirements for running tests for coverage.py
nose
mock
-pylint
+PyContracts
tox >= 1.9
+
+# and for linting...
+pyenchant
+pylint
+
+# and for kitting...
+twine
diff --git a/setup.py b/setup.py
index 5ba95f98..613198ca 100644
--- a/setup.py
+++ b/setup.py
@@ -1,4 +1,7 @@
-# setup.py for coverage.py
+# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
+# For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt
+
+# Distutils setup for coverage.py
"""Code coverage measurement for Python
@@ -8,7 +11,7 @@ library to determine which lines are executable, and which have been executed.
Coverage.py runs on CPython 2.6, 2.7, 3.3, 3.4 or 3.5, PyPy 2.4, and PyPy3 2.4.
-Documentation is at `nedbatchelder.com <%s>`_. Code repository and issue
+Documentation is on `Read the Docs <{docurl}>`_. Code repository and issue
tracker are on `Bitbucket <http://bitbucket.org/ned/coveragepy>`_, with a
mirrored repo on `GitHub <https://github.com/nedbat/coveragepy>`_.
@@ -32,28 +35,28 @@ New in 3.2: Branch coverage!
# This file is used unchanged under all versions of Python, 2.x and 3.x.
-classifiers = """\
-Environment :: Console
-Intended Audience :: Developers
-License :: OSI Approved :: BSD License
-Operating System :: OS Independent
-Programming Language :: Python :: 2
-Programming Language :: Python :: 3
-Topic :: Software Development :: Quality Assurance
-Topic :: Software Development :: Testing
-"""
-
# Pull in the tools we need.
import os, sys
from setuptools import setup
-from distutils.core import Extension # pylint: disable=no-name-in-module,import-error
+from distutils.core import Extension # pylint: disable=no-name-in-module, import-error
from distutils.command.build_ext import build_ext # pylint: disable=no-name-in-module, import-error
from distutils import errors # pylint: disable=no-name-in-module
# Get or massage our metadata. We exec coverage/version.py so we can avoid
# importing the product code into setup.py.
+classifiers = """\
+Environment :: Console
+Intended Audience :: Developers
+License :: OSI Approved :: Apache Software License
+Operating System :: OS Independent
+Programming Language :: Python :: 2
+Programming Language :: Python :: 3
+Topic :: Software Development :: Quality Assurance
+Topic :: Software Development :: Testing
+"""
+
doc = __doc__ # __doc__ will be overwritten by version.py.
__version__ = __url__ = "" # Keep pylint happy.
@@ -61,7 +64,7 @@ cov_ver_py = os.path.join(os.path.split(__file__)[0], "coverage/version.py")
with open(cov_ver_py) as version_file:
exec(compile(version_file.read(), cov_ver_py, 'exec'))
-doclines = (doc % __url__).splitlines()
+doclines = (doc.format(docurl=__url__)).splitlines()
classifier_list = classifiers.splitlines()
if 'a' in __version__:
@@ -75,10 +78,10 @@ classifier_list.append("Development Status :: " + devstat)
# Install a script as "coverage", and as "coverage[23]", and as
# "coverage-2.7" (or whatever).
scripts = [
- 'coverage = coverage:main',
- 'coverage%d = coverage:main' % sys.version_info[:1],
- 'coverage-%d.%d = coverage:main' % sys.version_info[:2],
- ]
+ 'coverage = coverage.cmdline:main',
+ 'coverage%d = coverage.cmdline:main' % sys.version_info[:1],
+ 'coverage-%d.%d = coverage.cmdline:main' % sys.version_info[:2],
+]
# Create the keyword arguments for setup()
@@ -88,13 +91,13 @@ setup_args = dict(
packages = [
'coverage',
- ],
+ ],
package_data = {
'coverage': [
'htmlfiles/*.*',
- ]
- },
+ ]
+ },
entry_points = {'console_scripts': scripts},
@@ -106,10 +109,10 @@ setup_args = dict(
description = doclines[0],
long_description = '\n'.join(doclines[2:]),
keywords = 'code coverage testing',
- license = 'BSD',
+ license = 'Apache 2.0',
classifiers = classifier_list,
url = __url__,
- )
+)
# A replacement for the build_ext command which raises a single exception
# if the build fails, so we can fallback nicely.
@@ -124,11 +127,13 @@ if sys.platform == 'win32':
# find the compiler
ext_errors += (IOError,)
+
class BuildFailed(Exception):
"""Raise this to indicate the C extension wouldn't build."""
def __init__(self):
Exception.__init__(self)
- self.cause = sys.exc_info()[1] # work around py 2/3 different syntax
+ self.cause = sys.exc_info()[1] # work around py 2/3 different syntax
+
class ve_build_ext(build_ext):
"""Build C extensions, but fail with a straightforward exception."""
@@ -150,7 +155,7 @@ class ve_build_ext(build_ext):
raise BuildFailed()
except ValueError as err:
# this can happen on Windows 64 bit, see Python issue 7511
- if "'path'" in str(err): # works with both py 2/3
+ if "'path'" in str(err): # works with both py 2/3
raise BuildFailed()
raise
@@ -170,19 +175,28 @@ if '__pypy__' in sys.builtin_module_names:
if compile_extension:
setup_args.update(dict(
ext_modules = [
- Extension("coverage.tracer", sources=["coverage/tracer.c"])
- ],
+ Extension(
+ "coverage.tracer",
+ sources=[
+ "coverage/ctracer/datastack.c",
+ "coverage/ctracer/filedisp.c",
+ "coverage/ctracer/module.c",
+ "coverage/ctracer/tracer.c",
+ ]
+ )
+ ],
cmdclass = {
'build_ext': ve_build_ext,
- },
- ))
+ },
+ ))
# Py3.x-specific details.
if sys.version_info >= (3, 0):
setup_args.update(dict(
use_2to3 = False,
- ))
+ ))
+
def main():
"""Actually invoke setup() with the arguments we built above."""
diff --git a/test_old.sh b/test_old.sh
index 72c3b35c..50cc2409 100644
--- a/test_old.sh
+++ b/test_old.sh
@@ -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
+
# Steps to prepare and run coverage.py tests, for Pythons < 2.5
# This should do the same steps as tox.ini
easy_install nose==1.2.1 mock==0.6.0
diff --git a/tests/backtest.py b/tests/backtest.py
index 50834721..827e891f 100644
--- a/tests/backtest.py
+++ b/tests/backtest.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
+
"""Add things to old Pythons so I can pretend they are newer, for tests."""
# pylint: disable=redefined-builtin
diff --git a/tests/coveragetest.py b/tests/coveragetest.py
index 0e80f4a9..9e0bb26e 100644
--- a/tests/coveragetest.py
+++ b/tests/coveragetest.py
@@ -1,5 +1,9 @@
-"""Base test case class for coverage testing."""
+# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
+# For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt
+"""Base test case class for coverage.py testing."""
+
+import datetime
import glob
import os
import random
@@ -11,7 +15,8 @@ import sys
import coverage
from coverage.backunittest import TestCase
from coverage.backward import StringIO, import_local_file, string_class
-from coverage.control import _TEST_NAME_FILE
+from coverage.cmdline import CoverageScript
+from coverage.debug import _TEST_NAME_FILE, DebugControl
from coverage.test_helpers import (
EnvironmentAwareMixin, StdStreamCapturingMixin, TempDirMixin,
)
@@ -31,7 +36,7 @@ class CoverageTest(
TempDirMixin,
TestCase
):
- """A base class for Coverage test cases."""
+ """A base class for coverage.py test cases."""
# Standard unittest setting: show me diffs even if they are very long.
maxDiff = None
@@ -93,7 +98,7 @@ class CoverageTest(
# Import the Python file, executing it.
mod = self.import_local_file(modname)
finally: # pragma: nested
- # Stop Coverage.
+ # Stop coverage.py.
cov.stop()
return mod
@@ -170,7 +175,7 @@ class CoverageTest(
"""
# We write the code into a file so that we can import it.
- # Coverage wants to deal with things as modules with file names.
+ # Coverage.py wants to deal with things as modules with file names.
modname = self.get_module_name()
self.make_file(modname+".py", text)
@@ -182,8 +187,8 @@ class CoverageTest(
if arcs_unpredicted is None and arcz_unpredicted is not None:
arcs_unpredicted = self.arcz_to_arcs(arcz_unpredicted)
- # Start up Coverage.
- cov = coverage.coverage(branch=(arcs_missing is not None))
+ # Start up coverage.py.
+ cov = coverage.Coverage(branch=(arcs_missing is not None))
cov.erase()
for exc in excludes or []:
cov.exclude(exc)
@@ -278,6 +283,14 @@ class CoverageTest(
if not s.startswith(prefix):
self.fail(msg or ("%r doesn't start with %r" % (s, prefix)))
+ def assert_recent_datetime(self, dt, seconds=10, msg=None):
+ """Assert that `dt` marks a time at most `seconds` seconds ago."""
+ age = datetime.datetime.now() - dt
+ # Python2.6 doesn't have total_seconds :(
+ self.assertEqual(age.days, 0, msg)
+ self.assertGreaterEqual(age.seconds, 0, msg)
+ self.assertLessEqual(age.seconds, seconds, msg)
+
def command_line(self, args, ret=OK, _covpkg=None):
"""Run `args` through the command line.
@@ -290,7 +303,7 @@ class CoverageTest(
Returns None.
"""
- script = coverage.CoverageScript(_covpkg=_covpkg)
+ script = CoverageScript(_covpkg=_covpkg)
ret_actual = script.command_line(shlex.split(args))
self.assertEqual(ret_actual, ret)
@@ -364,3 +377,13 @@ class CoverageTest(
def last_line_squeezed(self, report):
"""Return the last line of `report` with the spaces squeezed down."""
return self.squeezed_lines(report)[-1]
+
+
+class DebugControlString(DebugControl):
+ """A `DebugControl` that writes to a StringIO, for testing."""
+ def __init__(self, options):
+ super(DebugControlString, self).__init__(options, StringIO())
+
+ def get_output(self):
+ """Get the output text from the `DebugControl`."""
+ return self.output.getvalue()
diff --git a/tests/covmodzip1.py b/tests/covmodzip1.py
index 3ec4cdc4..6f135dd6 100644
--- a/tests/covmodzip1.py
+++ b/tests/covmodzip1.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
+
"""covmodzip.py: for putting into a zip file."""
j = 1
j += 1
diff --git a/tests/eggsrc/egg1/egg1.py b/tests/eggsrc/egg1/egg1.py
index 3fadde33..72600808 100644
--- a/tests/eggsrc/egg1/egg1.py
+++ b/tests/eggsrc/egg1/egg1.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
+
# My egg file!
walrus = "Eggman"
diff --git a/tests/eggsrc/setup.py b/tests/eggsrc/setup.py
index f9b8b9d0..c935798d 100644
--- a/tests/eggsrc/setup.py
+++ b/tests/eggsrc/setup.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
+
from setuptools import setup
setup(
diff --git a/tests/farm/annotate/annotate_dir.py b/tests/farm/annotate/annotate_dir.py
index 86c18cab..3bb2dbe7 100644
--- a/tests/farm/annotate/annotate_dir.py
+++ b/tests/farm/annotate/annotate_dir.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
+
copy("src", "run")
run("""
coverage run multi.py
diff --git a/tests/farm/annotate/gold/white.py,cover b/tests/farm/annotate/gold/white.py,cover
index 36b0b993..fc163226 100644
--- a/tests/farm/annotate/gold/white.py,cover
+++ b/tests/farm/annotate/gold/white.py,cover
@@ -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
+
# A test case sent to me by Steve White
> def f(self):
diff --git a/tests/farm/annotate/gold_anno_dir/a_a.py,cover b/tests/farm/annotate/gold_anno_dir/a_a.py,cover
index d0ff3c0c..4729cfbb 100644
--- a/tests/farm/annotate/gold_anno_dir/a_a.py,cover
+++ b/tests/farm/annotate/gold_anno_dir/a_a.py,cover
@@ -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
+
> def a(x):
> if x == 1:
> print("x is 1")
diff --git a/tests/farm/annotate/gold_anno_dir/b_b.py,cover b/tests/farm/annotate/gold_anno_dir/b_b.py,cover
index 90d076f1..228715f0 100644
--- a/tests/farm/annotate/gold_anno_dir/b_b.py,cover
+++ b/tests/farm/annotate/gold_anno_dir/b_b.py,cover
@@ -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
+
> def b(x):
> msg = "x is %s" % x
> print(msg)
diff --git a/tests/farm/annotate/gold_anno_dir/multi.py,cover b/tests/farm/annotate/gold_anno_dir/multi.py,cover
index 2a5c59ce..90a13c91 100644
--- a/tests/farm/annotate/gold_anno_dir/multi.py,cover
+++ b/tests/farm/annotate/gold_anno_dir/multi.py,cover
@@ -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
+
> import a.a
> import b.b
diff --git a/tests/farm/annotate/gold_encodings/utf8.py,cover b/tests/farm/annotate/gold_encodings/utf8.py,cover
new file mode 100644
index 00000000..3ef31e0f
--- /dev/null
+++ b/tests/farm/annotate/gold_encodings/utf8.py,cover
@@ -0,0 +1,7 @@
+ # -*- coding: utf-8 -*-
+ # Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
+ # For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt
+
+ # This comment has an accent: é
+
+> print("spam eggs")
diff --git a/tests/farm/annotate/gold_multi/a/a.py,cover b/tests/farm/annotate/gold_multi/a/a.py,cover
index fb3f5435..e5e97226 100644
--- a/tests/farm/annotate/gold_multi/a/a.py,cover
+++ b/tests/farm/annotate/gold_multi/a/a.py,cover
@@ -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
+
> def a(x):
> if x == 1:
> print "x is 1"
diff --git a/tests/farm/annotate/gold_multi/b/b.py,cover b/tests/farm/annotate/gold_multi/b/b.py,cover
index a3f5daec..26b25548 100644
--- a/tests/farm/annotate/gold_multi/b/b.py,cover
+++ b/tests/farm/annotate/gold_multi/b/b.py,cover
@@ -1,2 +1,5 @@
+ # Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
+ # For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt
+
> def b(x):
> print "x is %s" % x
diff --git a/tests/farm/annotate/gold_multi/multi.py,cover b/tests/farm/annotate/gold_multi/multi.py,cover
index 2a5c59ce..90a13c91 100644
--- a/tests/farm/annotate/gold_multi/multi.py,cover
+++ b/tests/farm/annotate/gold_multi/multi.py,cover
@@ -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
+
> import a.a
> import b.b
diff --git a/tests/farm/annotate/gold_v24/white.py,cover b/tests/farm/annotate/gold_v24/white.py,cover
index bbd8d428..8af136b8 100644
--- a/tests/farm/annotate/gold_v24/white.py,cover
+++ b/tests/farm/annotate/gold_v24/white.py,cover
@@ -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
+
# A test case sent to me by Steve White
> def f(self):
diff --git a/tests/farm/annotate/run.py b/tests/farm/annotate/run.py
index 236f401f..33e5f671 100644
--- a/tests/farm/annotate/run.py
+++ b/tests/farm/annotate/run.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
+
copy("src", "out")
run("""
coverage run white.py
diff --git a/tests/farm/annotate/run_encodings.py b/tests/farm/annotate/run_encodings.py
new file mode 100644
index 00000000..527cd88f
--- /dev/null
+++ b/tests/farm/annotate/run_encodings.py
@@ -0,0 +1,10 @@
+# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
+# For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt
+
+copy("src", "out")
+run("""
+ coverage run utf8.py
+ coverage annotate utf8.py
+ """, rundir="out")
+compare("out", "gold_encodings", "*,cover")
+clean("out")
diff --git a/tests/farm/annotate/run_multi.py b/tests/farm/annotate/run_multi.py
index ef1e8238..4646293e 100644
--- a/tests/farm/annotate/run_multi.py
+++ b/tests/farm/annotate/run_multi.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
+
copy("src", "out_multi")
run("""
coverage run multi.py
diff --git a/tests/farm/annotate/src/a/a.py b/tests/farm/annotate/src/a/a.py
index c2583d1e..e3e6631d 100644
--- a/tests/farm/annotate/src/a/a.py
+++ b/tests/farm/annotate/src/a/a.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
+
def a(x):
if x == 1:
print("x is 1")
diff --git a/tests/farm/annotate/src/b/b.py b/tests/farm/annotate/src/b/b.py
index 625a5490..b31d8c95 100644
--- a/tests/farm/annotate/src/b/b.py
+++ b/tests/farm/annotate/src/b/b.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
+
def b(x):
msg = "x is %s" % x
print(msg)
diff --git a/tests/farm/annotate/src/multi.py b/tests/farm/annotate/src/multi.py
index 19a6200c..bf8cfd5f 100644
--- a/tests/farm/annotate/src/multi.py
+++ b/tests/farm/annotate/src/multi.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
+
import a.a
import b.b
diff --git a/tests/farm/annotate/src/utf8.py b/tests/farm/annotate/src/utf8.py
new file mode 100644
index 00000000..fd43b2ab
--- /dev/null
+++ b/tests/farm/annotate/src/utf8.py
@@ -0,0 +1,7 @@
+# -*- coding: utf-8 -*-
+# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
+# For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt
+
+# This comment has an accent: é
+
+print("spam eggs")
diff --git a/tests/farm/annotate/src/white.py b/tests/farm/annotate/src/white.py
index ecbbd25a..21e8a627 100644
--- a/tests/farm/annotate/src/white.py
+++ b/tests/farm/annotate/src/white.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
+
# A test case sent to me by Steve White
def f(self):
diff --git a/tests/farm/html/gold_a/a_py.html b/tests/farm/html/gold_a/a_py.html
index 03c1fc95..e52b303e 100644
--- a/tests/farm/html/gold_a/a_py.html
+++ b/tests/farm/html/gold_a/a_py.html
@@ -1,96 +1,106 @@
+
+
+
<!DOCTYPE html>
<html>
<head>
- <meta http-equiv='Content-Type' content='text/html; charset=utf-8'>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
- <meta http-equiv='X-UA-Compatible' content='IE=emulateIE7' />
- <title>Coverage for a: 67%</title>
- <link rel='stylesheet' href='style.css' type='text/css'>
+ <meta http-equiv="X-UA-Compatible" content="IE=emulateIE7" />
+ <title>Coverage for a.py: 67%</title>
+ <link rel="stylesheet" href="style.css" type="text/css">
- <script type='text/javascript' src='jquery.min.js'></script>
- <script type='text/javascript' src='jquery.hotkeys.js'></script>
- <script type='text/javascript' src='jquery.isonscreen.js'></script>
- <script type='text/javascript' src='coverage_html.js'></script>
- <script type='text/javascript'>
+ <script type="text/javascript" src="jquery.min.js"></script>
+ <script type="text/javascript" src="jquery.hotkeys.js"></script>
+ <script type="text/javascript" src="jquery.isonscreen.js"></script>
+ <script type="text/javascript" src="coverage_html.js"></script>
+ <script type="text/javascript">
jQuery(document).ready(coverage.pyfile_ready);
</script>
</head>
-<body class='pyfile'>
+<body class="pyfile">
-<div id='header'>
- <div class='content'>
- <h1>Coverage for <b>a</b> :
- <span class='pc_cov'>67%</span>
+<div id="header">
+ <div class="content">
+ <h1>Coverage for <b>a.py</b> :
+ <span class="pc_cov">67%</span>
</h1>
- <img id='keyboard_icon' src='keybd_closed.png' alt='Show keyboard shortcuts' />
+ <img id="keyboard_icon" src="keybd_closed.png" alt="Show keyboard shortcuts" />
- <h2 class='stats'>
+ <h2 class="stats">
3 statements &nbsp;
- <span class='run hide_run shortkey_r button_toggle_run'>2 run</span>
- <span class='mis shortkey_m button_toggle_mis'>1 missing</span>
- <span class='exc shortkey_x button_toggle_exc'>0 excluded</span>
+ <span class="run hide_run shortkey_r button_toggle_run">2 run</span>
+ <span class="mis shortkey_m button_toggle_mis">1 missing</span>
+ <span class="exc shortkey_x button_toggle_exc">0 excluded</span>
</h2>
</div>
</div>
-<div class='help_panel'>
- <img id='panel_icon' src='keybd_open.png' alt='Hide keyboard shortcuts' />
-<p class='legend'>Hot-keys on this page</p>
+<div class="help_panel">
+ <img id="panel_icon" src="keybd_open.png" alt="Hide keyboard shortcuts" />
+<p class="legend">Hot-keys on this page</p>
<div>
-<p class='keyhelp'>
- <span class='key'>r</span>
- <span class='key'>m</span>
- <span class='key'>x</span>
- <span class='key'>p</span> &nbsp; toggle line displays
+<p class="keyhelp">
+ <span class="key">r</span>
+ <span class="key">m</span>
+ <span class="key">x</span>
+ <span class="key">p</span> &nbsp; toggle line displays
</p>
-<p class='keyhelp'>
- <span class='key'>j</span>
- <span class='key'>k</span> &nbsp; next/prev highlighted chunk
+<p class="keyhelp">
+ <span class="key">j</span>
+ <span class="key">k</span> &nbsp; next/prev highlighted chunk
</p>
-<p class='keyhelp'>
- <span class='key'>0</span> &nbsp; (zero) top of page
+<p class="keyhelp">
+ <span class="key">0</span> &nbsp; (zero) top of page
</p>
-<p class='keyhelp'>
- <span class='key'>1</span> &nbsp; (one) first highlighted chunk
+<p class="keyhelp">
+ <span class="key">1</span> &nbsp; (one) first highlighted chunk
</p>
</div>
</div>
-<div id='source'>
+<div id="source">
<table>
<tr>
- <td class='linenos'>
-<p id='n1' class='pln'><a href='#n1'>1</a></p>
-<p id='n2' class='pln'><a href='#n2'>2</a></p>
-<p id='n3' class='stm run hide_run'><a href='#n3'>3</a></p>
-<p id='n4' class='pln'><a href='#n4'>4</a></p>
-<p id='n5' class='stm run hide_run'><a href='#n5'>5</a></p>
-<p id='n6' class='pln'><a href='#n6'>6</a></p>
-<p id='n7' class='stm mis'><a href='#n7'>7</a></p>
+ <td class="linenos">
+<p id="n1" class="pln"><a href="#n1">1</a></p>
+<p id="n2" class="pln"><a href="#n2">2</a></p>
+<p id="n3" class="pln"><a href="#n3">3</a></p>
+<p id="n4" class="pln"><a href="#n4">4</a></p>
+<p id="n5" class="pln"><a href="#n5">5</a></p>
+<p id="n6" class="stm run hide_run"><a href="#n6">6</a></p>
+<p id="n7" class="pln"><a href="#n7">7</a></p>
+<p id="n8" class="stm run hide_run"><a href="#n8">8</a></p>
+<p id="n9" class="pln"><a href="#n9">9</a></p>
+<p id="n10" class="stm mis"><a href="#n10">10</a></p>
</td>
- <td class='text'>
-<p id='t1' class='pln'><span class='com'># A test file for HTML reporting by coverage.</span><span class='strut'>&nbsp;</span></p>
-<p id='t2' class='pln'><span class='strut'>&nbsp;</span></p>
-<p id='t3' class='stm run hide_run'><span class='key'>if</span> <span class='num'>1</span> <span class='op'>&lt;</span> <span class='num'>2</span><span class='op'>:</span><span class='strut'>&nbsp;</span></p>
-<p id='t4' class='pln'>&nbsp; &nbsp; <span class='com'># Needed a &lt; to look at HTML entities.</span><span class='strut'>&nbsp;</span></p>
-<p id='t5' class='stm run hide_run'>&nbsp; &nbsp; <span class='nam'>a</span> <span class='op'>=</span> <span class='num'>3</span><span class='strut'>&nbsp;</span></p>
-<p id='t6' class='pln'><span class='key'>else</span><span class='op'>:</span><span class='strut'>&nbsp;</span></p>
-<p id='t7' class='stm mis'>&nbsp; &nbsp; <span class='nam'>a</span> <span class='op'>=</span> <span class='num'>4</span><span class='strut'>&nbsp;</span></p>
+ <td class="text">
+<p id="t1" class="pln"><span class="com"># Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0</span><span class="strut">&nbsp;</span></p>
+<p id="t2" class="pln"><span class="com"># For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt</span><span class="strut">&nbsp;</span></p>
+<p id="t3" class="pln"><span class="strut">&nbsp;</span></p>
+<p id="t4" class="pln"><span class="com"># A test file for HTML reporting by coverage.py.</span><span class="strut">&nbsp;</span></p>
+<p id="t5" class="pln"><span class="strut">&nbsp;</span></p>
+<p id="t6" class="stm run hide_run"><span class="key">if</span> <span class="num">1</span> <span class="op">&lt;</span> <span class="num">2</span><span class="op">:</span><span class="strut">&nbsp;</span></p>
+<p id="t7" class="pln">&nbsp; &nbsp; <span class="com"># Needed a &lt; to look at HTML entities.</span><span class="strut">&nbsp;</span></p>
+<p id="t8" class="stm run hide_run">&nbsp; &nbsp; <span class="nam">a</span> <span class="op">=</span> <span class="num">3</span><span class="strut">&nbsp;</span></p>
+<p id="t9" class="pln"><span class="key">else</span><span class="op">:</span><span class="strut">&nbsp;</span></p>
+<p id="t10" class="stm mis">&nbsp; &nbsp; <span class="nam">a</span> <span class="op">=</span> <span class="num">4</span><span class="strut">&nbsp;</span></p>
</td>
</tr>
</table>
</div>
-<div id='footer'>
- <div class='content'>
+<div id="footer">
+ <div class="content">
<p>
- <a class='nav' href='index.html'>&#xab; index</a> &nbsp; &nbsp; <a class='nav' href='http://nedbatchelder.com/code/coverage/4.0a1'>coverage.py v4.0a1</a>
+ <a class="nav" href="index.html">&#xab; index</a> &nbsp; &nbsp; <a class="nav" href="https://coverage.readthedocs.org/en/coverage-4.0a7">coverage.py v4.0a7</a>,
+ created at 2015-07-24 09:04
</p>
</div>
</div>
diff --git a/tests/farm/html/gold_a/index.html b/tests/farm/html/gold_a/index.html
index 4a79fc57..4f462fe7 100644
--- a/tests/farm/html/gold_a/index.html
+++ b/tests/farm/html/gold_a/index.html
@@ -1,28 +1,31 @@
+
+
+
<!DOCTYPE html>
<html>
<head>
- <meta http-equiv='Content-Type' content='text/html; charset=utf-8'>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>Coverage report</title>
- <link rel='stylesheet' href='style.css' type='text/css'>
+ <link rel="stylesheet" href="style.css" type="text/css">
- <script type='text/javascript' src='jquery.min.js'></script>
- <script type='text/javascript' src='jquery.debounce.min.js'></script>
- <script type='text/javascript' src='jquery.tablesorter.min.js'></script>
- <script type='text/javascript' src='jquery.hotkeys.js'></script>
- <script type='text/javascript' src='coverage_html.js'></script>
- <script type='text/javascript'>
+ <script type="text/javascript" src="jquery.min.js"></script>
+ <script type="text/javascript" src="jquery.debounce.min.js"></script>
+ <script type="text/javascript" src="jquery.tablesorter.min.js"></script>
+ <script type="text/javascript" src="jquery.hotkeys.js"></script>
+ <script type="text/javascript" src="coverage_html.js"></script>
+ <script type="text/javascript">
jQuery(document).ready(coverage.index_ready);
</script>
</head>
-<body class='indexfile'>
+<body class="indexfile">
-<div id='header'>
- <div class='content'>
+<div id="header">
+ <div class="content">
<h1>Coverage report:
- <span class='pc_cov'>67%</span>
+ <span class="pc_cov">67%</span>
</h1>
- <img id='keyboard_icon' src='keybd_closed.png' alt='Show keyboard shortcuts' />
+ <img id="keyboard_icon" src="keybd_closed.png" alt="Show keyboard shortcuts" />
<form id="filter_container">
<input id="filter" type="text" value="" placeholder="filter..." />
@@ -30,54 +33,54 @@
</div>
</div>
-<div class='help_panel'>
- <img id='panel_icon' src='keybd_open.png' alt='Hide keyboard shortcuts' />
- <p class='legend'>Hot-keys on this page</p>
+<div class="help_panel">
+ <img id="panel_icon" src="keybd_open.png" alt="Hide keyboard shortcuts" />
+ <p class="legend">Hot-keys on this page</p>
<div>
- <p class='keyhelp'>
- <span class='key'>n</span>
- <span class='key'>s</span>
- <span class='key'>m</span>
- <span class='key'>x</span>
+ <p class="keyhelp">
+ <span class="key">n</span>
+ <span class="key">s</span>
+ <span class="key">m</span>
+ <span class="key">x</span>
- <span class='key'>c</span> &nbsp; change column sorting
+ <span class="key">c</span> &nbsp; change column sorting
</p>
</div>
</div>
-<div id='index'>
- <table class='index'>
+<div id="index">
+ <table class="index">
<thead>
- <tr class='tablehead' title='Click to sort'>
- <th class='name left headerSortDown shortkey_n'>Module</th>
- <th class='shortkey_s'>statements</th>
- <th class='shortkey_m'>missing</th>
- <th class='shortkey_x'>excluded</th>
+ <tr class="tablehead" title="Click to sort">
+ <th class="name left headerSortDown shortkey_n">Module</th>
+ <th class="shortkey_s">statements</th>
+ <th class="shortkey_m">missing</th>
+ <th class="shortkey_x">excluded</th>
- <th class='right shortkey_c'>coverage</th>
+ <th class="right shortkey_c">coverage</th>
</tr>
</thead>
<tfoot>
- <tr class='total'>
- <td class='name left'>Total</td>
+ <tr class="total">
+ <td class="name left">Total</td>
<td>3</td>
<td>1</td>
<td>0</td>
- <td class='right' data-ratio='2 3'>67%</td>
+ <td class="right" data-ratio="2 3">67%</td>
</tr>
</tfoot>
<tbody>
- <tr class='file'>
- <td class='name left'><a href='a.html'>a</a></td>
+ <tr class="file">
+ <td class="name left"><a href="a_py.html">a.py</a></td>
<td>3</td>
<td>1</td>
<td>0</td>
- <td class='right' data-ratio='2 3'>67%</td>
+ <td class="right" data-ratio="2 3">67%</td>
</tr>
</tbody>
@@ -88,10 +91,11 @@
</p>
</div>
-<div id='footer'>
- <div class='content'>
+<div id="footer">
+ <div class="content">
<p>
- <a class='nav' href='http://nedbatchelder.com/code/coverage/4.0a1'>coverage.py v4.0a1</a>
+ <a class="nav" href="https://coverage.readthedocs.org/en/coverage-4.0a7">coverage.py v4.0a7</a>,
+ created at 2015-07-24 09:31
</p>
</div>
</div>
diff --git a/tests/farm/html/gold_b_branch/b_py.html b/tests/farm/html/gold_b_branch/b_py.html
index 15b06661..4ee9e1c4 100644
--- a/tests/farm/html/gold_b_branch/b_py.html
+++ b/tests/farm/html/gold_b_branch/b_py.html
@@ -1,142 +1,152 @@
+
+
+
<!DOCTYPE html>
<html>
<head>
- <meta http-equiv='Content-Type' content='text/html; charset=utf-8'>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
- <meta http-equiv='X-UA-Compatible' content='IE=emulateIE7' />
- <title>Coverage for b: 70%</title>
- <link rel='stylesheet' href='style.css' type='text/css'>
+ <meta http-equiv="X-UA-Compatible" content="IE=emulateIE7" />
+ <title>Coverage for b.py: 70%</title>
+ <link rel="stylesheet" href="style.css" type="text/css">
- <script type='text/javascript' src='jquery.min.js'></script>
- <script type='text/javascript' src='jquery.hotkeys.js'></script>
- <script type='text/javascript' src='jquery.isonscreen.js'></script>
- <script type='text/javascript' src='coverage_html.js'></script>
- <script type='text/javascript'>
+ <script type="text/javascript" src="jquery.min.js"></script>
+ <script type="text/javascript" src="jquery.hotkeys.js"></script>
+ <script type="text/javascript" src="jquery.isonscreen.js"></script>
+ <script type="text/javascript" src="coverage_html.js"></script>
+ <script type="text/javascript">
jQuery(document).ready(coverage.pyfile_ready);
</script>
</head>
-<body class='pyfile'>
+<body class="pyfile">
-<div id='header'>
- <div class='content'>
- <h1>Coverage for <b>b</b> :
- <span class='pc_cov'>70%</span>
+<div id="header">
+ <div class="content">
+ <h1>Coverage for <b>b.py</b> :
+ <span class="pc_cov">70%</span>
</h1>
- <img id='keyboard_icon' src='keybd_closed.png' alt='Show keyboard shortcuts' />
+ <img id="keyboard_icon" src="keybd_closed.png" alt="Show keyboard shortcuts" />
- <h2 class='stats'>
+ <h2 class="stats">
17 statements &nbsp;
- <span class='run hide_run shortkey_r button_toggle_run'>14 run</span>
- <span class='mis shortkey_m button_toggle_mis'>3 missing</span>
- <span class='exc shortkey_x button_toggle_exc'>0 excluded</span>
+ <span class="run hide_run shortkey_r button_toggle_run">14 run</span>
+ <span class="mis shortkey_m button_toggle_mis">3 missing</span>
+ <span class="exc shortkey_x button_toggle_exc">0 excluded</span>
- <span class='par run hide_run shortkey_p button_toggle_par'>4 partial</span>
+ <span class="par run hide_run shortkey_p button_toggle_par">4 partial</span>
</h2>
</div>
</div>
-<div class='help_panel'>
- <img id='panel_icon' src='keybd_open.png' alt='Hide keyboard shortcuts' />
-<p class='legend'>Hot-keys on this page</p>
+<div class="help_panel">
+ <img id="panel_icon" src="keybd_open.png" alt="Hide keyboard shortcuts" />
+<p class="legend">Hot-keys on this page</p>
<div>
-<p class='keyhelp'>
- <span class='key'>r</span>
- <span class='key'>m</span>
- <span class='key'>x</span>
- <span class='key'>p</span> &nbsp; toggle line displays
+<p class="keyhelp">
+ <span class="key">r</span>
+ <span class="key">m</span>
+ <span class="key">x</span>
+ <span class="key">p</span> &nbsp; toggle line displays
</p>
-<p class='keyhelp'>
- <span class='key'>j</span>
- <span class='key'>k</span> &nbsp; next/prev highlighted chunk
+<p class="keyhelp">
+ <span class="key">j</span>
+ <span class="key">k</span> &nbsp; next/prev highlighted chunk
</p>
-<p class='keyhelp'>
- <span class='key'>0</span> &nbsp; (zero) top of page
+<p class="keyhelp">
+ <span class="key">0</span> &nbsp; (zero) top of page
</p>
-<p class='keyhelp'>
- <span class='key'>1</span> &nbsp; (one) first highlighted chunk
+<p class="keyhelp">
+ <span class="key">1</span> &nbsp; (one) first highlighted chunk
</p>
</div>
</div>
-<div id='source'>
+<div id="source">
<table>
<tr>
- <td class='linenos'>
-<p id='n1' class='pln'><a href='#n1'>1</a></p>
-<p id='n2' class='pln'><a href='#n2'>2</a></p>
-<p id='n3' class='stm run hide_run'><a href='#n3'>3</a></p>
-<p id='n4' class='pln'><a href='#n4'>4</a></p>
-<p id='n5' class='stm par run hide_run'><a href='#n5'>5</a></p>
-<p id='n6' class='stm run hide_run'><a href='#n6'>6</a></p>
-<p id='n7' class='pln'><a href='#n7'>7</a></p>
-<p id='n8' class='stm mis'><a href='#n8'>8</a></p>
-<p id='n9' class='pln'><a href='#n9'>9</a></p>
-<p id='n10' class='stm run hide_run'><a href='#n10'>10</a></p>
-<p id='n11' class='pln'><a href='#n11'>11</a></p>
-<p id='n12' class='stm run hide_run'><a href='#n12'>12</a></p>
-<p id='n13' class='pln'><a href='#n13'>13</a></p>
-<p id='n14' class='stm par run hide_run'><a href='#n14'>14</a></p>
-<p id='n15' class='stm run hide_run'><a href='#n15'>15</a></p>
-<p id='n16' class='pln'><a href='#n16'>16</a></p>
-<p id='n17' class='stm run hide_run'><a href='#n17'>17</a></p>
-<p id='n18' class='pln'><a href='#n18'>18</a></p>
-<p id='n19' class='stm run hide_run'><a href='#n19'>19</a></p>
-<p id='n20' class='stm run hide_run'><a href='#n20'>20</a></p>
-<p id='n21' class='pln'><a href='#n21'>21</a></p>
-<p id='n22' class='stm par run hide_run'><a href='#n22'>22</a></p>
-<p id='n23' class='stm mis'><a href='#n23'>23</a></p>
-<p id='n24' class='pln'><a href='#n24'>24</a></p>
-<p id='n25' class='stm mis'><a href='#n25'>25</a></p>
-<p id='n26' class='stm run hide_run'><a href='#n26'>26</a></p>
-<p id='n27' class='stm run hide_run'><a href='#n27'>27</a></p>
-<p id='n28' class='pln'><a href='#n28'>28</a></p>
-<p id='n29' class='stm run hide_run'><a href='#n29'>29</a></p>
+ <td class="linenos">
+<p id="n1" class="pln"><a href="#n1">1</a></p>
+<p id="n2" class="pln"><a href="#n2">2</a></p>
+<p id="n3" class="pln"><a href="#n3">3</a></p>
+<p id="n4" class="pln"><a href="#n4">4</a></p>
+<p id="n5" class="pln"><a href="#n5">5</a></p>
+<p id="n6" class="stm run hide_run"><a href="#n6">6</a></p>
+<p id="n7" class="pln"><a href="#n7">7</a></p>
+<p id="n8" class="stm par run hide_run"><a href="#n8">8</a></p>
+<p id="n9" class="stm run hide_run"><a href="#n9">9</a></p>
+<p id="n10" class="pln"><a href="#n10">10</a></p>
+<p id="n11" class="stm mis"><a href="#n11">11</a></p>
+<p id="n12" class="pln"><a href="#n12">12</a></p>
+<p id="n13" class="stm run hide_run"><a href="#n13">13</a></p>
+<p id="n14" class="pln"><a href="#n14">14</a></p>
+<p id="n15" class="stm run hide_run"><a href="#n15">15</a></p>
+<p id="n16" class="pln"><a href="#n16">16</a></p>
+<p id="n17" class="stm par run hide_run"><a href="#n17">17</a></p>
+<p id="n18" class="stm run hide_run"><a href="#n18">18</a></p>
+<p id="n19" class="pln"><a href="#n19">19</a></p>
+<p id="n20" class="stm run hide_run"><a href="#n20">20</a></p>
+<p id="n21" class="pln"><a href="#n21">21</a></p>
+<p id="n22" class="stm run hide_run"><a href="#n22">22</a></p>
+<p id="n23" class="stm run hide_run"><a href="#n23">23</a></p>
+<p id="n24" class="pln"><a href="#n24">24</a></p>
+<p id="n25" class="stm par run hide_run"><a href="#n25">25</a></p>
+<p id="n26" class="stm mis"><a href="#n26">26</a></p>
+<p id="n27" class="pln"><a href="#n27">27</a></p>
+<p id="n28" class="stm mis"><a href="#n28">28</a></p>
+<p id="n29" class="stm run hide_run"><a href="#n29">29</a></p>
+<p id="n30" class="stm run hide_run"><a href="#n30">30</a></p>
+<p id="n31" class="pln"><a href="#n31">31</a></p>
+<p id="n32" class="stm run hide_run"><a href="#n32">32</a></p>
</td>
- <td class='text'>
-<p id='t1' class='pln'><span class='com'># A test file for HTML reporting by coverage.</span><span class='strut'>&nbsp;</span></p>
-<p id='t2' class='pln'><span class='strut'>&nbsp;</span></p>
-<p id='t3' class='stm run hide_run'><span class='key'>def</span> <span class='nam'>one</span><span class='op'>(</span><span class='nam'>x</span><span class='op'>)</span><span class='op'>:</span><span class='strut'>&nbsp;</span></p>
-<p id='t4' class='pln'>&nbsp; &nbsp; <span class='com'># This will be a branch that misses the else.</span><span class='strut'>&nbsp;</span></p>
-<p id='t5' class='stm par run hide_run'><span class='annotate' title='no jump to this line number'>8</span>&nbsp; &nbsp; <span class='key'>if</span> <span class='nam'>x</span> <span class='op'>&lt;</span> <span class='num'>2</span><span class='op'>:</span><span class='strut'>&nbsp;</span></p>
-<p id='t6' class='stm run hide_run'>&nbsp; &nbsp; &nbsp; &nbsp; <span class='nam'>a</span> <span class='op'>=</span> <span class='num'>3</span><span class='strut'>&nbsp;</span></p>
-<p id='t7' class='pln'>&nbsp; &nbsp; <span class='key'>else</span><span class='op'>:</span><span class='strut'>&nbsp;</span></p>
-<p id='t8' class='stm mis'>&nbsp; &nbsp; &nbsp; &nbsp; <span class='nam'>a</span> <span class='op'>=</span> <span class='num'>4</span><span class='strut'>&nbsp;</span></p>
-<p id='t9' class='pln'><span class='strut'>&nbsp;</span></p>
-<p id='t10' class='stm run hide_run'><span class='nam'>one</span><span class='op'>(</span><span class='num'>1</span><span class='op'>)</span><span class='strut'>&nbsp;</span></p>
-<p id='t11' class='pln'><span class='strut'>&nbsp;</span></p>
-<p id='t12' class='stm run hide_run'><span class='key'>def</span> <span class='nam'>two</span><span class='op'>(</span><span class='nam'>x</span><span class='op'>)</span><span class='op'>:</span><span class='strut'>&nbsp;</span></p>
-<p id='t13' class='pln'>&nbsp; &nbsp; <span class='com'># A missed else that branches to &quot;exit&quot;</span><span class='strut'>&nbsp;</span></p>
-<p id='t14' class='stm par run hide_run'><span class='annotate' title='no jump to this line number'>exit</span>&nbsp; &nbsp; <span class='key'>if</span> <span class='nam'>x</span><span class='op'>:</span><span class='strut'>&nbsp;</span></p>
-<p id='t15' class='stm run hide_run'>&nbsp; &nbsp; &nbsp; &nbsp; <span class='nam'>a</span> <span class='op'>=</span> <span class='num'>5</span><span class='strut'>&nbsp;</span></p>
-<p id='t16' class='pln'><span class='strut'>&nbsp;</span></p>
-<p id='t17' class='stm run hide_run'><span class='nam'>two</span><span class='op'>(</span><span class='num'>1</span><span class='op'>)</span><span class='strut'>&nbsp;</span></p>
-<p id='t18' class='pln'><span class='strut'>&nbsp;</span></p>
-<p id='t19' class='stm run hide_run'><span class='key'>def</span> <span class='nam'>three</span><span class='op'>(</span><span class='op'>)</span><span class='op'>:</span><span class='strut'>&nbsp;</span></p>
-<p id='t20' class='stm run hide_run'>&nbsp; &nbsp; <span class='key'>try</span><span class='op'>:</span><span class='strut'>&nbsp;</span></p>
-<p id='t21' class='pln'>&nbsp; &nbsp; &nbsp; &nbsp; <span class='com'># This if has two branches, *neither* one taken.</span><span class='strut'>&nbsp;</span></p>
-<p id='t22' class='stm par run hide_run'><span class='annotate' title='no jumps to these line numbers'>23&nbsp;&nbsp; 25</span>&nbsp; &nbsp; &nbsp; &nbsp; <span class='key'>if</span> <span class='nam'>name_error_this_variable_doesnt_exist</span><span class='op'>:</span><span class='strut'>&nbsp;</span></p>
-<p id='t23' class='stm mis'>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class='nam'>a</span> <span class='op'>=</span> <span class='num'>1</span><span class='strut'>&nbsp;</span></p>
-<p id='t24' class='pln'>&nbsp; &nbsp; &nbsp; &nbsp; <span class='key'>else</span><span class='op'>:</span><span class='strut'>&nbsp;</span></p>
-<p id='t25' class='stm mis'>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class='nam'>a</span> <span class='op'>=</span> <span class='num'>2</span><span class='strut'>&nbsp;</span></p>
-<p id='t26' class='stm run hide_run'>&nbsp; &nbsp; <span class='key'>except</span><span class='op'>:</span><span class='strut'>&nbsp;</span></p>
-<p id='t27' class='stm run hide_run'>&nbsp; &nbsp; &nbsp; &nbsp; <span class='key'>pass</span><span class='strut'>&nbsp;</span></p>
-<p id='t28' class='pln'><span class='strut'>&nbsp;</span></p>
-<p id='t29' class='stm run hide_run'><span class='nam'>three</span><span class='op'>(</span><span class='op'>)</span><span class='strut'>&nbsp;</span></p>
+ <td class="text">
+<p id="t1" class="pln"><span class="com"># Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0</span><span class="strut">&nbsp;</span></p>
+<p id="t2" class="pln"><span class="com"># For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt</span><span class="strut">&nbsp;</span></p>
+<p id="t3" class="pln"><span class="strut">&nbsp;</span></p>
+<p id="t4" class="pln"><span class="com"># A test file for HTML reporting by coverage.py.</span><span class="strut">&nbsp;</span></p>
+<p id="t5" class="pln"><span class="strut">&nbsp;</span></p>
+<p id="t6" class="stm run hide_run"><span class="key">def</span> <span class="nam">one</span><span class="op">(</span><span class="nam">x</span><span class="op">)</span><span class="op">:</span><span class="strut">&nbsp;</span></p>
+<p id="t7" class="pln">&nbsp; &nbsp; <span class="com"># This will be a branch that misses the else.</span><span class="strut">&nbsp;</span></p>
+<p id="t8" class="stm par run hide_run"><span class="annotate" title="no jump to this line number">11</span>&nbsp; &nbsp; <span class="key">if</span> <span class="nam">x</span> <span class="op">&lt;</span> <span class="num">2</span><span class="op">:</span><span class="strut">&nbsp;</span></p>
+<p id="t9" class="stm run hide_run">&nbsp; &nbsp; &nbsp; &nbsp; <span class="nam">a</span> <span class="op">=</span> <span class="num">3</span><span class="strut">&nbsp;</span></p>
+<p id="t10" class="pln">&nbsp; &nbsp; <span class="key">else</span><span class="op">:</span><span class="strut">&nbsp;</span></p>
+<p id="t11" class="stm mis">&nbsp; &nbsp; &nbsp; &nbsp; <span class="nam">a</span> <span class="op">=</span> <span class="num">4</span><span class="strut">&nbsp;</span></p>
+<p id="t12" class="pln"><span class="strut">&nbsp;</span></p>
+<p id="t13" class="stm run hide_run"><span class="nam">one</span><span class="op">(</span><span class="num">1</span><span class="op">)</span><span class="strut">&nbsp;</span></p>
+<p id="t14" class="pln"><span class="strut">&nbsp;</span></p>
+<p id="t15" class="stm run hide_run"><span class="key">def</span> <span class="nam">two</span><span class="op">(</span><span class="nam">x</span><span class="op">)</span><span class="op">:</span><span class="strut">&nbsp;</span></p>
+<p id="t16" class="pln">&nbsp; &nbsp; <span class="com"># A missed else that branches to &quot;exit&quot;</span><span class="strut">&nbsp;</span></p>
+<p id="t17" class="stm par run hide_run"><span class="annotate" title="no jump to this line number">exit</span>&nbsp; &nbsp; <span class="key">if</span> <span class="nam">x</span><span class="op">:</span><span class="strut">&nbsp;</span></p>
+<p id="t18" class="stm run hide_run">&nbsp; &nbsp; &nbsp; &nbsp; <span class="nam">a</span> <span class="op">=</span> <span class="num">5</span><span class="strut">&nbsp;</span></p>
+<p id="t19" class="pln"><span class="strut">&nbsp;</span></p>
+<p id="t20" class="stm run hide_run"><span class="nam">two</span><span class="op">(</span><span class="num">1</span><span class="op">)</span><span class="strut">&nbsp;</span></p>
+<p id="t21" class="pln"><span class="strut">&nbsp;</span></p>
+<p id="t22" class="stm run hide_run"><span class="key">def</span> <span class="nam">three</span><span class="op">(</span><span class="op">)</span><span class="op">:</span><span class="strut">&nbsp;</span></p>
+<p id="t23" class="stm run hide_run">&nbsp; &nbsp; <span class="key">try</span><span class="op">:</span><span class="strut">&nbsp;</span></p>
+<p id="t24" class="pln">&nbsp; &nbsp; &nbsp; &nbsp; <span class="com"># This if has two branches, *neither* one taken.</span><span class="strut">&nbsp;</span></p>
+<p id="t25" class="stm par run hide_run"><span class="annotate" title="no jumps to these line numbers">26&nbsp;&nbsp; 28</span>&nbsp; &nbsp; &nbsp; &nbsp; <span class="key">if</span> <span class="nam">name_error_this_variable_doesnt_exist</span><span class="op">:</span><span class="strut">&nbsp;</span></p>
+<p id="t26" class="stm mis">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="nam">a</span> <span class="op">=</span> <span class="num">1</span><span class="strut">&nbsp;</span></p>
+<p id="t27" class="pln">&nbsp; &nbsp; &nbsp; &nbsp; <span class="key">else</span><span class="op">:</span><span class="strut">&nbsp;</span></p>
+<p id="t28" class="stm mis">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="nam">a</span> <span class="op">=</span> <span class="num">2</span><span class="strut">&nbsp;</span></p>
+<p id="t29" class="stm run hide_run">&nbsp; &nbsp; <span class="key">except</span><span class="op">:</span><span class="strut">&nbsp;</span></p>
+<p id="t30" class="stm run hide_run">&nbsp; &nbsp; &nbsp; &nbsp; <span class="key">pass</span><span class="strut">&nbsp;</span></p>
+<p id="t31" class="pln"><span class="strut">&nbsp;</span></p>
+<p id="t32" class="stm run hide_run"><span class="nam">three</span><span class="op">(</span><span class="op">)</span><span class="strut">&nbsp;</span></p>
</td>
</tr>
</table>
</div>
-<div id='footer'>
- <div class='content'>
+<div id="footer">
+ <div class="content">
<p>
- <a class='nav' href='index.html'>&#xab; index</a> &nbsp; &nbsp; <a class='nav' href='http://nedbatchelder.com/code/coverage/4.0a1'>coverage.py v4.0a1</a>
+ <a class="nav" href="index.html">&#xab; index</a> &nbsp; &nbsp; <a class="nav" href="https://coverage.readthedocs.org/en/coverage-4.0a7">coverage.py v4.0a7</a>,
+ created at 2015-07-24 09:04
</p>
</div>
</div>
diff --git a/tests/farm/html/gold_b_branch/index.html b/tests/farm/html/gold_b_branch/index.html
index be4adc89..ebc3a106 100644
--- a/tests/farm/html/gold_b_branch/index.html
+++ b/tests/farm/html/gold_b_branch/index.html
@@ -1,28 +1,28 @@
<!DOCTYPE html>
<html>
<head>
- <meta http-equiv='Content-Type' content='text/html; charset=utf-8'>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>Coverage report</title>
- <link rel='stylesheet' href='style.css' type='text/css'>
+ <link rel="stylesheet" href="style.css" type="text/css">
- <script type='text/javascript' src='jquery.min.js'></script>
- <script type='text/javascript' src='jquery.debounce.min.js'></script>
- <script type='text/javascript' src='jquery.tablesorter.min.js'></script>
- <script type='text/javascript' src='jquery.hotkeys.js'></script>
- <script type='text/javascript' src='coverage_html.js'></script>
- <script type='text/javascript'>
+ <script type="text/javascript" src="jquery.min.js"></script>
+ <script type="text/javascript" src="jquery.debounce.min.js"></script>
+ <script type="text/javascript" src="jquery.tablesorter.min.js"></script>
+ <script type="text/javascript" src="jquery.hotkeys.js"></script>
+ <script type="text/javascript" src="coverage_html.js"></script>
+ <script type="text/javascript">
jQuery(document).ready(coverage.index_ready);
</script>
</head>
-<body class='indexfile'>
+<body class="indexfile">
-<div id='header'>
- <div class='content'>
+<div id="header">
+ <div class="content">
<h1>Coverage report:
- <span class='pc_cov'>70%</span>
+ <span class="pc_cov">70%</span>
</h1>
- <img id='keyboard_icon' src='keybd_closed.png' alt='Show keyboard shortcuts' />
+ <img id="keyboard_icon" src="keybd_closed.png" alt="Show keyboard shortcuts" />
<form id="filter_container">
<input id="filter" type="text" value="" placeholder="filter..." />
@@ -30,44 +30,44 @@
</div>
</div>
-<div class='help_panel'>
- <img id='panel_icon' src='keybd_open.png' alt='Hide keyboard shortcuts' />
- <p class='legend'>Hot-keys on this page</p>
+<div class="help_panel">
+ <img id="panel_icon" src="keybd_open.png" alt="Hide keyboard shortcuts" />
+ <p class="legend">Hot-keys on this page</p>
<div>
- <p class='keyhelp'>
- <span class='key'>n</span>
- <span class='key'>s</span>
- <span class='key'>m</span>
- <span class='key'>x</span>
+ <p class="keyhelp">
+ <span class="key">n</span>
+ <span class="key">s</span>
+ <span class="key">m</span>
+ <span class="key">x</span>
- <span class='key'>b</span>
- <span class='key'>p</span>
+ <span class="key">b</span>
+ <span class="key">p</span>
- <span class='key'>c</span> &nbsp; change column sorting
+ <span class="key">c</span> &nbsp; change column sorting
</p>
</div>
</div>
-<div id='index'>
- <table class='index'>
+<div id="index">
+ <table class="index">
<thead>
- <tr class='tablehead' title='Click to sort'>
- <th class='name left headerSortDown shortkey_n'>Module</th>
- <th class='shortkey_s'>statements</th>
- <th class='shortkey_m'>missing</th>
- <th class='shortkey_x'>excluded</th>
+ <tr class="tablehead" title="Click to sort">
+ <th class="name left headerSortDown shortkey_n">Module</th>
+ <th class="shortkey_s">statements</th>
+ <th class="shortkey_m">missing</th>
+ <th class="shortkey_x">excluded</th>
- <th class='shortkey_b'>branches</th>
- <th class='shortkey_p'>partial</th>
+ <th class="shortkey_b">branches</th>
+ <th class="shortkey_p">partial</th>
- <th class='right shortkey_c'>coverage</th>
+ <th class="right shortkey_c">coverage</th>
</tr>
</thead>
<tfoot>
- <tr class='total'>
- <td class='name left'>Total</td>
+ <tr class="total">
+ <td class="name left">Total</td>
<td>17</td>
<td>3</td>
<td>0</td>
@@ -75,13 +75,13 @@
<td>6</td>
<td>4</td>
- <td class='right' data-ratio='16 23'>70%</td>
+ <td class="right" data-ratio="16 23">70%</td>
</tr>
</tfoot>
<tbody>
- <tr class='file'>
- <td class='name left'><a href='b.html'>b</a></td>
+ <tr class="file">
+ <td class="name left"><a href="b.html">b</a></td>
<td>17</td>
<td>3</td>
<td>0</td>
@@ -89,7 +89,7 @@
<td>6</td>
<td>4</td>
- <td class='right' data-ratio='16 23'>70%</td>
+ <td class="right" data-ratio="16 23">70%</td>
</tr>
</tbody>
@@ -100,10 +100,10 @@
</p>
</div>
-<div id='footer'>
- <div class='content'>
+<div id="footer">
+ <div class="content">
<p>
- <a class='nav' href='http://nedbatchelder.com/code/coverage/4.0a1'>coverage.py v4.0a1</a>
+ <a class="nav" href="http://nedbatchelder.com/code/coverage/4.0a1">coverage.py v4.0a1</a>
</p>
</div>
</div>
diff --git a/tests/farm/html/gold_bom/bom_py.html b/tests/farm/html/gold_bom/bom_py.html
index 40c64bdb..127f2f45 100644
--- a/tests/farm/html/gold_bom/bom_py.html
+++ b/tests/farm/html/gold_bom/bom_py.html
@@ -1,104 +1,104 @@
<!DOCTYPE html>
<html>
<head>
- <meta http-equiv='Content-Type' content='text/html; charset=utf-8'>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
- <meta http-equiv='X-UA-Compatible' content='IE=emulateIE7' />
+ <meta http-equiv="X-UA-Compatible" content="IE=emulateIE7" />
<title>Coverage for bom: 71%</title>
- <link rel='stylesheet' href='style.css' type='text/css'>
+ <link rel="stylesheet" href="style.css" type="text/css">
- <script type='text/javascript' src='jquery.min.js'></script>
- <script type='text/javascript' src='jquery.hotkeys.js'></script>
- <script type='text/javascript' src='jquery.isonscreen.js'></script>
- <script type='text/javascript' src='coverage_html.js'></script>
- <script type='text/javascript'>
+ <script type="text/javascript" src="jquery.min.js"></script>
+ <script type="text/javascript" src="jquery.hotkeys.js"></script>
+ <script type="text/javascript" src="jquery.isonscreen.js"></script>
+ <script type="text/javascript" src="coverage_html.js"></script>
+ <script type="text/javascript">
jQuery(document).ready(coverage.pyfile_ready);
</script>
</head>
-<body class='pyfile'>
+<body class="pyfile">
-<div id='header'>
- <div class='content'>
+<div id="header">
+ <div class="content">
<h1>Coverage for <b>bom</b> :
- <span class='pc_cov'>71%</span>
+ <span class="pc_cov">71%</span>
</h1>
- <img id='keyboard_icon' src='keybd_closed.png' alt='Show keyboard shortcuts' />
+ <img id="keyboard_icon" src="keybd_closed.png" alt="Show keyboard shortcuts" />
- <h2 class='stats'>
+ <h2 class="stats">
7 statements &nbsp;
- <span class='run hide_run shortkey_r button_toggle_run'>5 run</span>
- <span class='mis shortkey_m button_toggle_mis'>2 missing</span>
- <span class='exc shortkey_x button_toggle_exc'>0 excluded</span>
+ <span class="run hide_run shortkey_r button_toggle_run">5 run</span>
+ <span class="mis shortkey_m button_toggle_mis">2 missing</span>
+ <span class="exc shortkey_x button_toggle_exc">0 excluded</span>
</h2>
</div>
</div>
-<div class='help_panel'>
- <img id='panel_icon' src='keybd_open.png' alt='Hide keyboard shortcuts' />
-<p class='legend'>Hot-keys on this page</p>
+<div class="help_panel">
+ <img id="panel_icon" src="keybd_open.png" alt="Hide keyboard shortcuts" />
+<p class="legend">Hot-keys on this page</p>
<div>
-<p class='keyhelp'>
- <span class='key'>r</span>
- <span class='key'>m</span>
- <span class='key'>x</span>
- <span class='key'>p</span> &nbsp; toggle line displays
+<p class="keyhelp">
+ <span class="key">r</span>
+ <span class="key">m</span>
+ <span class="key">x</span>
+ <span class="key">p</span> &nbsp; toggle line displays
</p>
-<p class='keyhelp'>
- <span class='key'>j</span>
- <span class='key'>k</span> &nbsp; next/prev highlighted chunk
+<p class="keyhelp">
+ <span class="key">j</span>
+ <span class="key">k</span> &nbsp; next/prev highlighted chunk
</p>
-<p class='keyhelp'>
- <span class='key'>0</span> &nbsp; (zero) top of page
+<p class="keyhelp">
+ <span class="key">0</span> &nbsp; (zero) top of page
</p>
-<p class='keyhelp'>
- <span class='key'>1</span> &nbsp; (one) first highlighted chunk
+<p class="keyhelp">
+ <span class="key">1</span> &nbsp; (one) first highlighted chunk
</p>
</div>
</div>
-<div id='source'>
+<div id="source">
<table>
<tr>
- <td class='linenos'>
-<p id='n1' class='pln'><a href='#n1'>1</a></p>
-<p id='n2' class='stm run hide_run'><a href='#n2'>2</a></p>
-<p id='n3' class='pln'><a href='#n3'>3</a></p>
-<p id='n4' class='stm run hide_run'><a href='#n4'>4</a></p>
-<p id='n5' class='pln'><a href='#n5'>5</a></p>
-<p id='n6' class='stm run hide_run'><a href='#n6'>6</a></p>
-<p id='n7' class='stm mis'><a href='#n7'>7</a></p>
-<p id='n8' class='stm mis'><a href='#n8'>8</a></p>
-<p id='n9' class='pln'><a href='#n9'>9</a></p>
-<p id='n10' class='stm run hide_run'><a href='#n10'>10</a></p>
-<p id='n11' class='stm run hide_run'><a href='#n11'>11</a></p>
+ <td class="linenos">
+<p id="n1" class="pln"><a href="#n1">1</a></p>
+<p id="n2" class="stm run hide_run"><a href="#n2">2</a></p>
+<p id="n3" class="pln"><a href="#n3">3</a></p>
+<p id="n4" class="stm run hide_run"><a href="#n4">4</a></p>
+<p id="n5" class="pln"><a href="#n5">5</a></p>
+<p id="n6" class="stm run hide_run"><a href="#n6">6</a></p>
+<p id="n7" class="stm mis"><a href="#n7">7</a></p>
+<p id="n8" class="stm mis"><a href="#n8">8</a></p>
+<p id="n9" class="pln"><a href="#n9">9</a></p>
+<p id="n10" class="stm run hide_run"><a href="#n10">10</a></p>
+<p id="n11" class="stm run hide_run"><a href="#n11">11</a></p>
</td>
- <td class='text'>
-<p id='t1' class='pln'><span class='err'>&#65533;</span><span class='err'>&#65533;</span><span class='err'>&#65533;</span><span class='com'># A python source file in utf-8, with BOM</span><span class='strut'>&nbsp;</span></p>
-<p id='t2' class='stm run hide_run'><span class='nam'>math</span> <span class='op'>=</span> <span class='str'>&quot;3&#215;4 = 12, &#247;2 = 6&#177;0&quot;</span><span class='strut'>&nbsp;</span></p>
-<p id='t3' class='pln'><span class='strut'>&nbsp;</span></p>
-<p id='t4' class='stm run hide_run'><span class='key'>import</span> <span class='nam'>sys</span><span class='strut'>&nbsp;</span></p>
-<p id='t5' class='pln'><span class='strut'>&nbsp;</span></p>
-<p id='t6' class='stm run hide_run'><span class='key'>if</span> <span class='nam'>sys</span><span class='op'>.</span><span class='nam'>version_info</span> <span class='op'>&gt;=</span> <span class='op'>(</span><span class='num'>3</span><span class='op'>,</span> <span class='num'>0</span><span class='op'>)</span><span class='op'>:</span><span class='strut'>&nbsp;</span></p>
-<p id='t7' class='stm mis'>&nbsp; &nbsp; <span class='key'>assert</span> <span class='nam'>len</span><span class='op'>(</span><span class='nam'>math</span><span class='op'>)</span> <span class='op'>==</span> <span class='num'>18</span><span class='strut'>&nbsp;</span></p>
-<p id='t8' class='stm mis'>&nbsp; &nbsp; <span class='key'>assert</span> <span class='nam'>len</span><span class='op'>(</span><span class='nam'>math</span><span class='op'>.</span><span class='nam'>encode</span><span class='op'>(</span><span class='str'>&#39;utf-8&#39;</span><span class='op'>)</span><span class='op'>)</span> <span class='op'>==</span> <span class='num'>21</span><span class='strut'>&nbsp;</span></p>
-<p id='t9' class='pln'><span class='key'>else</span><span class='op'>:</span><span class='strut'>&nbsp;</span></p>
-<p id='t10' class='stm run hide_run'>&nbsp; &nbsp; <span class='key'>assert</span> <span class='nam'>len</span><span class='op'>(</span><span class='nam'>math</span><span class='op'>)</span> <span class='op'>==</span> <span class='num'>21</span><span class='strut'>&nbsp;</span></p>
-<p id='t11' class='stm run hide_run'>&nbsp; &nbsp; <span class='key'>assert</span> <span class='nam'>len</span><span class='op'>(</span><span class='nam'>math</span><span class='op'>.</span><span class='nam'>decode</span><span class='op'>(</span><span class='str'>&#39;utf-8&#39;</span><span class='op'>)</span><span class='op'>)</span> <span class='op'>==</span> <span class='num'>18</span><span class='strut'>&nbsp;</span></p>
+ <td class="text">
+<p id="t1" class="pln"><span class="err">&#65533;</span><span class="err">&#65533;</span><span class="err">&#65533;</span><span class="com"># A python source file in utf-8, with BOM</span><span class="strut">&nbsp;</span></p>
+<p id="t2" class="stm run hide_run"><span class="nam">math</span> <span class="op">=</span> <span class="str">&quot;3&#215;4 = 12, &#247;2 = 6&#177;0&quot;</span><span class="strut">&nbsp;</span></p>
+<p id="t3" class="pln"><span class="strut">&nbsp;</span></p>
+<p id="t4" class="stm run hide_run"><span class="key">import</span> <span class="nam">sys</span><span class="strut">&nbsp;</span></p>
+<p id="t5" class="pln"><span class="strut">&nbsp;</span></p>
+<p id="t6" class="stm run hide_run"><span class="key">if</span> <span class="nam">sys</span><span class="op">.</span><span class="nam">version_info</span> <span class="op">&gt;=</span> <span class="op">(</span><span class="num">3</span><span class="op">,</span> <span class="num">0</span><span class="op">)</span><span class="op">:</span><span class="strut">&nbsp;</span></p>
+<p id="t7" class="stm mis">&nbsp; &nbsp; <span class="key">assert</span> <span class="nam">len</span><span class="op">(</span><span class="nam">math</span><span class="op">)</span> <span class="op">==</span> <span class="num">18</span><span class="strut">&nbsp;</span></p>
+<p id="t8" class="stm mis">&nbsp; &nbsp; <span class="key">assert</span> <span class="nam">len</span><span class="op">(</span><span class="nam">math</span><span class="op">.</span><span class="nam">encode</span><span class="op">(</span><span class="str">&#39;utf-8&#39;</span><span class="op">)</span><span class="op">)</span> <span class="op">==</span> <span class="num">21</span><span class="strut">&nbsp;</span></p>
+<p id="t9" class="pln"><span class="key">else</span><span class="op">:</span><span class="strut">&nbsp;</span></p>
+<p id="t10" class="stm run hide_run">&nbsp; &nbsp; <span class="key">assert</span> <span class="nam">len</span><span class="op">(</span><span class="nam">math</span><span class="op">)</span> <span class="op">==</span> <span class="num">21</span><span class="strut">&nbsp;</span></p>
+<p id="t11" class="stm run hide_run">&nbsp; &nbsp; <span class="key">assert</span> <span class="nam">len</span><span class="op">(</span><span class="nam">math</span><span class="op">.</span><span class="nam">decode</span><span class="op">(</span><span class="str">&#39;utf-8&#39;</span><span class="op">)</span><span class="op">)</span> <span class="op">==</span> <span class="num">18</span><span class="strut">&nbsp;</span></p>
</td>
</tr>
</table>
</div>
-<div id='footer'>
- <div class='content'>
+<div id="footer">
+ <div class="content">
<p>
- <a class='nav' href='index.html'>&#xab; index</a> &nbsp; &nbsp; <a class='nav' href='http://nedbatchelder.com/code/coverage/4.0a1'>coverage.py v4.0a1</a>
+ <a class="nav" href="index.html">&#xab; index</a> &nbsp; &nbsp; <a class="nav" href="http://nedbatchelder.com/code/coverage/4.0a1">coverage.py v4.0a1</a>
</p>
</div>
</div>
diff --git a/tests/farm/html/gold_bom/index.html b/tests/farm/html/gold_bom/index.html
index 525e7778..77696727 100644
--- a/tests/farm/html/gold_bom/index.html
+++ b/tests/farm/html/gold_bom/index.html
@@ -1,28 +1,28 @@
<!DOCTYPE html>
<html>
<head>
- <meta http-equiv='Content-Type' content='text/html; charset=utf-8'>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>Coverage report</title>
- <link rel='stylesheet' href='style.css' type='text/css'>
+ <link rel="stylesheet" href="style.css" type="text/css">
- <script type='text/javascript' src='jquery.min.js'></script>
- <script type='text/javascript' src='jquery.debounce.min.js'></script>
- <script type='text/javascript' src='jquery.tablesorter.min.js'></script>
- <script type='text/javascript' src='jquery.hotkeys.js'></script>
- <script type='text/javascript' src='coverage_html.js'></script>
- <script type='text/javascript'>
+ <script type="text/javascript" src="jquery.min.js"></script>
+ <script type="text/javascript" src="jquery.debounce.min.js"></script>
+ <script type="text/javascript" src="jquery.tablesorter.min.js"></script>
+ <script type="text/javascript" src="jquery.hotkeys.js"></script>
+ <script type="text/javascript" src="coverage_html.js"></script>
+ <script type="text/javascript">
jQuery(document).ready(coverage.index_ready);
</script>
</head>
-<body class='indexfile'>
+<body class="indexfile">
-<div id='header'>
- <div class='content'>
+<div id="header">
+ <div class="content">
<h1>Coverage report:
- <span class='pc_cov'>71%</span>
+ <span class="pc_cov">71%</span>
</h1>
- <img id='keyboard_icon' src='keybd_closed.png' alt='Show keyboard shortcuts' />
+ <img id="keyboard_icon" src="keybd_closed.png" alt="Show keyboard shortcuts" />
<form id="filter_container">
<input id="filter" type="text" value="" placeholder="filter..." />
@@ -30,54 +30,54 @@
</div>
</div>
-<div class='help_panel'>
- <img id='panel_icon' src='keybd_open.png' alt='Hide keyboard shortcuts' />
- <p class='legend'>Hot-keys on this page</p>
+<div class="help_panel">
+ <img id="panel_icon" src="keybd_open.png" alt="Hide keyboard shortcuts" />
+ <p class="legend">Hot-keys on this page</p>
<div>
- <p class='keyhelp'>
- <span class='key'>n</span>
- <span class='key'>s</span>
- <span class='key'>m</span>
- <span class='key'>x</span>
+ <p class="keyhelp">
+ <span class="key">n</span>
+ <span class="key">s</span>
+ <span class="key">m</span>
+ <span class="key">x</span>
- <span class='key'>c</span> &nbsp; change column sorting
+ <span class="key">c</span> &nbsp; change column sorting
</p>
</div>
</div>
-<div id='index'>
- <table class='index'>
+<div id="index">
+ <table class="index">
<thead>
- <tr class='tablehead' title='Click to sort'>
- <th class='name left headerSortDown shortkey_n'>Module</th>
- <th class='shortkey_s'>statements</th>
- <th class='shortkey_m'>missing</th>
- <th class='shortkey_x'>excluded</th>
+ <tr class="tablehead" title="Click to sort">
+ <th class="name left headerSortDown shortkey_n">Module</th>
+ <th class="shortkey_s">statements</th>
+ <th class="shortkey_m">missing</th>
+ <th class="shortkey_x">excluded</th>
- <th class='right shortkey_c'>coverage</th>
+ <th class="right shortkey_c">coverage</th>
</tr>
</thead>
<tfoot>
- <tr class='total'>
- <td class='name left'>Total</td>
+ <tr class="total">
+ <td class="name left">Total</td>
<td>7</td>
<td>2</td>
<td>0</td>
- <td class='right' data-ratio='5 7'>71%</td>
+ <td class="right" data-ratio="5 7">71%</td>
</tr>
</tfoot>
<tbody>
- <tr class='file'>
- <td class='name left'><a href='bom.html'>bom</a></td>
+ <tr class="file">
+ <td class="name left"><a href="bom.html">bom</a></td>
<td>7</td>
<td>2</td>
<td>0</td>
- <td class='right' data-ratio='5 7'>71%</td>
+ <td class="right" data-ratio="5 7">71%</td>
</tr>
</tbody>
@@ -88,10 +88,10 @@
</p>
</div>
-<div id='footer'>
- <div class='content'>
+<div id="footer">
+ <div class="content">
<p>
- <a class='nav' href='http://nedbatchelder.com/code/coverage/4.0a1'>coverage.py v4.0a1</a>
+ <a class="nav" href="http://nedbatchelder.com/code/coverage/4.0a1">coverage.py v4.0a1</a>
</p>
</div>
</div>
diff --git a/tests/farm/html/gold_isolatin1/index.html b/tests/farm/html/gold_isolatin1/index.html
index b111b86f..ff4d26f9 100644
--- a/tests/farm/html/gold_isolatin1/index.html
+++ b/tests/farm/html/gold_isolatin1/index.html
@@ -1,28 +1,31 @@
+
+
+
<!DOCTYPE html>
<html>
<head>
- <meta http-equiv='Content-Type' content='text/html; charset=utf-8'>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>Coverage report</title>
- <link rel='stylesheet' href='style.css' type='text/css'>
+ <link rel="stylesheet" href="style.css" type="text/css">
- <script type='text/javascript' src='jquery.min.js'></script>
- <script type='text/javascript' src='jquery.debounce.min.js'></script>
- <script type='text/javascript' src='jquery.tablesorter.min.js'></script>
- <script type='text/javascript' src='jquery.hotkeys.js'></script>
- <script type='text/javascript' src='coverage_html.js'></script>
- <script type='text/javascript'>
+ <script type="text/javascript" src="jquery.min.js"></script>
+ <script type="text/javascript" src="jquery.debounce.min.js"></script>
+ <script type="text/javascript" src="jquery.tablesorter.min.js"></script>
+ <script type="text/javascript" src="jquery.hotkeys.js"></script>
+ <script type="text/javascript" src="coverage_html.js"></script>
+ <script type="text/javascript">
jQuery(document).ready(coverage.index_ready);
</script>
</head>
-<body class='indexfile'>
+<body class="indexfile">
-<div id='header'>
- <div class='content'>
+<div id="header">
+ <div class="content">
<h1>Coverage report:
- <span class='pc_cov'>100%</span>
+ <span class="pc_cov">100%</span>
</h1>
- <img id='keyboard_icon' src='keybd_closed.png' alt='Show keyboard shortcuts' />
+ <img id="keyboard_icon" src="keybd_closed.png" alt="Show keyboard shortcuts" />
<form id="filter_container">
<input id="filter" type="text" value="" placeholder="filter..." />
@@ -30,54 +33,54 @@
</div>
</div>
-<div class='help_panel'>
- <img id='panel_icon' src='keybd_open.png' alt='Hide keyboard shortcuts' />
- <p class='legend'>Hot-keys on this page</p>
+<div class="help_panel">
+ <img id="panel_icon" src="keybd_open.png" alt="Hide keyboard shortcuts" />
+ <p class="legend">Hot-keys on this page</p>
<div>
- <p class='keyhelp'>
- <span class='key'>n</span>
- <span class='key'>s</span>
- <span class='key'>m</span>
- <span class='key'>x</span>
+ <p class="keyhelp">
+ <span class="key">n</span>
+ <span class="key">s</span>
+ <span class="key">m</span>
+ <span class="key">x</span>
- <span class='key'>c</span> &nbsp; change column sorting
+ <span class="key">c</span> &nbsp; change column sorting
</p>
</div>
</div>
-<div id='index'>
- <table class='index'>
+<div id="index">
+ <table class="index">
<thead>
- <tr class='tablehead' title='Click to sort'>
- <th class='name left headerSortDown shortkey_n'>Module</th>
- <th class='shortkey_s'>statements</th>
- <th class='shortkey_m'>missing</th>
- <th class='shortkey_x'>excluded</th>
+ <tr class="tablehead" title="Click to sort">
+ <th class="name left headerSortDown shortkey_n">Module</th>
+ <th class="shortkey_s">statements</th>
+ <th class="shortkey_m">missing</th>
+ <th class="shortkey_x">excluded</th>
- <th class='right shortkey_c'>coverage</th>
+ <th class="right shortkey_c">coverage</th>
</tr>
</thead>
<tfoot>
- <tr class='total'>
- <td class='name left'>Total</td>
+ <tr class="total">
+ <td class="name left">Total</td>
<td>2</td>
<td>0</td>
<td>0</td>
- <td class='right' data-ratio='2 2'>100%</td>
+ <td class="right" data-ratio="2 2">100%</td>
</tr>
</tfoot>
<tbody>
- <tr class='file'>
- <td class='name left'><a href='isolatin1.html'>isolatin1</a></td>
+ <tr class="file">
+ <td class="name left"><a href="isolatin1_py.html">isolatin1.py</a></td>
<td>2</td>
<td>0</td>
<td>0</td>
- <td class='right' data-ratio='2 2'>100%</td>
+ <td class="right" data-ratio="2 2">100%</td>
</tr>
</tbody>
@@ -88,10 +91,11 @@
</p>
</div>
-<div id='footer'>
- <div class='content'>
+<div id="footer">
+ <div class="content">
<p>
- <a class='nav' href='http://nedbatchelder.com/code/coverage/4.0a1'>coverage.py v4.0a1</a>
+ <a class="nav" href="https://coverage.readthedocs.org/en/coverage-4.0a7">coverage.py v4.0a7</a>,
+ created at 2015-07-24 09:31
</p>
</div>
</div>
diff --git a/tests/farm/html/gold_isolatin1/isolatin1_py.html b/tests/farm/html/gold_isolatin1/isolatin1_py.html
index 0e4ac95a..3c9af361 100644
--- a/tests/farm/html/gold_isolatin1/isolatin1_py.html
+++ b/tests/farm/html/gold_isolatin1/isolatin1_py.html
@@ -1,92 +1,102 @@
+
+
+
<!DOCTYPE html>
<html>
<head>
- <meta http-equiv='Content-Type' content='text/html; charset=utf-8'>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
- <meta http-equiv='X-UA-Compatible' content='IE=emulateIE7' />
- <title>Coverage for isolatin1: 100%</title>
- <link rel='stylesheet' href='style.css' type='text/css'>
+ <meta http-equiv="X-UA-Compatible" content="IE=emulateIE7" />
+ <title>Coverage for isolatin1.py: 100%</title>
+ <link rel="stylesheet" href="style.css" type="text/css">
- <script type='text/javascript' src='jquery.min.js'></script>
- <script type='text/javascript' src='jquery.hotkeys.js'></script>
- <script type='text/javascript' src='jquery.isonscreen.js'></script>
- <script type='text/javascript' src='coverage_html.js'></script>
- <script type='text/javascript'>
+ <script type="text/javascript" src="jquery.min.js"></script>
+ <script type="text/javascript" src="jquery.hotkeys.js"></script>
+ <script type="text/javascript" src="jquery.isonscreen.js"></script>
+ <script type="text/javascript" src="coverage_html.js"></script>
+ <script type="text/javascript">
jQuery(document).ready(coverage.pyfile_ready);
</script>
</head>
-<body class='pyfile'>
+<body class="pyfile">
-<div id='header'>
- <div class='content'>
- <h1>Coverage for <b>isolatin1</b> :
- <span class='pc_cov'>100%</span>
+<div id="header">
+ <div class="content">
+ <h1>Coverage for <b>isolatin1.py</b> :
+ <span class="pc_cov">100%</span>
</h1>
- <img id='keyboard_icon' src='keybd_closed.png' alt='Show keyboard shortcuts' />
+ <img id="keyboard_icon" src="keybd_closed.png" alt="Show keyboard shortcuts" />
- <h2 class='stats'>
+ <h2 class="stats">
2 statements &nbsp;
- <span class='run hide_run shortkey_r button_toggle_run'>2 run</span>
- <span class='mis shortkey_m button_toggle_mis'>0 missing</span>
- <span class='exc shortkey_x button_toggle_exc'>0 excluded</span>
+ <span class="run hide_run shortkey_r button_toggle_run">2 run</span>
+ <span class="mis shortkey_m button_toggle_mis">0 missing</span>
+ <span class="exc shortkey_x button_toggle_exc">0 excluded</span>
</h2>
</div>
</div>
-<div class='help_panel'>
- <img id='panel_icon' src='keybd_open.png' alt='Hide keyboard shortcuts' />
-<p class='legend'>Hot-keys on this page</p>
+<div class="help_panel">
+ <img id="panel_icon" src="keybd_open.png" alt="Hide keyboard shortcuts" />
+<p class="legend">Hot-keys on this page</p>
<div>
-<p class='keyhelp'>
- <span class='key'>r</span>
- <span class='key'>m</span>
- <span class='key'>x</span>
- <span class='key'>p</span> &nbsp; toggle line displays
+<p class="keyhelp">
+ <span class="key">r</span>
+ <span class="key">m</span>
+ <span class="key">x</span>
+ <span class="key">p</span> &nbsp; toggle line displays
</p>
-<p class='keyhelp'>
- <span class='key'>j</span>
- <span class='key'>k</span> &nbsp; next/prev highlighted chunk
+<p class="keyhelp">
+ <span class="key">j</span>
+ <span class="key">k</span> &nbsp; next/prev highlighted chunk
</p>
-<p class='keyhelp'>
- <span class='key'>0</span> &nbsp; (zero) top of page
+<p class="keyhelp">
+ <span class="key">0</span> &nbsp; (zero) top of page
</p>
-<p class='keyhelp'>
- <span class='key'>1</span> &nbsp; (one) first highlighted chunk
+<p class="keyhelp">
+ <span class="key">1</span> &nbsp; (one) first highlighted chunk
</p>
</div>
</div>
-<div id='source'>
+<div id="source">
<table>
<tr>
- <td class='linenos'>
-<p id='n1' class='pln'><a href='#n1'>1</a></p>
-<p id='n2' class='pln'><a href='#n2'>2</a></p>
-<p id='n3' class='pln'><a href='#n3'>3</a></p>
-<p id='n4' class='stm run hide_run'><a href='#n4'>4</a></p>
-<p id='n5' class='stm run hide_run'><a href='#n5'>5</a></p>
+ <td class="linenos">
+<p id="n1" class="pln"><a href="#n1">1</a></p>
+<p id="n2" class="pln"><a href="#n2">2</a></p>
+<p id="n3" class="pln"><a href="#n3">3</a></p>
+<p id="n4" class="pln"><a href="#n4">4</a></p>
+<p id="n5" class="pln"><a href="#n5">5</a></p>
+<p id="n6" class="pln"><a href="#n6">6</a></p>
+<p id="n7" class="stm run hide_run"><a href="#n7">7</a></p>
+<p id="n8" class="stm run hide_run"><a href="#n8">8</a></p>
</td>
- <td class='text'>
-<p id='t1' class='pln'><span class='com'># A python source file in another encoding.</span><span class='strut'>&nbsp;</span></p>
-<p id='t2' class='pln'><span class='com'># -*- coding: iso8859-1 -*-</span><span class='strut'>&nbsp;</span></p>
-<p id='t3' class='pln'><span class='strut'>&nbsp;</span></p>
-<p id='t4' class='stm run hide_run'><span class='nam'>math</span> <span class='op'>=</span> <span class='str'>&quot;3&#215;4 = 12, &#247;2 = 6&#177;0&quot;</span><span class='strut'>&nbsp;</span></p>
-<p id='t5' class='stm run hide_run'><span class='key'>assert</span> <span class='nam'>len</span><span class='op'>(</span><span class='nam'>math</span><span class='op'>)</span> <span class='op'>==</span> <span class='num'>18</span><span class='strut'>&nbsp;</span></p>
+ <td class="text">
+<p id="t1" class="pln"><span class="com"># -*- coding: iso8859-1 -*-</span><span class="strut">&nbsp;</span></p>
+<p id="t2" class="pln"><span class="com"># Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0</span><span class="strut">&nbsp;</span></p>
+<p id="t3" class="pln"><span class="com"># For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt</span><span class="strut">&nbsp;</span></p>
+<p id="t4" class="pln"><span class="strut">&nbsp;</span></p>
+<p id="t5" class="pln"><span class="com"># A Python source file in another encoding.</span><span class="strut">&nbsp;</span></p>
+<p id="t6" class="pln"><span class="strut">&nbsp;</span></p>
+<p id="t7" class="stm run hide_run"><span class="nam">math</span> <span class="op">=</span> <span class="str">&quot;3&#215;4 = 12, &#247;2 = 6&#177;0&quot;</span><span class="strut">&nbsp;</span></p>
+<p id="t8" class="stm run hide_run"><span class="key">assert</span> <span class="nam">len</span><span class="op">(</span><span class="nam">math</span><span class="op">)</span> <span class="op">==</span> <span class="num">18</span><span class="strut">&nbsp;</span></p>
</td>
</tr>
</table>
</div>
-<div id='footer'>
- <div class='content'>
+<div id="footer">
+ <div class="content">
<p>
- <a class='nav' href='index.html'>&#xab; index</a> &nbsp; &nbsp; <a class='nav' href='http://nedbatchelder.com/code/coverage/4.0a1'>coverage.py v4.0a1</a>
+ <a class="nav" href="index.html">&#xab; index</a> &nbsp; &nbsp; <a class="nav" href="https://coverage.readthedocs.org/en/coverage-4.0a7">coverage.py v4.0a7</a>,
+ created at 2015-07-24 09:04
</p>
</div>
</div>
diff --git a/tests/farm/html/gold_omit_1/index.html b/tests/farm/html/gold_omit_1/index.html
index 7cacebbd..95ec804f 100644
--- a/tests/farm/html/gold_omit_1/index.html
+++ b/tests/farm/html/gold_omit_1/index.html
@@ -1,28 +1,31 @@
+
+
+
<!DOCTYPE html>
<html>
<head>
- <meta http-equiv='Content-Type' content='text/html; charset=utf-8'>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>Coverage report</title>
- <link rel='stylesheet' href='style.css' type='text/css'>
+ <link rel="stylesheet" href="style.css" type="text/css">
- <script type='text/javascript' src='jquery.min.js'></script>
- <script type='text/javascript' src='jquery.debounce.min.js'></script>
- <script type='text/javascript' src='jquery.tablesorter.min.js'></script>
- <script type='text/javascript' src='jquery.hotkeys.js'></script>
- <script type='text/javascript' src='coverage_html.js'></script>
- <script type='text/javascript'>
+ <script type="text/javascript" src="jquery.min.js"></script>
+ <script type="text/javascript" src="jquery.debounce.min.js"></script>
+ <script type="text/javascript" src="jquery.tablesorter.min.js"></script>
+ <script type="text/javascript" src="jquery.hotkeys.js"></script>
+ <script type="text/javascript" src="coverage_html.js"></script>
+ <script type="text/javascript">
jQuery(document).ready(coverage.index_ready);
</script>
</head>
-<body class='indexfile'>
+<body class="indexfile">
-<div id='header'>
- <div class='content'>
+<div id="header">
+ <div class="content">
<h1>Coverage report:
- <span class='pc_cov'>100%</span>
+ <span class="pc_cov">100%</span>
</h1>
- <img id='keyboard_icon' src='keybd_closed.png' alt='Show keyboard shortcuts' />
+ <img id="keyboard_icon" src="keybd_closed.png" alt="Show keyboard shortcuts" />
<form id="filter_container">
<input id="filter" type="text" value="" placeholder="filter..." />
@@ -30,81 +33,81 @@
</div>
</div>
-<div class='help_panel'>
- <img id='panel_icon' src='keybd_open.png' alt='Hide keyboard shortcuts' />
- <p class='legend'>Hot-keys on this page</p>
+<div class="help_panel">
+ <img id="panel_icon" src="keybd_open.png" alt="Hide keyboard shortcuts" />
+ <p class="legend">Hot-keys on this page</p>
<div>
- <p class='keyhelp'>
- <span class='key'>n</span>
- <span class='key'>s</span>
- <span class='key'>m</span>
- <span class='key'>x</span>
+ <p class="keyhelp">
+ <span class="key">n</span>
+ <span class="key">s</span>
+ <span class="key">m</span>
+ <span class="key">x</span>
- <span class='key'>c</span> &nbsp; change column sorting
+ <span class="key">c</span> &nbsp; change column sorting
</p>
</div>
</div>
-<div id='index'>
- <table class='index'>
+<div id="index">
+ <table class="index">
<thead>
- <tr class='tablehead' title='Click to sort'>
- <th class='name left headerSortDown shortkey_n'>Module</th>
- <th class='shortkey_s'>statements</th>
- <th class='shortkey_m'>missing</th>
- <th class='shortkey_x'>excluded</th>
+ <tr class="tablehead" title="Click to sort">
+ <th class="name left headerSortDown shortkey_n">Module</th>
+ <th class="shortkey_s">statements</th>
+ <th class="shortkey_m">missing</th>
+ <th class="shortkey_x">excluded</th>
- <th class='right shortkey_c'>coverage</th>
+ <th class="right shortkey_c">coverage</th>
</tr>
</thead>
<tfoot>
- <tr class='total'>
- <td class='name left'>Total</td>
+ <tr class="total">
+ <td class="name left">Total</td>
<td>14</td>
<td>0</td>
<td>0</td>
- <td class='right' data-ratio='14 14'>100%</td>
+ <td class="right" data-ratio="14 14">100%</td>
</tr>
</tfoot>
<tbody>
- <tr class='file'>
- <td class='name left'><a href='m1.html'>m1</a></td>
+ <tr class="file">
+ <td class="name left"><a href="m1_py.html">m1.py</a></td>
<td>2</td>
<td>0</td>
<td>0</td>
- <td class='right' data-ratio='2 2'>100%</td>
+ <td class="right" data-ratio="2 2">100%</td>
</tr>
- <tr class='file'>
- <td class='name left'><a href='m2.html'>m2</a></td>
+ <tr class="file">
+ <td class="name left"><a href="m2_py.html">m2.py</a></td>
<td>2</td>
<td>0</td>
<td>0</td>
- <td class='right' data-ratio='2 2'>100%</td>
+ <td class="right" data-ratio="2 2">100%</td>
</tr>
- <tr class='file'>
- <td class='name left'><a href='m3.html'>m3</a></td>
+ <tr class="file">
+ <td class="name left"><a href="m3_py.html">m3.py</a></td>
<td>2</td>
<td>0</td>
<td>0</td>
- <td class='right' data-ratio='2 2'>100%</td>
+ <td class="right" data-ratio="2 2">100%</td>
</tr>
- <tr class='file'>
- <td class='name left'><a href='main.html'>main</a></td>
+ <tr class="file">
+ <td class="name left"><a href="main_py.html">main.py</a></td>
<td>8</td>
<td>0</td>
<td>0</td>
- <td class='right' data-ratio='8 8'>100%</td>
+ <td class="right" data-ratio="8 8">100%</td>
</tr>
</tbody>
@@ -115,10 +118,11 @@
</p>
</div>
-<div id='footer'>
- <div class='content'>
+<div id="footer">
+ <div class="content">
<p>
- <a class='nav' href='http://nedbatchelder.com/code/coverage/4.0a1'>coverage.py v4.0a1</a>
+ <a class="nav" href="https://coverage.readthedocs.org/en/coverage-4.0a7">coverage.py v4.0a7</a>,
+ created at 2015-07-24 09:31
</p>
</div>
</div>
diff --git a/tests/farm/html/gold_omit_1/m1_py.html b/tests/farm/html/gold_omit_1/m1_py.html
index de44f789..8df0bf51 100644
--- a/tests/farm/html/gold_omit_1/m1_py.html
+++ b/tests/farm/html/gold_omit_1/m1_py.html
@@ -1,86 +1,96 @@
+
+
+
<!DOCTYPE html>
<html>
<head>
- <meta http-equiv='Content-Type' content='text/html; charset=utf-8'>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
- <meta http-equiv='X-UA-Compatible' content='IE=emulateIE7' />
- <title>Coverage for m1: 100%</title>
- <link rel='stylesheet' href='style.css' type='text/css'>
+ <meta http-equiv="X-UA-Compatible" content="IE=emulateIE7" />
+ <title>Coverage for m1.py: 100%</title>
+ <link rel="stylesheet" href="style.css" type="text/css">
- <script type='text/javascript' src='jquery.min.js'></script>
- <script type='text/javascript' src='jquery.hotkeys.js'></script>
- <script type='text/javascript' src='jquery.isonscreen.js'></script>
- <script type='text/javascript' src='coverage_html.js'></script>
- <script type='text/javascript'>
+ <script type="text/javascript" src="jquery.min.js"></script>
+ <script type="text/javascript" src="jquery.hotkeys.js"></script>
+ <script type="text/javascript" src="jquery.isonscreen.js"></script>
+ <script type="text/javascript" src="coverage_html.js"></script>
+ <script type="text/javascript">
jQuery(document).ready(coverage.pyfile_ready);
</script>
</head>
-<body class='pyfile'>
+<body class="pyfile">
-<div id='header'>
- <div class='content'>
- <h1>Coverage for <b>m1</b> :
- <span class='pc_cov'>100%</span>
+<div id="header">
+ <div class="content">
+ <h1>Coverage for <b>m1.py</b> :
+ <span class="pc_cov">100%</span>
</h1>
- <img id='keyboard_icon' src='keybd_closed.png' alt='Show keyboard shortcuts' />
+ <img id="keyboard_icon" src="keybd_closed.png" alt="Show keyboard shortcuts" />
- <h2 class='stats'>
+ <h2 class="stats">
2 statements &nbsp;
- <span class='run hide_run shortkey_r button_toggle_run'>2 run</span>
- <span class='mis shortkey_m button_toggle_mis'>0 missing</span>
- <span class='exc shortkey_x button_toggle_exc'>0 excluded</span>
+ <span class="run hide_run shortkey_r button_toggle_run">2 run</span>
+ <span class="mis shortkey_m button_toggle_mis">0 missing</span>
+ <span class="exc shortkey_x button_toggle_exc">0 excluded</span>
</h2>
</div>
</div>
-<div class='help_panel'>
- <img id='panel_icon' src='keybd_open.png' alt='Hide keyboard shortcuts' />
-<p class='legend'>Hot-keys on this page</p>
+<div class="help_panel">
+ <img id="panel_icon" src="keybd_open.png" alt="Hide keyboard shortcuts" />
+<p class="legend">Hot-keys on this page</p>
<div>
-<p class='keyhelp'>
- <span class='key'>r</span>
- <span class='key'>m</span>
- <span class='key'>x</span>
- <span class='key'>p</span> &nbsp; toggle line displays
+<p class="keyhelp">
+ <span class="key">r</span>
+ <span class="key">m</span>
+ <span class="key">x</span>
+ <span class="key">p</span> &nbsp; toggle line displays
</p>
-<p class='keyhelp'>
- <span class='key'>j</span>
- <span class='key'>k</span> &nbsp; next/prev highlighted chunk
+<p class="keyhelp">
+ <span class="key">j</span>
+ <span class="key">k</span> &nbsp; next/prev highlighted chunk
</p>
-<p class='keyhelp'>
- <span class='key'>0</span> &nbsp; (zero) top of page
+<p class="keyhelp">
+ <span class="key">0</span> &nbsp; (zero) top of page
</p>
-<p class='keyhelp'>
- <span class='key'>1</span> &nbsp; (one) first highlighted chunk
+<p class="keyhelp">
+ <span class="key">1</span> &nbsp; (one) first highlighted chunk
</p>
</div>
</div>
-<div id='source'>
+<div id="source">
<table>
<tr>
- <td class='linenos'>
-<p id='n1' class='stm run hide_run'><a href='#n1'>1</a></p>
-<p id='n2' class='stm run hide_run'><a href='#n2'>2</a></p>
+ <td class="linenos">
+<p id="n1" class="pln"><a href="#n1">1</a></p>
+<p id="n2" class="pln"><a href="#n2">2</a></p>
+<p id="n3" class="pln"><a href="#n3">3</a></p>
+<p id="n4" class="stm run hide_run"><a href="#n4">4</a></p>
+<p id="n5" class="stm run hide_run"><a href="#n5">5</a></p>
</td>
- <td class='text'>
-<p id='t1' class='stm run hide_run'><span class='nam'>m1a</span> <span class='op'>=</span> <span class='num'>1</span><span class='strut'>&nbsp;</span></p>
-<p id='t2' class='stm run hide_run'><span class='nam'>m1b</span> <span class='op'>=</span> <span class='num'>2</span><span class='strut'>&nbsp;</span></p>
+ <td class="text">
+<p id="t1" class="pln"><span class="com"># Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0</span><span class="strut">&nbsp;</span></p>
+<p id="t2" class="pln"><span class="com"># For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt</span><span class="strut">&nbsp;</span></p>
+<p id="t3" class="pln"><span class="strut">&nbsp;</span></p>
+<p id="t4" class="stm run hide_run"><span class="nam">m1a</span> <span class="op">=</span> <span class="num">1</span><span class="strut">&nbsp;</span></p>
+<p id="t5" class="stm run hide_run"><span class="nam">m1b</span> <span class="op">=</span> <span class="num">2</span><span class="strut">&nbsp;</span></p>
</td>
</tr>
</table>
</div>
-<div id='footer'>
- <div class='content'>
+<div id="footer">
+ <div class="content">
<p>
- <a class='nav' href='index.html'>&#xab; index</a> &nbsp; &nbsp; <a class='nav' href='http://nedbatchelder.com/code/coverage/4.0a1'>coverage.py v4.0a1</a>
+ <a class="nav" href="index.html">&#xab; index</a> &nbsp; &nbsp; <a class="nav" href="https://coverage.readthedocs.org/en/coverage-4.0a7">coverage.py v4.0a7</a>,
+ created at 2015-07-24 09:04
</p>
</div>
</div>
diff --git a/tests/farm/html/gold_omit_1/m2_py.html b/tests/farm/html/gold_omit_1/m2_py.html
index c775e6bb..2a7f13f0 100644
--- a/tests/farm/html/gold_omit_1/m2_py.html
+++ b/tests/farm/html/gold_omit_1/m2_py.html
@@ -1,86 +1,96 @@
+
+
+
<!DOCTYPE html>
<html>
<head>
- <meta http-equiv='Content-Type' content='text/html; charset=utf-8'>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
- <meta http-equiv='X-UA-Compatible' content='IE=emulateIE7' />
- <title>Coverage for m2: 100%</title>
- <link rel='stylesheet' href='style.css' type='text/css'>
+ <meta http-equiv="X-UA-Compatible" content="IE=emulateIE7" />
+ <title>Coverage for m2.py: 100%</title>
+ <link rel="stylesheet" href="style.css" type="text/css">
- <script type='text/javascript' src='jquery.min.js'></script>
- <script type='text/javascript' src='jquery.hotkeys.js'></script>
- <script type='text/javascript' src='jquery.isonscreen.js'></script>
- <script type='text/javascript' src='coverage_html.js'></script>
- <script type='text/javascript'>
+ <script type="text/javascript" src="jquery.min.js"></script>
+ <script type="text/javascript" src="jquery.hotkeys.js"></script>
+ <script type="text/javascript" src="jquery.isonscreen.js"></script>
+ <script type="text/javascript" src="coverage_html.js"></script>
+ <script type="text/javascript">
jQuery(document).ready(coverage.pyfile_ready);
</script>
</head>
-<body class='pyfile'>
+<body class="pyfile">
-<div id='header'>
- <div class='content'>
- <h1>Coverage for <b>m2</b> :
- <span class='pc_cov'>100%</span>
+<div id="header">
+ <div class="content">
+ <h1>Coverage for <b>m2.py</b> :
+ <span class="pc_cov">100%</span>
</h1>
- <img id='keyboard_icon' src='keybd_closed.png' alt='Show keyboard shortcuts' />
+ <img id="keyboard_icon" src="keybd_closed.png" alt="Show keyboard shortcuts" />
- <h2 class='stats'>
+ <h2 class="stats">
2 statements &nbsp;
- <span class='run hide_run shortkey_r button_toggle_run'>2 run</span>
- <span class='mis shortkey_m button_toggle_mis'>0 missing</span>
- <span class='exc shortkey_x button_toggle_exc'>0 excluded</span>
+ <span class="run hide_run shortkey_r button_toggle_run">2 run</span>
+ <span class="mis shortkey_m button_toggle_mis">0 missing</span>
+ <span class="exc shortkey_x button_toggle_exc">0 excluded</span>
</h2>
</div>
</div>
-<div class='help_panel'>
- <img id='panel_icon' src='keybd_open.png' alt='Hide keyboard shortcuts' />
-<p class='legend'>Hot-keys on this page</p>
+<div class="help_panel">
+ <img id="panel_icon" src="keybd_open.png" alt="Hide keyboard shortcuts" />
+<p class="legend">Hot-keys on this page</p>
<div>
-<p class='keyhelp'>
- <span class='key'>r</span>
- <span class='key'>m</span>
- <span class='key'>x</span>
- <span class='key'>p</span> &nbsp; toggle line displays
+<p class="keyhelp">
+ <span class="key">r</span>
+ <span class="key">m</span>
+ <span class="key">x</span>
+ <span class="key">p</span> &nbsp; toggle line displays
</p>
-<p class='keyhelp'>
- <span class='key'>j</span>
- <span class='key'>k</span> &nbsp; next/prev highlighted chunk
+<p class="keyhelp">
+ <span class="key">j</span>
+ <span class="key">k</span> &nbsp; next/prev highlighted chunk
</p>
-<p class='keyhelp'>
- <span class='key'>0</span> &nbsp; (zero) top of page
+<p class="keyhelp">
+ <span class="key">0</span> &nbsp; (zero) top of page
</p>
-<p class='keyhelp'>
- <span class='key'>1</span> &nbsp; (one) first highlighted chunk
+<p class="keyhelp">
+ <span class="key">1</span> &nbsp; (one) first highlighted chunk
</p>
</div>
</div>
-<div id='source'>
+<div id="source">
<table>
<tr>
- <td class='linenos'>
-<p id='n1' class='stm run hide_run'><a href='#n1'>1</a></p>
-<p id='n2' class='stm run hide_run'><a href='#n2'>2</a></p>
+ <td class="linenos">
+<p id="n1" class="pln"><a href="#n1">1</a></p>
+<p id="n2" class="pln"><a href="#n2">2</a></p>
+<p id="n3" class="pln"><a href="#n3">3</a></p>
+<p id="n4" class="stm run hide_run"><a href="#n4">4</a></p>
+<p id="n5" class="stm run hide_run"><a href="#n5">5</a></p>
</td>
- <td class='text'>
-<p id='t1' class='stm run hide_run'><span class='nam'>m2a</span> <span class='op'>=</span> <span class='num'>1</span><span class='strut'>&nbsp;</span></p>
-<p id='t2' class='stm run hide_run'><span class='nam'>m2b</span> <span class='op'>=</span> <span class='num'>2</span><span class='strut'>&nbsp;</span></p>
+ <td class="text">
+<p id="t1" class="pln"><span class="com"># Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0</span><span class="strut">&nbsp;</span></p>
+<p id="t2" class="pln"><span class="com"># For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt</span><span class="strut">&nbsp;</span></p>
+<p id="t3" class="pln"><span class="strut">&nbsp;</span></p>
+<p id="t4" class="stm run hide_run"><span class="nam">m2a</span> <span class="op">=</span> <span class="num">1</span><span class="strut">&nbsp;</span></p>
+<p id="t5" class="stm run hide_run"><span class="nam">m2b</span> <span class="op">=</span> <span class="num">2</span><span class="strut">&nbsp;</span></p>
</td>
</tr>
</table>
</div>
-<div id='footer'>
- <div class='content'>
+<div id="footer">
+ <div class="content">
<p>
- <a class='nav' href='index.html'>&#xab; index</a> &nbsp; &nbsp; <a class='nav' href='http://nedbatchelder.com/code/coverage/4.0a1'>coverage.py v4.0a1</a>
+ <a class="nav" href="index.html">&#xab; index</a> &nbsp; &nbsp; <a class="nav" href="https://coverage.readthedocs.org/en/coverage-4.0a7">coverage.py v4.0a7</a>,
+ created at 2015-07-24 09:04
</p>
</div>
</div>
diff --git a/tests/farm/html/gold_omit_1/m3_py.html b/tests/farm/html/gold_omit_1/m3_py.html
index 67354c48..b372a96e 100644
--- a/tests/farm/html/gold_omit_1/m3_py.html
+++ b/tests/farm/html/gold_omit_1/m3_py.html
@@ -1,86 +1,96 @@
+
+
+
<!DOCTYPE html>
<html>
<head>
- <meta http-equiv='Content-Type' content='text/html; charset=utf-8'>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
- <meta http-equiv='X-UA-Compatible' content='IE=emulateIE7' />
- <title>Coverage for m3: 100%</title>
- <link rel='stylesheet' href='style.css' type='text/css'>
+ <meta http-equiv="X-UA-Compatible" content="IE=emulateIE7" />
+ <title>Coverage for m3.py: 100%</title>
+ <link rel="stylesheet" href="style.css" type="text/css">
- <script type='text/javascript' src='jquery.min.js'></script>
- <script type='text/javascript' src='jquery.hotkeys.js'></script>
- <script type='text/javascript' src='jquery.isonscreen.js'></script>
- <script type='text/javascript' src='coverage_html.js'></script>
- <script type='text/javascript'>
+ <script type="text/javascript" src="jquery.min.js"></script>
+ <script type="text/javascript" src="jquery.hotkeys.js"></script>
+ <script type="text/javascript" src="jquery.isonscreen.js"></script>
+ <script type="text/javascript" src="coverage_html.js"></script>
+ <script type="text/javascript">
jQuery(document).ready(coverage.pyfile_ready);
</script>
</head>
-<body class='pyfile'>
+<body class="pyfile">
-<div id='header'>
- <div class='content'>
- <h1>Coverage for <b>m3</b> :
- <span class='pc_cov'>100%</span>
+<div id="header">
+ <div class="content">
+ <h1>Coverage for <b>m3.py</b> :
+ <span class="pc_cov">100%</span>
</h1>
- <img id='keyboard_icon' src='keybd_closed.png' alt='Show keyboard shortcuts' />
+ <img id="keyboard_icon" src="keybd_closed.png" alt="Show keyboard shortcuts" />
- <h2 class='stats'>
+ <h2 class="stats">
2 statements &nbsp;
- <span class='run hide_run shortkey_r button_toggle_run'>2 run</span>
- <span class='mis shortkey_m button_toggle_mis'>0 missing</span>
- <span class='exc shortkey_x button_toggle_exc'>0 excluded</span>
+ <span class="run hide_run shortkey_r button_toggle_run">2 run</span>
+ <span class="mis shortkey_m button_toggle_mis">0 missing</span>
+ <span class="exc shortkey_x button_toggle_exc">0 excluded</span>
</h2>
</div>
</div>
-<div class='help_panel'>
- <img id='panel_icon' src='keybd_open.png' alt='Hide keyboard shortcuts' />
-<p class='legend'>Hot-keys on this page</p>
+<div class="help_panel">
+ <img id="panel_icon" src="keybd_open.png" alt="Hide keyboard shortcuts" />
+<p class="legend">Hot-keys on this page</p>
<div>
-<p class='keyhelp'>
- <span class='key'>r</span>
- <span class='key'>m</span>
- <span class='key'>x</span>
- <span class='key'>p</span> &nbsp; toggle line displays
+<p class="keyhelp">
+ <span class="key">r</span>
+ <span class="key">m</span>
+ <span class="key">x</span>
+ <span class="key">p</span> &nbsp; toggle line displays
</p>
-<p class='keyhelp'>
- <span class='key'>j</span>
- <span class='key'>k</span> &nbsp; next/prev highlighted chunk
+<p class="keyhelp">
+ <span class="key">j</span>
+ <span class="key">k</span> &nbsp; next/prev highlighted chunk
</p>
-<p class='keyhelp'>
- <span class='key'>0</span> &nbsp; (zero) top of page
+<p class="keyhelp">
+ <span class="key">0</span> &nbsp; (zero) top of page
</p>
-<p class='keyhelp'>
- <span class='key'>1</span> &nbsp; (one) first highlighted chunk
+<p class="keyhelp">
+ <span class="key">1</span> &nbsp; (one) first highlighted chunk
</p>
</div>
</div>
-<div id='source'>
+<div id="source">
<table>
<tr>
- <td class='linenos'>
-<p id='n1' class='stm run hide_run'><a href='#n1'>1</a></p>
-<p id='n2' class='stm run hide_run'><a href='#n2'>2</a></p>
+ <td class="linenos">
+<p id="n1" class="pln"><a href="#n1">1</a></p>
+<p id="n2" class="pln"><a href="#n2">2</a></p>
+<p id="n3" class="pln"><a href="#n3">3</a></p>
+<p id="n4" class="stm run hide_run"><a href="#n4">4</a></p>
+<p id="n5" class="stm run hide_run"><a href="#n5">5</a></p>
</td>
- <td class='text'>
-<p id='t1' class='stm run hide_run'><span class='nam'>m3a</span> <span class='op'>=</span> <span class='num'>1</span><span class='strut'>&nbsp;</span></p>
-<p id='t2' class='stm run hide_run'><span class='nam'>m3b</span> <span class='op'>=</span> <span class='num'>2</span><span class='strut'>&nbsp;</span></p>
+ <td class="text">
+<p id="t1" class="pln"><span class="com"># Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0</span><span class="strut">&nbsp;</span></p>
+<p id="t2" class="pln"><span class="com"># For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt</span><span class="strut">&nbsp;</span></p>
+<p id="t3" class="pln"><span class="strut">&nbsp;</span></p>
+<p id="t4" class="stm run hide_run"><span class="nam">m3a</span> <span class="op">=</span> <span class="num">1</span><span class="strut">&nbsp;</span></p>
+<p id="t5" class="stm run hide_run"><span class="nam">m3b</span> <span class="op">=</span> <span class="num">2</span><span class="strut">&nbsp;</span></p>
</td>
</tr>
</table>
</div>
-<div id='footer'>
- <div class='content'>
+<div id="footer">
+ <div class="content">
<p>
- <a class='nav' href='index.html'>&#xab; index</a> &nbsp; &nbsp; <a class='nav' href='http://nedbatchelder.com/code/coverage/4.0a1'>coverage.py v4.0a1</a>
+ <a class="nav" href="index.html">&#xab; index</a> &nbsp; &nbsp; <a class="nav" href="https://coverage.readthedocs.org/en/coverage-4.0a7">coverage.py v4.0a7</a>,
+ created at 2015-07-24 09:04
</p>
</div>
</div>
diff --git a/tests/farm/html/gold_omit_1/main_py.html b/tests/farm/html/gold_omit_1/main_py.html
index a71b10e4..ba6e9680 100644
--- a/tests/farm/html/gold_omit_1/main_py.html
+++ b/tests/farm/html/gold_omit_1/main_py.html
@@ -1,102 +1,112 @@
+
+
+
<!DOCTYPE html>
<html>
<head>
- <meta http-equiv='Content-Type' content='text/html; charset=utf-8'>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
- <meta http-equiv='X-UA-Compatible' content='IE=emulateIE7' />
- <title>Coverage for main: 100%</title>
- <link rel='stylesheet' href='style.css' type='text/css'>
+ <meta http-equiv="X-UA-Compatible" content="IE=emulateIE7" />
+ <title>Coverage for main.py: 100%</title>
+ <link rel="stylesheet" href="style.css" type="text/css">
- <script type='text/javascript' src='jquery.min.js'></script>
- <script type='text/javascript' src='jquery.hotkeys.js'></script>
- <script type='text/javascript' src='jquery.isonscreen.js'></script>
- <script type='text/javascript' src='coverage_html.js'></script>
- <script type='text/javascript'>
+ <script type="text/javascript" src="jquery.min.js"></script>
+ <script type="text/javascript" src="jquery.hotkeys.js"></script>
+ <script type="text/javascript" src="jquery.isonscreen.js"></script>
+ <script type="text/javascript" src="coverage_html.js"></script>
+ <script type="text/javascript">
jQuery(document).ready(coverage.pyfile_ready);
</script>
</head>
-<body class='pyfile'>
+<body class="pyfile">
-<div id='header'>
- <div class='content'>
- <h1>Coverage for <b>main</b> :
- <span class='pc_cov'>100%</span>
+<div id="header">
+ <div class="content">
+ <h1>Coverage for <b>main.py</b> :
+ <span class="pc_cov">100%</span>
</h1>
- <img id='keyboard_icon' src='keybd_closed.png' alt='Show keyboard shortcuts' />
+ <img id="keyboard_icon" src="keybd_closed.png" alt="Show keyboard shortcuts" />
- <h2 class='stats'>
+ <h2 class="stats">
8 statements &nbsp;
- <span class='run hide_run shortkey_r button_toggle_run'>8 run</span>
- <span class='mis shortkey_m button_toggle_mis'>0 missing</span>
- <span class='exc shortkey_x button_toggle_exc'>0 excluded</span>
+ <span class="run hide_run shortkey_r button_toggle_run">8 run</span>
+ <span class="mis shortkey_m button_toggle_mis">0 missing</span>
+ <span class="exc shortkey_x button_toggle_exc">0 excluded</span>
</h2>
</div>
</div>
-<div class='help_panel'>
- <img id='panel_icon' src='keybd_open.png' alt='Hide keyboard shortcuts' />
-<p class='legend'>Hot-keys on this page</p>
+<div class="help_panel">
+ <img id="panel_icon" src="keybd_open.png" alt="Hide keyboard shortcuts" />
+<p class="legend">Hot-keys on this page</p>
<div>
-<p class='keyhelp'>
- <span class='key'>r</span>
- <span class='key'>m</span>
- <span class='key'>x</span>
- <span class='key'>p</span> &nbsp; toggle line displays
+<p class="keyhelp">
+ <span class="key">r</span>
+ <span class="key">m</span>
+ <span class="key">x</span>
+ <span class="key">p</span> &nbsp; toggle line displays
</p>
-<p class='keyhelp'>
- <span class='key'>j</span>
- <span class='key'>k</span> &nbsp; next/prev highlighted chunk
+<p class="keyhelp">
+ <span class="key">j</span>
+ <span class="key">k</span> &nbsp; next/prev highlighted chunk
</p>
-<p class='keyhelp'>
- <span class='key'>0</span> &nbsp; (zero) top of page
+<p class="keyhelp">
+ <span class="key">0</span> &nbsp; (zero) top of page
</p>
-<p class='keyhelp'>
- <span class='key'>1</span> &nbsp; (one) first highlighted chunk
+<p class="keyhelp">
+ <span class="key">1</span> &nbsp; (one) first highlighted chunk
</p>
</div>
</div>
-<div id='source'>
+<div id="source">
<table>
<tr>
- <td class='linenos'>
-<p id='n1' class='stm run hide_run'><a href='#n1'>1</a></p>
-<p id='n2' class='stm run hide_run'><a href='#n2'>2</a></p>
-<p id='n3' class='stm run hide_run'><a href='#n3'>3</a></p>
-<p id='n4' class='pln'><a href='#n4'>4</a></p>
-<p id='n5' class='stm run hide_run'><a href='#n5'>5</a></p>
-<p id='n6' class='stm run hide_run'><a href='#n6'>6</a></p>
-<p id='n7' class='pln'><a href='#n7'>7</a></p>
-<p id='n8' class='stm run hide_run'><a href='#n8'>8</a></p>
-<p id='n9' class='stm run hide_run'><a href='#n9'>9</a></p>
-<p id='n10' class='stm run hide_run'><a href='#n10'>10</a></p>
+ <td class="linenos">
+<p id="n1" class="pln"><a href="#n1">1</a></p>
+<p id="n2" class="pln"><a href="#n2">2</a></p>
+<p id="n3" class="pln"><a href="#n3">3</a></p>
+<p id="n4" class="stm run hide_run"><a href="#n4">4</a></p>
+<p id="n5" class="stm run hide_run"><a href="#n5">5</a></p>
+<p id="n6" class="stm run hide_run"><a href="#n6">6</a></p>
+<p id="n7" class="pln"><a href="#n7">7</a></p>
+<p id="n8" class="stm run hide_run"><a href="#n8">8</a></p>
+<p id="n9" class="stm run hide_run"><a href="#n9">9</a></p>
+<p id="n10" class="pln"><a href="#n10">10</a></p>
+<p id="n11" class="stm run hide_run"><a href="#n11">11</a></p>
+<p id="n12" class="stm run hide_run"><a href="#n12">12</a></p>
+<p id="n13" class="stm run hide_run"><a href="#n13">13</a></p>
</td>
- <td class='text'>
-<p id='t1' class='stm run hide_run'><span class='key'>import</span> <span class='nam'>m1</span><span class='strut'>&nbsp;</span></p>
-<p id='t2' class='stm run hide_run'><span class='key'>import</span> <span class='nam'>m2</span><span class='strut'>&nbsp;</span></p>
-<p id='t3' class='stm run hide_run'><span class='key'>import</span> <span class='nam'>m3</span><span class='strut'>&nbsp;</span></p>
-<p id='t4' class='pln'><span class='strut'>&nbsp;</span></p>
-<p id='t5' class='stm run hide_run'><span class='nam'>a</span> <span class='op'>=</span> <span class='num'>5</span><span class='strut'>&nbsp;</span></p>
-<p id='t6' class='stm run hide_run'><span class='nam'>b</span> <span class='op'>=</span> <span class='num'>6</span><span class='strut'>&nbsp;</span></p>
-<p id='t7' class='pln'><span class='strut'>&nbsp;</span></p>
-<p id='t8' class='stm run hide_run'><span class='key'>assert</span> <span class='nam'>m1</span><span class='op'>.</span><span class='nam'>m1a</span> <span class='op'>==</span> <span class='num'>1</span><span class='strut'>&nbsp;</span></p>
-<p id='t9' class='stm run hide_run'><span class='key'>assert</span> <span class='nam'>m2</span><span class='op'>.</span><span class='nam'>m2a</span> <span class='op'>==</span> <span class='num'>1</span><span class='strut'>&nbsp;</span></p>
-<p id='t10' class='stm run hide_run'><span class='key'>assert</span> <span class='nam'>m3</span><span class='op'>.</span><span class='nam'>m3a</span> <span class='op'>==</span> <span class='num'>1</span><span class='strut'>&nbsp;</span></p>
+ <td class="text">
+<p id="t1" class="pln"><span class="com"># Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0</span><span class="strut">&nbsp;</span></p>
+<p id="t2" class="pln"><span class="com"># For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt</span><span class="strut">&nbsp;</span></p>
+<p id="t3" class="pln"><span class="strut">&nbsp;</span></p>
+<p id="t4" class="stm run hide_run"><span class="key">import</span> <span class="nam">m1</span><span class="strut">&nbsp;</span></p>
+<p id="t5" class="stm run hide_run"><span class="key">import</span> <span class="nam">m2</span><span class="strut">&nbsp;</span></p>
+<p id="t6" class="stm run hide_run"><span class="key">import</span> <span class="nam">m3</span><span class="strut">&nbsp;</span></p>
+<p id="t7" class="pln"><span class="strut">&nbsp;</span></p>
+<p id="t8" class="stm run hide_run"><span class="nam">a</span> <span class="op">=</span> <span class="num">5</span><span class="strut">&nbsp;</span></p>
+<p id="t9" class="stm run hide_run"><span class="nam">b</span> <span class="op">=</span> <span class="num">6</span><span class="strut">&nbsp;</span></p>
+<p id="t10" class="pln"><span class="strut">&nbsp;</span></p>
+<p id="t11" class="stm run hide_run"><span class="key">assert</span> <span class="nam">m1</span><span class="op">.</span><span class="nam">m1a</span> <span class="op">==</span> <span class="num">1</span><span class="strut">&nbsp;</span></p>
+<p id="t12" class="stm run hide_run"><span class="key">assert</span> <span class="nam">m2</span><span class="op">.</span><span class="nam">m2a</span> <span class="op">==</span> <span class="num">1</span><span class="strut">&nbsp;</span></p>
+<p id="t13" class="stm run hide_run"><span class="key">assert</span> <span class="nam">m3</span><span class="op">.</span><span class="nam">m3a</span> <span class="op">==</span> <span class="num">1</span><span class="strut">&nbsp;</span></p>
</td>
</tr>
</table>
</div>
-<div id='footer'>
- <div class='content'>
+<div id="footer">
+ <div class="content">
<p>
- <a class='nav' href='index.html'>&#xab; index</a> &nbsp; &nbsp; <a class='nav' href='http://nedbatchelder.com/code/coverage/4.0a1'>coverage.py v4.0a1</a>
+ <a class="nav" href="index.html">&#xab; index</a> &nbsp; &nbsp; <a class="nav" href="https://coverage.readthedocs.org/en/coverage-4.0a7">coverage.py v4.0a7</a>,
+ created at 2015-07-24 09:04
</p>
</div>
</div>
diff --git a/tests/farm/html/gold_omit_2/index.html b/tests/farm/html/gold_omit_2/index.html
index b6d495c8..fc781120 100644
--- a/tests/farm/html/gold_omit_2/index.html
+++ b/tests/farm/html/gold_omit_2/index.html
@@ -1,28 +1,31 @@
+
+
+
<!DOCTYPE html>
<html>
<head>
- <meta http-equiv='Content-Type' content='text/html; charset=utf-8'>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>Coverage report</title>
- <link rel='stylesheet' href='style.css' type='text/css'>
+ <link rel="stylesheet" href="style.css" type="text/css">
- <script type='text/javascript' src='jquery.min.js'></script>
- <script type='text/javascript' src='jquery.debounce.min.js'></script>
- <script type='text/javascript' src='jquery.tablesorter.min.js'></script>
- <script type='text/javascript' src='jquery.hotkeys.js'></script>
- <script type='text/javascript' src='coverage_html.js'></script>
- <script type='text/javascript'>
+ <script type="text/javascript" src="jquery.min.js"></script>
+ <script type="text/javascript" src="jquery.debounce.min.js"></script>
+ <script type="text/javascript" src="jquery.tablesorter.min.js"></script>
+ <script type="text/javascript" src="jquery.hotkeys.js"></script>
+ <script type="text/javascript" src="coverage_html.js"></script>
+ <script type="text/javascript">
jQuery(document).ready(coverage.index_ready);
</script>
</head>
-<body class='indexfile'>
+<body class="indexfile">
-<div id='header'>
- <div class='content'>
+<div id="header">
+ <div class="content">
<h1>Coverage report:
- <span class='pc_cov'>100%</span>
+ <span class="pc_cov">100%</span>
</h1>
- <img id='keyboard_icon' src='keybd_closed.png' alt='Show keyboard shortcuts' />
+ <img id="keyboard_icon" src="keybd_closed.png" alt="Show keyboard shortcuts" />
<form id="filter_container">
<input id="filter" type="text" value="" placeholder="filter..." />
@@ -30,72 +33,72 @@
</div>
</div>
-<div class='help_panel'>
- <img id='panel_icon' src='keybd_open.png' alt='Hide keyboard shortcuts' />
- <p class='legend'>Hot-keys on this page</p>
+<div class="help_panel">
+ <img id="panel_icon" src="keybd_open.png" alt="Hide keyboard shortcuts" />
+ <p class="legend">Hot-keys on this page</p>
<div>
- <p class='keyhelp'>
- <span class='key'>n</span>
- <span class='key'>s</span>
- <span class='key'>m</span>
- <span class='key'>x</span>
+ <p class="keyhelp">
+ <span class="key">n</span>
+ <span class="key">s</span>
+ <span class="key">m</span>
+ <span class="key">x</span>
- <span class='key'>c</span> &nbsp; change column sorting
+ <span class="key">c</span> &nbsp; change column sorting
</p>
</div>
</div>
-<div id='index'>
- <table class='index'>
+<div id="index">
+ <table class="index">
<thead>
- <tr class='tablehead' title='Click to sort'>
- <th class='name left headerSortDown shortkey_n'>Module</th>
- <th class='shortkey_s'>statements</th>
- <th class='shortkey_m'>missing</th>
- <th class='shortkey_x'>excluded</th>
+ <tr class="tablehead" title="Click to sort">
+ <th class="name left headerSortDown shortkey_n">Module</th>
+ <th class="shortkey_s">statements</th>
+ <th class="shortkey_m">missing</th>
+ <th class="shortkey_x">excluded</th>
- <th class='right shortkey_c'>coverage</th>
+ <th class="right shortkey_c">coverage</th>
</tr>
</thead>
<tfoot>
- <tr class='total'>
- <td class='name left'>Total</td>
+ <tr class="total">
+ <td class="name left">Total</td>
<td>12</td>
<td>0</td>
<td>0</td>
- <td class='right' data-ratio='12 12'>100%</td>
+ <td class="right" data-ratio="12 12">100%</td>
</tr>
</tfoot>
<tbody>
- <tr class='file'>
- <td class='name left'><a href='m2.html'>m2</a></td>
+ <tr class="file">
+ <td class="name left"><a href="m2_py.html">m2.py</a></td>
<td>2</td>
<td>0</td>
<td>0</td>
- <td class='right' data-ratio='2 2'>100%</td>
+ <td class="right" data-ratio="2 2">100%</td>
</tr>
- <tr class='file'>
- <td class='name left'><a href='m3.html'>m3</a></td>
+ <tr class="file">
+ <td class="name left"><a href="m3_py.html">m3.py</a></td>
<td>2</td>
<td>0</td>
<td>0</td>
- <td class='right' data-ratio='2 2'>100%</td>
+ <td class="right" data-ratio="2 2">100%</td>
</tr>
- <tr class='file'>
- <td class='name left'><a href='main.html'>main</a></td>
+ <tr class="file">
+ <td class="name left"><a href="main_py.html">main.py</a></td>
<td>8</td>
<td>0</td>
<td>0</td>
- <td class='right' data-ratio='8 8'>100%</td>
+ <td class="right" data-ratio="8 8">100%</td>
</tr>
</tbody>
@@ -106,10 +109,11 @@
</p>
</div>
-<div id='footer'>
- <div class='content'>
+<div id="footer">
+ <div class="content">
<p>
- <a class='nav' href='http://nedbatchelder.com/code/coverage/4.0a1'>coverage.py v4.0a1</a>
+ <a class="nav" href="https://coverage.readthedocs.org/en/coverage-4.0a7">coverage.py v4.0a7</a>,
+ created at 2015-07-24 09:31
</p>
</div>
</div>
diff --git a/tests/farm/html/gold_omit_2/m2_py.html b/tests/farm/html/gold_omit_2/m2_py.html
index c775e6bb..2a7f13f0 100644
--- a/tests/farm/html/gold_omit_2/m2_py.html
+++ b/tests/farm/html/gold_omit_2/m2_py.html
@@ -1,86 +1,96 @@
+
+
+
<!DOCTYPE html>
<html>
<head>
- <meta http-equiv='Content-Type' content='text/html; charset=utf-8'>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
- <meta http-equiv='X-UA-Compatible' content='IE=emulateIE7' />
- <title>Coverage for m2: 100%</title>
- <link rel='stylesheet' href='style.css' type='text/css'>
+ <meta http-equiv="X-UA-Compatible" content="IE=emulateIE7" />
+ <title>Coverage for m2.py: 100%</title>
+ <link rel="stylesheet" href="style.css" type="text/css">
- <script type='text/javascript' src='jquery.min.js'></script>
- <script type='text/javascript' src='jquery.hotkeys.js'></script>
- <script type='text/javascript' src='jquery.isonscreen.js'></script>
- <script type='text/javascript' src='coverage_html.js'></script>
- <script type='text/javascript'>
+ <script type="text/javascript" src="jquery.min.js"></script>
+ <script type="text/javascript" src="jquery.hotkeys.js"></script>
+ <script type="text/javascript" src="jquery.isonscreen.js"></script>
+ <script type="text/javascript" src="coverage_html.js"></script>
+ <script type="text/javascript">
jQuery(document).ready(coverage.pyfile_ready);
</script>
</head>
-<body class='pyfile'>
+<body class="pyfile">
-<div id='header'>
- <div class='content'>
- <h1>Coverage for <b>m2</b> :
- <span class='pc_cov'>100%</span>
+<div id="header">
+ <div class="content">
+ <h1>Coverage for <b>m2.py</b> :
+ <span class="pc_cov">100%</span>
</h1>
- <img id='keyboard_icon' src='keybd_closed.png' alt='Show keyboard shortcuts' />
+ <img id="keyboard_icon" src="keybd_closed.png" alt="Show keyboard shortcuts" />
- <h2 class='stats'>
+ <h2 class="stats">
2 statements &nbsp;
- <span class='run hide_run shortkey_r button_toggle_run'>2 run</span>
- <span class='mis shortkey_m button_toggle_mis'>0 missing</span>
- <span class='exc shortkey_x button_toggle_exc'>0 excluded</span>
+ <span class="run hide_run shortkey_r button_toggle_run">2 run</span>
+ <span class="mis shortkey_m button_toggle_mis">0 missing</span>
+ <span class="exc shortkey_x button_toggle_exc">0 excluded</span>
</h2>
</div>
</div>
-<div class='help_panel'>
- <img id='panel_icon' src='keybd_open.png' alt='Hide keyboard shortcuts' />
-<p class='legend'>Hot-keys on this page</p>
+<div class="help_panel">
+ <img id="panel_icon" src="keybd_open.png" alt="Hide keyboard shortcuts" />
+<p class="legend">Hot-keys on this page</p>
<div>
-<p class='keyhelp'>
- <span class='key'>r</span>
- <span class='key'>m</span>
- <span class='key'>x</span>
- <span class='key'>p</span> &nbsp; toggle line displays
+<p class="keyhelp">
+ <span class="key">r</span>
+ <span class="key">m</span>
+ <span class="key">x</span>
+ <span class="key">p</span> &nbsp; toggle line displays
</p>
-<p class='keyhelp'>
- <span class='key'>j</span>
- <span class='key'>k</span> &nbsp; next/prev highlighted chunk
+<p class="keyhelp">
+ <span class="key">j</span>
+ <span class="key">k</span> &nbsp; next/prev highlighted chunk
</p>
-<p class='keyhelp'>
- <span class='key'>0</span> &nbsp; (zero) top of page
+<p class="keyhelp">
+ <span class="key">0</span> &nbsp; (zero) top of page
</p>
-<p class='keyhelp'>
- <span class='key'>1</span> &nbsp; (one) first highlighted chunk
+<p class="keyhelp">
+ <span class="key">1</span> &nbsp; (one) first highlighted chunk
</p>
</div>
</div>
-<div id='source'>
+<div id="source">
<table>
<tr>
- <td class='linenos'>
-<p id='n1' class='stm run hide_run'><a href='#n1'>1</a></p>
-<p id='n2' class='stm run hide_run'><a href='#n2'>2</a></p>
+ <td class="linenos">
+<p id="n1" class="pln"><a href="#n1">1</a></p>
+<p id="n2" class="pln"><a href="#n2">2</a></p>
+<p id="n3" class="pln"><a href="#n3">3</a></p>
+<p id="n4" class="stm run hide_run"><a href="#n4">4</a></p>
+<p id="n5" class="stm run hide_run"><a href="#n5">5</a></p>
</td>
- <td class='text'>
-<p id='t1' class='stm run hide_run'><span class='nam'>m2a</span> <span class='op'>=</span> <span class='num'>1</span><span class='strut'>&nbsp;</span></p>
-<p id='t2' class='stm run hide_run'><span class='nam'>m2b</span> <span class='op'>=</span> <span class='num'>2</span><span class='strut'>&nbsp;</span></p>
+ <td class="text">
+<p id="t1" class="pln"><span class="com"># Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0</span><span class="strut">&nbsp;</span></p>
+<p id="t2" class="pln"><span class="com"># For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt</span><span class="strut">&nbsp;</span></p>
+<p id="t3" class="pln"><span class="strut">&nbsp;</span></p>
+<p id="t4" class="stm run hide_run"><span class="nam">m2a</span> <span class="op">=</span> <span class="num">1</span><span class="strut">&nbsp;</span></p>
+<p id="t5" class="stm run hide_run"><span class="nam">m2b</span> <span class="op">=</span> <span class="num">2</span><span class="strut">&nbsp;</span></p>
</td>
</tr>
</table>
</div>
-<div id='footer'>
- <div class='content'>
+<div id="footer">
+ <div class="content">
<p>
- <a class='nav' href='index.html'>&#xab; index</a> &nbsp; &nbsp; <a class='nav' href='http://nedbatchelder.com/code/coverage/4.0a1'>coverage.py v4.0a1</a>
+ <a class="nav" href="index.html">&#xab; index</a> &nbsp; &nbsp; <a class="nav" href="https://coverage.readthedocs.org/en/coverage-4.0a7">coverage.py v4.0a7</a>,
+ created at 2015-07-24 09:04
</p>
</div>
</div>
diff --git a/tests/farm/html/gold_omit_2/m3_py.html b/tests/farm/html/gold_omit_2/m3_py.html
index 67354c48..b372a96e 100644
--- a/tests/farm/html/gold_omit_2/m3_py.html
+++ b/tests/farm/html/gold_omit_2/m3_py.html
@@ -1,86 +1,96 @@
+
+
+
<!DOCTYPE html>
<html>
<head>
- <meta http-equiv='Content-Type' content='text/html; charset=utf-8'>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
- <meta http-equiv='X-UA-Compatible' content='IE=emulateIE7' />
- <title>Coverage for m3: 100%</title>
- <link rel='stylesheet' href='style.css' type='text/css'>
+ <meta http-equiv="X-UA-Compatible" content="IE=emulateIE7" />
+ <title>Coverage for m3.py: 100%</title>
+ <link rel="stylesheet" href="style.css" type="text/css">
- <script type='text/javascript' src='jquery.min.js'></script>
- <script type='text/javascript' src='jquery.hotkeys.js'></script>
- <script type='text/javascript' src='jquery.isonscreen.js'></script>
- <script type='text/javascript' src='coverage_html.js'></script>
- <script type='text/javascript'>
+ <script type="text/javascript" src="jquery.min.js"></script>
+ <script type="text/javascript" src="jquery.hotkeys.js"></script>
+ <script type="text/javascript" src="jquery.isonscreen.js"></script>
+ <script type="text/javascript" src="coverage_html.js"></script>
+ <script type="text/javascript">
jQuery(document).ready(coverage.pyfile_ready);
</script>
</head>
-<body class='pyfile'>
+<body class="pyfile">
-<div id='header'>
- <div class='content'>
- <h1>Coverage for <b>m3</b> :
- <span class='pc_cov'>100%</span>
+<div id="header">
+ <div class="content">
+ <h1>Coverage for <b>m3.py</b> :
+ <span class="pc_cov">100%</span>
</h1>
- <img id='keyboard_icon' src='keybd_closed.png' alt='Show keyboard shortcuts' />
+ <img id="keyboard_icon" src="keybd_closed.png" alt="Show keyboard shortcuts" />
- <h2 class='stats'>
+ <h2 class="stats">
2 statements &nbsp;
- <span class='run hide_run shortkey_r button_toggle_run'>2 run</span>
- <span class='mis shortkey_m button_toggle_mis'>0 missing</span>
- <span class='exc shortkey_x button_toggle_exc'>0 excluded</span>
+ <span class="run hide_run shortkey_r button_toggle_run">2 run</span>
+ <span class="mis shortkey_m button_toggle_mis">0 missing</span>
+ <span class="exc shortkey_x button_toggle_exc">0 excluded</span>
</h2>
</div>
</div>
-<div class='help_panel'>
- <img id='panel_icon' src='keybd_open.png' alt='Hide keyboard shortcuts' />
-<p class='legend'>Hot-keys on this page</p>
+<div class="help_panel">
+ <img id="panel_icon" src="keybd_open.png" alt="Hide keyboard shortcuts" />
+<p class="legend">Hot-keys on this page</p>
<div>
-<p class='keyhelp'>
- <span class='key'>r</span>
- <span class='key'>m</span>
- <span class='key'>x</span>
- <span class='key'>p</span> &nbsp; toggle line displays
+<p class="keyhelp">
+ <span class="key">r</span>
+ <span class="key">m</span>
+ <span class="key">x</span>
+ <span class="key">p</span> &nbsp; toggle line displays
</p>
-<p class='keyhelp'>
- <span class='key'>j</span>
- <span class='key'>k</span> &nbsp; next/prev highlighted chunk
+<p class="keyhelp">
+ <span class="key">j</span>
+ <span class="key">k</span> &nbsp; next/prev highlighted chunk
</p>
-<p class='keyhelp'>
- <span class='key'>0</span> &nbsp; (zero) top of page
+<p class="keyhelp">
+ <span class="key">0</span> &nbsp; (zero) top of page
</p>
-<p class='keyhelp'>
- <span class='key'>1</span> &nbsp; (one) first highlighted chunk
+<p class="keyhelp">
+ <span class="key">1</span> &nbsp; (one) first highlighted chunk
</p>
</div>
</div>
-<div id='source'>
+<div id="source">
<table>
<tr>
- <td class='linenos'>
-<p id='n1' class='stm run hide_run'><a href='#n1'>1</a></p>
-<p id='n2' class='stm run hide_run'><a href='#n2'>2</a></p>
+ <td class="linenos">
+<p id="n1" class="pln"><a href="#n1">1</a></p>
+<p id="n2" class="pln"><a href="#n2">2</a></p>
+<p id="n3" class="pln"><a href="#n3">3</a></p>
+<p id="n4" class="stm run hide_run"><a href="#n4">4</a></p>
+<p id="n5" class="stm run hide_run"><a href="#n5">5</a></p>
</td>
- <td class='text'>
-<p id='t1' class='stm run hide_run'><span class='nam'>m3a</span> <span class='op'>=</span> <span class='num'>1</span><span class='strut'>&nbsp;</span></p>
-<p id='t2' class='stm run hide_run'><span class='nam'>m3b</span> <span class='op'>=</span> <span class='num'>2</span><span class='strut'>&nbsp;</span></p>
+ <td class="text">
+<p id="t1" class="pln"><span class="com"># Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0</span><span class="strut">&nbsp;</span></p>
+<p id="t2" class="pln"><span class="com"># For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt</span><span class="strut">&nbsp;</span></p>
+<p id="t3" class="pln"><span class="strut">&nbsp;</span></p>
+<p id="t4" class="stm run hide_run"><span class="nam">m3a</span> <span class="op">=</span> <span class="num">1</span><span class="strut">&nbsp;</span></p>
+<p id="t5" class="stm run hide_run"><span class="nam">m3b</span> <span class="op">=</span> <span class="num">2</span><span class="strut">&nbsp;</span></p>
</td>
</tr>
</table>
</div>
-<div id='footer'>
- <div class='content'>
+<div id="footer">
+ <div class="content">
<p>
- <a class='nav' href='index.html'>&#xab; index</a> &nbsp; &nbsp; <a class='nav' href='http://nedbatchelder.com/code/coverage/4.0a1'>coverage.py v4.0a1</a>
+ <a class="nav" href="index.html">&#xab; index</a> &nbsp; &nbsp; <a class="nav" href="https://coverage.readthedocs.org/en/coverage-4.0a7">coverage.py v4.0a7</a>,
+ created at 2015-07-24 09:04
</p>
</div>
</div>
diff --git a/tests/farm/html/gold_omit_2/main_py.html b/tests/farm/html/gold_omit_2/main_py.html
index a71b10e4..ba6e9680 100644
--- a/tests/farm/html/gold_omit_2/main_py.html
+++ b/tests/farm/html/gold_omit_2/main_py.html
@@ -1,102 +1,112 @@
+
+
+
<!DOCTYPE html>
<html>
<head>
- <meta http-equiv='Content-Type' content='text/html; charset=utf-8'>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
- <meta http-equiv='X-UA-Compatible' content='IE=emulateIE7' />
- <title>Coverage for main: 100%</title>
- <link rel='stylesheet' href='style.css' type='text/css'>
+ <meta http-equiv="X-UA-Compatible" content="IE=emulateIE7" />
+ <title>Coverage for main.py: 100%</title>
+ <link rel="stylesheet" href="style.css" type="text/css">
- <script type='text/javascript' src='jquery.min.js'></script>
- <script type='text/javascript' src='jquery.hotkeys.js'></script>
- <script type='text/javascript' src='jquery.isonscreen.js'></script>
- <script type='text/javascript' src='coverage_html.js'></script>
- <script type='text/javascript'>
+ <script type="text/javascript" src="jquery.min.js"></script>
+ <script type="text/javascript" src="jquery.hotkeys.js"></script>
+ <script type="text/javascript" src="jquery.isonscreen.js"></script>
+ <script type="text/javascript" src="coverage_html.js"></script>
+ <script type="text/javascript">
jQuery(document).ready(coverage.pyfile_ready);
</script>
</head>
-<body class='pyfile'>
+<body class="pyfile">
-<div id='header'>
- <div class='content'>
- <h1>Coverage for <b>main</b> :
- <span class='pc_cov'>100%</span>
+<div id="header">
+ <div class="content">
+ <h1>Coverage for <b>main.py</b> :
+ <span class="pc_cov">100%</span>
</h1>
- <img id='keyboard_icon' src='keybd_closed.png' alt='Show keyboard shortcuts' />
+ <img id="keyboard_icon" src="keybd_closed.png" alt="Show keyboard shortcuts" />
- <h2 class='stats'>
+ <h2 class="stats">
8 statements &nbsp;
- <span class='run hide_run shortkey_r button_toggle_run'>8 run</span>
- <span class='mis shortkey_m button_toggle_mis'>0 missing</span>
- <span class='exc shortkey_x button_toggle_exc'>0 excluded</span>
+ <span class="run hide_run shortkey_r button_toggle_run">8 run</span>
+ <span class="mis shortkey_m button_toggle_mis">0 missing</span>
+ <span class="exc shortkey_x button_toggle_exc">0 excluded</span>
</h2>
</div>
</div>
-<div class='help_panel'>
- <img id='panel_icon' src='keybd_open.png' alt='Hide keyboard shortcuts' />
-<p class='legend'>Hot-keys on this page</p>
+<div class="help_panel">
+ <img id="panel_icon" src="keybd_open.png" alt="Hide keyboard shortcuts" />
+<p class="legend">Hot-keys on this page</p>
<div>
-<p class='keyhelp'>
- <span class='key'>r</span>
- <span class='key'>m</span>
- <span class='key'>x</span>
- <span class='key'>p</span> &nbsp; toggle line displays
+<p class="keyhelp">
+ <span class="key">r</span>
+ <span class="key">m</span>
+ <span class="key">x</span>
+ <span class="key">p</span> &nbsp; toggle line displays
</p>
-<p class='keyhelp'>
- <span class='key'>j</span>
- <span class='key'>k</span> &nbsp; next/prev highlighted chunk
+<p class="keyhelp">
+ <span class="key">j</span>
+ <span class="key">k</span> &nbsp; next/prev highlighted chunk
</p>
-<p class='keyhelp'>
- <span class='key'>0</span> &nbsp; (zero) top of page
+<p class="keyhelp">
+ <span class="key">0</span> &nbsp; (zero) top of page
</p>
-<p class='keyhelp'>
- <span class='key'>1</span> &nbsp; (one) first highlighted chunk
+<p class="keyhelp">
+ <span class="key">1</span> &nbsp; (one) first highlighted chunk
</p>
</div>
</div>
-<div id='source'>
+<div id="source">
<table>
<tr>
- <td class='linenos'>
-<p id='n1' class='stm run hide_run'><a href='#n1'>1</a></p>
-<p id='n2' class='stm run hide_run'><a href='#n2'>2</a></p>
-<p id='n3' class='stm run hide_run'><a href='#n3'>3</a></p>
-<p id='n4' class='pln'><a href='#n4'>4</a></p>
-<p id='n5' class='stm run hide_run'><a href='#n5'>5</a></p>
-<p id='n6' class='stm run hide_run'><a href='#n6'>6</a></p>
-<p id='n7' class='pln'><a href='#n7'>7</a></p>
-<p id='n8' class='stm run hide_run'><a href='#n8'>8</a></p>
-<p id='n9' class='stm run hide_run'><a href='#n9'>9</a></p>
-<p id='n10' class='stm run hide_run'><a href='#n10'>10</a></p>
+ <td class="linenos">
+<p id="n1" class="pln"><a href="#n1">1</a></p>
+<p id="n2" class="pln"><a href="#n2">2</a></p>
+<p id="n3" class="pln"><a href="#n3">3</a></p>
+<p id="n4" class="stm run hide_run"><a href="#n4">4</a></p>
+<p id="n5" class="stm run hide_run"><a href="#n5">5</a></p>
+<p id="n6" class="stm run hide_run"><a href="#n6">6</a></p>
+<p id="n7" class="pln"><a href="#n7">7</a></p>
+<p id="n8" class="stm run hide_run"><a href="#n8">8</a></p>
+<p id="n9" class="stm run hide_run"><a href="#n9">9</a></p>
+<p id="n10" class="pln"><a href="#n10">10</a></p>
+<p id="n11" class="stm run hide_run"><a href="#n11">11</a></p>
+<p id="n12" class="stm run hide_run"><a href="#n12">12</a></p>
+<p id="n13" class="stm run hide_run"><a href="#n13">13</a></p>
</td>
- <td class='text'>
-<p id='t1' class='stm run hide_run'><span class='key'>import</span> <span class='nam'>m1</span><span class='strut'>&nbsp;</span></p>
-<p id='t2' class='stm run hide_run'><span class='key'>import</span> <span class='nam'>m2</span><span class='strut'>&nbsp;</span></p>
-<p id='t3' class='stm run hide_run'><span class='key'>import</span> <span class='nam'>m3</span><span class='strut'>&nbsp;</span></p>
-<p id='t4' class='pln'><span class='strut'>&nbsp;</span></p>
-<p id='t5' class='stm run hide_run'><span class='nam'>a</span> <span class='op'>=</span> <span class='num'>5</span><span class='strut'>&nbsp;</span></p>
-<p id='t6' class='stm run hide_run'><span class='nam'>b</span> <span class='op'>=</span> <span class='num'>6</span><span class='strut'>&nbsp;</span></p>
-<p id='t7' class='pln'><span class='strut'>&nbsp;</span></p>
-<p id='t8' class='stm run hide_run'><span class='key'>assert</span> <span class='nam'>m1</span><span class='op'>.</span><span class='nam'>m1a</span> <span class='op'>==</span> <span class='num'>1</span><span class='strut'>&nbsp;</span></p>
-<p id='t9' class='stm run hide_run'><span class='key'>assert</span> <span class='nam'>m2</span><span class='op'>.</span><span class='nam'>m2a</span> <span class='op'>==</span> <span class='num'>1</span><span class='strut'>&nbsp;</span></p>
-<p id='t10' class='stm run hide_run'><span class='key'>assert</span> <span class='nam'>m3</span><span class='op'>.</span><span class='nam'>m3a</span> <span class='op'>==</span> <span class='num'>1</span><span class='strut'>&nbsp;</span></p>
+ <td class="text">
+<p id="t1" class="pln"><span class="com"># Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0</span><span class="strut">&nbsp;</span></p>
+<p id="t2" class="pln"><span class="com"># For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt</span><span class="strut">&nbsp;</span></p>
+<p id="t3" class="pln"><span class="strut">&nbsp;</span></p>
+<p id="t4" class="stm run hide_run"><span class="key">import</span> <span class="nam">m1</span><span class="strut">&nbsp;</span></p>
+<p id="t5" class="stm run hide_run"><span class="key">import</span> <span class="nam">m2</span><span class="strut">&nbsp;</span></p>
+<p id="t6" class="stm run hide_run"><span class="key">import</span> <span class="nam">m3</span><span class="strut">&nbsp;</span></p>
+<p id="t7" class="pln"><span class="strut">&nbsp;</span></p>
+<p id="t8" class="stm run hide_run"><span class="nam">a</span> <span class="op">=</span> <span class="num">5</span><span class="strut">&nbsp;</span></p>
+<p id="t9" class="stm run hide_run"><span class="nam">b</span> <span class="op">=</span> <span class="num">6</span><span class="strut">&nbsp;</span></p>
+<p id="t10" class="pln"><span class="strut">&nbsp;</span></p>
+<p id="t11" class="stm run hide_run"><span class="key">assert</span> <span class="nam">m1</span><span class="op">.</span><span class="nam">m1a</span> <span class="op">==</span> <span class="num">1</span><span class="strut">&nbsp;</span></p>
+<p id="t12" class="stm run hide_run"><span class="key">assert</span> <span class="nam">m2</span><span class="op">.</span><span class="nam">m2a</span> <span class="op">==</span> <span class="num">1</span><span class="strut">&nbsp;</span></p>
+<p id="t13" class="stm run hide_run"><span class="key">assert</span> <span class="nam">m3</span><span class="op">.</span><span class="nam">m3a</span> <span class="op">==</span> <span class="num">1</span><span class="strut">&nbsp;</span></p>
</td>
</tr>
</table>
</div>
-<div id='footer'>
- <div class='content'>
+<div id="footer">
+ <div class="content">
<p>
- <a class='nav' href='index.html'>&#xab; index</a> &nbsp; &nbsp; <a class='nav' href='http://nedbatchelder.com/code/coverage/4.0a1'>coverage.py v4.0a1</a>
+ <a class="nav" href="index.html">&#xab; index</a> &nbsp; &nbsp; <a class="nav" href="https://coverage.readthedocs.org/en/coverage-4.0a7">coverage.py v4.0a7</a>,
+ created at 2015-07-24 09:04
</p>
</div>
</div>
diff --git a/tests/farm/html/gold_omit_3/index.html b/tests/farm/html/gold_omit_3/index.html
index db0ad581..cc9e9ea5 100644
--- a/tests/farm/html/gold_omit_3/index.html
+++ b/tests/farm/html/gold_omit_3/index.html
@@ -1,28 +1,31 @@
+
+
+
<!DOCTYPE html>
<html>
<head>
- <meta http-equiv='Content-Type' content='text/html; charset=utf-8'>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>Coverage report</title>
- <link rel='stylesheet' href='style.css' type='text/css'>
+ <link rel="stylesheet" href="style.css" type="text/css">
- <script type='text/javascript' src='jquery.min.js'></script>
- <script type='text/javascript' src='jquery.debounce.min.js'></script>
- <script type='text/javascript' src='jquery.tablesorter.min.js'></script>
- <script type='text/javascript' src='jquery.hotkeys.js'></script>
- <script type='text/javascript' src='coverage_html.js'></script>
- <script type='text/javascript'>
+ <script type="text/javascript" src="jquery.min.js"></script>
+ <script type="text/javascript" src="jquery.debounce.min.js"></script>
+ <script type="text/javascript" src="jquery.tablesorter.min.js"></script>
+ <script type="text/javascript" src="jquery.hotkeys.js"></script>
+ <script type="text/javascript" src="coverage_html.js"></script>
+ <script type="text/javascript">
jQuery(document).ready(coverage.index_ready);
</script>
</head>
-<body class='indexfile'>
+<body class="indexfile">
-<div id='header'>
- <div class='content'>
+<div id="header">
+ <div class="content">
<h1>Coverage report:
- <span class='pc_cov'>100%</span>
+ <span class="pc_cov">100%</span>
</h1>
- <img id='keyboard_icon' src='keybd_closed.png' alt='Show keyboard shortcuts' />
+ <img id="keyboard_icon" src="keybd_closed.png" alt="Show keyboard shortcuts" />
<form id="filter_container">
<input id="filter" type="text" value="" placeholder="filter..." />
@@ -30,63 +33,63 @@
</div>
</div>
-<div class='help_panel'>
- <img id='panel_icon' src='keybd_open.png' alt='Hide keyboard shortcuts' />
- <p class='legend'>Hot-keys on this page</p>
+<div class="help_panel">
+ <img id="panel_icon" src="keybd_open.png" alt="Hide keyboard shortcuts" />
+ <p class="legend">Hot-keys on this page</p>
<div>
- <p class='keyhelp'>
- <span class='key'>n</span>
- <span class='key'>s</span>
- <span class='key'>m</span>
- <span class='key'>x</span>
+ <p class="keyhelp">
+ <span class="key">n</span>
+ <span class="key">s</span>
+ <span class="key">m</span>
+ <span class="key">x</span>
- <span class='key'>c</span> &nbsp; change column sorting
+ <span class="key">c</span> &nbsp; change column sorting
</p>
</div>
</div>
-<div id='index'>
- <table class='index'>
+<div id="index">
+ <table class="index">
<thead>
- <tr class='tablehead' title='Click to sort'>
- <th class='name left headerSortDown shortkey_n'>Module</th>
- <th class='shortkey_s'>statements</th>
- <th class='shortkey_m'>missing</th>
- <th class='shortkey_x'>excluded</th>
+ <tr class="tablehead" title="Click to sort">
+ <th class="name left headerSortDown shortkey_n">Module</th>
+ <th class="shortkey_s">statements</th>
+ <th class="shortkey_m">missing</th>
+ <th class="shortkey_x">excluded</th>
- <th class='right shortkey_c'>coverage</th>
+ <th class="right shortkey_c">coverage</th>
</tr>
</thead>
<tfoot>
- <tr class='total'>
- <td class='name left'>Total</td>
+ <tr class="total">
+ <td class="name left">Total</td>
<td>10</td>
<td>0</td>
<td>0</td>
- <td class='right' data-ratio='10 10'>100%</td>
+ <td class="right" data-ratio="10 10">100%</td>
</tr>
</tfoot>
<tbody>
- <tr class='file'>
- <td class='name left'><a href='m3.html'>m3</a></td>
+ <tr class="file">
+ <td class="name left"><a href="m3_py.html">m3.py</a></td>
<td>2</td>
<td>0</td>
<td>0</td>
- <td class='right' data-ratio='2 2'>100%</td>
+ <td class="right" data-ratio="2 2">100%</td>
</tr>
- <tr class='file'>
- <td class='name left'><a href='main.html'>main</a></td>
+ <tr class="file">
+ <td class="name left"><a href="main_py.html">main.py</a></td>
<td>8</td>
<td>0</td>
<td>0</td>
- <td class='right' data-ratio='8 8'>100%</td>
+ <td class="right" data-ratio="8 8">100%</td>
</tr>
</tbody>
@@ -97,10 +100,11 @@
</p>
</div>
-<div id='footer'>
- <div class='content'>
+<div id="footer">
+ <div class="content">
<p>
- <a class='nav' href='http://nedbatchelder.com/code/coverage/4.0a1'>coverage.py v4.0a1</a>
+ <a class="nav" href="https://coverage.readthedocs.org/en/coverage-4.0a7">coverage.py v4.0a7</a>,
+ created at 2015-07-24 09:31
</p>
</div>
</div>
diff --git a/tests/farm/html/gold_omit_3/m3_py.html b/tests/farm/html/gold_omit_3/m3_py.html
index 67354c48..b372a96e 100644
--- a/tests/farm/html/gold_omit_3/m3_py.html
+++ b/tests/farm/html/gold_omit_3/m3_py.html
@@ -1,86 +1,96 @@
+
+
+
<!DOCTYPE html>
<html>
<head>
- <meta http-equiv='Content-Type' content='text/html; charset=utf-8'>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
- <meta http-equiv='X-UA-Compatible' content='IE=emulateIE7' />
- <title>Coverage for m3: 100%</title>
- <link rel='stylesheet' href='style.css' type='text/css'>
+ <meta http-equiv="X-UA-Compatible" content="IE=emulateIE7" />
+ <title>Coverage for m3.py: 100%</title>
+ <link rel="stylesheet" href="style.css" type="text/css">
- <script type='text/javascript' src='jquery.min.js'></script>
- <script type='text/javascript' src='jquery.hotkeys.js'></script>
- <script type='text/javascript' src='jquery.isonscreen.js'></script>
- <script type='text/javascript' src='coverage_html.js'></script>
- <script type='text/javascript'>
+ <script type="text/javascript" src="jquery.min.js"></script>
+ <script type="text/javascript" src="jquery.hotkeys.js"></script>
+ <script type="text/javascript" src="jquery.isonscreen.js"></script>
+ <script type="text/javascript" src="coverage_html.js"></script>
+ <script type="text/javascript">
jQuery(document).ready(coverage.pyfile_ready);
</script>
</head>
-<body class='pyfile'>
+<body class="pyfile">
-<div id='header'>
- <div class='content'>
- <h1>Coverage for <b>m3</b> :
- <span class='pc_cov'>100%</span>
+<div id="header">
+ <div class="content">
+ <h1>Coverage for <b>m3.py</b> :
+ <span class="pc_cov">100%</span>
</h1>
- <img id='keyboard_icon' src='keybd_closed.png' alt='Show keyboard shortcuts' />
+ <img id="keyboard_icon" src="keybd_closed.png" alt="Show keyboard shortcuts" />
- <h2 class='stats'>
+ <h2 class="stats">
2 statements &nbsp;
- <span class='run hide_run shortkey_r button_toggle_run'>2 run</span>
- <span class='mis shortkey_m button_toggle_mis'>0 missing</span>
- <span class='exc shortkey_x button_toggle_exc'>0 excluded</span>
+ <span class="run hide_run shortkey_r button_toggle_run">2 run</span>
+ <span class="mis shortkey_m button_toggle_mis">0 missing</span>
+ <span class="exc shortkey_x button_toggle_exc">0 excluded</span>
</h2>
</div>
</div>
-<div class='help_panel'>
- <img id='panel_icon' src='keybd_open.png' alt='Hide keyboard shortcuts' />
-<p class='legend'>Hot-keys on this page</p>
+<div class="help_panel">
+ <img id="panel_icon" src="keybd_open.png" alt="Hide keyboard shortcuts" />
+<p class="legend">Hot-keys on this page</p>
<div>
-<p class='keyhelp'>
- <span class='key'>r</span>
- <span class='key'>m</span>
- <span class='key'>x</span>
- <span class='key'>p</span> &nbsp; toggle line displays
+<p class="keyhelp">
+ <span class="key">r</span>
+ <span class="key">m</span>
+ <span class="key">x</span>
+ <span class="key">p</span> &nbsp; toggle line displays
</p>
-<p class='keyhelp'>
- <span class='key'>j</span>
- <span class='key'>k</span> &nbsp; next/prev highlighted chunk
+<p class="keyhelp">
+ <span class="key">j</span>
+ <span class="key">k</span> &nbsp; next/prev highlighted chunk
</p>
-<p class='keyhelp'>
- <span class='key'>0</span> &nbsp; (zero) top of page
+<p class="keyhelp">
+ <span class="key">0</span> &nbsp; (zero) top of page
</p>
-<p class='keyhelp'>
- <span class='key'>1</span> &nbsp; (one) first highlighted chunk
+<p class="keyhelp">
+ <span class="key">1</span> &nbsp; (one) first highlighted chunk
</p>
</div>
</div>
-<div id='source'>
+<div id="source">
<table>
<tr>
- <td class='linenos'>
-<p id='n1' class='stm run hide_run'><a href='#n1'>1</a></p>
-<p id='n2' class='stm run hide_run'><a href='#n2'>2</a></p>
+ <td class="linenos">
+<p id="n1" class="pln"><a href="#n1">1</a></p>
+<p id="n2" class="pln"><a href="#n2">2</a></p>
+<p id="n3" class="pln"><a href="#n3">3</a></p>
+<p id="n4" class="stm run hide_run"><a href="#n4">4</a></p>
+<p id="n5" class="stm run hide_run"><a href="#n5">5</a></p>
</td>
- <td class='text'>
-<p id='t1' class='stm run hide_run'><span class='nam'>m3a</span> <span class='op'>=</span> <span class='num'>1</span><span class='strut'>&nbsp;</span></p>
-<p id='t2' class='stm run hide_run'><span class='nam'>m3b</span> <span class='op'>=</span> <span class='num'>2</span><span class='strut'>&nbsp;</span></p>
+ <td class="text">
+<p id="t1" class="pln"><span class="com"># Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0</span><span class="strut">&nbsp;</span></p>
+<p id="t2" class="pln"><span class="com"># For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt</span><span class="strut">&nbsp;</span></p>
+<p id="t3" class="pln"><span class="strut">&nbsp;</span></p>
+<p id="t4" class="stm run hide_run"><span class="nam">m3a</span> <span class="op">=</span> <span class="num">1</span><span class="strut">&nbsp;</span></p>
+<p id="t5" class="stm run hide_run"><span class="nam">m3b</span> <span class="op">=</span> <span class="num">2</span><span class="strut">&nbsp;</span></p>
</td>
</tr>
</table>
</div>
-<div id='footer'>
- <div class='content'>
+<div id="footer">
+ <div class="content">
<p>
- <a class='nav' href='index.html'>&#xab; index</a> &nbsp; &nbsp; <a class='nav' href='http://nedbatchelder.com/code/coverage/4.0a1'>coverage.py v4.0a1</a>
+ <a class="nav" href="index.html">&#xab; index</a> &nbsp; &nbsp; <a class="nav" href="https://coverage.readthedocs.org/en/coverage-4.0a7">coverage.py v4.0a7</a>,
+ created at 2015-07-24 09:04
</p>
</div>
</div>
diff --git a/tests/farm/html/gold_omit_3/main_py.html b/tests/farm/html/gold_omit_3/main_py.html
index a71b10e4..ba6e9680 100644
--- a/tests/farm/html/gold_omit_3/main_py.html
+++ b/tests/farm/html/gold_omit_3/main_py.html
@@ -1,102 +1,112 @@
+
+
+
<!DOCTYPE html>
<html>
<head>
- <meta http-equiv='Content-Type' content='text/html; charset=utf-8'>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
- <meta http-equiv='X-UA-Compatible' content='IE=emulateIE7' />
- <title>Coverage for main: 100%</title>
- <link rel='stylesheet' href='style.css' type='text/css'>
+ <meta http-equiv="X-UA-Compatible" content="IE=emulateIE7" />
+ <title>Coverage for main.py: 100%</title>
+ <link rel="stylesheet" href="style.css" type="text/css">
- <script type='text/javascript' src='jquery.min.js'></script>
- <script type='text/javascript' src='jquery.hotkeys.js'></script>
- <script type='text/javascript' src='jquery.isonscreen.js'></script>
- <script type='text/javascript' src='coverage_html.js'></script>
- <script type='text/javascript'>
+ <script type="text/javascript" src="jquery.min.js"></script>
+ <script type="text/javascript" src="jquery.hotkeys.js"></script>
+ <script type="text/javascript" src="jquery.isonscreen.js"></script>
+ <script type="text/javascript" src="coverage_html.js"></script>
+ <script type="text/javascript">
jQuery(document).ready(coverage.pyfile_ready);
</script>
</head>
-<body class='pyfile'>
+<body class="pyfile">
-<div id='header'>
- <div class='content'>
- <h1>Coverage for <b>main</b> :
- <span class='pc_cov'>100%</span>
+<div id="header">
+ <div class="content">
+ <h1>Coverage for <b>main.py</b> :
+ <span class="pc_cov">100%</span>
</h1>
- <img id='keyboard_icon' src='keybd_closed.png' alt='Show keyboard shortcuts' />
+ <img id="keyboard_icon" src="keybd_closed.png" alt="Show keyboard shortcuts" />
- <h2 class='stats'>
+ <h2 class="stats">
8 statements &nbsp;
- <span class='run hide_run shortkey_r button_toggle_run'>8 run</span>
- <span class='mis shortkey_m button_toggle_mis'>0 missing</span>
- <span class='exc shortkey_x button_toggle_exc'>0 excluded</span>
+ <span class="run hide_run shortkey_r button_toggle_run">8 run</span>
+ <span class="mis shortkey_m button_toggle_mis">0 missing</span>
+ <span class="exc shortkey_x button_toggle_exc">0 excluded</span>
</h2>
</div>
</div>
-<div class='help_panel'>
- <img id='panel_icon' src='keybd_open.png' alt='Hide keyboard shortcuts' />
-<p class='legend'>Hot-keys on this page</p>
+<div class="help_panel">
+ <img id="panel_icon" src="keybd_open.png" alt="Hide keyboard shortcuts" />
+<p class="legend">Hot-keys on this page</p>
<div>
-<p class='keyhelp'>
- <span class='key'>r</span>
- <span class='key'>m</span>
- <span class='key'>x</span>
- <span class='key'>p</span> &nbsp; toggle line displays
+<p class="keyhelp">
+ <span class="key">r</span>
+ <span class="key">m</span>
+ <span class="key">x</span>
+ <span class="key">p</span> &nbsp; toggle line displays
</p>
-<p class='keyhelp'>
- <span class='key'>j</span>
- <span class='key'>k</span> &nbsp; next/prev highlighted chunk
+<p class="keyhelp">
+ <span class="key">j</span>
+ <span class="key">k</span> &nbsp; next/prev highlighted chunk
</p>
-<p class='keyhelp'>
- <span class='key'>0</span> &nbsp; (zero) top of page
+<p class="keyhelp">
+ <span class="key">0</span> &nbsp; (zero) top of page
</p>
-<p class='keyhelp'>
- <span class='key'>1</span> &nbsp; (one) first highlighted chunk
+<p class="keyhelp">
+ <span class="key">1</span> &nbsp; (one) first highlighted chunk
</p>
</div>
</div>
-<div id='source'>
+<div id="source">
<table>
<tr>
- <td class='linenos'>
-<p id='n1' class='stm run hide_run'><a href='#n1'>1</a></p>
-<p id='n2' class='stm run hide_run'><a href='#n2'>2</a></p>
-<p id='n3' class='stm run hide_run'><a href='#n3'>3</a></p>
-<p id='n4' class='pln'><a href='#n4'>4</a></p>
-<p id='n5' class='stm run hide_run'><a href='#n5'>5</a></p>
-<p id='n6' class='stm run hide_run'><a href='#n6'>6</a></p>
-<p id='n7' class='pln'><a href='#n7'>7</a></p>
-<p id='n8' class='stm run hide_run'><a href='#n8'>8</a></p>
-<p id='n9' class='stm run hide_run'><a href='#n9'>9</a></p>
-<p id='n10' class='stm run hide_run'><a href='#n10'>10</a></p>
+ <td class="linenos">
+<p id="n1" class="pln"><a href="#n1">1</a></p>
+<p id="n2" class="pln"><a href="#n2">2</a></p>
+<p id="n3" class="pln"><a href="#n3">3</a></p>
+<p id="n4" class="stm run hide_run"><a href="#n4">4</a></p>
+<p id="n5" class="stm run hide_run"><a href="#n5">5</a></p>
+<p id="n6" class="stm run hide_run"><a href="#n6">6</a></p>
+<p id="n7" class="pln"><a href="#n7">7</a></p>
+<p id="n8" class="stm run hide_run"><a href="#n8">8</a></p>
+<p id="n9" class="stm run hide_run"><a href="#n9">9</a></p>
+<p id="n10" class="pln"><a href="#n10">10</a></p>
+<p id="n11" class="stm run hide_run"><a href="#n11">11</a></p>
+<p id="n12" class="stm run hide_run"><a href="#n12">12</a></p>
+<p id="n13" class="stm run hide_run"><a href="#n13">13</a></p>
</td>
- <td class='text'>
-<p id='t1' class='stm run hide_run'><span class='key'>import</span> <span class='nam'>m1</span><span class='strut'>&nbsp;</span></p>
-<p id='t2' class='stm run hide_run'><span class='key'>import</span> <span class='nam'>m2</span><span class='strut'>&nbsp;</span></p>
-<p id='t3' class='stm run hide_run'><span class='key'>import</span> <span class='nam'>m3</span><span class='strut'>&nbsp;</span></p>
-<p id='t4' class='pln'><span class='strut'>&nbsp;</span></p>
-<p id='t5' class='stm run hide_run'><span class='nam'>a</span> <span class='op'>=</span> <span class='num'>5</span><span class='strut'>&nbsp;</span></p>
-<p id='t6' class='stm run hide_run'><span class='nam'>b</span> <span class='op'>=</span> <span class='num'>6</span><span class='strut'>&nbsp;</span></p>
-<p id='t7' class='pln'><span class='strut'>&nbsp;</span></p>
-<p id='t8' class='stm run hide_run'><span class='key'>assert</span> <span class='nam'>m1</span><span class='op'>.</span><span class='nam'>m1a</span> <span class='op'>==</span> <span class='num'>1</span><span class='strut'>&nbsp;</span></p>
-<p id='t9' class='stm run hide_run'><span class='key'>assert</span> <span class='nam'>m2</span><span class='op'>.</span><span class='nam'>m2a</span> <span class='op'>==</span> <span class='num'>1</span><span class='strut'>&nbsp;</span></p>
-<p id='t10' class='stm run hide_run'><span class='key'>assert</span> <span class='nam'>m3</span><span class='op'>.</span><span class='nam'>m3a</span> <span class='op'>==</span> <span class='num'>1</span><span class='strut'>&nbsp;</span></p>
+ <td class="text">
+<p id="t1" class="pln"><span class="com"># Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0</span><span class="strut">&nbsp;</span></p>
+<p id="t2" class="pln"><span class="com"># For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt</span><span class="strut">&nbsp;</span></p>
+<p id="t3" class="pln"><span class="strut">&nbsp;</span></p>
+<p id="t4" class="stm run hide_run"><span class="key">import</span> <span class="nam">m1</span><span class="strut">&nbsp;</span></p>
+<p id="t5" class="stm run hide_run"><span class="key">import</span> <span class="nam">m2</span><span class="strut">&nbsp;</span></p>
+<p id="t6" class="stm run hide_run"><span class="key">import</span> <span class="nam">m3</span><span class="strut">&nbsp;</span></p>
+<p id="t7" class="pln"><span class="strut">&nbsp;</span></p>
+<p id="t8" class="stm run hide_run"><span class="nam">a</span> <span class="op">=</span> <span class="num">5</span><span class="strut">&nbsp;</span></p>
+<p id="t9" class="stm run hide_run"><span class="nam">b</span> <span class="op">=</span> <span class="num">6</span><span class="strut">&nbsp;</span></p>
+<p id="t10" class="pln"><span class="strut">&nbsp;</span></p>
+<p id="t11" class="stm run hide_run"><span class="key">assert</span> <span class="nam">m1</span><span class="op">.</span><span class="nam">m1a</span> <span class="op">==</span> <span class="num">1</span><span class="strut">&nbsp;</span></p>
+<p id="t12" class="stm run hide_run"><span class="key">assert</span> <span class="nam">m2</span><span class="op">.</span><span class="nam">m2a</span> <span class="op">==</span> <span class="num">1</span><span class="strut">&nbsp;</span></p>
+<p id="t13" class="stm run hide_run"><span class="key">assert</span> <span class="nam">m3</span><span class="op">.</span><span class="nam">m3a</span> <span class="op">==</span> <span class="num">1</span><span class="strut">&nbsp;</span></p>
</td>
</tr>
</table>
</div>
-<div id='footer'>
- <div class='content'>
+<div id="footer">
+ <div class="content">
<p>
- <a class='nav' href='index.html'>&#xab; index</a> &nbsp; &nbsp; <a class='nav' href='http://nedbatchelder.com/code/coverage/4.0a1'>coverage.py v4.0a1</a>
+ <a class="nav" href="index.html">&#xab; index</a> &nbsp; &nbsp; <a class="nav" href="https://coverage.readthedocs.org/en/coverage-4.0a7">coverage.py v4.0a7</a>,
+ created at 2015-07-24 09:04
</p>
</div>
</div>
diff --git a/tests/farm/html/gold_omit_4/index.html b/tests/farm/html/gold_omit_4/index.html
index 130e2993..c0289fd5 100644
--- a/tests/farm/html/gold_omit_4/index.html
+++ b/tests/farm/html/gold_omit_4/index.html
@@ -1,28 +1,31 @@
+
+
+
<!DOCTYPE html>
<html>
<head>
- <meta http-equiv='Content-Type' content='text/html; charset=utf-8'>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>Coverage report</title>
- <link rel='stylesheet' href='style.css' type='text/css'>
+ <link rel="stylesheet" href="style.css" type="text/css">
- <script type='text/javascript' src='jquery.min.js'></script>
- <script type='text/javascript' src='jquery.debounce.min.js'></script>
- <script type='text/javascript' src='jquery.tablesorter.min.js'></script>
- <script type='text/javascript' src='jquery.hotkeys.js'></script>
- <script type='text/javascript' src='coverage_html.js'></script>
- <script type='text/javascript'>
+ <script type="text/javascript" src="jquery.min.js"></script>
+ <script type="text/javascript" src="jquery.debounce.min.js"></script>
+ <script type="text/javascript" src="jquery.tablesorter.min.js"></script>
+ <script type="text/javascript" src="jquery.hotkeys.js"></script>
+ <script type="text/javascript" src="coverage_html.js"></script>
+ <script type="text/javascript">
jQuery(document).ready(coverage.index_ready);
</script>
</head>
-<body class='indexfile'>
+<body class="indexfile">
-<div id='header'>
- <div class='content'>
+<div id="header">
+ <div class="content">
<h1>Coverage report:
- <span class='pc_cov'>100%</span>
+ <span class="pc_cov">100%</span>
</h1>
- <img id='keyboard_icon' src='keybd_closed.png' alt='Show keyboard shortcuts' />
+ <img id="keyboard_icon" src="keybd_closed.png" alt="Show keyboard shortcuts" />
<form id="filter_container">
<input id="filter" type="text" value="" placeholder="filter..." />
@@ -30,72 +33,72 @@
</div>
</div>
-<div class='help_panel'>
- <img id='panel_icon' src='keybd_open.png' alt='Hide keyboard shortcuts' />
- <p class='legend'>Hot-keys on this page</p>
+<div class="help_panel">
+ <img id="panel_icon" src="keybd_open.png" alt="Hide keyboard shortcuts" />
+ <p class="legend">Hot-keys on this page</p>
<div>
- <p class='keyhelp'>
- <span class='key'>n</span>
- <span class='key'>s</span>
- <span class='key'>m</span>
- <span class='key'>x</span>
+ <p class="keyhelp">
+ <span class="key">n</span>
+ <span class="key">s</span>
+ <span class="key">m</span>
+ <span class="key">x</span>
- <span class='key'>c</span> &nbsp; change column sorting
+ <span class="key">c</span> &nbsp; change column sorting
</p>
</div>
</div>
-<div id='index'>
- <table class='index'>
+<div id="index">
+ <table class="index">
<thead>
- <tr class='tablehead' title='Click to sort'>
- <th class='name left headerSortDown shortkey_n'>Module</th>
- <th class='shortkey_s'>statements</th>
- <th class='shortkey_m'>missing</th>
- <th class='shortkey_x'>excluded</th>
+ <tr class="tablehead" title="Click to sort">
+ <th class="name left headerSortDown shortkey_n">Module</th>
+ <th class="shortkey_s">statements</th>
+ <th class="shortkey_m">missing</th>
+ <th class="shortkey_x">excluded</th>
- <th class='right shortkey_c'>coverage</th>
+ <th class="right shortkey_c">coverage</th>
</tr>
</thead>
<tfoot>
- <tr class='total'>
- <td class='name left'>Total</td>
+ <tr class="total">
+ <td class="name left">Total</td>
<td>12</td>
<td>0</td>
<td>0</td>
- <td class='right' data-ratio='12 12'>100%</td>
+ <td class="right" data-ratio="12 12">100%</td>
</tr>
</tfoot>
<tbody>
- <tr class='file'>
- <td class='name left'><a href='m1.html'>m1</a></td>
+ <tr class="file">
+ <td class="name left"><a href="m1_py.html">m1.py</a></td>
<td>2</td>
<td>0</td>
<td>0</td>
- <td class='right' data-ratio='2 2'>100%</td>
+ <td class="right" data-ratio="2 2">100%</td>
</tr>
- <tr class='file'>
- <td class='name left'><a href='m3.html'>m3</a></td>
+ <tr class="file">
+ <td class="name left"><a href="m3_py.html">m3.py</a></td>
<td>2</td>
<td>0</td>
<td>0</td>
- <td class='right' data-ratio='2 2'>100%</td>
+ <td class="right" data-ratio="2 2">100%</td>
</tr>
- <tr class='file'>
- <td class='name left'><a href='main.html'>main</a></td>
+ <tr class="file">
+ <td class="name left"><a href="main_py.html">main.py</a></td>
<td>8</td>
<td>0</td>
<td>0</td>
- <td class='right' data-ratio='8 8'>100%</td>
+ <td class="right" data-ratio="8 8">100%</td>
</tr>
</tbody>
@@ -106,10 +109,11 @@
</p>
</div>
-<div id='footer'>
- <div class='content'>
+<div id="footer">
+ <div class="content">
<p>
- <a class='nav' href='http://nedbatchelder.com/code/coverage/4.0a1'>coverage.py v4.0a1</a>
+ <a class="nav" href="https://coverage.readthedocs.org/en/coverage-4.0a7">coverage.py v4.0a7</a>,
+ created at 2015-07-24 09:31
</p>
</div>
</div>
diff --git a/tests/farm/html/gold_omit_4/m1_py.html b/tests/farm/html/gold_omit_4/m1_py.html
index de44f789..8df0bf51 100644
--- a/tests/farm/html/gold_omit_4/m1_py.html
+++ b/tests/farm/html/gold_omit_4/m1_py.html
@@ -1,86 +1,96 @@
+
+
+
<!DOCTYPE html>
<html>
<head>
- <meta http-equiv='Content-Type' content='text/html; charset=utf-8'>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
- <meta http-equiv='X-UA-Compatible' content='IE=emulateIE7' />
- <title>Coverage for m1: 100%</title>
- <link rel='stylesheet' href='style.css' type='text/css'>
+ <meta http-equiv="X-UA-Compatible" content="IE=emulateIE7" />
+ <title>Coverage for m1.py: 100%</title>
+ <link rel="stylesheet" href="style.css" type="text/css">
- <script type='text/javascript' src='jquery.min.js'></script>
- <script type='text/javascript' src='jquery.hotkeys.js'></script>
- <script type='text/javascript' src='jquery.isonscreen.js'></script>
- <script type='text/javascript' src='coverage_html.js'></script>
- <script type='text/javascript'>
+ <script type="text/javascript" src="jquery.min.js"></script>
+ <script type="text/javascript" src="jquery.hotkeys.js"></script>
+ <script type="text/javascript" src="jquery.isonscreen.js"></script>
+ <script type="text/javascript" src="coverage_html.js"></script>
+ <script type="text/javascript">
jQuery(document).ready(coverage.pyfile_ready);
</script>
</head>
-<body class='pyfile'>
+<body class="pyfile">
-<div id='header'>
- <div class='content'>
- <h1>Coverage for <b>m1</b> :
- <span class='pc_cov'>100%</span>
+<div id="header">
+ <div class="content">
+ <h1>Coverage for <b>m1.py</b> :
+ <span class="pc_cov">100%</span>
</h1>
- <img id='keyboard_icon' src='keybd_closed.png' alt='Show keyboard shortcuts' />
+ <img id="keyboard_icon" src="keybd_closed.png" alt="Show keyboard shortcuts" />
- <h2 class='stats'>
+ <h2 class="stats">
2 statements &nbsp;
- <span class='run hide_run shortkey_r button_toggle_run'>2 run</span>
- <span class='mis shortkey_m button_toggle_mis'>0 missing</span>
- <span class='exc shortkey_x button_toggle_exc'>0 excluded</span>
+ <span class="run hide_run shortkey_r button_toggle_run">2 run</span>
+ <span class="mis shortkey_m button_toggle_mis">0 missing</span>
+ <span class="exc shortkey_x button_toggle_exc">0 excluded</span>
</h2>
</div>
</div>
-<div class='help_panel'>
- <img id='panel_icon' src='keybd_open.png' alt='Hide keyboard shortcuts' />
-<p class='legend'>Hot-keys on this page</p>
+<div class="help_panel">
+ <img id="panel_icon" src="keybd_open.png" alt="Hide keyboard shortcuts" />
+<p class="legend">Hot-keys on this page</p>
<div>
-<p class='keyhelp'>
- <span class='key'>r</span>
- <span class='key'>m</span>
- <span class='key'>x</span>
- <span class='key'>p</span> &nbsp; toggle line displays
+<p class="keyhelp">
+ <span class="key">r</span>
+ <span class="key">m</span>
+ <span class="key">x</span>
+ <span class="key">p</span> &nbsp; toggle line displays
</p>
-<p class='keyhelp'>
- <span class='key'>j</span>
- <span class='key'>k</span> &nbsp; next/prev highlighted chunk
+<p class="keyhelp">
+ <span class="key">j</span>
+ <span class="key">k</span> &nbsp; next/prev highlighted chunk
</p>
-<p class='keyhelp'>
- <span class='key'>0</span> &nbsp; (zero) top of page
+<p class="keyhelp">
+ <span class="key">0</span> &nbsp; (zero) top of page
</p>
-<p class='keyhelp'>
- <span class='key'>1</span> &nbsp; (one) first highlighted chunk
+<p class="keyhelp">
+ <span class="key">1</span> &nbsp; (one) first highlighted chunk
</p>
</div>
</div>
-<div id='source'>
+<div id="source">
<table>
<tr>
- <td class='linenos'>
-<p id='n1' class='stm run hide_run'><a href='#n1'>1</a></p>
-<p id='n2' class='stm run hide_run'><a href='#n2'>2</a></p>
+ <td class="linenos">
+<p id="n1" class="pln"><a href="#n1">1</a></p>
+<p id="n2" class="pln"><a href="#n2">2</a></p>
+<p id="n3" class="pln"><a href="#n3">3</a></p>
+<p id="n4" class="stm run hide_run"><a href="#n4">4</a></p>
+<p id="n5" class="stm run hide_run"><a href="#n5">5</a></p>
</td>
- <td class='text'>
-<p id='t1' class='stm run hide_run'><span class='nam'>m1a</span> <span class='op'>=</span> <span class='num'>1</span><span class='strut'>&nbsp;</span></p>
-<p id='t2' class='stm run hide_run'><span class='nam'>m1b</span> <span class='op'>=</span> <span class='num'>2</span><span class='strut'>&nbsp;</span></p>
+ <td class="text">
+<p id="t1" class="pln"><span class="com"># Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0</span><span class="strut">&nbsp;</span></p>
+<p id="t2" class="pln"><span class="com"># For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt</span><span class="strut">&nbsp;</span></p>
+<p id="t3" class="pln"><span class="strut">&nbsp;</span></p>
+<p id="t4" class="stm run hide_run"><span class="nam">m1a</span> <span class="op">=</span> <span class="num">1</span><span class="strut">&nbsp;</span></p>
+<p id="t5" class="stm run hide_run"><span class="nam">m1b</span> <span class="op">=</span> <span class="num">2</span><span class="strut">&nbsp;</span></p>
</td>
</tr>
</table>
</div>
-<div id='footer'>
- <div class='content'>
+<div id="footer">
+ <div class="content">
<p>
- <a class='nav' href='index.html'>&#xab; index</a> &nbsp; &nbsp; <a class='nav' href='http://nedbatchelder.com/code/coverage/4.0a1'>coverage.py v4.0a1</a>
+ <a class="nav" href="index.html">&#xab; index</a> &nbsp; &nbsp; <a class="nav" href="https://coverage.readthedocs.org/en/coverage-4.0a7">coverage.py v4.0a7</a>,
+ created at 2015-07-24 09:04
</p>
</div>
</div>
diff --git a/tests/farm/html/gold_omit_4/m3_py.html b/tests/farm/html/gold_omit_4/m3_py.html
index 67354c48..b372a96e 100644
--- a/tests/farm/html/gold_omit_4/m3_py.html
+++ b/tests/farm/html/gold_omit_4/m3_py.html
@@ -1,86 +1,96 @@
+
+
+
<!DOCTYPE html>
<html>
<head>
- <meta http-equiv='Content-Type' content='text/html; charset=utf-8'>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
- <meta http-equiv='X-UA-Compatible' content='IE=emulateIE7' />
- <title>Coverage for m3: 100%</title>
- <link rel='stylesheet' href='style.css' type='text/css'>
+ <meta http-equiv="X-UA-Compatible" content="IE=emulateIE7" />
+ <title>Coverage for m3.py: 100%</title>
+ <link rel="stylesheet" href="style.css" type="text/css">
- <script type='text/javascript' src='jquery.min.js'></script>
- <script type='text/javascript' src='jquery.hotkeys.js'></script>
- <script type='text/javascript' src='jquery.isonscreen.js'></script>
- <script type='text/javascript' src='coverage_html.js'></script>
- <script type='text/javascript'>
+ <script type="text/javascript" src="jquery.min.js"></script>
+ <script type="text/javascript" src="jquery.hotkeys.js"></script>
+ <script type="text/javascript" src="jquery.isonscreen.js"></script>
+ <script type="text/javascript" src="coverage_html.js"></script>
+ <script type="text/javascript">
jQuery(document).ready(coverage.pyfile_ready);
</script>
</head>
-<body class='pyfile'>
+<body class="pyfile">
-<div id='header'>
- <div class='content'>
- <h1>Coverage for <b>m3</b> :
- <span class='pc_cov'>100%</span>
+<div id="header">
+ <div class="content">
+ <h1>Coverage for <b>m3.py</b> :
+ <span class="pc_cov">100%</span>
</h1>
- <img id='keyboard_icon' src='keybd_closed.png' alt='Show keyboard shortcuts' />
+ <img id="keyboard_icon" src="keybd_closed.png" alt="Show keyboard shortcuts" />
- <h2 class='stats'>
+ <h2 class="stats">
2 statements &nbsp;
- <span class='run hide_run shortkey_r button_toggle_run'>2 run</span>
- <span class='mis shortkey_m button_toggle_mis'>0 missing</span>
- <span class='exc shortkey_x button_toggle_exc'>0 excluded</span>
+ <span class="run hide_run shortkey_r button_toggle_run">2 run</span>
+ <span class="mis shortkey_m button_toggle_mis">0 missing</span>
+ <span class="exc shortkey_x button_toggle_exc">0 excluded</span>
</h2>
</div>
</div>
-<div class='help_panel'>
- <img id='panel_icon' src='keybd_open.png' alt='Hide keyboard shortcuts' />
-<p class='legend'>Hot-keys on this page</p>
+<div class="help_panel">
+ <img id="panel_icon" src="keybd_open.png" alt="Hide keyboard shortcuts" />
+<p class="legend">Hot-keys on this page</p>
<div>
-<p class='keyhelp'>
- <span class='key'>r</span>
- <span class='key'>m</span>
- <span class='key'>x</span>
- <span class='key'>p</span> &nbsp; toggle line displays
+<p class="keyhelp">
+ <span class="key">r</span>
+ <span class="key">m</span>
+ <span class="key">x</span>
+ <span class="key">p</span> &nbsp; toggle line displays
</p>
-<p class='keyhelp'>
- <span class='key'>j</span>
- <span class='key'>k</span> &nbsp; next/prev highlighted chunk
+<p class="keyhelp">
+ <span class="key">j</span>
+ <span class="key">k</span> &nbsp; next/prev highlighted chunk
</p>
-<p class='keyhelp'>
- <span class='key'>0</span> &nbsp; (zero) top of page
+<p class="keyhelp">
+ <span class="key">0</span> &nbsp; (zero) top of page
</p>
-<p class='keyhelp'>
- <span class='key'>1</span> &nbsp; (one) first highlighted chunk
+<p class="keyhelp">
+ <span class="key">1</span> &nbsp; (one) first highlighted chunk
</p>
</div>
</div>
-<div id='source'>
+<div id="source">
<table>
<tr>
- <td class='linenos'>
-<p id='n1' class='stm run hide_run'><a href='#n1'>1</a></p>
-<p id='n2' class='stm run hide_run'><a href='#n2'>2</a></p>
+ <td class="linenos">
+<p id="n1" class="pln"><a href="#n1">1</a></p>
+<p id="n2" class="pln"><a href="#n2">2</a></p>
+<p id="n3" class="pln"><a href="#n3">3</a></p>
+<p id="n4" class="stm run hide_run"><a href="#n4">4</a></p>
+<p id="n5" class="stm run hide_run"><a href="#n5">5</a></p>
</td>
- <td class='text'>
-<p id='t1' class='stm run hide_run'><span class='nam'>m3a</span> <span class='op'>=</span> <span class='num'>1</span><span class='strut'>&nbsp;</span></p>
-<p id='t2' class='stm run hide_run'><span class='nam'>m3b</span> <span class='op'>=</span> <span class='num'>2</span><span class='strut'>&nbsp;</span></p>
+ <td class="text">
+<p id="t1" class="pln"><span class="com"># Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0</span><span class="strut">&nbsp;</span></p>
+<p id="t2" class="pln"><span class="com"># For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt</span><span class="strut">&nbsp;</span></p>
+<p id="t3" class="pln"><span class="strut">&nbsp;</span></p>
+<p id="t4" class="stm run hide_run"><span class="nam">m3a</span> <span class="op">=</span> <span class="num">1</span><span class="strut">&nbsp;</span></p>
+<p id="t5" class="stm run hide_run"><span class="nam">m3b</span> <span class="op">=</span> <span class="num">2</span><span class="strut">&nbsp;</span></p>
</td>
</tr>
</table>
</div>
-<div id='footer'>
- <div class='content'>
+<div id="footer">
+ <div class="content">
<p>
- <a class='nav' href='index.html'>&#xab; index</a> &nbsp; &nbsp; <a class='nav' href='http://nedbatchelder.com/code/coverage/4.0a1'>coverage.py v4.0a1</a>
+ <a class="nav" href="index.html">&#xab; index</a> &nbsp; &nbsp; <a class="nav" href="https://coverage.readthedocs.org/en/coverage-4.0a7">coverage.py v4.0a7</a>,
+ created at 2015-07-24 09:04
</p>
</div>
</div>
diff --git a/tests/farm/html/gold_omit_4/main_py.html b/tests/farm/html/gold_omit_4/main_py.html
index a71b10e4..ba6e9680 100644
--- a/tests/farm/html/gold_omit_4/main_py.html
+++ b/tests/farm/html/gold_omit_4/main_py.html
@@ -1,102 +1,112 @@
+
+
+
<!DOCTYPE html>
<html>
<head>
- <meta http-equiv='Content-Type' content='text/html; charset=utf-8'>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
- <meta http-equiv='X-UA-Compatible' content='IE=emulateIE7' />
- <title>Coverage for main: 100%</title>
- <link rel='stylesheet' href='style.css' type='text/css'>
+ <meta http-equiv="X-UA-Compatible" content="IE=emulateIE7" />
+ <title>Coverage for main.py: 100%</title>
+ <link rel="stylesheet" href="style.css" type="text/css">
- <script type='text/javascript' src='jquery.min.js'></script>
- <script type='text/javascript' src='jquery.hotkeys.js'></script>
- <script type='text/javascript' src='jquery.isonscreen.js'></script>
- <script type='text/javascript' src='coverage_html.js'></script>
- <script type='text/javascript'>
+ <script type="text/javascript" src="jquery.min.js"></script>
+ <script type="text/javascript" src="jquery.hotkeys.js"></script>
+ <script type="text/javascript" src="jquery.isonscreen.js"></script>
+ <script type="text/javascript" src="coverage_html.js"></script>
+ <script type="text/javascript">
jQuery(document).ready(coverage.pyfile_ready);
</script>
</head>
-<body class='pyfile'>
+<body class="pyfile">
-<div id='header'>
- <div class='content'>
- <h1>Coverage for <b>main</b> :
- <span class='pc_cov'>100%</span>
+<div id="header">
+ <div class="content">
+ <h1>Coverage for <b>main.py</b> :
+ <span class="pc_cov">100%</span>
</h1>
- <img id='keyboard_icon' src='keybd_closed.png' alt='Show keyboard shortcuts' />
+ <img id="keyboard_icon" src="keybd_closed.png" alt="Show keyboard shortcuts" />
- <h2 class='stats'>
+ <h2 class="stats">
8 statements &nbsp;
- <span class='run hide_run shortkey_r button_toggle_run'>8 run</span>
- <span class='mis shortkey_m button_toggle_mis'>0 missing</span>
- <span class='exc shortkey_x button_toggle_exc'>0 excluded</span>
+ <span class="run hide_run shortkey_r button_toggle_run">8 run</span>
+ <span class="mis shortkey_m button_toggle_mis">0 missing</span>
+ <span class="exc shortkey_x button_toggle_exc">0 excluded</span>
</h2>
</div>
</div>
-<div class='help_panel'>
- <img id='panel_icon' src='keybd_open.png' alt='Hide keyboard shortcuts' />
-<p class='legend'>Hot-keys on this page</p>
+<div class="help_panel">
+ <img id="panel_icon" src="keybd_open.png" alt="Hide keyboard shortcuts" />
+<p class="legend">Hot-keys on this page</p>
<div>
-<p class='keyhelp'>
- <span class='key'>r</span>
- <span class='key'>m</span>
- <span class='key'>x</span>
- <span class='key'>p</span> &nbsp; toggle line displays
+<p class="keyhelp">
+ <span class="key">r</span>
+ <span class="key">m</span>
+ <span class="key">x</span>
+ <span class="key">p</span> &nbsp; toggle line displays
</p>
-<p class='keyhelp'>
- <span class='key'>j</span>
- <span class='key'>k</span> &nbsp; next/prev highlighted chunk
+<p class="keyhelp">
+ <span class="key">j</span>
+ <span class="key">k</span> &nbsp; next/prev highlighted chunk
</p>
-<p class='keyhelp'>
- <span class='key'>0</span> &nbsp; (zero) top of page
+<p class="keyhelp">
+ <span class="key">0</span> &nbsp; (zero) top of page
</p>
-<p class='keyhelp'>
- <span class='key'>1</span> &nbsp; (one) first highlighted chunk
+<p class="keyhelp">
+ <span class="key">1</span> &nbsp; (one) first highlighted chunk
</p>
</div>
</div>
-<div id='source'>
+<div id="source">
<table>
<tr>
- <td class='linenos'>
-<p id='n1' class='stm run hide_run'><a href='#n1'>1</a></p>
-<p id='n2' class='stm run hide_run'><a href='#n2'>2</a></p>
-<p id='n3' class='stm run hide_run'><a href='#n3'>3</a></p>
-<p id='n4' class='pln'><a href='#n4'>4</a></p>
-<p id='n5' class='stm run hide_run'><a href='#n5'>5</a></p>
-<p id='n6' class='stm run hide_run'><a href='#n6'>6</a></p>
-<p id='n7' class='pln'><a href='#n7'>7</a></p>
-<p id='n8' class='stm run hide_run'><a href='#n8'>8</a></p>
-<p id='n9' class='stm run hide_run'><a href='#n9'>9</a></p>
-<p id='n10' class='stm run hide_run'><a href='#n10'>10</a></p>
+ <td class="linenos">
+<p id="n1" class="pln"><a href="#n1">1</a></p>
+<p id="n2" class="pln"><a href="#n2">2</a></p>
+<p id="n3" class="pln"><a href="#n3">3</a></p>
+<p id="n4" class="stm run hide_run"><a href="#n4">4</a></p>
+<p id="n5" class="stm run hide_run"><a href="#n5">5</a></p>
+<p id="n6" class="stm run hide_run"><a href="#n6">6</a></p>
+<p id="n7" class="pln"><a href="#n7">7</a></p>
+<p id="n8" class="stm run hide_run"><a href="#n8">8</a></p>
+<p id="n9" class="stm run hide_run"><a href="#n9">9</a></p>
+<p id="n10" class="pln"><a href="#n10">10</a></p>
+<p id="n11" class="stm run hide_run"><a href="#n11">11</a></p>
+<p id="n12" class="stm run hide_run"><a href="#n12">12</a></p>
+<p id="n13" class="stm run hide_run"><a href="#n13">13</a></p>
</td>
- <td class='text'>
-<p id='t1' class='stm run hide_run'><span class='key'>import</span> <span class='nam'>m1</span><span class='strut'>&nbsp;</span></p>
-<p id='t2' class='stm run hide_run'><span class='key'>import</span> <span class='nam'>m2</span><span class='strut'>&nbsp;</span></p>
-<p id='t3' class='stm run hide_run'><span class='key'>import</span> <span class='nam'>m3</span><span class='strut'>&nbsp;</span></p>
-<p id='t4' class='pln'><span class='strut'>&nbsp;</span></p>
-<p id='t5' class='stm run hide_run'><span class='nam'>a</span> <span class='op'>=</span> <span class='num'>5</span><span class='strut'>&nbsp;</span></p>
-<p id='t6' class='stm run hide_run'><span class='nam'>b</span> <span class='op'>=</span> <span class='num'>6</span><span class='strut'>&nbsp;</span></p>
-<p id='t7' class='pln'><span class='strut'>&nbsp;</span></p>
-<p id='t8' class='stm run hide_run'><span class='key'>assert</span> <span class='nam'>m1</span><span class='op'>.</span><span class='nam'>m1a</span> <span class='op'>==</span> <span class='num'>1</span><span class='strut'>&nbsp;</span></p>
-<p id='t9' class='stm run hide_run'><span class='key'>assert</span> <span class='nam'>m2</span><span class='op'>.</span><span class='nam'>m2a</span> <span class='op'>==</span> <span class='num'>1</span><span class='strut'>&nbsp;</span></p>
-<p id='t10' class='stm run hide_run'><span class='key'>assert</span> <span class='nam'>m3</span><span class='op'>.</span><span class='nam'>m3a</span> <span class='op'>==</span> <span class='num'>1</span><span class='strut'>&nbsp;</span></p>
+ <td class="text">
+<p id="t1" class="pln"><span class="com"># Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0</span><span class="strut">&nbsp;</span></p>
+<p id="t2" class="pln"><span class="com"># For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt</span><span class="strut">&nbsp;</span></p>
+<p id="t3" class="pln"><span class="strut">&nbsp;</span></p>
+<p id="t4" class="stm run hide_run"><span class="key">import</span> <span class="nam">m1</span><span class="strut">&nbsp;</span></p>
+<p id="t5" class="stm run hide_run"><span class="key">import</span> <span class="nam">m2</span><span class="strut">&nbsp;</span></p>
+<p id="t6" class="stm run hide_run"><span class="key">import</span> <span class="nam">m3</span><span class="strut">&nbsp;</span></p>
+<p id="t7" class="pln"><span class="strut">&nbsp;</span></p>
+<p id="t8" class="stm run hide_run"><span class="nam">a</span> <span class="op">=</span> <span class="num">5</span><span class="strut">&nbsp;</span></p>
+<p id="t9" class="stm run hide_run"><span class="nam">b</span> <span class="op">=</span> <span class="num">6</span><span class="strut">&nbsp;</span></p>
+<p id="t10" class="pln"><span class="strut">&nbsp;</span></p>
+<p id="t11" class="stm run hide_run"><span class="key">assert</span> <span class="nam">m1</span><span class="op">.</span><span class="nam">m1a</span> <span class="op">==</span> <span class="num">1</span><span class="strut">&nbsp;</span></p>
+<p id="t12" class="stm run hide_run"><span class="key">assert</span> <span class="nam">m2</span><span class="op">.</span><span class="nam">m2a</span> <span class="op">==</span> <span class="num">1</span><span class="strut">&nbsp;</span></p>
+<p id="t13" class="stm run hide_run"><span class="key">assert</span> <span class="nam">m3</span><span class="op">.</span><span class="nam">m3a</span> <span class="op">==</span> <span class="num">1</span><span class="strut">&nbsp;</span></p>
</td>
</tr>
</table>
</div>
-<div id='footer'>
- <div class='content'>
+<div id="footer">
+ <div class="content">
<p>
- <a class='nav' href='index.html'>&#xab; index</a> &nbsp; &nbsp; <a class='nav' href='http://nedbatchelder.com/code/coverage/4.0a1'>coverage.py v4.0a1</a>
+ <a class="nav" href="index.html">&#xab; index</a> &nbsp; &nbsp; <a class="nav" href="https://coverage.readthedocs.org/en/coverage-4.0a7">coverage.py v4.0a7</a>,
+ created at 2015-07-24 09:04
</p>
</div>
</div>
diff --git a/tests/farm/html/gold_omit_5/index.html b/tests/farm/html/gold_omit_5/index.html
index 66306a29..5e9a2413 100644
--- a/tests/farm/html/gold_omit_5/index.html
+++ b/tests/farm/html/gold_omit_5/index.html
@@ -1,28 +1,31 @@
+
+
+
<!DOCTYPE html>
<html>
<head>
- <meta http-equiv='Content-Type' content='text/html; charset=utf-8'>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>Coverage report</title>
- <link rel='stylesheet' href='style.css' type='text/css'>
+ <link rel="stylesheet" href="style.css" type="text/css">
- <script type='text/javascript' src='jquery.min.js'></script>
- <script type='text/javascript' src='jquery.debounce.min.js'></script>
- <script type='text/javascript' src='jquery.tablesorter.min.js'></script>
- <script type='text/javascript' src='jquery.hotkeys.js'></script>
- <script type='text/javascript' src='coverage_html.js'></script>
- <script type='text/javascript'>
+ <script type="text/javascript" src="jquery.min.js"></script>
+ <script type="text/javascript" src="jquery.debounce.min.js"></script>
+ <script type="text/javascript" src="jquery.tablesorter.min.js"></script>
+ <script type="text/javascript" src="jquery.hotkeys.js"></script>
+ <script type="text/javascript" src="coverage_html.js"></script>
+ <script type="text/javascript">
jQuery(document).ready(coverage.index_ready);
</script>
</head>
-<body class='indexfile'>
+<body class="indexfile">
-<div id='header'>
- <div class='content'>
+<div id="header">
+ <div class="content">
<h1>Coverage report:
- <span class='pc_cov'>100%</span>
+ <span class="pc_cov">100%</span>
</h1>
- <img id='keyboard_icon' src='keybd_closed.png' alt='Show keyboard shortcuts' />
+ <img id="keyboard_icon" src="keybd_closed.png" alt="Show keyboard shortcuts" />
<form id="filter_container">
<input id="filter" type="text" value="" placeholder="filter..." />
@@ -30,63 +33,63 @@
</div>
</div>
-<div class='help_panel'>
- <img id='panel_icon' src='keybd_open.png' alt='Hide keyboard shortcuts' />
- <p class='legend'>Hot-keys on this page</p>
+<div class="help_panel">
+ <img id="panel_icon" src="keybd_open.png" alt="Hide keyboard shortcuts" />
+ <p class="legend">Hot-keys on this page</p>
<div>
- <p class='keyhelp'>
- <span class='key'>n</span>
- <span class='key'>s</span>
- <span class='key'>m</span>
- <span class='key'>x</span>
+ <p class="keyhelp">
+ <span class="key">n</span>
+ <span class="key">s</span>
+ <span class="key">m</span>
+ <span class="key">x</span>
- <span class='key'>c</span> &nbsp; change column sorting
+ <span class="key">c</span> &nbsp; change column sorting
</p>
</div>
</div>
-<div id='index'>
- <table class='index'>
+<div id="index">
+ <table class="index">
<thead>
- <tr class='tablehead' title='Click to sort'>
- <th class='name left headerSortDown shortkey_n'>Module</th>
- <th class='shortkey_s'>statements</th>
- <th class='shortkey_m'>missing</th>
- <th class='shortkey_x'>excluded</th>
+ <tr class="tablehead" title="Click to sort">
+ <th class="name left headerSortDown shortkey_n">Module</th>
+ <th class="shortkey_s">statements</th>
+ <th class="shortkey_m">missing</th>
+ <th class="shortkey_x">excluded</th>
- <th class='right shortkey_c'>coverage</th>
+ <th class="right shortkey_c">coverage</th>
</tr>
</thead>
<tfoot>
- <tr class='total'>
- <td class='name left'>Total</td>
+ <tr class="total">
+ <td class="name left">Total</td>
<td>10</td>
<td>0</td>
<td>0</td>
- <td class='right' data-ratio='10 10'>100%</td>
+ <td class="right" data-ratio="10 10">100%</td>
</tr>
</tfoot>
<tbody>
- <tr class='file'>
- <td class='name left'><a href='m1.html'>m1</a></td>
+ <tr class="file">
+ <td class="name left"><a href="m1_py.html">m1.py</a></td>
<td>2</td>
<td>0</td>
<td>0</td>
- <td class='right' data-ratio='2 2'>100%</td>
+ <td class="right" data-ratio="2 2">100%</td>
</tr>
- <tr class='file'>
- <td class='name left'><a href='main.html'>main</a></td>
+ <tr class="file">
+ <td class="name left"><a href="main_py.html">main.py</a></td>
<td>8</td>
<td>0</td>
<td>0</td>
- <td class='right' data-ratio='8 8'>100%</td>
+ <td class="right" data-ratio="8 8">100%</td>
</tr>
</tbody>
@@ -97,10 +100,11 @@
</p>
</div>
-<div id='footer'>
- <div class='content'>
+<div id="footer">
+ <div class="content">
<p>
- <a class='nav' href='http://nedbatchelder.com/code/coverage/4.0a1'>coverage.py v4.0a1</a>
+ <a class="nav" href="https://coverage.readthedocs.org/en/coverage-4.0a7">coverage.py v4.0a7</a>,
+ created at 2015-07-24 09:31
</p>
</div>
</div>
diff --git a/tests/farm/html/gold_omit_5/m1_py.html b/tests/farm/html/gold_omit_5/m1_py.html
index de44f789..8df0bf51 100644
--- a/tests/farm/html/gold_omit_5/m1_py.html
+++ b/tests/farm/html/gold_omit_5/m1_py.html
@@ -1,86 +1,96 @@
+
+
+
<!DOCTYPE html>
<html>
<head>
- <meta http-equiv='Content-Type' content='text/html; charset=utf-8'>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
- <meta http-equiv='X-UA-Compatible' content='IE=emulateIE7' />
- <title>Coverage for m1: 100%</title>
- <link rel='stylesheet' href='style.css' type='text/css'>
+ <meta http-equiv="X-UA-Compatible" content="IE=emulateIE7" />
+ <title>Coverage for m1.py: 100%</title>
+ <link rel="stylesheet" href="style.css" type="text/css">
- <script type='text/javascript' src='jquery.min.js'></script>
- <script type='text/javascript' src='jquery.hotkeys.js'></script>
- <script type='text/javascript' src='jquery.isonscreen.js'></script>
- <script type='text/javascript' src='coverage_html.js'></script>
- <script type='text/javascript'>
+ <script type="text/javascript" src="jquery.min.js"></script>
+ <script type="text/javascript" src="jquery.hotkeys.js"></script>
+ <script type="text/javascript" src="jquery.isonscreen.js"></script>
+ <script type="text/javascript" src="coverage_html.js"></script>
+ <script type="text/javascript">
jQuery(document).ready(coverage.pyfile_ready);
</script>
</head>
-<body class='pyfile'>
+<body class="pyfile">
-<div id='header'>
- <div class='content'>
- <h1>Coverage for <b>m1</b> :
- <span class='pc_cov'>100%</span>
+<div id="header">
+ <div class="content">
+ <h1>Coverage for <b>m1.py</b> :
+ <span class="pc_cov">100%</span>
</h1>
- <img id='keyboard_icon' src='keybd_closed.png' alt='Show keyboard shortcuts' />
+ <img id="keyboard_icon" src="keybd_closed.png" alt="Show keyboard shortcuts" />
- <h2 class='stats'>
+ <h2 class="stats">
2 statements &nbsp;
- <span class='run hide_run shortkey_r button_toggle_run'>2 run</span>
- <span class='mis shortkey_m button_toggle_mis'>0 missing</span>
- <span class='exc shortkey_x button_toggle_exc'>0 excluded</span>
+ <span class="run hide_run shortkey_r button_toggle_run">2 run</span>
+ <span class="mis shortkey_m button_toggle_mis">0 missing</span>
+ <span class="exc shortkey_x button_toggle_exc">0 excluded</span>
</h2>
</div>
</div>
-<div class='help_panel'>
- <img id='panel_icon' src='keybd_open.png' alt='Hide keyboard shortcuts' />
-<p class='legend'>Hot-keys on this page</p>
+<div class="help_panel">
+ <img id="panel_icon" src="keybd_open.png" alt="Hide keyboard shortcuts" />
+<p class="legend">Hot-keys on this page</p>
<div>
-<p class='keyhelp'>
- <span class='key'>r</span>
- <span class='key'>m</span>
- <span class='key'>x</span>
- <span class='key'>p</span> &nbsp; toggle line displays
+<p class="keyhelp">
+ <span class="key">r</span>
+ <span class="key">m</span>
+ <span class="key">x</span>
+ <span class="key">p</span> &nbsp; toggle line displays
</p>
-<p class='keyhelp'>
- <span class='key'>j</span>
- <span class='key'>k</span> &nbsp; next/prev highlighted chunk
+<p class="keyhelp">
+ <span class="key">j</span>
+ <span class="key">k</span> &nbsp; next/prev highlighted chunk
</p>
-<p class='keyhelp'>
- <span class='key'>0</span> &nbsp; (zero) top of page
+<p class="keyhelp">
+ <span class="key">0</span> &nbsp; (zero) top of page
</p>
-<p class='keyhelp'>
- <span class='key'>1</span> &nbsp; (one) first highlighted chunk
+<p class="keyhelp">
+ <span class="key">1</span> &nbsp; (one) first highlighted chunk
</p>
</div>
</div>
-<div id='source'>
+<div id="source">
<table>
<tr>
- <td class='linenos'>
-<p id='n1' class='stm run hide_run'><a href='#n1'>1</a></p>
-<p id='n2' class='stm run hide_run'><a href='#n2'>2</a></p>
+ <td class="linenos">
+<p id="n1" class="pln"><a href="#n1">1</a></p>
+<p id="n2" class="pln"><a href="#n2">2</a></p>
+<p id="n3" class="pln"><a href="#n3">3</a></p>
+<p id="n4" class="stm run hide_run"><a href="#n4">4</a></p>
+<p id="n5" class="stm run hide_run"><a href="#n5">5</a></p>
</td>
- <td class='text'>
-<p id='t1' class='stm run hide_run'><span class='nam'>m1a</span> <span class='op'>=</span> <span class='num'>1</span><span class='strut'>&nbsp;</span></p>
-<p id='t2' class='stm run hide_run'><span class='nam'>m1b</span> <span class='op'>=</span> <span class='num'>2</span><span class='strut'>&nbsp;</span></p>
+ <td class="text">
+<p id="t1" class="pln"><span class="com"># Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0</span><span class="strut">&nbsp;</span></p>
+<p id="t2" class="pln"><span class="com"># For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt</span><span class="strut">&nbsp;</span></p>
+<p id="t3" class="pln"><span class="strut">&nbsp;</span></p>
+<p id="t4" class="stm run hide_run"><span class="nam">m1a</span> <span class="op">=</span> <span class="num">1</span><span class="strut">&nbsp;</span></p>
+<p id="t5" class="stm run hide_run"><span class="nam">m1b</span> <span class="op">=</span> <span class="num">2</span><span class="strut">&nbsp;</span></p>
</td>
</tr>
</table>
</div>
-<div id='footer'>
- <div class='content'>
+<div id="footer">
+ <div class="content">
<p>
- <a class='nav' href='index.html'>&#xab; index</a> &nbsp; &nbsp; <a class='nav' href='http://nedbatchelder.com/code/coverage/4.0a1'>coverage.py v4.0a1</a>
+ <a class="nav" href="index.html">&#xab; index</a> &nbsp; &nbsp; <a class="nav" href="https://coverage.readthedocs.org/en/coverage-4.0a7">coverage.py v4.0a7</a>,
+ created at 2015-07-24 09:04
</p>
</div>
</div>
diff --git a/tests/farm/html/gold_omit_5/main_py.html b/tests/farm/html/gold_omit_5/main_py.html
index a71b10e4..ba6e9680 100644
--- a/tests/farm/html/gold_omit_5/main_py.html
+++ b/tests/farm/html/gold_omit_5/main_py.html
@@ -1,102 +1,112 @@
+
+
+
<!DOCTYPE html>
<html>
<head>
- <meta http-equiv='Content-Type' content='text/html; charset=utf-8'>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
- <meta http-equiv='X-UA-Compatible' content='IE=emulateIE7' />
- <title>Coverage for main: 100%</title>
- <link rel='stylesheet' href='style.css' type='text/css'>
+ <meta http-equiv="X-UA-Compatible" content="IE=emulateIE7" />
+ <title>Coverage for main.py: 100%</title>
+ <link rel="stylesheet" href="style.css" type="text/css">
- <script type='text/javascript' src='jquery.min.js'></script>
- <script type='text/javascript' src='jquery.hotkeys.js'></script>
- <script type='text/javascript' src='jquery.isonscreen.js'></script>
- <script type='text/javascript' src='coverage_html.js'></script>
- <script type='text/javascript'>
+ <script type="text/javascript" src="jquery.min.js"></script>
+ <script type="text/javascript" src="jquery.hotkeys.js"></script>
+ <script type="text/javascript" src="jquery.isonscreen.js"></script>
+ <script type="text/javascript" src="coverage_html.js"></script>
+ <script type="text/javascript">
jQuery(document).ready(coverage.pyfile_ready);
</script>
</head>
-<body class='pyfile'>
+<body class="pyfile">
-<div id='header'>
- <div class='content'>
- <h1>Coverage for <b>main</b> :
- <span class='pc_cov'>100%</span>
+<div id="header">
+ <div class="content">
+ <h1>Coverage for <b>main.py</b> :
+ <span class="pc_cov">100%</span>
</h1>
- <img id='keyboard_icon' src='keybd_closed.png' alt='Show keyboard shortcuts' />
+ <img id="keyboard_icon" src="keybd_closed.png" alt="Show keyboard shortcuts" />
- <h2 class='stats'>
+ <h2 class="stats">
8 statements &nbsp;
- <span class='run hide_run shortkey_r button_toggle_run'>8 run</span>
- <span class='mis shortkey_m button_toggle_mis'>0 missing</span>
- <span class='exc shortkey_x button_toggle_exc'>0 excluded</span>
+ <span class="run hide_run shortkey_r button_toggle_run">8 run</span>
+ <span class="mis shortkey_m button_toggle_mis">0 missing</span>
+ <span class="exc shortkey_x button_toggle_exc">0 excluded</span>
</h2>
</div>
</div>
-<div class='help_panel'>
- <img id='panel_icon' src='keybd_open.png' alt='Hide keyboard shortcuts' />
-<p class='legend'>Hot-keys on this page</p>
+<div class="help_panel">
+ <img id="panel_icon" src="keybd_open.png" alt="Hide keyboard shortcuts" />
+<p class="legend">Hot-keys on this page</p>
<div>
-<p class='keyhelp'>
- <span class='key'>r</span>
- <span class='key'>m</span>
- <span class='key'>x</span>
- <span class='key'>p</span> &nbsp; toggle line displays
+<p class="keyhelp">
+ <span class="key">r</span>
+ <span class="key">m</span>
+ <span class="key">x</span>
+ <span class="key">p</span> &nbsp; toggle line displays
</p>
-<p class='keyhelp'>
- <span class='key'>j</span>
- <span class='key'>k</span> &nbsp; next/prev highlighted chunk
+<p class="keyhelp">
+ <span class="key">j</span>
+ <span class="key">k</span> &nbsp; next/prev highlighted chunk
</p>
-<p class='keyhelp'>
- <span class='key'>0</span> &nbsp; (zero) top of page
+<p class="keyhelp">
+ <span class="key">0</span> &nbsp; (zero) top of page
</p>
-<p class='keyhelp'>
- <span class='key'>1</span> &nbsp; (one) first highlighted chunk
+<p class="keyhelp">
+ <span class="key">1</span> &nbsp; (one) first highlighted chunk
</p>
</div>
</div>
-<div id='source'>
+<div id="source">
<table>
<tr>
- <td class='linenos'>
-<p id='n1' class='stm run hide_run'><a href='#n1'>1</a></p>
-<p id='n2' class='stm run hide_run'><a href='#n2'>2</a></p>
-<p id='n3' class='stm run hide_run'><a href='#n3'>3</a></p>
-<p id='n4' class='pln'><a href='#n4'>4</a></p>
-<p id='n5' class='stm run hide_run'><a href='#n5'>5</a></p>
-<p id='n6' class='stm run hide_run'><a href='#n6'>6</a></p>
-<p id='n7' class='pln'><a href='#n7'>7</a></p>
-<p id='n8' class='stm run hide_run'><a href='#n8'>8</a></p>
-<p id='n9' class='stm run hide_run'><a href='#n9'>9</a></p>
-<p id='n10' class='stm run hide_run'><a href='#n10'>10</a></p>
+ <td class="linenos">
+<p id="n1" class="pln"><a href="#n1">1</a></p>
+<p id="n2" class="pln"><a href="#n2">2</a></p>
+<p id="n3" class="pln"><a href="#n3">3</a></p>
+<p id="n4" class="stm run hide_run"><a href="#n4">4</a></p>
+<p id="n5" class="stm run hide_run"><a href="#n5">5</a></p>
+<p id="n6" class="stm run hide_run"><a href="#n6">6</a></p>
+<p id="n7" class="pln"><a href="#n7">7</a></p>
+<p id="n8" class="stm run hide_run"><a href="#n8">8</a></p>
+<p id="n9" class="stm run hide_run"><a href="#n9">9</a></p>
+<p id="n10" class="pln"><a href="#n10">10</a></p>
+<p id="n11" class="stm run hide_run"><a href="#n11">11</a></p>
+<p id="n12" class="stm run hide_run"><a href="#n12">12</a></p>
+<p id="n13" class="stm run hide_run"><a href="#n13">13</a></p>
</td>
- <td class='text'>
-<p id='t1' class='stm run hide_run'><span class='key'>import</span> <span class='nam'>m1</span><span class='strut'>&nbsp;</span></p>
-<p id='t2' class='stm run hide_run'><span class='key'>import</span> <span class='nam'>m2</span><span class='strut'>&nbsp;</span></p>
-<p id='t3' class='stm run hide_run'><span class='key'>import</span> <span class='nam'>m3</span><span class='strut'>&nbsp;</span></p>
-<p id='t4' class='pln'><span class='strut'>&nbsp;</span></p>
-<p id='t5' class='stm run hide_run'><span class='nam'>a</span> <span class='op'>=</span> <span class='num'>5</span><span class='strut'>&nbsp;</span></p>
-<p id='t6' class='stm run hide_run'><span class='nam'>b</span> <span class='op'>=</span> <span class='num'>6</span><span class='strut'>&nbsp;</span></p>
-<p id='t7' class='pln'><span class='strut'>&nbsp;</span></p>
-<p id='t8' class='stm run hide_run'><span class='key'>assert</span> <span class='nam'>m1</span><span class='op'>.</span><span class='nam'>m1a</span> <span class='op'>==</span> <span class='num'>1</span><span class='strut'>&nbsp;</span></p>
-<p id='t9' class='stm run hide_run'><span class='key'>assert</span> <span class='nam'>m2</span><span class='op'>.</span><span class='nam'>m2a</span> <span class='op'>==</span> <span class='num'>1</span><span class='strut'>&nbsp;</span></p>
-<p id='t10' class='stm run hide_run'><span class='key'>assert</span> <span class='nam'>m3</span><span class='op'>.</span><span class='nam'>m3a</span> <span class='op'>==</span> <span class='num'>1</span><span class='strut'>&nbsp;</span></p>
+ <td class="text">
+<p id="t1" class="pln"><span class="com"># Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0</span><span class="strut">&nbsp;</span></p>
+<p id="t2" class="pln"><span class="com"># For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt</span><span class="strut">&nbsp;</span></p>
+<p id="t3" class="pln"><span class="strut">&nbsp;</span></p>
+<p id="t4" class="stm run hide_run"><span class="key">import</span> <span class="nam">m1</span><span class="strut">&nbsp;</span></p>
+<p id="t5" class="stm run hide_run"><span class="key">import</span> <span class="nam">m2</span><span class="strut">&nbsp;</span></p>
+<p id="t6" class="stm run hide_run"><span class="key">import</span> <span class="nam">m3</span><span class="strut">&nbsp;</span></p>
+<p id="t7" class="pln"><span class="strut">&nbsp;</span></p>
+<p id="t8" class="stm run hide_run"><span class="nam">a</span> <span class="op">=</span> <span class="num">5</span><span class="strut">&nbsp;</span></p>
+<p id="t9" class="stm run hide_run"><span class="nam">b</span> <span class="op">=</span> <span class="num">6</span><span class="strut">&nbsp;</span></p>
+<p id="t10" class="pln"><span class="strut">&nbsp;</span></p>
+<p id="t11" class="stm run hide_run"><span class="key">assert</span> <span class="nam">m1</span><span class="op">.</span><span class="nam">m1a</span> <span class="op">==</span> <span class="num">1</span><span class="strut">&nbsp;</span></p>
+<p id="t12" class="stm run hide_run"><span class="key">assert</span> <span class="nam">m2</span><span class="op">.</span><span class="nam">m2a</span> <span class="op">==</span> <span class="num">1</span><span class="strut">&nbsp;</span></p>
+<p id="t13" class="stm run hide_run"><span class="key">assert</span> <span class="nam">m3</span><span class="op">.</span><span class="nam">m3a</span> <span class="op">==</span> <span class="num">1</span><span class="strut">&nbsp;</span></p>
</td>
</tr>
</table>
</div>
-<div id='footer'>
- <div class='content'>
+<div id="footer">
+ <div class="content">
<p>
- <a class='nav' href='index.html'>&#xab; index</a> &nbsp; &nbsp; <a class='nav' href='http://nedbatchelder.com/code/coverage/4.0a1'>coverage.py v4.0a1</a>
+ <a class="nav" href="index.html">&#xab; index</a> &nbsp; &nbsp; <a class="nav" href="https://coverage.readthedocs.org/en/coverage-4.0a7">coverage.py v4.0a7</a>,
+ created at 2015-07-24 09:04
</p>
</div>
</div>
diff --git a/tests/farm/html/gold_other/blah_blah_other_py.html b/tests/farm/html/gold_other/blah_blah_other_py.html
index f070f3ef..51b7708e 100644
--- a/tests/farm/html/gold_other/blah_blah_other_py.html
+++ b/tests/farm/html/gold_other/blah_blah_other_py.html
@@ -1,90 +1,100 @@
+
+
+
<!DOCTYPE html>
<html>
<head>
- <meta http-equiv='Content-Type' content='text/html; charset=utf-8'>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
- <meta http-equiv='X-UA-Compatible' content='IE=emulateIE7' />
- <title>Coverage for /Users/ned/coverage/trunk/tests/farm/html/othersrc/other: 100%</title>
- <link rel='stylesheet' href='style.css' type='text/css'>
+ <meta http-equiv="X-UA-Compatible" content="IE=emulateIE7" />
+ <title>Coverage for /Users/ned/coverage/trunk/tests/farm/html/othersrc/other.py: 100%</title>
+ <link rel="stylesheet" href="style.css" type="text/css">
- <script type='text/javascript' src='jquery.min.js'></script>
- <script type='text/javascript' src='jquery.hotkeys.js'></script>
- <script type='text/javascript' src='jquery.isonscreen.js'></script>
- <script type='text/javascript' src='coverage_html.js'></script>
- <script type='text/javascript'>
+ <script type="text/javascript" src="jquery.min.js"></script>
+ <script type="text/javascript" src="jquery.hotkeys.js"></script>
+ <script type="text/javascript" src="jquery.isonscreen.js"></script>
+ <script type="text/javascript" src="coverage_html.js"></script>
+ <script type="text/javascript">
jQuery(document).ready(coverage.pyfile_ready);
</script>
</head>
-<body class='pyfile'>
+<body class="pyfile">
-<div id='header'>
- <div class='content'>
- <h1>Coverage for <b>/Users/ned/coverage/trunk/tests/farm/html/othersrc/other</b> :
- <span class='pc_cov'>100%</span>
+<div id="header">
+ <div class="content">
+ <h1>Coverage for <b>/Users/ned/coverage/trunk/tests/farm/html/othersrc/other.py</b> :
+ <span class="pc_cov">100%</span>
</h1>
- <img id='keyboard_icon' src='keybd_closed.png' alt='Show keyboard shortcuts' />
+ <img id="keyboard_icon" src="keybd_closed.png" alt="Show keyboard shortcuts" />
- <h2 class='stats'>
+ <h2 class="stats">
1 statements &nbsp;
- <span class='run hide_run shortkey_r button_toggle_run'>1 run</span>
- <span class='mis shortkey_m button_toggle_mis'>0 missing</span>
- <span class='exc shortkey_x button_toggle_exc'>0 excluded</span>
+ <span class="run hide_run shortkey_r button_toggle_run">1 run</span>
+ <span class="mis shortkey_m button_toggle_mis">0 missing</span>
+ <span class="exc shortkey_x button_toggle_exc">0 excluded</span>
</h2>
</div>
</div>
-<div class='help_panel'>
- <img id='panel_icon' src='keybd_open.png' alt='Hide keyboard shortcuts' />
-<p class='legend'>Hot-keys on this page</p>
+<div class="help_panel">
+ <img id="panel_icon" src="keybd_open.png" alt="Hide keyboard shortcuts" />
+<p class="legend">Hot-keys on this page</p>
<div>
-<p class='keyhelp'>
- <span class='key'>r</span>
- <span class='key'>m</span>
- <span class='key'>x</span>
- <span class='key'>p</span> &nbsp; toggle line displays
+<p class="keyhelp">
+ <span class="key">r</span>
+ <span class="key">m</span>
+ <span class="key">x</span>
+ <span class="key">p</span> &nbsp; toggle line displays
</p>
-<p class='keyhelp'>
- <span class='key'>j</span>
- <span class='key'>k</span> &nbsp; next/prev highlighted chunk
+<p class="keyhelp">
+ <span class="key">j</span>
+ <span class="key">k</span> &nbsp; next/prev highlighted chunk
</p>
-<p class='keyhelp'>
- <span class='key'>0</span> &nbsp; (zero) top of page
+<p class="keyhelp">
+ <span class="key">0</span> &nbsp; (zero) top of page
</p>
-<p class='keyhelp'>
- <span class='key'>1</span> &nbsp; (one) first highlighted chunk
+<p class="keyhelp">
+ <span class="key">1</span> &nbsp; (one) first highlighted chunk
</p>
</div>
</div>
-<div id='source'>
+<div id="source">
<table>
<tr>
- <td class='linenos'>
-<p id='n1' class='pln'><a href='#n1'>1</a></p>
-<p id='n2' class='pln'><a href='#n2'>2</a></p>
-<p id='n3' class='pln'><a href='#n3'>3</a></p>
-<p id='n4' class='stm run hide_run'><a href='#n4'>4</a></p>
+ <td class="linenos">
+<p id="n1" class="pln"><a href="#n1">1</a></p>
+<p id="n2" class="pln"><a href="#n2">2</a></p>
+<p id="n3" class="pln"><a href="#n3">3</a></p>
+<p id="n4" class="pln"><a href="#n4">4</a></p>
+<p id="n5" class="pln"><a href="#n5">5</a></p>
+<p id="n6" class="pln"><a href="#n6">6</a></p>
+<p id="n7" class="stm run hide_run"><a href="#n7">7</a></p>
</td>
- <td class='text'>
-<p id='t1' class='pln'><span class='com'># A file in another directory.&nbsp; We&#39;re checking that it ends up in the</span><span class='strut'>&nbsp;</span></p>
-<p id='t2' class='pln'><span class='com'># HTML report.</span><span class='strut'>&nbsp;</span></p>
-<p id='t3' class='pln'><span class='strut'>&nbsp;</span></p>
-<p id='t4' class='stm run hide_run'><span class='key'>print</span><span class='op'>(</span><span class='str'>&quot;This is the other src!&quot;</span><span class='op'>)</span><span class='strut'>&nbsp;</span></p>
+ <td class="text">
+<p id="t1" class="pln"><span class="com"># Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0</span><span class="strut">&nbsp;</span></p>
+<p id="t2" class="pln"><span class="com"># For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt</span><span class="strut">&nbsp;</span></p>
+<p id="t3" class="pln"><span class="strut">&nbsp;</span></p>
+<p id="t4" class="pln"><span class="com"># A file in another directory.&nbsp; We&#39;re checking that it ends up in the</span><span class="strut">&nbsp;</span></p>
+<p id="t5" class="pln"><span class="com"># HTML report.</span><span class="strut">&nbsp;</span></p>
+<p id="t6" class="pln"><span class="strut">&nbsp;</span></p>
+<p id="t7" class="stm run hide_run"><span class="key">print</span><span class="op">(</span><span class="str">&quot;This is the other src!&quot;</span><span class="op">)</span><span class="strut">&nbsp;</span></p>
</td>
</tr>
</table>
</div>
-<div id='footer'>
- <div class='content'>
+<div id="footer">
+ <div class="content">
<p>
- <a class='nav' href='index.html'>&#xab; index</a> &nbsp; &nbsp; <a class='nav' href='http://nedbatchelder.com/code/coverage/4.0a1'>coverage.py v4.0a1</a>
+ <a class="nav" href="index.html">&#xab; index</a> &nbsp; &nbsp; <a class="nav" href="https://coverage.readthedocs.org/en/coverage-4.0a7">coverage.py v4.0a7</a>,
+ created at 2015-07-24 09:04
</p>
</div>
</div>
diff --git a/tests/farm/html/gold_other/here_py.html b/tests/farm/html/gold_other/here_py.html
index e091395a..36310f5b 100644
--- a/tests/farm/html/gold_other/here_py.html
+++ b/tests/farm/html/gold_other/here_py.html
@@ -1,98 +1,108 @@
+
+
+
<!DOCTYPE html>
<html>
<head>
- <meta http-equiv='Content-Type' content='text/html; charset=utf-8'>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
- <meta http-equiv='X-UA-Compatible' content='IE=emulateIE7' />
- <title>Coverage for here: 75%</title>
- <link rel='stylesheet' href='style.css' type='text/css'>
+ <meta http-equiv="X-UA-Compatible" content="IE=emulateIE7" />
+ <title>Coverage for here.py: 75%</title>
+ <link rel="stylesheet" href="style.css" type="text/css">
- <script type='text/javascript' src='jquery.min.js'></script>
- <script type='text/javascript' src='jquery.hotkeys.js'></script>
- <script type='text/javascript' src='jquery.isonscreen.js'></script>
- <script type='text/javascript' src='coverage_html.js'></script>
- <script type='text/javascript'>
+ <script type="text/javascript" src="jquery.min.js"></script>
+ <script type="text/javascript" src="jquery.hotkeys.js"></script>
+ <script type="text/javascript" src="jquery.isonscreen.js"></script>
+ <script type="text/javascript" src="coverage_html.js"></script>
+ <script type="text/javascript">
jQuery(document).ready(coverage.pyfile_ready);
</script>
</head>
-<body class='pyfile'>
+<body class="pyfile">
-<div id='header'>
- <div class='content'>
- <h1>Coverage for <b>here</b> :
- <span class='pc_cov'>75%</span>
+<div id="header">
+ <div class="content">
+ <h1>Coverage for <b>here.py</b> :
+ <span class="pc_cov">75%</span>
</h1>
- <img id='keyboard_icon' src='keybd_closed.png' alt='Show keyboard shortcuts' />
+ <img id="keyboard_icon" src="keybd_closed.png" alt="Show keyboard shortcuts" />
- <h2 class='stats'>
+ <h2 class="stats">
4 statements &nbsp;
- <span class='run hide_run shortkey_r button_toggle_run'>3 run</span>
- <span class='mis shortkey_m button_toggle_mis'>1 missing</span>
- <span class='exc shortkey_x button_toggle_exc'>0 excluded</span>
+ <span class="run hide_run shortkey_r button_toggle_run">3 run</span>
+ <span class="mis shortkey_m button_toggle_mis">1 missing</span>
+ <span class="exc shortkey_x button_toggle_exc">0 excluded</span>
</h2>
</div>
</div>
-<div class='help_panel'>
- <img id='panel_icon' src='keybd_open.png' alt='Hide keyboard shortcuts' />
-<p class='legend'>Hot-keys on this page</p>
+<div class="help_panel">
+ <img id="panel_icon" src="keybd_open.png" alt="Hide keyboard shortcuts" />
+<p class="legend">Hot-keys on this page</p>
<div>
-<p class='keyhelp'>
- <span class='key'>r</span>
- <span class='key'>m</span>
- <span class='key'>x</span>
- <span class='key'>p</span> &nbsp; toggle line displays
+<p class="keyhelp">
+ <span class="key">r</span>
+ <span class="key">m</span>
+ <span class="key">x</span>
+ <span class="key">p</span> &nbsp; toggle line displays
</p>
-<p class='keyhelp'>
- <span class='key'>j</span>
- <span class='key'>k</span> &nbsp; next/prev highlighted chunk
+<p class="keyhelp">
+ <span class="key">j</span>
+ <span class="key">k</span> &nbsp; next/prev highlighted chunk
</p>
-<p class='keyhelp'>
- <span class='key'>0</span> &nbsp; (zero) top of page
+<p class="keyhelp">
+ <span class="key">0</span> &nbsp; (zero) top of page
</p>
-<p class='keyhelp'>
- <span class='key'>1</span> &nbsp; (one) first highlighted chunk
+<p class="keyhelp">
+ <span class="key">1</span> &nbsp; (one) first highlighted chunk
</p>
</div>
</div>
-<div id='source'>
+<div id="source">
<table>
<tr>
- <td class='linenos'>
-<p id='n1' class='pln'><a href='#n1'>1</a></p>
-<p id='n2' class='pln'><a href='#n2'>2</a></p>
-<p id='n3' class='stm run hide_run'><a href='#n3'>3</a></p>
-<p id='n4' class='pln'><a href='#n4'>4</a></p>
-<p id='n5' class='stm run hide_run'><a href='#n5'>5</a></p>
-<p id='n6' class='stm run hide_run'><a href='#n6'>6</a></p>
-<p id='n7' class='pln'><a href='#n7'>7</a></p>
-<p id='n8' class='stm mis'><a href='#n8'>8</a></p>
+ <td class="linenos">
+<p id="n1" class="pln"><a href="#n1">1</a></p>
+<p id="n2" class="pln"><a href="#n2">2</a></p>
+<p id="n3" class="pln"><a href="#n3">3</a></p>
+<p id="n4" class="pln"><a href="#n4">4</a></p>
+<p id="n5" class="pln"><a href="#n5">5</a></p>
+<p id="n6" class="stm run hide_run"><a href="#n6">6</a></p>
+<p id="n7" class="pln"><a href="#n7">7</a></p>
+<p id="n8" class="stm run hide_run"><a href="#n8">8</a></p>
+<p id="n9" class="stm run hide_run"><a href="#n9">9</a></p>
+<p id="n10" class="pln"><a href="#n10">10</a></p>
+<p id="n11" class="stm mis"><a href="#n11">11</a></p>
</td>
- <td class='text'>
-<p id='t1' class='pln'><span class='com'># A test file for HTML reporting by coverage.</span><span class='strut'>&nbsp;</span></p>
-<p id='t2' class='pln'><span class='strut'>&nbsp;</span></p>
-<p id='t3' class='stm run hide_run'><span class='key'>import</span> <span class='nam'>other</span><span class='strut'>&nbsp;</span></p>
-<p id='t4' class='pln'><span class='strut'>&nbsp;</span></p>
-<p id='t5' class='stm run hide_run'><span class='key'>if</span> <span class='num'>1</span> <span class='op'>&lt;</span> <span class='num'>2</span><span class='op'>:</span><span class='strut'>&nbsp;</span></p>
-<p id='t6' class='stm run hide_run'>&nbsp; &nbsp; <span class='nam'>h</span> <span class='op'>=</span> <span class='num'>3</span><span class='strut'>&nbsp;</span></p>
-<p id='t7' class='pln'><span class='key'>else</span><span class='op'>:</span><span class='strut'>&nbsp;</span></p>
-<p id='t8' class='stm mis'>&nbsp; &nbsp; <span class='nam'>h</span> <span class='op'>=</span> <span class='num'>4</span><span class='strut'>&nbsp;</span></p>
+ <td class="text">
+<p id="t1" class="pln"><span class="com"># Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0</span><span class="strut">&nbsp;</span></p>
+<p id="t2" class="pln"><span class="com"># For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt</span><span class="strut">&nbsp;</span></p>
+<p id="t3" class="pln"><span class="strut">&nbsp;</span></p>
+<p id="t4" class="pln"><span class="com"># A test file for HTML reporting by coverage.py.</span><span class="strut">&nbsp;</span></p>
+<p id="t5" class="pln"><span class="strut">&nbsp;</span></p>
+<p id="t6" class="stm run hide_run"><span class="key">import</span> <span class="nam">other</span><span class="strut">&nbsp;</span></p>
+<p id="t7" class="pln"><span class="strut">&nbsp;</span></p>
+<p id="t8" class="stm run hide_run"><span class="key">if</span> <span class="num">1</span> <span class="op">&lt;</span> <span class="num">2</span><span class="op">:</span><span class="strut">&nbsp;</span></p>
+<p id="t9" class="stm run hide_run">&nbsp; &nbsp; <span class="nam">h</span> <span class="op">=</span> <span class="num">3</span><span class="strut">&nbsp;</span></p>
+<p id="t10" class="pln"><span class="key">else</span><span class="op">:</span><span class="strut">&nbsp;</span></p>
+<p id="t11" class="stm mis">&nbsp; &nbsp; <span class="nam">h</span> <span class="op">=</span> <span class="num">4</span><span class="strut">&nbsp;</span></p>
</td>
</tr>
</table>
</div>
-<div id='footer'>
- <div class='content'>
+<div id="footer">
+ <div class="content">
<p>
- <a class='nav' href='index.html'>&#xab; index</a> &nbsp; &nbsp; <a class='nav' href='http://nedbatchelder.com/code/coverage/4.0a1'>coverage.py v4.0a1</a>
+ <a class="nav" href="index.html">&#xab; index</a> &nbsp; &nbsp; <a class="nav" href="https://coverage.readthedocs.org/en/coverage-4.0a7">coverage.py v4.0a7</a>,
+ created at 2015-07-24 09:04
</p>
</div>
</div>
diff --git a/tests/farm/html/gold_other/index.html b/tests/farm/html/gold_other/index.html
index a27295c4..25d437e9 100644
--- a/tests/farm/html/gold_other/index.html
+++ b/tests/farm/html/gold_other/index.html
@@ -1,28 +1,31 @@
+
+
+
<!DOCTYPE html>
<html>
<head>
- <meta http-equiv='Content-Type' content='text/html; charset=utf-8'>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>Coverage report</title>
- <link rel='stylesheet' href='style.css' type='text/css'>
+ <link rel="stylesheet" href="style.css" type="text/css">
- <script type='text/javascript' src='jquery.min.js'></script>
- <script type='text/javascript' src='jquery.debounce.min.js'></script>
- <script type='text/javascript' src='jquery.tablesorter.min.js'></script>
- <script type='text/javascript' src='jquery.hotkeys.js'></script>
- <script type='text/javascript' src='coverage_html.js'></script>
- <script type='text/javascript'>
+ <script type="text/javascript" src="jquery.min.js"></script>
+ <script type="text/javascript" src="jquery.debounce.min.js"></script>
+ <script type="text/javascript" src="jquery.tablesorter.min.js"></script>
+ <script type="text/javascript" src="jquery.hotkeys.js"></script>
+ <script type="text/javascript" src="coverage_html.js"></script>
+ <script type="text/javascript">
jQuery(document).ready(coverage.index_ready);
</script>
</head>
-<body class='indexfile'>
+<body class="indexfile">
-<div id='header'>
- <div class='content'>
+<div id="header">
+ <div class="content">
<h1>Coverage report:
- <span class='pc_cov'>80%</span>
+ <span class="pc_cov">80%</span>
</h1>
- <img id='keyboard_icon' src='keybd_closed.png' alt='Show keyboard shortcuts' />
+ <img id="keyboard_icon" src="keybd_closed.png" alt="Show keyboard shortcuts" />
<form id="filter_container">
<input id="filter" type="text" value="" placeholder="filter..." />
@@ -30,63 +33,63 @@
</div>
</div>
-<div class='help_panel'>
- <img id='panel_icon' src='keybd_open.png' alt='Hide keyboard shortcuts' />
- <p class='legend'>Hot-keys on this page</p>
+<div class="help_panel">
+ <img id="panel_icon" src="keybd_open.png" alt="Hide keyboard shortcuts" />
+ <p class="legend">Hot-keys on this page</p>
<div>
- <p class='keyhelp'>
- <span class='key'>n</span>
- <span class='key'>s</span>
- <span class='key'>m</span>
- <span class='key'>x</span>
+ <p class="keyhelp">
+ <span class="key">n</span>
+ <span class="key">s</span>
+ <span class="key">m</span>
+ <span class="key">x</span>
- <span class='key'>c</span> &nbsp; change column sorting
+ <span class="key">c</span> &nbsp; change column sorting
</p>
</div>
</div>
-<div id='index'>
- <table class='index'>
+<div id="index">
+ <table class="index">
<thead>
- <tr class='tablehead' title='Click to sort'>
- <th class='name left headerSortDown shortkey_n'>Module</th>
- <th class='shortkey_s'>statements</th>
- <th class='shortkey_m'>missing</th>
- <th class='shortkey_x'>excluded</th>
+ <tr class="tablehead" title="Click to sort">
+ <th class="name left headerSortDown shortkey_n">Module</th>
+ <th class="shortkey_s">statements</th>
+ <th class="shortkey_m">missing</th>
+ <th class="shortkey_x">excluded</th>
- <th class='right shortkey_c'>coverage</th>
+ <th class="right shortkey_c">coverage</th>
</tr>
</thead>
<tfoot>
- <tr class='total'>
- <td class='name left'>Total</td>
+ <tr class="total">
+ <td class="name left">Total</td>
<td>5</td>
<td>1</td>
<td>0</td>
- <td class='right' data-ratio='4 5'>80%</td>
+ <td class="right" data-ratio="4 5">80%</td>
</tr>
</tfoot>
<tbody>
- <tr class='file'>
- <td class='name left'><a href='_Users_ned_coverage_trunk_tests_farm_html_othersrc_other.html'>/Users/ned/coverage/trunk/tests/farm/html/othersrc/other</a></td>
+ <tr class="file">
+ <td class="name left"><a href="_Users_ned_coverage_trunk_tests_farm_html_othersrc_other_py.html">/Users/ned/coverage/trunk/tests/farm/html/othersrc/other.py</a></td>
<td>1</td>
<td>0</td>
<td>0</td>
- <td class='right' data-ratio='1 1'>100%</td>
+ <td class="right" data-ratio="1 1">100%</td>
</tr>
- <tr class='file'>
- <td class='name left'><a href='here.html'>here</a></td>
+ <tr class="file">
+ <td class="name left"><a href="here_py.html">here.py</a></td>
<td>4</td>
<td>1</td>
<td>0</td>
- <td class='right' data-ratio='3 4'>75%</td>
+ <td class="right" data-ratio="3 4">75%</td>
</tr>
</tbody>
@@ -97,10 +100,11 @@
</p>
</div>
-<div id='footer'>
- <div class='content'>
+<div id="footer">
+ <div class="content">
<p>
- <a class='nav' href='http://nedbatchelder.com/code/coverage/4.0a1'>coverage.py v4.0a1</a>
+ <a class="nav" href="https://coverage.readthedocs.org/en/coverage-4.0a7">coverage.py v4.0a7</a>,
+ created at 2015-07-24 09:31
</p>
</div>
</div>
diff --git a/tests/farm/html/gold_partial/index.html b/tests/farm/html/gold_partial/index.html
index 5445d769..506d80d4 100644
--- a/tests/farm/html/gold_partial/index.html
+++ b/tests/farm/html/gold_partial/index.html
@@ -1,28 +1,31 @@
+
+
+
<!DOCTYPE html>
<html>
<head>
- <meta http-equiv='Content-Type' content='text/html; charset=utf-8'>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>Coverage report</title>
- <link rel='stylesheet' href='style.css' type='text/css'>
+ <link rel="stylesheet" href="style.css" type="text/css">
- <script type='text/javascript' src='jquery.min.js'></script>
- <script type='text/javascript' src='jquery.debounce.min.js'></script>
- <script type='text/javascript' src='jquery.tablesorter.min.js'></script>
- <script type='text/javascript' src='jquery.hotkeys.js'></script>
- <script type='text/javascript' src='coverage_html.js'></script>
- <script type='text/javascript'>
+ <script type="text/javascript" src="jquery.min.js"></script>
+ <script type="text/javascript" src="jquery.debounce.min.js"></script>
+ <script type="text/javascript" src="jquery.tablesorter.min.js"></script>
+ <script type="text/javascript" src="jquery.hotkeys.js"></script>
+ <script type="text/javascript" src="coverage_html.js"></script>
+ <script type="text/javascript">
jQuery(document).ready(coverage.index_ready);
</script>
</head>
-<body class='indexfile'>
+<body class="indexfile">
-<div id='header'>
- <div class='content'>
+<div id="header">
+ <div class="content">
<h1>Coverage report:
- <span class='pc_cov'>100%</span>
+ <span class="pc_cov">100%</span>
</h1>
- <img id='keyboard_icon' src='keybd_closed.png' alt='Show keyboard shortcuts' />
+ <img id="keyboard_icon" src="keybd_closed.png" alt="Show keyboard shortcuts" />
<form id="filter_container">
<input id="filter" type="text" value="" placeholder="filter..." />
@@ -30,44 +33,44 @@
</div>
</div>
-<div class='help_panel'>
- <img id='panel_icon' src='keybd_open.png' alt='Hide keyboard shortcuts' />
- <p class='legend'>Hot-keys on this page</p>
+<div class="help_panel">
+ <img id="panel_icon" src="keybd_open.png" alt="Hide keyboard shortcuts" />
+ <p class="legend">Hot-keys on this page</p>
<div>
- <p class='keyhelp'>
- <span class='key'>n</span>
- <span class='key'>s</span>
- <span class='key'>m</span>
- <span class='key'>x</span>
+ <p class="keyhelp">
+ <span class="key">n</span>
+ <span class="key">s</span>
+ <span class="key">m</span>
+ <span class="key">x</span>
- <span class='key'>b</span>
- <span class='key'>p</span>
+ <span class="key">b</span>
+ <span class="key">p</span>
- <span class='key'>c</span> &nbsp; change column sorting
+ <span class="key">c</span> &nbsp; change column sorting
</p>
</div>
</div>
-<div id='index'>
- <table class='index'>
+<div id="index">
+ <table class="index">
<thead>
- <tr class='tablehead' title='Click to sort'>
- <th class='name left headerSortDown shortkey_n'>Module</th>
- <th class='shortkey_s'>statements</th>
- <th class='shortkey_m'>missing</th>
- <th class='shortkey_x'>excluded</th>
+ <tr class="tablehead" title="Click to sort">
+ <th class="name left headerSortDown shortkey_n">Module</th>
+ <th class="shortkey_s">statements</th>
+ <th class="shortkey_m">missing</th>
+ <th class="shortkey_x">excluded</th>
- <th class='shortkey_b'>branches</th>
- <th class='shortkey_p'>partial</th>
+ <th class="shortkey_b">branches</th>
+ <th class="shortkey_p">partial</th>
- <th class='right shortkey_c'>coverage</th>
+ <th class="right shortkey_c">coverage</th>
</tr>
</thead>
<tfoot>
- <tr class='total'>
- <td class='name left'>Total</td>
+ <tr class="total">
+ <td class="name left">Total</td>
<td>8</td>
<td>0</td>
<td>0</td>
@@ -75,13 +78,13 @@
<td>4</td>
<td>0</td>
- <td class='right' data-ratio='12 12'>100%</td>
+ <td class="right" data-ratio="12 12">100%</td>
</tr>
</tfoot>
<tbody>
- <tr class='file'>
- <td class='name left'><a href='partial.html'>partial</a></td>
+ <tr class="file">
+ <td class="name left"><a href="partial_py.html">partial.py</a></td>
<td>8</td>
<td>0</td>
<td>0</td>
@@ -89,7 +92,7 @@
<td>4</td>
<td>0</td>
- <td class='right' data-ratio='12 12'>100%</td>
+ <td class="right" data-ratio="12 12">100%</td>
</tr>
</tbody>
@@ -100,10 +103,11 @@
</p>
</div>
-<div id='footer'>
- <div class='content'>
+<div id="footer">
+ <div class="content">
<p>
- <a class='nav' href='http://nedbatchelder.com/code/coverage/4.0a1'>coverage.py v4.0a1</a>
+ <a class="nav" href="https://coverage.readthedocs.org/en/coverage-4.0a7">coverage.py v4.0a7</a>,
+ created at 2015-07-24 09:31
</p>
</div>
</div>
diff --git a/tests/farm/html/gold_partial/partial_py.html b/tests/farm/html/gold_partial/partial_py.html
index 64dfacfa..53997f57 100644
--- a/tests/farm/html/gold_partial/partial_py.html
+++ b/tests/farm/html/gold_partial/partial_py.html
@@ -1,120 +1,130 @@
+
+
+
<!DOCTYPE html>
<html>
<head>
- <meta http-equiv='Content-Type' content='text/html; charset=utf-8'>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
- <meta http-equiv='X-UA-Compatible' content='IE=emulateIE7' />
- <title>Coverage for partial: 100%</title>
- <link rel='stylesheet' href='style.css' type='text/css'>
+ <meta http-equiv="X-UA-Compatible" content="IE=emulateIE7" />
+ <title>Coverage for partial.py: 100%</title>
+ <link rel="stylesheet" href="style.css" type="text/css">
- <script type='text/javascript' src='jquery.min.js'></script>
- <script type='text/javascript' src='jquery.hotkeys.js'></script>
- <script type='text/javascript' src='jquery.isonscreen.js'></script>
- <script type='text/javascript' src='coverage_html.js'></script>
- <script type='text/javascript'>
+ <script type="text/javascript" src="jquery.min.js"></script>
+ <script type="text/javascript" src="jquery.hotkeys.js"></script>
+ <script type="text/javascript" src="jquery.isonscreen.js"></script>
+ <script type="text/javascript" src="coverage_html.js"></script>
+ <script type="text/javascript">
jQuery(document).ready(coverage.pyfile_ready);
</script>
</head>
-<body class='pyfile'>
+<body class="pyfile">
-<div id='header'>
- <div class='content'>
- <h1>Coverage for <b>partial</b> :
- <span class='pc_cov'>100%</span>
+<div id="header">
+ <div class="content">
+ <h1>Coverage for <b>partial.py</b> :
+ <span class="pc_cov">100%</span>
</h1>
- <img id='keyboard_icon' src='keybd_closed.png' alt='Show keyboard shortcuts' />
+ <img id="keyboard_icon" src="keybd_closed.png" alt="Show keyboard shortcuts" />
- <h2 class='stats'>
+ <h2 class="stats">
8 statements &nbsp;
- <span class='run hide_run shortkey_r button_toggle_run'>8 run</span>
- <span class='mis shortkey_m button_toggle_mis'>0 missing</span>
- <span class='exc shortkey_x button_toggle_exc'>0 excluded</span>
+ <span class="run hide_run shortkey_r button_toggle_run">8 run</span>
+ <span class="mis shortkey_m button_toggle_mis">0 missing</span>
+ <span class="exc shortkey_x button_toggle_exc">0 excluded</span>
- <span class='par run hide_run shortkey_p button_toggle_par'>0 partial</span>
+ <span class="par run hide_run shortkey_p button_toggle_par">0 partial</span>
</h2>
</div>
</div>
-<div class='help_panel'>
- <img id='panel_icon' src='keybd_open.png' alt='Hide keyboard shortcuts' />
-<p class='legend'>Hot-keys on this page</p>
+<div class="help_panel">
+ <img id="panel_icon" src="keybd_open.png" alt="Hide keyboard shortcuts" />
+<p class="legend">Hot-keys on this page</p>
<div>
-<p class='keyhelp'>
- <span class='key'>r</span>
- <span class='key'>m</span>
- <span class='key'>x</span>
- <span class='key'>p</span> &nbsp; toggle line displays
+<p class="keyhelp">
+ <span class="key">r</span>
+ <span class="key">m</span>
+ <span class="key">x</span>
+ <span class="key">p</span> &nbsp; toggle line displays
</p>
-<p class='keyhelp'>
- <span class='key'>j</span>
- <span class='key'>k</span> &nbsp; next/prev highlighted chunk
+<p class="keyhelp">
+ <span class="key">j</span>
+ <span class="key">k</span> &nbsp; next/prev highlighted chunk
</p>
-<p class='keyhelp'>
- <span class='key'>0</span> &nbsp; (zero) top of page
+<p class="keyhelp">
+ <span class="key">0</span> &nbsp; (zero) top of page
</p>
-<p class='keyhelp'>
- <span class='key'>1</span> &nbsp; (one) first highlighted chunk
+<p class="keyhelp">
+ <span class="key">1</span> &nbsp; (one) first highlighted chunk
</p>
</div>
</div>
-<div id='source'>
+<div id="source">
<table>
<tr>
- <td class='linenos'>
-<p id='n1' class='pln'><a href='#n1'>1</a></p>
-<p id='n2' class='pln'><a href='#n2'>2</a></p>
-<p id='n3' class='stm run hide_run'><a href='#n3'>3</a></p>
-<p id='n4' class='pln'><a href='#n4'>4</a></p>
-<p id='n5' class='stm run hide_run'><a href='#n5'>5</a></p>
-<p id='n6' class='stm run hide_run'><a href='#n6'>6</a></p>
-<p id='n7' class='pln'><a href='#n7'>7</a></p>
-<p id='n8' class='stm run hide_run'><a href='#n8'>8</a></p>
-<p id='n9' class='stm run hide_run'><a href='#n9'>9</a></p>
-<p id='n10' class='pln'><a href='#n10'>10</a></p>
-<p id='n11' class='stm run hide_run'><a href='#n11'>11</a></p>
-<p id='n12' class='stm run hide_run'><a href='#n12'>12</a></p>
-<p id='n13' class='pln'><a href='#n13'>13</a></p>
-<p id='n14' class='pln'><a href='#n14'>14</a></p>
-<p id='n15' class='pln'><a href='#n15'>15</a></p>
-<p id='n16' class='pln'><a href='#n16'>16</a></p>
-<p id='n17' class='pln'><a href='#n17'>17</a></p>
-<p id='n18' class='stm run hide_run'><a href='#n18'>18</a></p>
+ <td class="linenos">
+<p id="n1" class="pln"><a href="#n1">1</a></p>
+<p id="n2" class="pln"><a href="#n2">2</a></p>
+<p id="n3" class="pln"><a href="#n3">3</a></p>
+<p id="n4" class="pln"><a href="#n4">4</a></p>
+<p id="n5" class="pln"><a href="#n5">5</a></p>
+<p id="n6" class="stm run hide_run"><a href="#n6">6</a></p>
+<p id="n7" class="pln"><a href="#n7">7</a></p>
+<p id="n8" class="stm run hide_run"><a href="#n8">8</a></p>
+<p id="n9" class="stm run hide_run"><a href="#n9">9</a></p>
+<p id="n10" class="pln"><a href="#n10">10</a></p>
+<p id="n11" class="stm run hide_run"><a href="#n11">11</a></p>
+<p id="n12" class="stm run hide_run"><a href="#n12">12</a></p>
+<p id="n13" class="pln"><a href="#n13">13</a></p>
+<p id="n14" class="stm run hide_run"><a href="#n14">14</a></p>
+<p id="n15" class="stm run hide_run"><a href="#n15">15</a></p>
+<p id="n16" class="pln"><a href="#n16">16</a></p>
+<p id="n17" class="pln"><a href="#n17">17</a></p>
+<p id="n18" class="pln"><a href="#n18">18</a></p>
+<p id="n19" class="pln"><a href="#n19">19</a></p>
+<p id="n20" class="pln"><a href="#n20">20</a></p>
+<p id="n21" class="stm run hide_run"><a href="#n21">21</a></p>
</td>
- <td class='text'>
-<p id='t1' class='pln'><span class='com'># partial branches</span><span class='strut'>&nbsp;</span></p>
-<p id='t2' class='pln'><span class='strut'>&nbsp;</span></p>
-<p id='t3' class='stm run hide_run'><span class='nam'>a</span> <span class='op'>=</span> <span class='num'>3</span><span class='strut'>&nbsp;</span></p>
-<p id='t4' class='pln'><span class='strut'>&nbsp;</span></p>
-<p id='t5' class='stm run hide_run'><span class='key'>while</span> <span class='nam'>True</span><span class='op'>:</span><span class='strut'>&nbsp;</span></p>
-<p id='t6' class='stm run hide_run'>&nbsp; &nbsp; <span class='key'>break</span><span class='strut'>&nbsp;</span></p>
-<p id='t7' class='pln'><span class='strut'>&nbsp;</span></p>
-<p id='t8' class='stm run hide_run'><span class='key'>while</span> <span class='num'>1</span><span class='op'>:</span><span class='strut'>&nbsp;</span></p>
-<p id='t9' class='stm run hide_run'>&nbsp; &nbsp; <span class='key'>break</span><span class='strut'>&nbsp;</span></p>
-<p id='t10' class='pln'><span class='strut'>&nbsp;</span></p>
-<p id='t11' class='stm run hide_run'><span class='key'>while</span> <span class='nam'>a</span><span class='op'>:</span>&nbsp; &nbsp; &nbsp; &nbsp; <span class='com'># pragma: no branch</span><span class='strut'>&nbsp;</span></p>
-<p id='t12' class='stm run hide_run'>&nbsp; &nbsp; <span class='key'>break</span><span class='strut'>&nbsp;</span></p>
-<p id='t13' class='pln'><span class='strut'>&nbsp;</span></p>
-<p id='t14' class='pln'><span class='key'>if</span> <span class='num'>0</span><span class='op'>:</span><span class='strut'>&nbsp;</span></p>
-<p id='t15' class='pln'>&nbsp; &nbsp; <span class='nam'>never_happen</span><span class='op'>(</span><span class='op'>)</span><span class='strut'>&nbsp;</span></p>
-<p id='t16' class='pln'><span class='strut'>&nbsp;</span></p>
-<p id='t17' class='pln'><span class='key'>if</span> <span class='num'>1</span><span class='op'>:</span><span class='strut'>&nbsp;</span></p>
-<p id='t18' class='stm run hide_run'>&nbsp; &nbsp; <span class='nam'>a</span> <span class='op'>=</span> <span class='num'>13</span><span class='strut'>&nbsp;</span></p>
+ <td class="text">
+<p id="t1" class="pln"><span class="com"># Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0</span><span class="strut">&nbsp;</span></p>
+<p id="t2" class="pln"><span class="com"># For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt</span><span class="strut">&nbsp;</span></p>
+<p id="t3" class="pln"><span class="strut">&nbsp;</span></p>
+<p id="t4" class="pln"><span class="com"># partial branches</span><span class="strut">&nbsp;</span></p>
+<p id="t5" class="pln"><span class="strut">&nbsp;</span></p>
+<p id="t6" class="stm run hide_run"><span class="nam">a</span> <span class="op">=</span> <span class="num">3</span><span class="strut">&nbsp;</span></p>
+<p id="t7" class="pln"><span class="strut">&nbsp;</span></p>
+<p id="t8" class="stm run hide_run"><span class="key">while</span> <span class="nam">True</span><span class="op">:</span><span class="strut">&nbsp;</span></p>
+<p id="t9" class="stm run hide_run">&nbsp; &nbsp; <span class="key">break</span><span class="strut">&nbsp;</span></p>
+<p id="t10" class="pln"><span class="strut">&nbsp;</span></p>
+<p id="t11" class="stm run hide_run"><span class="key">while</span> <span class="num">1</span><span class="op">:</span><span class="strut">&nbsp;</span></p>
+<p id="t12" class="stm run hide_run">&nbsp; &nbsp; <span class="key">break</span><span class="strut">&nbsp;</span></p>
+<p id="t13" class="pln"><span class="strut">&nbsp;</span></p>
+<p id="t14" class="stm run hide_run"><span class="key">while</span> <span class="nam">a</span><span class="op">:</span>&nbsp; &nbsp; &nbsp; &nbsp; <span class="com"># pragma: no branch</span><span class="strut">&nbsp;</span></p>
+<p id="t15" class="stm run hide_run">&nbsp; &nbsp; <span class="key">break</span><span class="strut">&nbsp;</span></p>
+<p id="t16" class="pln"><span class="strut">&nbsp;</span></p>
+<p id="t17" class="pln"><span class="key">if</span> <span class="num">0</span><span class="op">:</span><span class="strut">&nbsp;</span></p>
+<p id="t18" class="pln">&nbsp; &nbsp; <span class="nam">never_happen</span><span class="op">(</span><span class="op">)</span><span class="strut">&nbsp;</span></p>
+<p id="t19" class="pln"><span class="strut">&nbsp;</span></p>
+<p id="t20" class="pln"><span class="key">if</span> <span class="num">1</span><span class="op">:</span><span class="strut">&nbsp;</span></p>
+<p id="t21" class="stm run hide_run">&nbsp; &nbsp; <span class="nam">a</span> <span class="op">=</span> <span class="num">13</span><span class="strut">&nbsp;</span></p>
</td>
</tr>
</table>
</div>
-<div id='footer'>
- <div class='content'>
+<div id="footer">
+ <div class="content">
<p>
- <a class='nav' href='index.html'>&#xab; index</a> &nbsp; &nbsp; <a class='nav' href='http://nedbatchelder.com/code/coverage/4.0a1'>coverage.py v4.0a1</a>
+ <a class="nav" href="index.html">&#xab; index</a> &nbsp; &nbsp; <a class="nav" href="https://coverage.readthedocs.org/en/coverage-4.0a7">coverage.py v4.0a7</a>,
+ created at 2015-07-24 09:04
</p>
</div>
</div>
diff --git a/tests/farm/html/gold_styled/a_py.html b/tests/farm/html/gold_styled/a_py.html
index 9fdaee17..890a8268 100644
--- a/tests/farm/html/gold_styled/a_py.html
+++ b/tests/farm/html/gold_styled/a_py.html
@@ -1,98 +1,108 @@
+
+
+
<!DOCTYPE html>
<html>
<head>
- <meta http-equiv='Content-Type' content='text/html; charset=utf-8'>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
- <meta http-equiv='X-UA-Compatible' content='IE=emulateIE7' />
- <title>Coverage for a: 67%</title>
- <link rel='stylesheet' href='style.css' type='text/css'>
+ <meta http-equiv="X-UA-Compatible" content="IE=emulateIE7" />
+ <title>Coverage for a.py: 67%</title>
+ <link rel="stylesheet" href="style.css" type="text/css">
- <link rel='stylesheet' href='extra.css' type='text/css'>
+ <link rel="stylesheet" href="extra.css" type="text/css">
- <script type='text/javascript' src='jquery.min.js'></script>
- <script type='text/javascript' src='jquery.hotkeys.js'></script>
- <script type='text/javascript' src='jquery.isonscreen.js'></script>
- <script type='text/javascript' src='coverage_html.js'></script>
- <script type='text/javascript'>
+ <script type="text/javascript" src="jquery.min.js"></script>
+ <script type="text/javascript" src="jquery.hotkeys.js"></script>
+ <script type="text/javascript" src="jquery.isonscreen.js"></script>
+ <script type="text/javascript" src="coverage_html.js"></script>
+ <script type="text/javascript">
jQuery(document).ready(coverage.pyfile_ready);
</script>
</head>
-<body class='pyfile'>
+<body class="pyfile">
-<div id='header'>
- <div class='content'>
- <h1>Coverage for <b>a</b> :
- <span class='pc_cov'>67%</span>
+<div id="header">
+ <div class="content">
+ <h1>Coverage for <b>a.py</b> :
+ <span class="pc_cov">67%</span>
</h1>
- <img id='keyboard_icon' src='keybd_closed.png' alt='Show keyboard shortcuts' />
+ <img id="keyboard_icon" src="keybd_closed.png" alt="Show keyboard shortcuts" />
- <h2 class='stats'>
+ <h2 class="stats">
3 statements &nbsp;
- <span class='run hide_run shortkey_r button_toggle_run'>2 run</span>
- <span class='mis shortkey_m button_toggle_mis'>1 missing</span>
- <span class='exc shortkey_x button_toggle_exc'>0 excluded</span>
+ <span class="run hide_run shortkey_r button_toggle_run">2 run</span>
+ <span class="mis shortkey_m button_toggle_mis">1 missing</span>
+ <span class="exc shortkey_x button_toggle_exc">0 excluded</span>
</h2>
</div>
</div>
-<div class='help_panel'>
- <img id='panel_icon' src='keybd_open.png' alt='Hide keyboard shortcuts' />
-<p class='legend'>Hot-keys on this page</p>
+<div class="help_panel">
+ <img id="panel_icon" src="keybd_open.png" alt="Hide keyboard shortcuts" />
+<p class="legend">Hot-keys on this page</p>
<div>
-<p class='keyhelp'>
- <span class='key'>r</span>
- <span class='key'>m</span>
- <span class='key'>x</span>
- <span class='key'>p</span> &nbsp; toggle line displays
+<p class="keyhelp">
+ <span class="key">r</span>
+ <span class="key">m</span>
+ <span class="key">x</span>
+ <span class="key">p</span> &nbsp; toggle line displays
</p>
-<p class='keyhelp'>
- <span class='key'>j</span>
- <span class='key'>k</span> &nbsp; next/prev highlighted chunk
+<p class="keyhelp">
+ <span class="key">j</span>
+ <span class="key">k</span> &nbsp; next/prev highlighted chunk
</p>
-<p class='keyhelp'>
- <span class='key'>0</span> &nbsp; (zero) top of page
+<p class="keyhelp">
+ <span class="key">0</span> &nbsp; (zero) top of page
</p>
-<p class='keyhelp'>
- <span class='key'>1</span> &nbsp; (one) first highlighted chunk
+<p class="keyhelp">
+ <span class="key">1</span> &nbsp; (one) first highlighted chunk
</p>
</div>
</div>
-<div id='source'>
+<div id="source">
<table>
<tr>
- <td class='linenos'>
-<p id='n1' class='pln'><a href='#n1'>1</a></p>
-<p id='n2' class='pln'><a href='#n2'>2</a></p>
-<p id='n3' class='stm run hide_run'><a href='#n3'>3</a></p>
-<p id='n4' class='pln'><a href='#n4'>4</a></p>
-<p id='n5' class='stm run hide_run'><a href='#n5'>5</a></p>
-<p id='n6' class='pln'><a href='#n6'>6</a></p>
-<p id='n7' class='stm mis'><a href='#n7'>7</a></p>
+ <td class="linenos">
+<p id="n1" class="pln"><a href="#n1">1</a></p>
+<p id="n2" class="pln"><a href="#n2">2</a></p>
+<p id="n3" class="pln"><a href="#n3">3</a></p>
+<p id="n4" class="pln"><a href="#n4">4</a></p>
+<p id="n5" class="pln"><a href="#n5">5</a></p>
+<p id="n6" class="stm run hide_run"><a href="#n6">6</a></p>
+<p id="n7" class="pln"><a href="#n7">7</a></p>
+<p id="n8" class="stm run hide_run"><a href="#n8">8</a></p>
+<p id="n9" class="pln"><a href="#n9">9</a></p>
+<p id="n10" class="stm mis"><a href="#n10">10</a></p>
</td>
- <td class='text'>
-<p id='t1' class='pln'><span class='com'># A test file for HTML reporting by coverage.</span><span class='strut'>&nbsp;</span></p>
-<p id='t2' class='pln'><span class='strut'>&nbsp;</span></p>
-<p id='t3' class='stm run hide_run'><span class='key'>if</span> <span class='num'>1</span> <span class='op'>&lt;</span> <span class='num'>2</span><span class='op'>:</span><span class='strut'>&nbsp;</span></p>
-<p id='t4' class='pln'>&nbsp; &nbsp; <span class='com'># Needed a &lt; to look at HTML entities.</span><span class='strut'>&nbsp;</span></p>
-<p id='t5' class='stm run hide_run'>&nbsp; &nbsp; <span class='nam'>a</span> <span class='op'>=</span> <span class='num'>3</span><span class='strut'>&nbsp;</span></p>
-<p id='t6' class='pln'><span class='key'>else</span><span class='op'>:</span><span class='strut'>&nbsp;</span></p>
-<p id='t7' class='stm mis'>&nbsp; &nbsp; <span class='nam'>a</span> <span class='op'>=</span> <span class='num'>4</span><span class='strut'>&nbsp;</span></p>
+ <td class="text">
+<p id="t1" class="pln"><span class="com"># Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0</span><span class="strut">&nbsp;</span></p>
+<p id="t2" class="pln"><span class="com"># For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt</span><span class="strut">&nbsp;</span></p>
+<p id="t3" class="pln"><span class="strut">&nbsp;</span></p>
+<p id="t4" class="pln"><span class="com"># A test file for HTML reporting by coverage.py.</span><span class="strut">&nbsp;</span></p>
+<p id="t5" class="pln"><span class="strut">&nbsp;</span></p>
+<p id="t6" class="stm run hide_run"><span class="key">if</span> <span class="num">1</span> <span class="op">&lt;</span> <span class="num">2</span><span class="op">:</span><span class="strut">&nbsp;</span></p>
+<p id="t7" class="pln">&nbsp; &nbsp; <span class="com"># Needed a &lt; to look at HTML entities.</span><span class="strut">&nbsp;</span></p>
+<p id="t8" class="stm run hide_run">&nbsp; &nbsp; <span class="nam">a</span> <span class="op">=</span> <span class="num">3</span><span class="strut">&nbsp;</span></p>
+<p id="t9" class="pln"><span class="key">else</span><span class="op">:</span><span class="strut">&nbsp;</span></p>
+<p id="t10" class="stm mis">&nbsp; &nbsp; <span class="nam">a</span> <span class="op">=</span> <span class="num">4</span><span class="strut">&nbsp;</span></p>
</td>
</tr>
</table>
</div>
-<div id='footer'>
- <div class='content'>
+<div id="footer">
+ <div class="content">
<p>
- <a class='nav' href='index.html'>&#xab; index</a> &nbsp; &nbsp; <a class='nav' href='http://nedbatchelder.com/code/coverage/4.0a1'>coverage.py v4.0a1</a>
+ <a class="nav" href="index.html">&#xab; index</a> &nbsp; &nbsp; <a class="nav" href="https://coverage.readthedocs.org/en/coverage-4.0a7">coverage.py v4.0a7</a>,
+ created at 2015-07-24 09:04
</p>
</div>
</div>
diff --git a/tests/farm/html/gold_styled/index.html b/tests/farm/html/gold_styled/index.html
index 87a8c3db..7209b64a 100644
--- a/tests/farm/html/gold_styled/index.html
+++ b/tests/farm/html/gold_styled/index.html
@@ -1,30 +1,33 @@
+
+
+
<!DOCTYPE html>
<html>
<head>
- <meta http-equiv='Content-Type' content='text/html; charset=utf-8'>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>Coverage report</title>
- <link rel='stylesheet' href='style.css' type='text/css'>
+ <link rel="stylesheet" href="style.css" type="text/css">
- <link rel='stylesheet' href='extra.css' type='text/css'>
+ <link rel="stylesheet" href="extra.css" type="text/css">
- <script type='text/javascript' src='jquery.min.js'></script>
- <script type='text/javascript' src='jquery.debounce.min.js'></script>
- <script type='text/javascript' src='jquery.tablesorter.min.js'></script>
- <script type='text/javascript' src='jquery.hotkeys.js'></script>
- <script type='text/javascript' src='coverage_html.js'></script>
- <script type='text/javascript'>
+ <script type="text/javascript" src="jquery.min.js"></script>
+ <script type="text/javascript" src="jquery.debounce.min.js"></script>
+ <script type="text/javascript" src="jquery.tablesorter.min.js"></script>
+ <script type="text/javascript" src="jquery.hotkeys.js"></script>
+ <script type="text/javascript" src="coverage_html.js"></script>
+ <script type="text/javascript">
jQuery(document).ready(coverage.index_ready);
</script>
</head>
-<body class='indexfile'>
+<body class="indexfile">
-<div id='header'>
- <div class='content'>
+<div id="header">
+ <div class="content">
<h1>Coverage report:
- <span class='pc_cov'>67%</span>
+ <span class="pc_cov">67%</span>
</h1>
- <img id='keyboard_icon' src='keybd_closed.png' alt='Show keyboard shortcuts' />
+ <img id="keyboard_icon" src="keybd_closed.png" alt="Show keyboard shortcuts" />
<form id="filter_container">
<input id="filter" type="text" value="" placeholder="filter..." />
@@ -32,54 +35,54 @@
</div>
</div>
-<div class='help_panel'>
- <img id='panel_icon' src='keybd_open.png' alt='Hide keyboard shortcuts' />
- <p class='legend'>Hot-keys on this page</p>
+<div class="help_panel">
+ <img id="panel_icon" src="keybd_open.png" alt="Hide keyboard shortcuts" />
+ <p class="legend">Hot-keys on this page</p>
<div>
- <p class='keyhelp'>
- <span class='key'>n</span>
- <span class='key'>s</span>
- <span class='key'>m</span>
- <span class='key'>x</span>
+ <p class="keyhelp">
+ <span class="key">n</span>
+ <span class="key">s</span>
+ <span class="key">m</span>
+ <span class="key">x</span>
- <span class='key'>c</span> &nbsp; change column sorting
+ <span class="key">c</span> &nbsp; change column sorting
</p>
</div>
</div>
-<div id='index'>
- <table class='index'>
+<div id="index">
+ <table class="index">
<thead>
- <tr class='tablehead' title='Click to sort'>
- <th class='name left headerSortDown shortkey_n'>Module</th>
- <th class='shortkey_s'>statements</th>
- <th class='shortkey_m'>missing</th>
- <th class='shortkey_x'>excluded</th>
+ <tr class="tablehead" title="Click to sort">
+ <th class="name left headerSortDown shortkey_n">Module</th>
+ <th class="shortkey_s">statements</th>
+ <th class="shortkey_m">missing</th>
+ <th class="shortkey_x">excluded</th>
- <th class='right shortkey_c'>coverage</th>
+ <th class="right shortkey_c">coverage</th>
</tr>
</thead>
<tfoot>
- <tr class='total'>
- <td class='name left'>Total</td>
+ <tr class="total">
+ <td class="name left">Total</td>
<td>3</td>
<td>1</td>
<td>0</td>
- <td class='right' data-ratio='2 3'>67%</td>
+ <td class="right" data-ratio="2 3">67%</td>
</tr>
</tfoot>
<tbody>
- <tr class='file'>
- <td class='name left'><a href='a.html'>a</a></td>
+ <tr class="file">
+ <td class="name left"><a href="a_py.html">a.py</a></td>
<td>3</td>
<td>1</td>
<td>0</td>
- <td class='right' data-ratio='2 3'>67%</td>
+ <td class="right" data-ratio="2 3">67%</td>
</tr>
</tbody>
@@ -90,10 +93,11 @@
</p>
</div>
-<div id='footer'>
- <div class='content'>
+<div id="footer">
+ <div class="content">
<p>
- <a class='nav' href='http://nedbatchelder.com/code/coverage/4.0a1'>coverage.py v4.0a1</a>
+ <a class="nav" href="https://coverage.readthedocs.org/en/coverage-4.0a7">coverage.py v4.0a7</a>,
+ created at 2015-07-24 09:31
</p>
</div>
</div>
diff --git a/tests/farm/html/gold_styled/style.css b/tests/farm/html/gold_styled/style.css
index 038335c1..2dfb8f65 100644
--- a/tests/farm/html/gold_styled/style.css
+++ b/tests/farm/html/gold_styled/style.css
@@ -1,4 +1,7 @@
-/* CSS styles for Coverage. */
+/* Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 */
+/* For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt */
+
+/* CSS styles for coverage.py. */
/* Page-wide styles */
html, body, h1, h2, h3, p, table, td, th {
margin: 0;
diff --git a/tests/farm/html/gold_unicode/index.html b/tests/farm/html/gold_unicode/index.html
index dae53ea0..58529525 100644
--- a/tests/farm/html/gold_unicode/index.html
+++ b/tests/farm/html/gold_unicode/index.html
@@ -1,28 +1,31 @@
+
+
+
<!DOCTYPE html>
<html>
<head>
- <meta http-equiv='Content-Type' content='text/html; charset=utf-8'>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>Coverage report</title>
- <link rel='stylesheet' href='style.css' type='text/css'>
+ <link rel="stylesheet" href="style.css" type="text/css">
- <script type='text/javascript' src='jquery.min.js'></script>
- <script type='text/javascript' src='jquery.debounce.min.js'></script>
- <script type='text/javascript' src='jquery.tablesorter.min.js'></script>
- <script type='text/javascript' src='jquery.hotkeys.js'></script>
- <script type='text/javascript' src='coverage_html.js'></script>
- <script type='text/javascript'>
+ <script type="text/javascript" src="jquery.min.js"></script>
+ <script type="text/javascript" src="jquery.debounce.min.js"></script>
+ <script type="text/javascript" src="jquery.tablesorter.min.js"></script>
+ <script type="text/javascript" src="jquery.hotkeys.js"></script>
+ <script type="text/javascript" src="coverage_html.js"></script>
+ <script type="text/javascript">
jQuery(document).ready(coverage.index_ready);
</script>
</head>
-<body class='indexfile'>
+<body class="indexfile">
-<div id='header'>
- <div class='content'>
+<div id="header">
+ <div class="content">
<h1>Coverage report:
- <span class='pc_cov'>100%</span>
+ <span class="pc_cov">100%</span>
</h1>
- <img id='keyboard_icon' src='keybd_closed.png' alt='Show keyboard shortcuts' />
+ <img id="keyboard_icon" src="keybd_closed.png" alt="Show keyboard shortcuts" />
<form id="filter_container">
<input id="filter" type="text" value="" placeholder="filter..." />
@@ -30,54 +33,54 @@
</div>
</div>
-<div class='help_panel'>
- <img id='panel_icon' src='keybd_open.png' alt='Hide keyboard shortcuts' />
- <p class='legend'>Hot-keys on this page</p>
+<div class="help_panel">
+ <img id="panel_icon" src="keybd_open.png" alt="Hide keyboard shortcuts" />
+ <p class="legend">Hot-keys on this page</p>
<div>
- <p class='keyhelp'>
- <span class='key'>n</span>
- <span class='key'>s</span>
- <span class='key'>m</span>
- <span class='key'>x</span>
+ <p class="keyhelp">
+ <span class="key">n</span>
+ <span class="key">s</span>
+ <span class="key">m</span>
+ <span class="key">x</span>
- <span class='key'>c</span> &nbsp; change column sorting
+ <span class="key">c</span> &nbsp; change column sorting
</p>
</div>
</div>
-<div id='index'>
- <table class='index'>
+<div id="index">
+ <table class="index">
<thead>
- <tr class='tablehead' title='Click to sort'>
- <th class='name left headerSortDown shortkey_n'>Module</th>
- <th class='shortkey_s'>statements</th>
- <th class='shortkey_m'>missing</th>
- <th class='shortkey_x'>excluded</th>
+ <tr class="tablehead" title="Click to sort">
+ <th class="name left headerSortDown shortkey_n">Module</th>
+ <th class="shortkey_s">statements</th>
+ <th class="shortkey_m">missing</th>
+ <th class="shortkey_x">excluded</th>
- <th class='right shortkey_c'>coverage</th>
+ <th class="right shortkey_c">coverage</th>
</tr>
</thead>
<tfoot>
- <tr class='total'>
- <td class='name left'>Total</td>
+ <tr class="total">
+ <td class="name left">Total</td>
<td>2</td>
<td>0</td>
<td>0</td>
- <td class='right' data-ratio='2 2'>100%</td>
+ <td class="right" data-ratio="2 2">100%</td>
</tr>
</tfoot>
<tbody>
- <tr class='file'>
- <td class='name left'><a href='unicode.html'>unicode</a></td>
+ <tr class="file">
+ <td class="name left"><a href="unicode_py.html">unicode.py</a></td>
<td>2</td>
<td>0</td>
<td>0</td>
- <td class='right' data-ratio='2 2'>100%</td>
+ <td class="right" data-ratio="2 2">100%</td>
</tr>
</tbody>
@@ -88,10 +91,11 @@
</p>
</div>
-<div id='footer'>
- <div class='content'>
+<div id="footer">
+ <div class="content">
<p>
- <a class='nav' href='http://nedbatchelder.com/code/coverage/4.0a1'>coverage.py v4.0a1</a>
+ <a class="nav" href="https://coverage.readthedocs.org/en/coverage-4.0a7">coverage.py v4.0a7</a>,
+ created at 2015-07-24 09:31
</p>
</div>
</div>
diff --git a/tests/farm/html/gold_unicode/unicode_py.html b/tests/farm/html/gold_unicode/unicode_py.html
index d67af56d..d0d58afc 100644
--- a/tests/farm/html/gold_unicode/unicode_py.html
+++ b/tests/farm/html/gold_unicode/unicode_py.html
@@ -1,92 +1,102 @@
+
+
+
<!DOCTYPE html>
<html>
<head>
- <meta http-equiv='Content-Type' content='text/html; charset=utf-8'>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
- <meta http-equiv='X-UA-Compatible' content='IE=emulateIE7' />
- <title>Coverage for unicode: 100%</title>
- <link rel='stylesheet' href='style.css' type='text/css'>
+ <meta http-equiv="X-UA-Compatible" content="IE=emulateIE7" />
+ <title>Coverage for unicode.py: 100%</title>
+ <link rel="stylesheet" href="style.css" type="text/css">
- <script type='text/javascript' src='jquery.min.js'></script>
- <script type='text/javascript' src='jquery.hotkeys.js'></script>
- <script type='text/javascript' src='jquery.isonscreen.js'></script>
- <script type='text/javascript' src='coverage_html.js'></script>
- <script type='text/javascript'>
+ <script type="text/javascript" src="jquery.min.js"></script>
+ <script type="text/javascript" src="jquery.hotkeys.js"></script>
+ <script type="text/javascript" src="jquery.isonscreen.js"></script>
+ <script type="text/javascript" src="coverage_html.js"></script>
+ <script type="text/javascript">
jQuery(document).ready(coverage.pyfile_ready);
</script>
</head>
-<body class='pyfile'>
+<body class="pyfile">
-<div id='header'>
- <div class='content'>
- <h1>Coverage for <b>unicode</b> :
- <span class='pc_cov'>100%</span>
+<div id="header">
+ <div class="content">
+ <h1>Coverage for <b>unicode.py</b> :
+ <span class="pc_cov">100%</span>
</h1>
- <img id='keyboard_icon' src='keybd_closed.png' alt='Show keyboard shortcuts' />
+ <img id="keyboard_icon" src="keybd_closed.png" alt="Show keyboard shortcuts" />
- <h2 class='stats'>
+ <h2 class="stats">
2 statements &nbsp;
- <span class='run hide_run shortkey_r button_toggle_run'>2 run</span>
- <span class='mis shortkey_m button_toggle_mis'>0 missing</span>
- <span class='exc shortkey_x button_toggle_exc'>0 excluded</span>
+ <span class="run hide_run shortkey_r button_toggle_run">2 run</span>
+ <span class="mis shortkey_m button_toggle_mis">0 missing</span>
+ <span class="exc shortkey_x button_toggle_exc">0 excluded</span>
</h2>
</div>
</div>
-<div class='help_panel'>
- <img id='panel_icon' src='keybd_open.png' alt='Hide keyboard shortcuts' />
-<p class='legend'>Hot-keys on this page</p>
+<div class="help_panel">
+ <img id="panel_icon" src="keybd_open.png" alt="Hide keyboard shortcuts" />
+<p class="legend">Hot-keys on this page</p>
<div>
-<p class='keyhelp'>
- <span class='key'>r</span>
- <span class='key'>m</span>
- <span class='key'>x</span>
- <span class='key'>p</span> &nbsp; toggle line displays
+<p class="keyhelp">
+ <span class="key">r</span>
+ <span class="key">m</span>
+ <span class="key">x</span>
+ <span class="key">p</span> &nbsp; toggle line displays
</p>
-<p class='keyhelp'>
- <span class='key'>j</span>
- <span class='key'>k</span> &nbsp; next/prev highlighted chunk
+<p class="keyhelp">
+ <span class="key">j</span>
+ <span class="key">k</span> &nbsp; next/prev highlighted chunk
</p>
-<p class='keyhelp'>
- <span class='key'>0</span> &nbsp; (zero) top of page
+<p class="keyhelp">
+ <span class="key">0</span> &nbsp; (zero) top of page
</p>
-<p class='keyhelp'>
- <span class='key'>1</span> &nbsp; (one) first highlighted chunk
+<p class="keyhelp">
+ <span class="key">1</span> &nbsp; (one) first highlighted chunk
</p>
</div>
</div>
-<div id='source'>
+<div id="source">
<table>
<tr>
- <td class='linenos'>
-<p id='n1' class='pln'><a href='#n1'>1</a></p>
-<p id='n2' class='pln'><a href='#n2'>2</a></p>
-<p id='n3' class='pln'><a href='#n3'>3</a></p>
-<p id='n4' class='stm run hide_run'><a href='#n4'>4</a></p>
-<p id='n5' class='stm run hide_run'><a href='#n5'>5</a></p>
+ <td class="linenos">
+<p id="n1" class="pln"><a href="#n1">1</a></p>
+<p id="n2" class="pln"><a href="#n2">2</a></p>
+<p id="n3" class="pln"><a href="#n3">3</a></p>
+<p id="n4" class="pln"><a href="#n4">4</a></p>
+<p id="n5" class="pln"><a href="#n5">5</a></p>
+<p id="n6" class="pln"><a href="#n6">6</a></p>
+<p id="n7" class="stm run hide_run"><a href="#n7">7</a></p>
+<p id="n8" class="stm run hide_run"><a href="#n8">8</a></p>
</td>
- <td class='text'>
-<p id='t1' class='pln'><span class='com'># A python source file with exotic characters</span><span class='strut'>&nbsp;</span></p>
-<p id='t2' class='pln'><span class='com'># -*- coding: utf-8 -*-</span><span class='strut'>&nbsp;</span></p>
-<p id='t3' class='pln'><span class='strut'>&nbsp;</span></p>
-<p id='t4' class='stm run hide_run'><span class='nam'>upside_down</span> <span class='op'>=</span> <span class='str'>&quot;&#654;d&#729;&#477;b&#592;&#633;&#477;&#652;o&#596;&quot;</span><span class='strut'>&nbsp;</span></p>
-<p id='t5' class='stm run hide_run'><span class='nam'>surrogate</span> <span class='op'>=</span> <span class='str'>&quot;db40,dd00: x&#917760;&quot;</span><span class='strut'>&nbsp;</span></p>
+ <td class="text">
+<p id="t1" class="pln"><span class="com"># -*- coding: utf-8 -*-</span><span class="strut">&nbsp;</span></p>
+<p id="t2" class="pln"><span class="com"># Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0</span><span class="strut">&nbsp;</span></p>
+<p id="t3" class="pln"><span class="com"># For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt</span><span class="strut">&nbsp;</span></p>
+<p id="t4" class="pln"><span class="strut">&nbsp;</span></p>
+<p id="t5" class="pln"><span class="com"># A Python source file with exotic characters.</span><span class="strut">&nbsp;</span></p>
+<p id="t6" class="pln"><span class="strut">&nbsp;</span></p>
+<p id="t7" class="stm run hide_run"><span class="nam">upside_down</span> <span class="op">=</span> <span class="str">&quot;&#654;d&#729;&#477;b&#592;&#633;&#477;&#652;o&#596;&quot;</span><span class="strut">&nbsp;</span></p>
+<p id="t8" class="stm run hide_run"><span class="nam">surrogate</span> <span class="op">=</span> <span class="str">&quot;db40,dd00: x&#917760;&quot;</span><span class="strut">&nbsp;</span></p>
</td>
</tr>
</table>
</div>
-<div id='footer'>
- <div class='content'>
+<div id="footer">
+ <div class="content">
<p>
- <a class='nav' href='index.html'>&#xab; index</a> &nbsp; &nbsp; <a class='nav' href='http://nedbatchelder.com/code/coverage/4.0a1'>coverage.py v4.0a1</a>
+ <a class="nav" href="index.html">&#xab; index</a> &nbsp; &nbsp; <a class="nav" href="https://coverage.readthedocs.org/en/coverage-4.0a7">coverage.py v4.0a7</a>,
+ created at 2015-07-24 09:04
</p>
</div>
</div>
diff --git a/tests/farm/html/gold_x_xml/coverage.xml b/tests/farm/html/gold_x_xml/coverage.xml
index 1511ae89..487e850d 100644
--- a/tests/farm/html/gold_x_xml/coverage.xml
+++ b/tests/farm/html/gold_x_xml/coverage.xml
@@ -1,23 +1,22 @@
-<?xml version="1.0" ?>
-<!DOCTYPE coverage
- SYSTEM 'http://cobertura.sourceforge.net/xml/coverage-03.dtd'>
-<coverage branch-rate="0" line-rate="0.6667" timestamp="1253972570431" version="3.1b1">
- <!-- Generated by coverage.py: https://coverage.readthedocs.org/VER -->
- <sources>
- <source></source>
- </sources>
- <packages>
- <package branch-rate="0" complexity="0" line-rate="0.6667" name=".">
- <classes>
- <class branch-rate="0" complexity="0" filename="a.py" line-rate="0.6667" name="a.py">
- <methods/>
- <lines>
- <line hits="1" number="3"/>
- <line hits="1" number="5"/>
- <line hits="0" number="7"/>
- </lines>
- </class>
- </classes>
- </package>
- </packages>
-</coverage>
+<?xml version="1.0" ?>
+<coverage branch-rate="0" line-rate="0.6667" timestamp="1437745880639" version="4.0a7">
+ <!-- Generated by coverage.py: https://coverage.readthedocs.org/en/coverage-4.0a7 -->
+ <!-- Based on https://raw.githubusercontent.com/cobertura/web/f0366e5e2cf18f111cbd61fc34ef720a6584ba02/htdocs/xml/coverage-03.dtd -->
+ <sources>
+ <source>/Users/ned/coverage/trunk/tests/farm/html/src</source>
+ </sources>
+ <packages>
+ <package branch-rate="0" complexity="0" line-rate="0.6667" name=".">
+ <classes>
+ <class branch-rate="0" complexity="0" filename="a.py" line-rate="0.6667" name="a.py">
+ <methods/>
+ <lines>
+ <line hits="1" number="6"/>
+ <line hits="1" number="8"/>
+ <line hits="0" number="10"/>
+ </lines>
+ </class>
+ </classes>
+ </package>
+ </packages>
+</coverage>
diff --git a/tests/farm/html/gold_y_xml_branch/coverage.xml b/tests/farm/html/gold_y_xml_branch/coverage.xml
index 8e098fe8..a6acbfa1 100644
--- a/tests/farm/html/gold_y_xml_branch/coverage.xml
+++ b/tests/farm/html/gold_y_xml_branch/coverage.xml
@@ -1,25 +1,24 @@
-<?xml version="1.0" ?>
-<!DOCTYPE coverage
- SYSTEM 'http://cobertura.sourceforge.net/xml/coverage-03.dtd'>
-<coverage branch-rate="0.5" line-rate="0.8" timestamp="1259288252325" version="3.2b4">
- <!-- Generated by coverage.py: https://coverage.readthedocs.org/VER -->
- <sources>
- <source></source>
- </sources>
- <packages>
- <package branch-rate="0.5" complexity="0" line-rate="0.8" name=".">
- <classes>
- <class branch-rate="0.5" complexity="0" filename="y.py" line-rate="0.8" name="y.py">
- <methods/>
- <lines>
- <line hits="1" number="3"/>
- <line branch="true" condition-coverage="50% (1/2)" hits="1" number="4"/>
- <line hits="1" number="5"/>
- <line hits="0" number="7"/>
- <line hits="1" number="9"/>
- </lines>
- </class>
- </classes>
- </package>
- </packages>
-</coverage>
+<?xml version="1.0" ?>
+<coverage branch-rate="0.5" line-rate="0.8" timestamp="1437745880882" version="4.0a7">
+ <!-- Generated by coverage.py: https://coverage.readthedocs.org/en/coverage-4.0a7 -->
+ <!-- Based on https://raw.githubusercontent.com/cobertura/web/f0366e5e2cf18f111cbd61fc34ef720a6584ba02/htdocs/xml/coverage-03.dtd -->
+ <sources>
+ <source>/Users/ned/coverage/trunk/tests/farm/html/src</source>
+ </sources>
+ <packages>
+ <package branch-rate="0.5" complexity="0" line-rate="0.8" name=".">
+ <classes>
+ <class branch-rate="0.5" complexity="0" filename="y.py" line-rate="0.8" name="y.py">
+ <methods/>
+ <lines>
+ <line hits="1" number="6"/>
+ <line branch="true" condition-coverage="50% (1/2)" hits="1" missing-branches="10" number="7"/>
+ <line hits="1" number="8"/>
+ <line hits="0" number="10"/>
+ <line hits="1" number="12"/>
+ </lines>
+ </class>
+ </classes>
+ </package>
+ </packages>
+</coverage>
diff --git a/tests/farm/html/othersrc/other.py b/tests/farm/html/othersrc/other.py
index 6d3f86e7..bf0304d2 100644
--- a/tests/farm/html/othersrc/other.py
+++ b/tests/farm/html/othersrc/other.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
+
# A file in another directory. We're checking that it ends up in the
# HTML report.
diff --git a/tests/farm/html/run_a.py b/tests/farm/html/run_a.py
index 7963d2e5..1ec6220f 100644
--- a/tests/farm/html/run_a.py
+++ b/tests/farm/html/run_a.py
@@ -1,7 +1,10 @@
+# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
+# For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt
+
def html_it():
- """Run coverage and make an HTML report for a."""
+ """Run coverage.py and make an HTML report for a."""
import coverage
- cov = coverage.coverage()
+ cov = coverage.Coverage()
cov.start()
import a # pragma: nested
cov.stop() # pragma: nested
@@ -13,14 +16,14 @@ runfunc(html_it, rundir="src")
# and check that certain key strings are in the output.
compare("gold_a", "html_a", size_within=10, file_pattern="*.html")
contains("html_a/a_py.html",
- "<span class='key'>if</span> <span class='num'>1</span> <span class='op'>&lt;</span> <span class='num'>2</span>",
- "&nbsp; &nbsp; <span class='nam'>a</span> <span class='op'>=</span> <span class='num'>3</span>",
- "<span class='pc_cov'>67%</span>"
+ '<span class="key">if</span> <span class="num">1</span> <span class="op">&lt;</span> <span class="num">2</span>',
+ '&nbsp; &nbsp; <span class="nam">a</span> <span class="op">=</span> <span class="num">3</span>',
+ '<span class="pc_cov">67%</span>'
)
contains("html_a/index.html",
- "<a href='a_py.html'>a.py</a>",
- "<span class='pc_cov'>67%</span>",
- "<td class='right' data-ratio='2 3'>67%</td>",
+ '<a href="a_py.html">a.py</a>',
+ '<span class="pc_cov">67%</span>',
+ '<td class="right" data-ratio="2 3">67%</td>',
)
clean("html_a")
diff --git a/tests/farm/html/run_a_xml_1.py b/tests/farm/html/run_a_xml_1.py
index 593beae2..e1bf1828 100644
--- a/tests/farm/html/run_a_xml_1.py
+++ b/tests/farm/html/run_a_xml_1.py
@@ -1,15 +1,18 @@
+# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
+# For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt
+
source_path = None
def html_it():
- """Run coverage and make an XML report for a."""
- import coverage
- cov = coverage.coverage()
+ """Run coverage.py and make an XML report for a."""
+ import coverage, coverage.files
+ cov = coverage.Coverage()
cov.start()
import a # pragma: nested
cov.stop() # pragma: nested
cov.xml_report(a, outfile="../xml_1/coverage.xml")
global source_path
- source_path = cov.file_locator.relative_dir.rstrip('/')
+ source_path = coverage.files.relative_directory().rstrip('/')
import os
if not os.path.exists("xml_1"):
diff --git a/tests/farm/html/run_a_xml_2.py b/tests/farm/html/run_a_xml_2.py
index 4d691b3b..f53e04aa 100644
--- a/tests/farm/html/run_a_xml_2.py
+++ b/tests/farm/html/run_a_xml_2.py
@@ -1,15 +1,18 @@
+# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
+# For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt
+
source_path = None
def html_it():
- """Run coverage and make an XML report for a."""
- import coverage
- cov = coverage.coverage(config_file="run_a_xml_2.ini")
+ """Run coverage.py and make an XML report for a."""
+ import coverage, coverage.files
+ cov = coverage.Coverage(config_file="run_a_xml_2.ini")
cov.start()
import a # pragma: nested
cov.stop() # pragma: nested
cov.xml_report(a)
global source_path
- source_path = cov.file_locator.relative_dir.rstrip('/')
+ source_path = coverage.files.relative_directory().rstrip('/')
import os
if not os.path.exists("xml_2"):
diff --git a/tests/farm/html/run_b_branch.py b/tests/farm/html/run_b_branch.py
index c92252ce..2451f069 100644
--- a/tests/farm/html/run_b_branch.py
+++ b/tests/farm/html/run_b_branch.py
@@ -1,7 +1,10 @@
+# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
+# For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt
+
def html_it():
- """Run coverage with branches and make an HTML report for b."""
+ """Run coverage.py with branches and make an HTML report for b."""
import coverage
- cov = coverage.coverage(branch=True)
+ cov = coverage.Coverage(branch=True)
cov.start()
import b # pragma: nested
cov.stop() # pragma: nested
@@ -13,17 +16,17 @@ runfunc(html_it, rundir="src")
# and check that certain key strings are in the output.
compare("gold_b_branch", "html_b_branch", size_within=10, file_pattern="*.html")
contains("html_b_branch/b_py.html",
- "<span class='key'>if</span> <span class='nam'>x</span> <span class='op'>&lt;</span> <span class='num'>2</span>",
- "&nbsp; &nbsp; <span class='nam'>a</span> <span class='op'>=</span> <span class='num'>3</span>",
- "<span class='pc_cov'>70%</span>",
- "<span class='annotate' title='no jump to this line number'>8</span>",
- "<span class='annotate' title='no jump to this line number'>exit</span>",
- "<span class='annotate' title='no jumps to these line numbers'>23&nbsp;&nbsp; 25</span>",
+ '<span class="key">if</span> <span class="nam">x</span> <span class="op">&lt;</span> <span class="num">2</span>',
+ '&nbsp; &nbsp; <span class="nam">a</span> <span class="op">=</span> <span class="num">3</span>',
+ '<span class="pc_cov">70%</span>',
+ '<span class="annotate" title="no jump to this line number">11</span>',
+ '<span class="annotate" title="no jump to this line number">exit</span>',
+ '<span class="annotate" title="no jumps to these line numbers">26&nbsp;&nbsp; 28</span>',
)
contains("html_b_branch/index.html",
- "<a href='b_py.html'>b.py</a>",
- "<span class='pc_cov'>70%</span>",
- "<td class='right' data-ratio='16 23'>70%</td>",
+ '<a href="b_py.html">b.py</a>',
+ '<span class="pc_cov">70%</span>',
+ '<td class="right" data-ratio="16 23">70%</td>',
)
clean("html_b_branch")
diff --git a/tests/farm/html/run_bom.py b/tests/farm/html/run_bom.py
index 96949756..a34fab9f 100644
--- a/tests/farm/html/run_bom.py
+++ b/tests/farm/html/run_bom.py
@@ -1,9 +1,12 @@
+# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
+# For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt
+
import sys
def html_it():
- """Run coverage and make an HTML report for bom.py."""
+ """Run coverage.py and make an HTML report for bom.py."""
import coverage
- cov = coverage.coverage()
+ cov = coverage.Coverage()
cov.start()
import bom # pragma: nested
cov.stop() # pragma: nested
@@ -15,7 +18,7 @@ runfunc(html_it, rundir="src")
# and check that certain key strings are in the output.
compare("gold_bom", "html_bom", size_within=10, file_pattern="*.html")
contains("html_bom/bom_py.html",
- "<span class='str'>&quot;3&#215;4 = 12, &#247;2 = 6&#177;0&quot;</span>",
+ '<span class="str">&quot;3&#215;4 = 12, &#247;2 = 6&#177;0&quot;</span>',
)
clean("html_bom")
diff --git a/tests/farm/html/run_isolatin1.py b/tests/farm/html/run_isolatin1.py
index bf3746d2..6729a275 100644
--- a/tests/farm/html/run_isolatin1.py
+++ b/tests/farm/html/run_isolatin1.py
@@ -1,9 +1,12 @@
+# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
+# For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt
+
import sys
def html_it():
- """Run coverage and make an HTML report for isolatin1.py."""
+ """Run coverage.py and make an HTML report for isolatin1.py."""
import coverage
- cov = coverage.coverage()
+ cov = coverage.Coverage()
cov.start()
import isolatin1 # pragma: nested
cov.stop() # pragma: nested
@@ -15,7 +18,7 @@ runfunc(html_it, rundir="src")
# and check that certain key strings are in the output.
compare("gold_isolatin1", "html_isolatin1", size_within=10, file_pattern="*.html")
contains("html_isolatin1/isolatin1_py.html",
- "<span class='str'>&quot;3&#215;4 = 12, &#247;2 = 6&#177;0&quot;</span>",
+ '<span class="str">&quot;3&#215;4 = 12, &#247;2 = 6&#177;0&quot;</span>',
)
clean("html_isolatin1")
diff --git a/tests/farm/html/run_omit_1.py b/tests/farm/html/run_omit_1.py
index 102aeb2a..137d37ca 100644
--- a/tests/farm/html/run_omit_1.py
+++ b/tests/farm/html/run_omit_1.py
@@ -1,7 +1,10 @@
+# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
+# For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt
+
def html_it():
- """Run coverage and make an HTML report for main."""
+ """Run coverage.py and make an HTML report for main."""
import coverage
- cov = coverage.coverage(include=["./*"])
+ cov = coverage.Coverage(include=["./*"])
cov.start()
import main # pragma: nested
cov.stop() # pragma: nested
diff --git a/tests/farm/html/run_omit_2.py b/tests/farm/html/run_omit_2.py
index a149c518..dd021b96 100644
--- a/tests/farm/html/run_omit_2.py
+++ b/tests/farm/html/run_omit_2.py
@@ -1,7 +1,10 @@
+# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
+# For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt
+
def html_it():
- """Run coverage and make an HTML report for main."""
+ """Run coverage.py and make an HTML report for main."""
import coverage
- cov = coverage.coverage(include=["./*"])
+ cov = coverage.Coverage(include=["./*"])
cov.start()
import main # pragma: nested
cov.stop() # pragma: nested
diff --git a/tests/farm/html/run_omit_3.py b/tests/farm/html/run_omit_3.py
index 07b38a8a..96ed0822 100644
--- a/tests/farm/html/run_omit_3.py
+++ b/tests/farm/html/run_omit_3.py
@@ -1,7 +1,10 @@
+# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
+# For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt
+
def html_it():
- """Run coverage and make an HTML report for main."""
+ """Run coverage.py and make an HTML report for main."""
import coverage
- cov = coverage.coverage(include=["./*"])
+ cov = coverage.Coverage(include=["./*"])
cov.start()
import main # pragma: nested
cov.stop() # pragma: nested
diff --git a/tests/farm/html/run_omit_4.py b/tests/farm/html/run_omit_4.py
index 8c0c789c..b212ef40 100644
--- a/tests/farm/html/run_omit_4.py
+++ b/tests/farm/html/run_omit_4.py
@@ -1,7 +1,10 @@
+# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
+# For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt
+
def html_it():
- """Run coverage and make an HTML report for main."""
+ """Run coverage.py and make an HTML report for main."""
import coverage
- cov = coverage.coverage(config_file="omit4.ini", include=["./*"])
+ cov = coverage.Coverage(config_file="omit4.ini", include=["./*"])
cov.start()
import main # pragma: nested
cov.stop() # pragma: nested
diff --git a/tests/farm/html/run_omit_5.py b/tests/farm/html/run_omit_5.py
index 4ba5e50c..ed61d209 100644
--- a/tests/farm/html/run_omit_5.py
+++ b/tests/farm/html/run_omit_5.py
@@ -1,7 +1,10 @@
+# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
+# For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt
+
def html_it():
- """Run coverage and make an HTML report for main."""
+ """Run coverage.py and make an HTML report for main."""
import coverage
- cov = coverage.coverage(config_file="omit5.ini", include=["./*"])
+ cov = coverage.Coverage(config_file="omit5.ini", include=["./*"])
cov.start()
import main # pragma: nested
cov.stop() # pragma: nested
diff --git a/tests/farm/html/run_other.py b/tests/farm/html/run_other.py
index 05efa0fc..92753d4b 100644
--- a/tests/farm/html/run_other.py
+++ b/tests/farm/html/run_other.py
@@ -1,7 +1,10 @@
+# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
+# For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt
+
def html_it():
- """Run coverage and make an HTML report for everything."""
+ """Run coverage.py and make an HTML report for everything."""
import coverage
- cov = coverage.coverage(include=["./*", "../othersrc/*"])
+ cov = coverage.Coverage(include=["./*", "../othersrc/*"])
cov.start()
import here # pragma: nested
cov.stop() # pragma: nested
@@ -19,8 +22,8 @@ for p in glob.glob("html_other/*_other_py.html"):
# and check that certain key strings are in the output.
compare("gold_other", "html_other", size_within=10, file_pattern="*.html")
contains("html_other/index.html",
- "<a href='here_py.html'>here.py</a>",
- "other_py.html'>", "other.py</a>",
+ '<a href="here_py.html">here.py</a>',
+ 'other_py.html">', 'other.py</a>',
)
clean("html_other")
diff --git a/tests/farm/html/run_partial.py b/tests/farm/html/run_partial.py
index 38790796..fedf7082 100644
--- a/tests/farm/html/run_partial.py
+++ b/tests/farm/html/run_partial.py
@@ -1,9 +1,12 @@
+# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
+# For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt
+
import sys
def html_it():
- """Run coverage and make an HTML report for partial."""
+ """Run coverage.py and make an HTML report for partial."""
import coverage
- cov = coverage.coverage(branch=True)
+ cov = coverage.Coverage(branch=True)
cov.start()
import partial # pragma: nested
cov.stop() # pragma: nested
@@ -15,17 +18,17 @@ runfunc(html_it, rundir="src")
# and check that certain key strings are in the output.
compare("gold_partial", "html_partial", size_within=10, file_pattern="*.html")
contains("html_partial/partial_py.html",
- "<p id='t5' class='stm run hide_run'>",
- "<p id='t8' class='stm run hide_run'>",
- "<p id='t11' class='stm run hide_run'>",
+ '<p id="t8" class="stm run hide_run">',
+ '<p id="t11" class="stm run hide_run">',
+ '<p id="t14" class="stm run hide_run">',
# The "if 0" and "if 1" statements are optimized away.
- "<p id='t14' class='pln'>",
+ '<p id="t17" class="pln">',
)
contains("html_partial/index.html",
- "<a href='partial_py.html'>partial.py</a>",
+ '<a href="partial_py.html">partial.py</a>',
)
contains("html_partial/index.html",
- "<span class='pc_cov'>100%</span>"
+ '<span class="pc_cov">100%</span>'
)
clean("html_partial")
diff --git a/tests/farm/html/run_styled.py b/tests/farm/html/run_styled.py
index a18096a4..eeebfe61 100644
--- a/tests/farm/html/run_styled.py
+++ b/tests/farm/html/run_styled.py
@@ -1,7 +1,10 @@
+# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
+# For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt
+
def html_it():
- """Run coverage and make an HTML report for a."""
+ """Run coverage.py and make an HTML report for a."""
import coverage
- cov = coverage.coverage()
+ cov = coverage.Coverage()
cov.start()
import a # pragma: nested
cov.stop() # pragma: nested
@@ -14,15 +17,15 @@ runfunc(html_it, rundir="src")
compare("gold_styled", "html_styled", size_within=10, file_pattern="*.html")
compare("gold_styled", "html_styled", size_within=10, file_pattern="*.css")
contains("html_styled/a_py.html",
- "<link rel='stylesheet' href='extra.css' type='text/css'>",
- "<span class='key'>if</span> <span class='num'>1</span> <span class='op'>&lt;</span> <span class='num'>2</span>",
- "&nbsp; &nbsp; <span class='nam'>a</span> <span class='op'>=</span> <span class='num'>3</span>",
- "<span class='pc_cov'>67%</span>"
+ '<link rel="stylesheet" href="extra.css" type="text/css">',
+ '<span class="key">if</span> <span class="num">1</span> <span class="op">&lt;</span> <span class="num">2</span>',
+ '&nbsp; &nbsp; <span class="nam">a</span> <span class="op">=</span> <span class="num">3</span>',
+ '<span class="pc_cov">67%</span>'
)
contains("html_styled/index.html",
- "<link rel='stylesheet' href='extra.css' type='text/css'>",
- "<a href='a_py.html'>a.py</a>",
- "<span class='pc_cov'>67%</span>"
+ '<link rel="stylesheet" href="extra.css" type="text/css">',
+ '<a href="a_py.html">a.py</a>',
+ '<span class="pc_cov">67%</span>'
)
clean("html_styled")
diff --git a/tests/farm/html/run_tabbed.py b/tests/farm/html/run_tabbed.py
index 679db2ed..6dacff53 100644
--- a/tests/farm/html/run_tabbed.py
+++ b/tests/farm/html/run_tabbed.py
@@ -1,7 +1,10 @@
+# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
+# For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt
+
def html_it():
- """Run coverage and make an HTML report for tabbed."""
+ """Run coverage.py and make an HTML report for tabbed."""
import coverage
- cov = coverage.coverage()
+ cov = coverage.Coverage()
cov.start()
import tabbed # pragma: nested
cov.stop() # pragma: nested
@@ -13,11 +16,11 @@ runfunc(html_it, rundir="src")
contains("src/tabbed.py", "\tif x:\t\t\t\t\t# look nice")
contains("html_tabbed/tabbed_py.html",
- ">&nbsp; &nbsp; &nbsp; &nbsp; <span class='key'>if</span> "
- "<span class='nam'>x</span><span class='op'>:</span>"
- "&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; "
- "&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp; "
- "<span class='com'># look nice</span>"
+ '>&nbsp; &nbsp; &nbsp; &nbsp; <span class="key">if</span> '
+ '<span class="nam">x</span><span class="op">:</span>'
+ '&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; '
+ '&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp; '
+ '<span class="com"># look nice</span>'
)
doesnt_contain("html_tabbed/tabbed_py.html", "\t")
diff --git a/tests/farm/html/run_unicode.py b/tests/farm/html/run_unicode.py
index ba34f63e..888d7e54 100644
--- a/tests/farm/html/run_unicode.py
+++ b/tests/farm/html/run_unicode.py
@@ -1,7 +1,10 @@
+# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
+# For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt
+
def html_it():
- """Run coverage and make an HTML report for unicode.py."""
+ """Run coverage.py and make an HTML report for unicode.py."""
import coverage
- cov = coverage.coverage()
+ cov = coverage.Coverage()
cov.start()
import unicode # pragma: nested
cov.stop() # pragma: nested
@@ -13,12 +16,12 @@ runfunc(html_it, rundir="src")
# and check that certain key strings are in the output.
compare("gold_unicode", "html_unicode", size_within=10, file_pattern="*.html")
contains("html_unicode/unicode_py.html",
- "<span class='str'>&quot;&#654;d&#729;&#477;b&#592;&#633;&#477;&#652;o&#596;&quot;</span>",
+ '<span class="str">&quot;&#654;d&#729;&#477;b&#592;&#633;&#477;&#652;o&#596;&quot;</span>',
)
contains_any("html_unicode/unicode_py.html",
- "<span class='str'>&quot;db40,dd00: x&#56128;&#56576;&quot;</span>",
- "<span class='str'>&quot;db40,dd00: x&#917760;&quot;</span>",
+ '<span class="str">&quot;db40,dd00: x&#56128;&#56576;&quot;</span>',
+ '<span class="str">&quot;db40,dd00: x&#917760;&quot;</span>',
)
clean("html_unicode")
diff --git a/tests/farm/html/run_y_xml_branch.py b/tests/farm/html/run_y_xml_branch.py
index 59228077..9d3aba2f 100644
--- a/tests/farm/html/run_y_xml_branch.py
+++ b/tests/farm/html/run_y_xml_branch.py
@@ -1,15 +1,18 @@
+# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
+# For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt
+
source_path = None
def xml_it():
- """Run coverage and make an XML report for y."""
- import coverage
- cov = coverage.coverage(branch=True)
+ """Run coverage.py and make an XML report for y."""
+ import coverage, coverage.files
+ cov = coverage.Coverage(branch=True)
cov.start()
import y # pragma: nested
cov.stop() # pragma: nested
cov.xml_report(y, outfile="../xml_branch/coverage.xml")
global source_path
- source_path = cov.file_locator.relative_dir.rstrip('/')
+ source_path = coverage.files.relative_directory().rstrip('/')
import os
if not os.path.exists("xml_branch"):
diff --git a/tests/farm/html/src/a.py b/tests/farm/html/src/a.py
index 9e71aebd..85764e21 100644
--- a/tests/farm/html/src/a.py
+++ b/tests/farm/html/src/a.py
@@ -1,4 +1,7 @@
-# A test file for HTML reporting by coverage.
+# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
+# For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt
+
+# A test file for HTML reporting by coverage.py.
if 1 < 2:
# Needed a < to look at HTML entities.
diff --git a/tests/farm/html/src/b.py b/tests/farm/html/src/b.py
index 3bf73a9f..cb673c22 100644
--- a/tests/farm/html/src/b.py
+++ b/tests/farm/html/src/b.py
@@ -1,4 +1,7 @@
-# A test file for HTML reporting by coverage.
+# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
+# For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt
+
+# A test file for HTML reporting by coverage.py.
def one(x):
# This will be a branch that misses the else.
diff --git a/tests/farm/html/src/bom.py b/tests/farm/html/src/bom.py
index 1aff5d52..21d26ca2 100644
--- a/tests/farm/html/src/bom.py
+++ b/tests/farm/html/src/bom.py
@@ -1,11 +1,14 @@
-# A Python source file in utf-8, with BOM.
-math = "3×4 = 12, ÷2 = 6±0"
+# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
+# For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt
-import sys
-
-if sys.version_info >= (3, 0):
- assert len(math) == 18
- assert len(math.encode('utf-8')) == 21
-else:
- assert len(math) == 21
- assert len(math.decode('utf-8')) == 18
+# A Python source file in utf-8, with BOM.
+math = "3×4 = 12, ÷2 = 6±0"
+
+import sys
+
+if sys.version_info >= (3, 0):
+ assert len(math) == 18
+ assert len(math.encode('utf-8')) == 21
+else:
+ assert len(math) == 21
+ assert len(math.decode('utf-8')) == 18
diff --git a/tests/farm/html/src/coverage.xml b/tests/farm/html/src/coverage.xml
index e20cdaec..ccffd201 100644
--- a/tests/farm/html/src/coverage.xml
+++ b/tests/farm/html/src/coverage.xml
@@ -1,6 +1,6 @@
<?xml version="1.0" ?>
<!DOCTYPE coverage
- SYSTEM 'http://cobertura.sourceforge.net/xml/coverage-03.dtd'>
+ SYSTEM 'https://raw.githubusercontent.com/cobertura/web/f0366e5e2cf18f111cbd61fc34ef720a6584ba02/htdocs/xml/coverage-03.dtd'>
<coverage branch-rate="0.0" line-rate="0.666666666667" timestamp="1263087779313" version="3.3a1">
<!-- Generated by coverage.py: http://nedbatchelder.com/code/coverage -->
<sources>
diff --git a/tests/farm/html/src/here.py b/tests/farm/html/src/here.py
index d0d26ea7..fee9960d 100644
--- a/tests/farm/html/src/here.py
+++ b/tests/farm/html/src/here.py
@@ -1,4 +1,7 @@
-# A test file for HTML reporting by coverage.
+# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
+# For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt
+
+# A test file for HTML reporting by coverage.py.
import other
diff --git a/tests/farm/html/src/isolatin1.py b/tests/farm/html/src/isolatin1.py
index 7a49b07d..55a6f7de 100644
--- a/tests/farm/html/src/isolatin1.py
+++ b/tests/farm/html/src/isolatin1.py
@@ -1,5 +1,8 @@
-# A Python source file in another encoding.
# -*- coding: iso8859-1 -*-
+# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
+# For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt
+
+# A Python source file in another encoding.
math = "34 = 12, 2 = 60"
assert len(math) == 18
diff --git a/tests/farm/html/src/m1.py b/tests/farm/html/src/m1.py
index 927e1f6b..524fb0aa 100644
--- a/tests/farm/html/src/m1.py
+++ b/tests/farm/html/src/m1.py
@@ -1,2 +1,5 @@
+# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
+# For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt
+
m1a = 1
m1b = 2
diff --git a/tests/farm/html/src/m2.py b/tests/farm/html/src/m2.py
index ffddf6cf..2d13bfe2 100644
--- a/tests/farm/html/src/m2.py
+++ b/tests/farm/html/src/m2.py
@@ -1,2 +1,5 @@
+# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
+# For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt
+
m2a = 1
m2b = 2
diff --git a/tests/farm/html/src/m3.py b/tests/farm/html/src/m3.py
index 395d7d25..96e8b992 100644
--- a/tests/farm/html/src/m3.py
+++ b/tests/farm/html/src/m3.py
@@ -1,2 +1,5 @@
+# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
+# For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt
+
m3a = 1
m3b = 2
diff --git a/tests/farm/html/src/main.py b/tests/farm/html/src/main.py
index ce894465..238d0b58 100644
--- a/tests/farm/html/src/main.py
+++ b/tests/farm/html/src/main.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
+
import m1
import m2
import m3
diff --git a/tests/farm/html/src/omit4.ini b/tests/farm/html/src/omit4.ini
index 6821ecda..b792e703 100644
--- a/tests/farm/html/src/omit4.ini
+++ b/tests/farm/html/src/omit4.ini
@@ -1,2 +1,5 @@
+# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
+# For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt
+
[report]
omit = m2.py
diff --git a/tests/farm/html/src/omit5.ini b/tests/farm/html/src/omit5.ini
index 7e32b414..3d8dbcf2 100644
--- a/tests/farm/html/src/omit5.ini
+++ b/tests/farm/html/src/omit5.ini
@@ -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
+
[report]
omit =
fooey
diff --git a/tests/farm/html/src/partial.py b/tests/farm/html/src/partial.py
index 8d62f5c5..66dddacd 100644
--- a/tests/farm/html/src/partial.py
+++ b/tests/farm/html/src/partial.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
+
# partial branches
a = 3
diff --git a/tests/farm/html/src/run_a_xml_2.ini b/tests/farm/html/src/run_a_xml_2.ini
index 8d28f97b..5b5f18dc 100644
--- a/tests/farm/html/src/run_a_xml_2.ini
+++ b/tests/farm/html/src/run_a_xml_2.ini
@@ -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
+
# Put all the XML output in xml_2
[xml]
output = ../xml_2/coverage.xml
diff --git a/tests/farm/html/src/tabbed.py b/tests/farm/html/src/tabbed.py
index 2035852f..e897e9fa 100644
--- a/tests/farm/html/src/tabbed.py
+++ b/tests/farm/html/src/tabbed.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
+
# This file should have tabs.
x = 1
if x:
diff --git a/tests/farm/html/src/unicode.py b/tests/farm/html/src/unicode.py
index 08e1b540..37c5533a 100644
--- a/tests/farm/html/src/unicode.py
+++ b/tests/farm/html/src/unicode.py
@@ -1,5 +1,8 @@
-# A Python source file with exotic characters.
# -*- coding: utf-8 -*-
+# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
+# For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt
+
+# A Python source file with exotic characters.
upside_down = "ʎd˙ǝbɐɹǝʌoɔ"
surrogate = "db40,dd00: x󠄀"
diff --git a/tests/farm/html/src/y.py b/tests/farm/html/src/y.py
index af7c9689..a50bb629 100644
--- a/tests/farm/html/src/y.py
+++ b/tests/farm/html/src/y.py
@@ -1,4 +1,7 @@
-# A test file for XML reporting by coverage.
+# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
+# For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt
+
+# A test file for XML reporting by coverage.py.
def choice(x):
if x < 2:
diff --git a/tests/farm/run/run_chdir.py b/tests/farm/run/run_chdir.py
index 367cd0ad..9e3c7515 100644
--- a/tests/farm/run/run_chdir.py
+++ b/tests/farm/run/run_chdir.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
+
copy("src", "out")
run("""
coverage run chdir.py
diff --git a/tests/farm/run/run_timid.py b/tests/farm/run/run_timid.py
index 99155b85..a632cea3 100644
--- a/tests/farm/run/run_timid.py
+++ b/tests/farm/run/run_timid.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
+
# Test that the --timid command line argument properly swaps the tracer
# function for a simpler one.
#
diff --git a/tests/farm/run/run_xxx.py b/tests/farm/run/run_xxx.py
index 6fedc934..62a862e5 100644
--- a/tests/farm/run/run_xxx.py
+++ b/tests/farm/run/run_xxx.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
+
copy("src", "out")
run("""
coverage run xxx
diff --git a/tests/farm/run/src/chdir.py b/tests/farm/run/src/chdir.py
index 6d834924..35cfcc81 100644
--- a/tests/farm/run/src/chdir.py
+++ b/tests/farm/run/src/chdir.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
+
import os
print("Line One")
os.chdir("subdir")
diff --git a/tests/farm/run/src/showtrace.py b/tests/farm/run/src/showtrace.py
index e97412e0..3a2750a6 100644
--- a/tests/farm/run/src/showtrace.py
+++ b/tests/farm/run/src/showtrace.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
+
# Show the current frame's trace function, so that we can test what the
# command-line options do to the trace function used.
diff --git a/tests/farm/run/src/xxx b/tests/farm/run/src/xxx
index 8f727f08..864da457 100644
--- a/tests/farm/run/src/xxx
+++ b/tests/farm/run/src/xxx
@@ -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
+
# This is a python file though it doesn't look like it, like a main script.
a = b = c = d = 0
a = 3
diff --git a/tests/helpers.py b/tests/helpers.py
index c26f4859..3d6be141 100644
--- a/tests/helpers.py
+++ b/tests/helpers.py
@@ -1,10 +1,11 @@
+# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
+# For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt
+
"""Helpers for coverage.py tests."""
import subprocess
-# This isn't really a backward compatibility thing, should be moved into a
-# helpers file or something.
def run_command(cmd):
"""Run a command in a sub-process.
diff --git a/tests/js/tests.js b/tests/js/tests.js
index 73b4ce2b..504a158e 100644
--- a/tests/js/tests.js
+++ b/tests/js/tests.js
@@ -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 of coverage.py HTML report chunk navigation.
/*global coverage, test, module, equals, jQuery, $ */
diff --git a/tests/modules/covmod1.py b/tests/modules/covmod1.py
index b3f5e5f2..0f9638b8 100644
--- a/tests/modules/covmod1.py
+++ b/tests/modules/covmod1.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
+
# covmod1.py: Simplest module for testing.
i = 1
i += 1
diff --git a/tests/modules/pkg1/p1a.py b/tests/modules/pkg1/p1a.py
index 337add49..5d81b1fa 100644
--- a/tests/modules/pkg1/p1a.py
+++ b/tests/modules/pkg1/p1a.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
+
import os, sys
# Invoke functions in os and sys so we can see if we measure code there.
diff --git a/tests/modules/pkg1/p1b.py b/tests/modules/pkg1/p1b.py
index 59d6fb54..53505cef 100644
--- a/tests/modules/pkg1/p1b.py
+++ b/tests/modules/pkg1/p1b.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
+
x = 1
y = 2
z = 3
diff --git a/tests/modules/pkg1/p1c.py b/tests/modules/pkg1/p1c.py
index a9aeef04..98f319e8 100644
--- a/tests/modules/pkg1/p1c.py
+++ b/tests/modules/pkg1/p1c.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
+
a = 1
b = 2
c = 3
diff --git a/tests/modules/pkg1/runmod2.py b/tests/modules/pkg1/runmod2.py
index b52964cb..5911db7b 100644
--- a/tests/modules/pkg1/runmod2.py
+++ b/tests/modules/pkg1/runmod2.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
+
# Used in the tests for run_python_module
import sys
print("runmod2: passed %s" % sys.argv[1])
diff --git a/tests/modules/pkg1/sub/ps1a.py b/tests/modules/pkg1/sub/ps1a.py
index 4b6a15cc..44d3b274 100644
--- a/tests/modules/pkg1/sub/ps1a.py
+++ b/tests/modules/pkg1/sub/ps1a.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
+
d = 1
e = 2
f = 3
diff --git a/tests/modules/pkg1/sub/runmod3.py b/tests/modules/pkg1/sub/runmod3.py
index 3a1ad155..1f5ce27e 100644
--- a/tests/modules/pkg1/sub/runmod3.py
+++ b/tests/modules/pkg1/sub/runmod3.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
+
# Used in the tests for run_python_module
import sys
print("runmod3: passed %s" % sys.argv[1])
diff --git a/tests/modules/pkg2/p2a.py b/tests/modules/pkg2/p2a.py
index b606711d..62caae22 100644
--- a/tests/modules/pkg2/p2a.py
+++ b/tests/modules/pkg2/p2a.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
+
q = 1
r = 1
s = 1
diff --git a/tests/modules/pkg2/p2b.py b/tests/modules/pkg2/p2b.py
index 7a34e2c6..73716eb4 100644
--- a/tests/modules/pkg2/p2b.py
+++ b/tests/modules/pkg2/p2b.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
+
t = 1
u = 1
v = 1
diff --git a/tests/modules/plugins/a_plugin.py b/tests/modules/plugins/a_plugin.py
index 2ff84dac..2a9910d0 100644
--- a/tests/modules/plugins/a_plugin.py
+++ b/tests/modules/plugins/a_plugin.py
@@ -4,3 +4,6 @@ from coverage import CoveragePlugin
class Plugin(CoveragePlugin):
pass
+
+def coverage_init(reg, options):
+ reg.add_file_tracer(Plugin())
diff --git a/tests/modules/plugins/another.py b/tests/modules/plugins/another.py
index 2ff84dac..096d3b9d 100644
--- a/tests/modules/plugins/another.py
+++ b/tests/modules/plugins/another.py
@@ -1,6 +1,12 @@
+# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
+# For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt
+
"""A plugin for tests to reference."""
from coverage import CoveragePlugin
class Plugin(CoveragePlugin):
pass
+
+def coverage_init(reg, options):
+ reg.add_file_tracer(Plugin())
diff --git a/tests/modules/runmod1.py b/tests/modules/runmod1.py
index 671d81ef..b43b299a 100644
--- a/tests/modules/runmod1.py
+++ b/tests/modules/runmod1.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
+
# Used in the tests for run_python_module
import sys
print("runmod1: passed %s" % sys.argv[1])
diff --git a/tests/modules/usepkgs.py b/tests/modules/usepkgs.py
index 93c7d904..4e94acaa 100644
--- a/tests/modules/usepkgs.py
+++ b/tests/modules/usepkgs.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
+
import pkg1.p1a, pkg1.p1b
import pkg2.p2a, pkg2.p2b
import othermods.othera, othermods.otherb
diff --git a/tests/moremodules/othermods/othera.py b/tests/moremodules/othermods/othera.py
index 78896928..b3ee9c04 100644
--- a/tests/moremodules/othermods/othera.py
+++ b/tests/moremodules/othermods/othera.py
@@ -1,2 +1,5 @@
+# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
+# For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt
+
o = 1
p = 2
diff --git a/tests/moremodules/othermods/otherb.py b/tests/moremodules/othermods/otherb.py
index 2bd8a441..334fdc4a 100644
--- a/tests/moremodules/othermods/otherb.py
+++ b/tests/moremodules/othermods/otherb.py
@@ -1,2 +1,5 @@
+# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
+# For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt
+
q = 3
r = 4
diff --git a/tests/moremodules/othermods/sub/osa.py b/tests/moremodules/othermods/sub/osa.py
index 0139d28b..4005640e 100644
--- a/tests/moremodules/othermods/sub/osa.py
+++ b/tests/moremodules/othermods/sub/osa.py
@@ -1,2 +1,5 @@
+# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
+# For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt
+
s = 5
t = 6
diff --git a/tests/moremodules/othermods/sub/osb.py b/tests/moremodules/othermods/sub/osb.py
index b024b148..7d96fb79 100644
--- a/tests/moremodules/othermods/sub/osb.py
+++ b/tests/moremodules/othermods/sub/osb.py
@@ -1,2 +1,5 @@
+# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
+# For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt
+
u = 7
v = 8
diff --git a/tests/osinfo.py b/tests/osinfo.py
index 0b86ef54..54153349 100644
--- a/tests/osinfo.py
+++ b/tests/osinfo.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
+
"""OS information for testing."""
from coverage import env
diff --git a/tests/plugin1.py b/tests/plugin1.py
index f9da35c8..c28b886f 100644
--- a/tests/plugin1.py
+++ b/tests/plugin1.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
+
"""A plugin for test_plugins.py to import."""
import os.path
@@ -44,3 +47,8 @@ class FileReporter(coverage.plugin.FileReporter):
def excluded_statements(self):
return set([])
+
+
+def coverage_init(reg, options): # pylint: disable=unused-argument
+ """Called by coverage to initialize the plugins here."""
+ reg.add_file_tracer(Plugin())
diff --git a/tests/plugin2.py b/tests/plugin2.py
index 1d5d9e9f..cbd2fc11 100644
--- a/tests/plugin2.py
+++ b/tests/plugin2.py
@@ -1,13 +1,15 @@
+# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
+# For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt
+
"""A plugin for test_plugins.py to import."""
import os.path
import coverage
-# pylint: disable=missing-docstring
-
class Plugin(coverage.CoveragePlugin):
+ """A plugin for testing."""
def file_tracer(self, filename):
if "render.py" in filename:
return RenderFileTracer()
@@ -25,7 +27,8 @@ class RenderFileTracer(coverage.plugin.FileTracer):
def dynamic_source_filename(self, filename, frame):
if frame.f_code.co_name != "render":
return None
- return frame.f_locals['filename']
+ source_filename = os.path.abspath(frame.f_locals['filename'])
+ return source_filename
def line_number_range(self, frame):
lineno = frame.f_locals['linenum']
@@ -33,8 +36,14 @@ class RenderFileTracer(coverage.plugin.FileTracer):
class FileReporter(coverage.plugin.FileReporter):
+ """A goofy file reporter."""
def statements(self):
# Goofy test arrangement: claim that the file has as many lines as the
# number in its name.
num = os.path.basename(self.filename).split(".")[0].split("_")[1]
return set(range(1, int(num)+1))
+
+
+def coverage_init(reg, options): # pylint: disable=unused-argument
+ """Called by coverage to initialize the plugins here."""
+ reg.add_file_tracer(Plugin())
diff --git a/tests/stress_phystoken.tok b/tests/stress_phystoken.tok
index 8d1b6bec..eb2fb669 100644
--- a/tests/stress_phystoken.tok
+++ b/tests/stress_phystoken.tok
@@ -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
+
# Here's some random Python so that test_tokenize_myself will have some
# stressful stuff to try. This file is .tok instead of .py so pylint won't
# complain about it, check_eol won't look at it, etc.
diff --git a/tests/stress_phystoken_dos.tok b/tests/stress_phystoken_dos.tok
index b08fd70e..5b016a77 100644
--- a/tests/stress_phystoken_dos.tok
+++ b/tests/stress_phystoken_dos.tok
@@ -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
+
# Here's some random Python so that test_tokenize_myself will have some
# stressful stuff to try. This file is .tok instead of .py so pylint won't
# complain about it, check_eol won't look at it, etc.
diff --git a/tests/test_api.py b/tests/test_api.py
index a21372ad..25d104c4 100644
--- a/tests/test_api.py
+++ b/tests/test_api.py
@@ -1,93 +1,22 @@
-"""Tests for Coverage's API."""
+# 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 coverage.py's API."""
import fnmatch
import os
-import re
import sys
import textwrap
import coverage
from coverage.backward import StringIO
+from coverage.misc import CoverageException
from tests.coveragetest import CoverageTest
-class SingletonApiTest(CoverageTest):
- """Tests of the old-fashioned singleton API."""
-
- def setUp(self):
- super(SingletonApiTest, self).setUp()
- # These tests use the singleton module interface. Prevent it from
- # writing .coverage files at exit.
- coverage.use_cache(0)
-
- def do_report_work(self, modname):
- """Create a module named `modname`, then measure it."""
- coverage.erase()
-
- self.make_file(modname+".py", """\
- a = 1
- b = 2
- if b == 3:
- c = 4
- d = 5
- e = 6
- f = 7
- """)
-
- # Import the Python file, executing it.
- self.start_import_stop(coverage, modname)
-
- def test_simple(self):
- coverage.erase()
-
- self.make_file("mycode.py", """\
- a = 1
- b = 2
- if b == 3:
- c = 4
- d = 5
- """)
-
- # Import the Python file, executing it.
- self.start_import_stop(coverage, "mycode")
-
- _, statements, missing, missingtext = coverage.analysis("mycode.py")
- self.assertEqual(statements, [1,2,3,4,5])
- self.assertEqual(missing, [4])
- self.assertEqual(missingtext, "4")
-
- def test_report(self):
- self.do_report_work("mycode2")
- coverage.report(["mycode2.py"])
- self.assertEqual(self.stdout(), textwrap.dedent("""\
- Name Stmts Miss Cover Missing
- ------------------------------------------
- mycode2.py 7 3 57% 4-6
- """))
-
- def test_report_file(self):
- # The file= argument of coverage.report makes the report go there.
- self.do_report_work("mycode3")
- fout = StringIO()
- coverage.report(["mycode3.py"], file=fout)
- self.assertEqual(self.stdout(), "")
- self.assertEqual(fout.getvalue(), textwrap.dedent("""\
- Name Stmts Miss Cover Missing
- ------------------------------------------
- mycode3.py 7 3 57% 4-6
- """))
-
- def test_report_default(self):
- # Calling report() with no morfs will report on whatever was executed.
- self.do_report_work("mycode4")
- coverage.report()
- rpt = re.sub(r"\s+", " ", self.stdout())
- self.assertIn("mycode4.py 7 3 57% 4-6", rpt)
-
-
class ApiTest(CoverageTest):
- """Api-oriented tests for Coverage."""
+ """Api-oriented tests for coverage.py."""
def clean_files(self, files, pats):
"""Remove names matching `pats` from `files`, a list of filenames."""
@@ -107,7 +36,7 @@ class ApiTest(CoverageTest):
self.assertCountEqual(here, files)
def test_unexecuted_file(self):
- cov = coverage.coverage()
+ cov = coverage.Coverage()
self.make_file("mycode.py", """\
a = 1
@@ -140,7 +69,7 @@ class ApiTest(CoverageTest):
""")
# Import the Python file, executing it.
- cov = coverage.coverage()
+ cov = coverage.Coverage()
self.start_import_stop(cov, "mymain")
filename, _, _, _ = cov.analysis("mymain.py")
@@ -155,7 +84,7 @@ class ApiTest(CoverageTest):
# Import the Python file, executing it again, once it's been compiled
# already.
- cov = coverage.coverage()
+ cov = coverage.Coverage()
self.start_import_stop(cov, "mymain")
filename, _, _, _ = cov.analysis("mymain.py")
@@ -176,7 +105,7 @@ class ApiTest(CoverageTest):
""")
# Measure without the stdlib.
- cov1 = coverage.coverage()
+ cov1 = coverage.Coverage()
self.assertEqual(cov1.config.cover_pylib, False)
self.start_import_stop(cov1, "mymain")
@@ -188,7 +117,7 @@ class ApiTest(CoverageTest):
self.assertEqual(statements, missing)
# Measure with the stdlib.
- cov2 = coverage.coverage(cover_pylib=True)
+ cov2 = coverage.Coverage(cover_pylib=True)
self.start_import_stop(cov2, "mymain")
# some statements were marked executed in mymain.py
@@ -207,7 +136,7 @@ class ApiTest(CoverageTest):
""")
# Measure without the stdlib, but include colorsys.
- cov1 = coverage.coverage(cover_pylib=False, include=["*/colorsys.py"])
+ cov1 = coverage.Coverage(cover_pylib=False, include=["*/colorsys.py"])
self.start_import_stop(cov1, "mymain")
# some statements were marked executed in colorsys.py
@@ -218,7 +147,7 @@ class ApiTest(CoverageTest):
self.assertEqual(statements, missing)
def test_exclude_list(self):
- cov = coverage.coverage()
+ cov = coverage.Coverage()
cov.clear_exclude()
self.assertEqual(cov.get_exclude_list(), [])
cov.exclude("foo")
@@ -230,7 +159,7 @@ class ApiTest(CoverageTest):
self.assertEqual(cov.get_exclude_list(), [])
def test_exclude_partial_list(self):
- cov = coverage.coverage()
+ cov = coverage.Coverage()
cov.clear_exclude(which='partial')
self.assertEqual(cov.get_exclude_list(which='partial'), [])
cov.exclude("foo", which='partial')
@@ -244,7 +173,7 @@ class ApiTest(CoverageTest):
self.assertEqual(cov.get_exclude_list(which='partial'), [])
def test_exclude_and_partial_are_separate_lists(self):
- cov = coverage.coverage()
+ cov = coverage.Coverage()
cov.clear_exclude(which='partial')
cov.clear_exclude(which='exclude')
cov.exclude("foo", which='partial')
@@ -271,7 +200,7 @@ class ApiTest(CoverageTest):
""")
self.assertFiles(["datatest1.py"])
- cov = coverage.coverage()
+ cov = coverage.Coverage()
self.start_import_stop(cov, "datatest1")
cov.save()
self.assertFiles(["datatest1.py", ".coverage"])
@@ -283,7 +212,7 @@ class ApiTest(CoverageTest):
""")
self.assertFiles(["datatest2.py"])
- cov = coverage.coverage(data_file="cov.data")
+ cov = coverage.Coverage(data_file="cov.data")
self.start_import_stop(cov, "datatest2")
cov.save()
self.assertFiles(["datatest2.py", "cov.data"])
@@ -295,7 +224,7 @@ class ApiTest(CoverageTest):
""")
self.assertFiles(["datatest3.py"])
- cov = coverage.coverage(data_file="cov.data", data_suffix="14")
+ cov = coverage.Coverage(data_file="cov.data", data_suffix="14")
self.start_import_stop(cov, "datatest3")
cov.save()
self.assertFiles(["datatest3.py", "cov.data.14"])
@@ -311,16 +240,16 @@ class ApiTest(CoverageTest):
""")
self.assertFiles(["datatest4.py", ".coveragerc"])
- cov = coverage.coverage()
+ cov = coverage.Coverage()
self.start_import_stop(cov, "datatest4")
cov.save()
self.assertFiles(["datatest4.py", ".coveragerc", "mydata.dat"])
def test_empty_reporting(self):
- # Used to be you'd get an exception reporting on nothing...
- cov = coverage.coverage()
+ # empty summary reports raise exception, just like the xml report
+ cov = coverage.Coverage()
cov.erase()
- cov.report()
+ self.assertRaises(CoverageException, cov.report)
def test_start_stop_start_stop(self):
self.make_file("code1.py", """\
@@ -330,7 +259,7 @@ class ApiTest(CoverageTest):
code2 = 1
code2 = 2
""")
- cov = coverage.coverage()
+ cov = coverage.Coverage()
self.start_import_stop(cov, "code1")
cov.save()
self.start_import_stop(cov, "code2")
@@ -351,7 +280,7 @@ class ApiTest(CoverageTest):
code2 = 1
code2 = 2
""")
- cov = coverage.coverage()
+ cov = coverage.Coverage()
cov.start()
self.import_local_file("code1")
cov.save()
@@ -445,15 +374,15 @@ class SourceOmitIncludeTest(OmitIncludeTestsMixin, CoverageTest):
def coverage_usepkgs(self, **kwargs):
"""Run coverage on usepkgs and return the line summary.
- Arguments are passed to the `coverage.coverage` constructor.
+ Arguments are passed to the `coverage.Coverage` constructor.
"""
- cov = coverage.coverage(**kwargs)
+ cov = coverage.Coverage(**kwargs)
cov.start()
import usepkgs # pragma: nested # pylint: disable=import-error,unused-variable
cov.stop() # pragma: nested
- cov._harvest_data() # private! sshhh...
- summary = cov.data.summary()
+ data = cov.get_data()
+ summary = data.line_counts()
for k, v in list(summary.items()):
assert k.endswith(".py")
summary[k[:-3]] = v
@@ -487,7 +416,7 @@ class ReportIncludeOmitTest(OmitIncludeTestsMixin, CoverageTest):
def coverage_usepkgs(self, **kwargs):
"""Try coverage.report()."""
- cov = coverage.coverage()
+ cov = coverage.Coverage()
cov.start()
import usepkgs # pragma: nested # pylint: disable=import-error,unused-variable
cov.stop() # pragma: nested
@@ -506,7 +435,7 @@ class XmlIncludeOmitTest(OmitIncludeTestsMixin, CoverageTest):
def coverage_usepkgs(self, **kwargs):
"""Try coverage.xml_report()."""
- cov = coverage.coverage()
+ cov = coverage.Coverage()
cov.start()
import usepkgs # pragma: nested # pylint: disable=import-error,unused-variable
cov.stop() # pragma: nested
@@ -517,7 +446,7 @@ class XmlIncludeOmitTest(OmitIncludeTestsMixin, CoverageTest):
class AnalysisTest(CoverageTest):
"""Test the numerical analysis of results."""
def test_many_missing_branches(self):
- cov = coverage.coverage(branch=True)
+ cov = coverage.Coverage(branch=True)
self.make_file("missing.py", """\
def fun1(x):
@@ -555,7 +484,7 @@ class PluginTest(CoverageTest):
"""
def pretend_to_be_nose_with_cover(self, erase):
"""This is what the nose --with-cover plugin does."""
- cov = coverage.coverage()
+ cov = coverage.Coverage()
self.make_file("no_biggie.py", """\
a = 1
diff --git a/tests/test_arcs.py b/tests/test_arcs.py
index 81fa7e6a..df303d8b 100644
--- a/tests/test_arcs.py
+++ b/tests/test_arcs.py
@@ -1,4 +1,7 @@
-"""Tests for Coverage.py's arc measurement."""
+# 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 coverage.py's arc measurement."""
from tests.coveragetest import CoverageTest
@@ -8,7 +11,7 @@ from coverage.files import abs_file
class SimpleArcTest(CoverageTest):
- """Tests for Coverage.py's arc measurement."""
+ """Tests for coverage.py's arc measurement."""
def test_simple_sequence(self):
self.check_coverage("""\
@@ -560,10 +563,123 @@ class ExceptionArcTest(CoverageTest):
arcz_missing="67 7B", arcz_unpredicted="68")
+class YieldTest(CoverageTest):
+ """Arc tests for generators."""
+
+ def test_yield_in_loop(self):
+ self.check_coverage("""\
+ def gen(inp):
+ for n in inp:
+ yield n
+
+ list(gen([1,2,3]))
+ """,
+ arcz=".1 .2 23 2. 32 15 5.",
+ arcz_missing="",
+ arcz_unpredicted="")
+
+ def test_padded_yield_in_loop(self):
+ self.check_coverage("""\
+ def gen(inp):
+ i = 2
+ for n in inp:
+ i = 4
+ yield n
+ i = 6
+ i = 7
+
+ list(gen([1,2,3]))
+ """,
+ arcz=".1 19 9. .2 23 34 45 56 63 37 7.",
+ arcz_missing="",
+ arcz_unpredicted="")
+
+ def test_bug_308(self):
+ self.check_coverage("""\
+ def run():
+ for i in range(10):
+ yield lambda: i
+
+ for f in run():
+ print(f())
+ """,
+ arcz=".1 15 56 65 5. .2 23 32 2. .3 3-3",
+ arcz_missing="",
+ arcz_unpredicted="")
+
+ self.check_coverage("""\
+ def run():
+ yield lambda: 100
+ for i in range(10):
+ yield lambda: i
+
+ for f in run():
+ print(f())
+ """,
+ arcz=".1 16 67 76 6. .2 23 34 43 3. 2-2 .4 4-4",
+ arcz_missing="",
+ arcz_unpredicted="")
+
+ self.check_coverage("""\
+ def run():
+ yield lambda: 100 # no branch miss
+
+ for f in run():
+ print(f())
+ """,
+ arcz=".1 14 45 54 4. .2 2. 2-2",
+ arcz_missing="",
+ arcz_unpredicted="")
+
+ def test_bug_324(self):
+ # This code is tricky: the list() call pulls all the values from gen(),
+ # but each of them is a generator itself that is never iterated. As a
+ # result, the generator expression on line 3 is never entered or run.
+ self.check_coverage("""\
+ def gen(inp):
+ for n in inp:
+ yield (i * 2 for i in range(n))
+
+ list(gen([1,2,3]))
+ """,
+ arcz=
+ ".1 15 5. " # The module level
+ ".2 23 32 2. " # The gen() function
+ ".3 3-3", # The generator expression
+ arcz_missing=".3 3-3",
+ arcz_unpredicted="")
+
+ def test_coroutines(self):
+ self.check_coverage("""\
+ def double_inputs():
+ while [1]: # avoid compiler differences
+ x = yield
+ x *= 2
+ yield x
+
+ gen = double_inputs()
+ next(gen)
+ print(gen.send(10))
+ next(gen)
+ print(gen.send(6))
+ """,
+ arcz=
+ ".1 17 78 89 9A AB B. "
+ ".2 23 34 45 52 2.",
+ arcz_missing="2.",
+ arcz_unpredicted="")
+ self.assertEqual(self.stdout(), "20\n12\n")
+
+
class MiscArcTest(CoverageTest):
"""Miscellaneous arc-measuring tests."""
def test_dict_literal(self):
+ if env.PYVERSION < (3, 5):
+ arcz = ".1 19 9."
+ else:
+ # Python 3.5 changed how dict literals are constructed.
+ arcz = ".1 19 9-2"
self.check_coverage("""\
d = {
'a': 2,
@@ -575,7 +691,7 @@ class MiscArcTest(CoverageTest):
}
assert d
""",
- arcz=".1 19 9.")
+ arcz=arcz)
def test_pathologically_long_code_object(self):
# https://bitbucket.org/ned/coveragepy/issue/359
@@ -647,6 +763,6 @@ class LineDataTest(CoverageTest):
self.start_import_stop(cov, "fun1")
- cov._harvest_data()
- fun1_lines = cov.data.line_data()[abs_file("fun1.py")]
- self.assertEqual(fun1_lines, [1, 2, 5])
+ data = cov.get_data()
+ fun1_lines = data.lines(abs_file("fun1.py"))
+ self.assertCountEqual(fun1_lines, [1, 2, 5])
diff --git a/tests/test_backward.py b/tests/test_backward.py
index 09803ba7..fbb9ad8b 100644
--- a/tests/test_backward.py
+++ b/tests/test_backward.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 that our version shims in backward.py are working."""
from coverage.backunittest import TestCase
diff --git a/tests/test_cmdline.py b/tests/test_cmdline.py
index 775e0033..a379d402 100644
--- a/tests/test_cmdline.py
+++ b/tests/test_cmdline.py
@@ -1,4 +1,7 @@
-"""Test cmdline.py for coverage."""
+# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
+# For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt
+
+"""Test cmdline.py for coverage.py."""
import pprint
import re
@@ -11,6 +14,7 @@ import mock
import coverage
import coverage.cmdline
from coverage.config import CoverageConfig
+from coverage.data import CoverageData, CoverageDataFiles
from coverage.misc import ExceptionDuringRun
from tests.coveragetest import CoverageTest, OK, ERR
@@ -62,7 +66,7 @@ class BaseCmdLineTest(CoverageTest):
"""
m = self.model_object()
- ret = coverage.CoverageScript(
+ ret = coverage.cmdline.CoverageScript(
_covpkg=m, _run_python_file=m.run_python_file,
_run_python_module=m.run_python_module, _help_fn=m.help_fn
).command_line(shlex.split(args))
@@ -139,38 +143,6 @@ class BaseCmdLineTestTest(BaseCmdLineTest):
self.cmd_executes_same("run", "debug")
-class FakeCoverageForDebugData(object):
- """Just enough of a fake coverage package for the 'debug data' tests."""
- def __init__(self, summary, plugin_data=None):
- self._summary = summary
- self._plugin_data = plugin_data or {}
- self.filename = "FILENAME"
- self.data = self
-
- # package members
- def coverage(self, *unused_args, **unused_kwargs):
- """The coverage class in the package."""
- return self
-
- # coverage methods
- def load(self):
- """Fake coverage().load()"""
- pass
-
- # data methods
- def has_arcs(self):
- """Fake coverage().data.has_arcs()"""
- return False
-
- def summary(self, fullpath): # pylint: disable=unused-argument
- """Fake coverage().data.summary()"""
- return self._summary
-
- def plugin_data(self):
- """Fake coverage().data.plugin_data()"""
- return self._plugin_data
-
-
class CmdLineTest(BaseCmdLineTest):
"""Tests of the coverage.py command line."""
@@ -213,11 +185,33 @@ class CmdLineTest(BaseCmdLineTest):
""")
def test_combine(self):
- # coverage combine
+ # coverage combine with args
+ self.cmd_executes("combine datadir1", """\
+ .coverage()
+ .load()
+ .combine(["datadir1"])
+ .save()
+ """)
+ # coverage combine without args
self.cmd_executes("combine", """\
.coverage()
.load()
- .combine()
+ .combine(None)
+ .save()
+ """)
+
+ def test_combine_doesnt_confuse_options_with_args(self):
+ # https://bitbucket.org/ned/coveragepy/issues/385/coverage-combine-doesnt-work-with-rcfile
+ self.cmd_executes("combine --rcfile cov.ini", """\
+ .coverage(config_file='cov.ini')
+ .load()
+ .combine(None)
+ .save()
+ """)
+ self.cmd_executes("combine --rcfile cov.ini data1 data2/more", """\
+ .coverage(config_file='cov.ini')
+ .load()
+ .combine(["data1", "data2/more"])
.save()
""")
@@ -225,36 +219,6 @@ class CmdLineTest(BaseCmdLineTest):
self.cmd_help("debug", "What information would you like: data, sys?")
self.cmd_help("debug foo", "Don't know what you mean by 'foo'")
- def test_debug_data(self):
- fake = FakeCoverageForDebugData(
- summary={
- 'file1.py': 17, 'file2.py': 23,
- },
- plugin_data={
- 'file1.py': 'a_plugin',
- },
- )
- self.command_line("debug data", _covpkg=fake)
- self.assertMultiLineEqual(self.stdout(), textwrap.dedent("""\
- -- data ------------------------------------------------------
- path: FILENAME
- has_arcs: False
-
- 2 files:
- file1.py: 17 lines [a_plugin]
- file2.py: 23 lines
- """))
-
- def test_debug_data_with_no_data(self):
- fake = FakeCoverageForDebugData(summary={})
- self.command_line("debug data", _covpkg=fake)
- self.assertMultiLineEqual(self.stdout(), textwrap.dedent("""\
- -- data ------------------------------------------------------
- path: FILENAME
- has_arcs: False
- No data collected
- """))
-
def test_debug_sys(self):
self.command_line("debug sys")
out = self.stdout()
@@ -386,10 +350,10 @@ class CmdLineTest(BaseCmdLineTest):
# run -a calls coverage.load first without erasing.
self.cmd_executes("run -a foo.py", """\
.coverage()
- .load()
.start()
.run_python_file('foo.py', ['foo.py'])
.stop()
+ .combine(data_paths=['.coverage'])
.save()
""")
# --timid sets a flag, and program arguments get passed through.
@@ -518,6 +482,14 @@ class CmdLineTest(BaseCmdLineTest):
""")
self.cmd_executes_same("run -m mymodule", "run --module mymodule")
+ def test_run_nothing(self):
+ self.command_line("run", ret=ERR)
+ self.assertIn("Nothing to do", self.stdout())
+
+ def test_cant_append_parallel(self):
+ self.command_line("run --append --parallel-mode foo.py", ret=ERR)
+ self.assertIn("Can't append to data files in parallel mode.", self.stdout())
+
def test_xml(self):
# coverage xml [-i] [--omit DIR,...] [FILE1 FILE2 ...]
self.cmd_executes("xml", """\
@@ -568,6 +540,43 @@ class CmdLineTest(BaseCmdLineTest):
self.cmd_help("xyzzy", "Unknown command: 'xyzzy'")
+class CmdLineWithFilesTest(BaseCmdLineTest):
+ """Test the command line in ways that need temp files."""
+
+ run_in_temp_dir = True
+ no_files_in_temp_dir = True
+
+ def test_debug_data(self):
+ data = CoverageData()
+ data.set_lines({
+ "file1.py": dict.fromkeys(range(1, 18)),
+ "file2.py": dict.fromkeys(range(1, 24)),
+ })
+ data.set_file_tracers({"file1.py": "a_plugin"})
+ data_files = CoverageDataFiles()
+ data_files.write(data)
+
+ self.command_line("debug data")
+ self.assertMultiLineEqual(self.stdout(), textwrap.dedent("""\
+ -- data ------------------------------------------------------
+ path: FILENAME
+ has_arcs: False
+
+ 2 files:
+ file1.py: 17 lines [a_plugin]
+ file2.py: 23 lines
+ """).replace("FILENAME", data_files.filename))
+
+ def test_debug_data_with_no_data(self):
+ data_files = CoverageDataFiles()
+ self.command_line("debug data")
+ self.assertMultiLineEqual(self.stdout(), textwrap.dedent("""\
+ -- data ------------------------------------------------------
+ path: FILENAME
+ No data collected
+ """).replace("FILENAME", data_files.filename))
+
+
class CmdLineStdoutTest(BaseCmdLineTest):
"""Test the command line with real stdout output."""
diff --git a/tests/test_collector.py b/tests/test_collector.py
index 26360091..bd963415 100644
--- a/tests/test_collector.py
+++ b/tests/test_collector.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 of coverage/collector.py and other collectors."""
import os.path
@@ -35,7 +38,7 @@ class CollectorTest(CoverageTest):
# Trace one file, but not the other. CheckUniqueFilenames will assert
# that _should_trace hasn't been called twice for the same file.
- cov = coverage.coverage(include=["f1.py"])
+ cov = coverage.Coverage(include=["f1.py"])
should_trace_hook = CheckUniqueFilenames.hook(cov, '_should_trace')
# Import the Python file, executing it.
diff --git a/tests/test_concurrency.py b/tests/test_concurrency.py
index 93809dff..f6af0e8a 100644
--- a/tests/test_concurrency.py
+++ b/tests/test_concurrency.py
@@ -1,11 +1,13 @@
+# 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 concurrency libraries."""
-import os
-import os.path
import threading
import coverage
from coverage import env
+from coverage.files import abs_file
from tests.coveragetest import CoverageTest
@@ -148,13 +150,13 @@ class ConcurrencyTest(CoverageTest):
data.read_file(".coverage")
# If the test fails, it's helpful to see this info:
- fname = os.path.abspath("try_it.py")
- linenos = data.executed_lines(fname).keys()
+ fname = abs_file("try_it.py")
+ linenos = data.lines(fname)
print("{0}: {1}".format(len(linenos), linenos))
print_simple_annotation(code, linenos)
lines = line_count(code)
- self.assertEqual(data.summary()['try_it.py'], lines)
+ self.assertEqual(data.line_counts()['try_it.py'], lines)
else:
expected_out = (
"Can't support concurrency=%s with PyTracer, "
diff --git a/tests/test_config.py b/tests/test_config.py
index 58615f6c..9d3c95dc 100644
--- a/tests/test_config.py
+++ b/tests/test_config.py
@@ -1,4 +1,7 @@
-# -*- coding: utf-8 -*-
+# coding: utf-8
+# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
+# For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt
+
"""Test the config file handling for coverage.py"""
import sys
@@ -15,14 +18,14 @@ class ConfigTest(CoverageTest):
def test_default_config(self):
# Just constructing a coverage() object gets the right defaults.
- cov = coverage.coverage()
+ cov = coverage.Coverage()
self.assertFalse(cov.config.timid)
self.assertFalse(cov.config.branch)
self.assertEqual(cov.config.data_file, ".coverage")
def test_arguments(self):
# Arguments to the constructor are applied to the configuration.
- cov = coverage.coverage(timid=True, data_file="fooey.dat")
+ cov = coverage.Coverage(timid=True, data_file="fooey.dat")
self.assertTrue(cov.config.timid)
self.assertFalse(cov.config.branch)
self.assertEqual(cov.config.data_file, "fooey.dat")
@@ -35,7 +38,7 @@ class ConfigTest(CoverageTest):
timid = True
data_file = .hello_kitty.data
""")
- cov = coverage.coverage()
+ cov = coverage.Coverage()
self.assertTrue(cov.config.timid)
self.assertFalse(cov.config.branch)
self.assertEqual(cov.config.data_file, ".hello_kitty.data")
@@ -48,7 +51,7 @@ class ConfigTest(CoverageTest):
; I wouldn't really use this as a data file...
data_file = delete.me
""")
- cov = coverage.coverage(config_file="my_cov.ini")
+ cov = coverage.Coverage(config_file="my_cov.ini")
self.assertTrue(cov.config.timid)
self.assertFalse(cov.config.branch)
self.assertEqual(cov.config.data_file, "delete.me")
@@ -60,7 +63,7 @@ class ConfigTest(CoverageTest):
timid = True
data_file = delete.me
""")
- cov = coverage.coverage(config_file=False)
+ cov = coverage.Coverage(config_file=False)
self.assertFalse(cov.config.timid)
self.assertFalse(cov.config.branch)
self.assertEqual(cov.config.data_file, ".coverage")
@@ -72,7 +75,7 @@ class ConfigTest(CoverageTest):
timid = True
data_file = weirdo.file
""")
- cov = coverage.coverage(timid=False, data_file=".mycov")
+ cov = coverage.Coverage(timid=False, data_file=".mycov")
self.assertFalse(cov.config.timid)
self.assertFalse(cov.config.branch)
self.assertEqual(cov.config.data_file, ".mycov")
@@ -85,10 +88,10 @@ class ConfigTest(CoverageTest):
data_file = weirdo.file
""")
self.set_environ("COVERAGE_FILE", "fromenv.dat")
- cov = coverage.coverage()
+ cov = coverage.Coverage()
self.assertEqual(cov.config.data_file, "fromenv.dat")
# But the constructor arguments override the environment variable.
- cov = coverage.coverage(data_file="fromarg.dat")
+ cov = coverage.Coverage(data_file="fromarg.dat")
self.assertEqual(cov.config.data_file, "fromarg.dat")
def test_parse_errors(self):
@@ -99,21 +102,21 @@ class ConfigTest(CoverageTest):
("[run\n", r"\[run"),
("[report]\nexclude_lines = foo(\n",
r"Invalid \[report\].exclude_lines value 'foo\(': "
- r"unbalanced parenthesis"),
+ r"(unbalanced parenthesis|missing \))"),
("[report]\npartial_branches = foo[\n",
r"Invalid \[report\].partial_branches value 'foo\[': "
- r"unexpected end of regular expression"),
+ r"(unexpected end of regular expression|unterminated character set)"),
("[report]\npartial_branches_always = foo***\n",
r"Invalid \[report\].partial_branches_always value "
- r"'foo\*\*\*': "
- r"multiple repeat"),
- ]
+ r"'foo\*\*\*': "
+ r"multiple repeat"),
+ ]
for bad_config, msg in bad_configs_and_msgs:
print("Trying %r" % bad_config)
self.make_file(".coveragerc", bad_config)
with self.assertRaisesRegex(CoverageException, msg):
- coverage.coverage()
+ coverage.Coverage()
def test_environment_vars_in_config(self):
# Config files can have $envvars in them.
@@ -132,16 +135,17 @@ class ConfigTest(CoverageTest):
self.set_environ("DATA_FILE", "hello-world")
self.set_environ("THING", "ZZZ")
self.set_environ("OKAY", "yes")
- cov = coverage.coverage()
+ cov = coverage.Coverage()
self.assertEqual(cov.config.data_file, "hello-world.fooey")
self.assertEqual(cov.config.branch, True)
- self.assertEqual(cov.config.exclude_list,
+ self.assertEqual(
+ cov.config.exclude_list,
["the_$one", "anotherZZZ", "xZZZy", "xy", "huh${X}what"]
- )
+ )
def test_tweaks_after_constructor(self):
# Arguments to the constructor are applied to the configuration.
- cov = coverage.coverage(timid=True, data_file="fooey.dat")
+ cov = coverage.Coverage(timid=True, data_file="fooey.dat")
cov.config["run:timid"] = False
self.assertFalse(cov.config.timid)
@@ -154,7 +158,7 @@ class ConfigTest(CoverageTest):
def test_tweak_error_checking(self):
# Trying to set an unknown config value raises an error.
- cov = coverage.coverage()
+ cov = coverage.Coverage()
with self.assertRaises(CoverageException):
cov.config["run:xyzzy"] = 12
with self.assertRaises(CoverageException):
@@ -166,7 +170,7 @@ class ConfigTest(CoverageTest):
def test_tweak_plugin_options(self):
# Plugin options have a more flexible syntax.
- cov = coverage.coverage()
+ cov = coverage.Coverage()
cov.config["run:plugins"] = ["fooey.plugin", "xyzzy.coverage.plugin"]
cov.config["fooey.plugin:xyzzy"] = 17
cov.config["xyzzy.coverage.plugin:plugh"] = ["a", "b"]
@@ -178,6 +182,33 @@ class ConfigTest(CoverageTest):
with self.assertRaises(CoverageException):
_ = cov.config["no_such.plugin:foo"]
+ def test_unknown_option(self):
+ self.make_file(".coveragerc", """\
+ [run]
+ xyzzy = 17
+ """)
+ msg = r"Unrecognized option '\[run\] xyzzy=' in config file .coveragerc"
+ with self.assertRaisesRegex(CoverageException, msg):
+ _ = coverage.Coverage()
+
+ def test_misplaced_option(self):
+ self.make_file(".coveragerc", """\
+ [report]
+ branch = True
+ """)
+ msg = r"Unrecognized option '\[report\] branch=' in config file .coveragerc"
+ with self.assertRaisesRegex(CoverageException, msg):
+ _ = coverage.Coverage()
+
+ def test_unknown_option_in_other_ini_file(self):
+ self.make_file("setup.cfg", """\
+ [coverage:run]
+ huh = what?
+ """)
+ msg = r"Unrecognized option '\[coverage:run\] huh=' in config file setup.cfg"
+ with self.assertRaisesRegex(CoverageException, msg):
+ _ = coverage.Coverage()
+
class ConfigFileTest(CoverageTest):
"""Tests of the config file settings in particular."""
@@ -270,25 +301,15 @@ class ConfigFileTest(CoverageTest):
self.assertTrue(cov.config.parallel)
self.assertEqual(cov.config.concurrency, "thread")
- self.assertEqual(cov.get_exclude_list(),
- ["if 0:", r"pragma:?\s+no cover", "another_tab"]
- )
+ self.assertEqual(cov.get_exclude_list(), ["if 0:", r"pragma:?\s+no cover", "another_tab"])
self.assertTrue(cov.config.ignore_errors)
self.assertEqual(cov.config.include, ["a/", "b/"])
- self.assertEqual(cov.config.omit,
- ["one", "another", "some_more", "yet_more"]
- )
+ self.assertEqual(cov.config.omit, ["one", "another", "some_more", "yet_more"])
self.assertEqual(cov.config.precision, 3)
- self.assertEqual(cov.config.partial_list,
- [r"pragma:?\s+no branch"]
- )
- self.assertEqual(cov.config.partial_always_list,
- ["if 0:", "while True:"]
- )
- self.assertEqual(cov.config.plugins,
- ["plugins.a_plugin", "plugins.another"]
- )
+ self.assertEqual(cov.config.partial_list, [r"pragma:?\s+no branch"])
+ self.assertEqual(cov.config.partial_always_list, ["if 0:", "while True:"])
+ self.assertEqual(cov.config.plugins, ["plugins.a_plugin", "plugins.another"])
self.assertTrue(cov.config.show_missing)
self.assertTrue(cov.config.skip_covered)
self.assertEqual(cov.config.html_dir, r"c:\tricky\dir.somewhere")
@@ -301,17 +322,17 @@ class ConfigFileTest(CoverageTest):
self.assertEqual(cov.config.paths, {
'source': ['.', '/home/ned/src/'],
'other': ['other', '/home/ned/other', 'c:\\Ned\\etc']
- })
+ })
self.assertEqual(cov.config.get_plugin_options("plugins.a_plugin"), {
'hello': 'world',
'names': 'Jane/John/Jenny',
- })
+ })
self.assertEqual(cov.config.get_plugin_options("plugins.another"), {})
def test_config_file_settings(self):
self.make_file(".coveragerc", self.LOTSA_SETTINGS.format(section=""))
- cov = coverage.coverage()
+ cov = coverage.Coverage()
self.assert_config_settings_are_correct(cov)
def test_config_file_settings_in_setupcfg(self):
@@ -319,7 +340,7 @@ class ConfigFileTest(CoverageTest):
# "coverage:"
nested = self.LOTSA_SETTINGS.format(section="coverage:")
self.make_file("setup.cfg", nested + "\n" + self.SETUP_CFG)
- cov = coverage.coverage()
+ cov = coverage.Coverage()
self.assert_config_settings_are_correct(cov)
def test_config_file_settings_in_setupcfg_if_coveragerc_specified(self):
@@ -328,7 +349,7 @@ class ConfigFileTest(CoverageTest):
# .coveragerc file.
nested = self.LOTSA_SETTINGS.format(section="coverage:")
self.make_file("setup.cfg", nested + "\n" + self.SETUP_CFG)
- cov = coverage.coverage(config_file=".coveragerc")
+ cov = coverage.Coverage(config_file=".coveragerc")
self.assert_config_settings_are_correct(cov)
def test_setupcfg_only_if_not_coveragerc(self):
@@ -341,7 +362,7 @@ class ConfigFileTest(CoverageTest):
omit = bar
branch = true
""")
- cov = coverage.coverage()
+ cov = coverage.Coverage()
self.assertEqual(cov.config.include, ["foo"])
self.assertEqual(cov.config.omit, None)
self.assertEqual(cov.config.branch, False)
@@ -352,7 +373,7 @@ class ConfigFileTest(CoverageTest):
omit = bar
branch = true
""")
- cov = coverage.coverage()
+ cov = coverage.Coverage()
self.assertEqual(cov.config.omit, None)
self.assertEqual(cov.config.branch, False)
@@ -361,11 +382,9 @@ class ConfigFileTest(CoverageTest):
[html]
title = tabblo & «ταБЬℓσ» # numbers
""")
- cov = coverage.coverage()
+ cov = coverage.Coverage()
- self.assertEqual(cov.config.html_title,
- "tabblo & «ταБЬℓσ» # numbers"
- )
+ self.assertEqual(cov.config.html_title, "tabblo & «ταБЬℓσ» # numbers")
def test_unreadable_config(self):
# If a config file is explicitly specified, then it is an error for it
@@ -373,14 +392,14 @@ class ConfigFileTest(CoverageTest):
bad_files = [
"nosuchfile.txt",
".",
- ]
+ ]
for bad_file in bad_files:
msg = "Couldn't read %r as a config file" % bad_file
with self.assertRaisesRegex(CoverageException, msg):
- coverage.coverage(config_file=bad_file)
+ coverage.Coverage(config_file=bad_file)
def test_nocoveragerc_file_when_specified(self):
- cov = coverage.coverage(config_file=".coveragerc")
+ cov = coverage.Coverage(config_file=".coveragerc")
self.assertFalse(cov.config.timid)
self.assertFalse(cov.config.branch)
self.assertEqual(cov.config.data_file, ".coverage")
diff --git a/tests/test_coverage.py b/tests/test_coverage.py
index 35c7c25c..e2f0a614 100644
--- a/tests/test_coverage.py
+++ b/tests/test_coverage.py
@@ -1,5 +1,7 @@
-"""Tests for Coverage."""
-# http://nedbatchelder.com/code/coverage
+# 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 coverage.py."""
import coverage
from coverage import env
@@ -1424,7 +1426,7 @@ class ExcludeTest(CoverageTest):
[1,2,3,4,5,6,9], "", excludes=['#pragma: NO COVER'])
def test_excluding_if_pass(self):
- # From a comment on the coverage page by Michael McNeil Forbes:
+ # From a comment on the coverage.py page by Michael McNeil Forbes:
self.check_coverage("""\
def f():
if False: # pragma: no cover
@@ -1663,8 +1665,11 @@ class ModuleTest(CoverageTest):
def test_not_singleton(self):
# You *can* create another coverage object.
- coverage.coverage()
- coverage.coverage()
+ coverage.Coverage()
+ coverage.Coverage()
+
+ def test_old_name_and_new_name(self):
+ self.assertIs(coverage.coverage, coverage.Coverage)
class ReportingTest(CoverageTest):
@@ -1672,7 +1677,7 @@ class ReportingTest(CoverageTest):
# We don't make any temp files, but we need an empty directory to run the
# tests in.
- run_in_temp_dir = True
+ no_files_in_temp_dir = True
def test_no_data_to_report_on_annotate(self):
# Reporting with no data produces a nice message and no output dir.
@@ -1680,10 +1685,6 @@ class ReportingTest(CoverageTest):
self.command_line("annotate -d ann")
self.assert_doesnt_exist("ann")
- # CoverageTest will yell at us for using a temp directory with no files
- # made. Instead of adding a way to shut it up, just make a file.
- self.make_file("touch.txt", "")
-
def test_no_data_to_report_on_html(self):
# Reporting with no data produces a nice message and no output dir.
with self.assertRaisesRegex(CoverageException, "No data to report."):
diff --git a/tests/test_data.py b/tests/test_data.py
index 0549a3c0..b370aa21 100644
--- a/tests/test_data.py
+++ b/tests/test_data.py
@@ -1,13 +1,25 @@
+# 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 coverage.data"""
-from coverage.backward import pickle
-from coverage.data import CoverageData
-from coverage.files import PathAliases
+import glob
+import json
+import os
+import os.path
+import re
+
+import mock
+
+from coverage.backward import iitems
+from coverage.data import CoverageData, CoverageDataFiles, debug_main
+from coverage.files import PathAliases, canonical_filename
+from coverage.misc import CoverageException
-from tests.coveragetest import CoverageTest
+from tests.coveragetest import CoverageTest, DebugControlString
-DATA_1 = {
+LINES_1 = {
'a.py': {1: None, 2: None},
'b.py': {3: None},
}
@@ -16,141 +28,701 @@ MEASURED_FILES_1 = ['a.py', 'b.py']
A_PY_LINES_1 = [1, 2]
B_PY_LINES_1 = [3]
-DATA_2 = {
+LINES_2 = {
'a.py': {1: None, 5: None},
'c.py': {17: None},
}
SUMMARY_1_2 = {'a.py': 3, 'b.py': 1, 'c.py': 1}
MEASURED_FILES_1_2 = ['a.py', 'b.py', 'c.py']
-ARC_DATA_3 = {
+ARCS_3 = {
'x.py': {
+ (-1, 1): None,
(1, 2): None,
(2, 3): None,
+ (3, -1): None,
},
'y.py': {
+ (-1, 17): None,
(17, 23): None,
+ (23, -1): None,
},
}
-X_PY_ARCS_3 = [(1, 2), (2, 3)]
-Y_PY_ARCS_3 = [(17, 23)]
+X_PY_ARCS_3 = [(-1, 1), (1, 2), (2, 3), (3, -1)]
+Y_PY_ARCS_3 = [(-1, 17), (17, 23), (23, -1)]
+SUMMARY_3 = {'x.py': 3, 'y.py': 2}
+MEASURED_FILES_3 = ['x.py', 'y.py']
+X_PY_LINES_3 = [1, 2, 3]
+Y_PY_LINES_3 = [17, 23]
+ARCS_4 = {
+ 'x.py': {
+ (-1, 2): None,
+ (2, 5): None,
+ (5, -1): None,
+ },
+ 'z.py': {
+ (-1, 1000): None,
+ (1000, -1): None,
+ },
+}
+SUMMARY_3_4 = {'x.py': 5, 'y.py': 2, 'z.py': 1}
+MEASURED_FILES_3_4 = ['x.py', 'y.py', 'z.py']
-class DataTest(CoverageTest):
- """Test cases for coverage.data."""
- run_in_temp_dir = False
+class DataTestHelpers(CoverageTest):
+ """Test helpers for data tests."""
- def assert_summary(self, covdata, summary, fullpath=False):
- """Check that the summary of `covdata` is `summary`."""
- self.assertEqual(covdata.summary(fullpath), summary)
+ def assert_line_counts(self, covdata, line_counts, fullpath=False):
+ """Check that the line_counts of `covdata` is `line_counts`."""
+ self.assertEqual(covdata.line_counts(fullpath), line_counts)
def assert_measured_files(self, covdata, measured):
"""Check that `covdata`'s measured files are `measured`."""
self.assertCountEqual(covdata.measured_files(), measured)
- def test_reading_empty(self):
+
+class CoverageDataTest(DataTestHelpers, CoverageTest):
+ """Test cases for CoverageData."""
+
+ run_in_temp_dir = False
+
+ def test_empty_data_is_false(self):
+ covdata = CoverageData()
+ self.assertFalse(covdata)
+
+ def test_line_data_is_true(self):
+ covdata = CoverageData()
+ covdata.set_lines(LINES_1)
+ self.assertTrue(covdata)
+
+ def test_arc_data_is_true(self):
covdata = CoverageData()
- covdata.read()
- self.assert_summary(covdata, {})
+ covdata.set_arcs(ARCS_3)
+ self.assertTrue(covdata)
- def test_adding_data(self):
+ def test_adding_lines(self):
covdata = CoverageData()
- covdata.add_line_data(DATA_1)
- self.assert_summary(covdata, SUMMARY_1)
+ covdata.set_lines(LINES_1)
+ self.assert_line_counts(covdata, SUMMARY_1)
self.assert_measured_files(covdata, MEASURED_FILES_1)
+ self.assertCountEqual(covdata.lines("a.py"), A_PY_LINES_1)
+ self.assertFalse(covdata.has_arcs())
- def test_touch_file(self):
+ def test_adding_arcs(self):
covdata = CoverageData()
- covdata.add_line_data(DATA_1)
- covdata.touch_file('x.py')
- self.assert_measured_files(covdata, MEASURED_FILES_1 + ['x.py'])
+ covdata.set_arcs(ARCS_3)
+ self.assert_line_counts(covdata, SUMMARY_3)
+ self.assert_measured_files(covdata, MEASURED_FILES_3)
+ self.assertCountEqual(covdata.lines("x.py"), X_PY_LINES_3)
+ self.assertCountEqual(covdata.arcs("x.py"), X_PY_ARCS_3)
+ self.assertCountEqual(covdata.lines("y.py"), Y_PY_LINES_3)
+ self.assertCountEqual(covdata.arcs("y.py"), Y_PY_ARCS_3)
+ self.assertTrue(covdata.has_arcs())
+
+ def test_cant_set_arcs_with_lines(self):
+ covdata = CoverageData()
+ covdata.set_lines(LINES_1)
+ with self.assertRaisesRegex(CoverageException, "Can't add arcs to existing line data"):
+ covdata.set_arcs(ARCS_3)
+
+ def test_cant_set_lines_with_arcs(self):
+ covdata = CoverageData()
+ covdata.set_arcs(ARCS_3)
+ with self.assertRaisesRegex(CoverageException, "Can't add lines to existing arc data"):
+ covdata.set_lines(LINES_1)
+
+ def test_touch_file_with_lines(self):
+ covdata = CoverageData()
+ covdata.set_lines(LINES_1)
+ covdata.touch_file('zzz.py')
+ self.assert_measured_files(covdata, MEASURED_FILES_1 + ['zzz.py'])
+
+ def test_touch_file_with_arcs(self):
+ covdata = CoverageData()
+ covdata.set_arcs(ARCS_3)
+ covdata.touch_file('zzz.py')
+ self.assert_measured_files(covdata, MEASURED_FILES_3 + ['zzz.py'])
+
+ def test_no_lines_vs_unmeasured_file(self):
+ covdata = CoverageData()
+ covdata.set_lines(LINES_1)
+ covdata.touch_file('zzz.py')
+ self.assertEqual(covdata.lines('zzz.py'), [])
+ self.assertIsNone(covdata.lines('no_such_file.py'))
+
+ def test_run_info(self):
+ covdata = CoverageData()
+ self.assertEqual(covdata.run_infos(), [])
+ covdata.add_run_info(hello="there")
+ self.assertEqual(covdata.run_infos(), [{"hello": "there"}])
+ covdata.add_run_info(count=17)
+ self.assertEqual(covdata.run_infos(), [{"hello": "there", "count": 17}])
+
+ def test_no_arcs_vs_unmeasured_file(self):
+ covdata = CoverageData()
+ covdata.set_arcs(ARCS_3)
+ covdata.touch_file('zzz.py')
+ self.assertEqual(covdata.lines('zzz.py'), [])
+ self.assertIsNone(covdata.lines('no_such_file.py'))
+ self.assertEqual(covdata.arcs('zzz.py'), [])
+ self.assertIsNone(covdata.arcs('no_such_file.py'))
+
+ def test_file_tracer_name(self):
+ covdata = CoverageData()
+ covdata.set_lines({
+ "p1.foo": dict.fromkeys([1, 2, 3]),
+ "p2.html": dict.fromkeys([10, 11, 12]),
+ "main.py": dict.fromkeys([20]),
+ })
+ covdata.set_file_tracers({"p1.foo": "p1.plugin", "p2.html": "p2.plugin"})
+ self.assertEqual(covdata.file_tracer("p1.foo"), "p1.plugin")
+ self.assertEqual(covdata.file_tracer("main.py"), "")
+ self.assertIsNone(covdata.file_tracer("p3.not_here"))
+
+ def test_cant_file_tracer_unmeasured_files(self):
+ covdata = CoverageData()
+ msg = "Can't add file tracer data for unmeasured file 'p1.foo'"
+ with self.assertRaisesRegex(CoverageException, msg):
+ covdata.set_file_tracers({"p1.foo": "p1.plugin"})
+
+ covdata.set_lines({"p2.html": dict.fromkeys([10, 11, 12])})
+ with self.assertRaisesRegex(CoverageException, msg):
+ covdata.set_file_tracers({"p1.foo": "p1.plugin"})
+
+ def test_cant_change_file_tracer_name(self):
+ covdata = CoverageData()
+ covdata.set_lines({"p1.foo": dict.fromkeys([1, 2, 3])})
+ covdata.set_file_tracers({"p1.foo": "p1.plugin"})
+
+ msg = "Conflicting file tracer name for 'p1.foo': 'p1.plugin' vs 'p1.plugin.foo'"
+ with self.assertRaisesRegex(CoverageException, msg):
+ covdata.set_file_tracers({"p1.foo": "p1.plugin.foo"})
+
+ def test_update_lines(self):
+ covdata1 = CoverageData()
+ covdata1.set_lines(LINES_1)
+
+ covdata2 = CoverageData()
+ covdata2.set_lines(LINES_2)
+
+ covdata3 = CoverageData()
+ covdata3.update(covdata1)
+ covdata3.update(covdata2)
+
+ self.assert_line_counts(covdata3, SUMMARY_1_2)
+ self.assert_measured_files(covdata3, MEASURED_FILES_1_2)
+ self.assertEqual(covdata3.run_infos(), [])
+
+ def test_update_arcs(self):
+ covdata1 = CoverageData()
+ covdata1.set_arcs(ARCS_3)
+
+ covdata2 = CoverageData()
+ covdata2.set_arcs(ARCS_4)
+
+ covdata3 = CoverageData()
+ covdata3.update(covdata1)
+ covdata3.update(covdata2)
+
+ self.assert_line_counts(covdata3, SUMMARY_3_4)
+ self.assert_measured_files(covdata3, MEASURED_FILES_3_4)
+ self.assertEqual(covdata3.run_infos(), [])
+
+ def test_update_run_info(self):
+ covdata1 = CoverageData()
+ covdata1.set_arcs(ARCS_3)
+ covdata1.add_run_info(hello="there", count=17)
+
+ covdata2 = CoverageData()
+ covdata2.set_arcs(ARCS_4)
+ covdata2.add_run_info(hello="goodbye", count=23)
+
+ covdata3 = CoverageData()
+ covdata3.update(covdata1)
+ covdata3.update(covdata2)
+
+ self.assertEqual(covdata3.run_infos(), [
+ {'hello': 'there', 'count': 17},
+ {'hello': 'goodbye', 'count': 23},
+ ])
+
+ def test_update_cant_mix_lines_and_arcs(self):
+ covdata1 = CoverageData()
+ covdata1.set_lines(LINES_1)
+
+ covdata2 = CoverageData()
+ covdata2.set_arcs(ARCS_3)
+
+ with self.assertRaisesRegex(CoverageException, "Can't combine arc data with line data"):
+ covdata1.update(covdata2)
+
+ with self.assertRaisesRegex(CoverageException, "Can't combine line data with arc data"):
+ covdata2.update(covdata1)
+
+ def test_update_file_tracers(self):
+ covdata1 = CoverageData()
+ covdata1.set_lines({
+ "p1.html": dict.fromkeys([1, 2, 3, 4]),
+ "p2.html": dict.fromkeys([5, 6, 7]),
+ "main.py": dict.fromkeys([10, 11, 12]),
+ })
+ covdata1.set_file_tracers({
+ "p1.html": "html.plugin",
+ "p2.html": "html.plugin2",
+ })
+
+ covdata2 = CoverageData()
+ covdata2.set_lines({
+ "p1.html": dict.fromkeys([3, 4, 5, 6]),
+ "p2.html": dict.fromkeys([7, 8, 9]),
+ "p3.foo": dict.fromkeys([1000, 1001]),
+ "main.py": dict.fromkeys([10, 11, 12]),
+ })
+ covdata2.set_file_tracers({
+ "p1.html": "html.plugin",
+ "p2.html": "html.plugin2",
+ "p3.foo": "foo_plugin",
+ })
+
+ covdata3 = CoverageData()
+ covdata3.update(covdata1)
+ covdata3.update(covdata2)
+ self.assertEqual(covdata3.file_tracer("p1.html"), "html.plugin")
+ self.assertEqual(covdata3.file_tracer("p2.html"), "html.plugin2")
+ self.assertEqual(covdata3.file_tracer("p3.foo"), "foo_plugin")
+ self.assertEqual(covdata3.file_tracer("main.py"), "")
+
+ def test_update_conflicting_file_tracers(self):
+ covdata1 = CoverageData()
+ covdata1.set_lines({"p1.html": dict.fromkeys([1, 2, 3])})
+ covdata1.set_file_tracers({"p1.html": "html.plugin"})
+
+ covdata2 = CoverageData()
+ covdata2.set_lines({"p1.html": dict.fromkeys([1, 2, 3])})
+ covdata2.set_file_tracers({"p1.html": "html.other_plugin"})
+
+ msg = "Conflicting file tracer name for 'p1.html': 'html.plugin' vs 'html.other_plugin'"
+ with self.assertRaisesRegex(CoverageException, msg):
+ covdata1.update(covdata2)
+
+ msg = "Conflicting file tracer name for 'p1.html': 'html.other_plugin' vs 'html.plugin'"
+ with self.assertRaisesRegex(CoverageException, msg):
+ covdata2.update(covdata1)
+
+ def test_update_file_tracer_vs_no_file_tracer(self):
+ covdata1 = CoverageData()
+ covdata1.set_lines({"p1.html": dict.fromkeys([1, 2, 3])})
+ covdata1.set_file_tracers({"p1.html": "html.plugin"})
+
+ covdata2 = CoverageData()
+ covdata2.set_lines({"p1.html": dict.fromkeys([1, 2, 3])})
+
+ msg = "Conflicting file tracer name for 'p1.html': 'html.plugin' vs ''"
+ with self.assertRaisesRegex(CoverageException, msg):
+ covdata1.update(covdata2)
+
+ msg = "Conflicting file tracer name for 'p1.html': '' vs 'html.plugin'"
+ with self.assertRaisesRegex(CoverageException, msg):
+ covdata2.update(covdata1)
+
+ def test_add_to_hash_with_lines(self):
+ covdata = CoverageData()
+ covdata.set_lines(LINES_1)
+ hasher = mock.Mock()
+ covdata.add_to_hash("a.py", hasher)
+ self.assertEqual(hasher.method_calls, [
+ mock.call.update([1, 2]), # lines
+ mock.call.update(""), # file_tracer name
+ ])
+
+ def test_add_to_hash_with_arcs(self):
+ covdata = CoverageData()
+ covdata.set_arcs(ARCS_3)
+ covdata.set_file_tracers({"y.py": "hologram_plugin"})
+ hasher = mock.Mock()
+ covdata.add_to_hash("y.py", hasher)
+ self.assertEqual(hasher.method_calls, [
+ mock.call.update([(-1, 17), (17, 23), (23, -1)]), # arcs
+ mock.call.update("hologram_plugin"), # file_tracer name
+ ])
+
+
+class CoverageDataTestInTempDir(DataTestHelpers, CoverageTest):
+ """Tests of CoverageData that need a temp dir to make files."""
+
+ def test_read_write_lines(self):
+ covdata1 = CoverageData()
+ covdata1.set_lines(LINES_1)
+ covdata1.write_file("lines.dat")
+
+ covdata2 = CoverageData()
+ covdata2.read_file("lines.dat")
+ self.assert_line_counts(covdata2, SUMMARY_1)
+ self.assert_measured_files(covdata2, MEASURED_FILES_1)
+ self.assertCountEqual(covdata2.lines("a.py"), A_PY_LINES_1)
+ self.assertEqual(covdata2.run_infos(), [])
+
+ def test_read_write_arcs(self):
+ covdata1 = CoverageData()
+ covdata1.set_arcs(ARCS_3)
+ covdata1.write_file("arcs.dat")
+
+ covdata2 = CoverageData()
+ covdata2.read_file("arcs.dat")
+ self.assert_line_counts(covdata2, SUMMARY_3)
+ self.assert_measured_files(covdata2, MEASURED_FILES_3)
+ self.assertCountEqual(covdata2.lines("x.py"), X_PY_LINES_3)
+ self.assertCountEqual(covdata2.arcs("x.py"), X_PY_ARCS_3)
+ self.assertCountEqual(covdata2.lines("y.py"), Y_PY_LINES_3)
+ self.assertCountEqual(covdata2.arcs("y.py"), Y_PY_ARCS_3)
+ self.assertEqual(covdata2.run_infos(), [])
+
+ def test_read_errors(self):
+ covdata = CoverageData()
+
+ msg = r"Couldn't read data from '{0}': \S+"
+ self.make_file("xyzzy.dat", "xyzzy")
+ with self.assertRaisesRegex(CoverageException, msg.format("xyzzy.dat")):
+ covdata.read_file("xyzzy.dat")
+
+ self.make_file("empty.dat", "")
+ with self.assertRaisesRegex(CoverageException, msg.format("empty.dat")):
+ covdata.read_file("empty.dat")
+
+ with self.assertRaisesRegex(CoverageException, msg.format("nonexistent.dat")):
+ covdata.read_file("nonexistent.dat")
+
+ self.make_file("misleading.dat", CoverageData._GO_AWAY + " this isn't JSON")
+ with self.assertRaisesRegex(CoverageException, msg.format("misleading.dat")):
+ covdata.read_file("misleading.dat")
+
+ # After all that, no data should be in our CoverageData.
+ self.assertFalse(covdata)
+
+ def test_debug_main(self):
+ covdata1 = CoverageData()
+ covdata1.set_lines(LINES_1)
+ covdata1.write_file(".coverage")
+ debug_main([])
+
+ covdata2 = CoverageData()
+ covdata2.set_arcs(ARCS_3)
+ covdata2.set_file_tracers({"y.py": "magic_plugin"})
+ covdata2.add_run_info(version="v3.14", chunks=["z", "a"])
+ covdata2.write_file("arcs.dat")
+
+ covdata3 = CoverageData()
+ covdata3.write_file("empty.dat")
+ debug_main(["arcs.dat", "empty.dat"])
+
+ expected = {
+ ".coverage": {
+ "lines": {
+ "a.py": [1, 2],
+ "b.py": [3],
+ },
+ },
+ "arcs.dat": {
+ "arcs": {
+ "x.py": [[-1, 1], [1, 2], [2, 3], [3, -1]],
+ "y.py": [[-1, 17], [17, 23], [23, -1]],
+ },
+ "file_tracers": {"y.py": "magic_plugin"},
+ "runs": [
+ {
+ "chunks": ["z", "a"],
+ "version": "v3.14",
+ },
+ ],
+ },
+ "empty.dat": {"lines": {}},
+ }
+ pieces = re.split(r"(?m)-+ ([\w.]+) -+$", self.stdout())
+ for name, json_out in zip(pieces[1::2], pieces[2::2]):
+ json_got = json.loads(json_out)
+ canonicalize_json_data(json_got)
+ self.assertEqual(expected[name], json_got)
+
+
+def canonicalize_json_data(data):
+ """Canonicalize our JSON data so it can be compared."""
+ for fname, lines in iitems(data.get('lines', {})):
+ data['lines'][fname] = sorted(lines)
+ for fname, arcs in iitems(data.get('arcs', {})):
+ data['arcs'][fname] = sorted(arcs)
+
+
+class CoverageDataFilesTest(DataTestHelpers, CoverageTest):
+ """Tests of CoverageDataFiles."""
+
+ no_files_in_temp_dir = True
+
+ def setUp(self):
+ super(CoverageDataFilesTest, self).setUp()
+ self.data_files = CoverageDataFiles()
+
+ def test_reading_missing(self):
+ self.assert_doesnt_exist(".coverage")
+ covdata = CoverageData()
+ self.data_files.read(covdata)
+ self.assert_line_counts(covdata, {})
def test_writing_and_reading(self):
covdata1 = CoverageData()
- covdata1.add_line_data(DATA_1)
- covdata1.write()
+ covdata1.set_lines(LINES_1)
+ self.data_files.write(covdata1)
covdata2 = CoverageData()
- covdata2.read()
- self.assert_summary(covdata2, SUMMARY_1)
+ self.data_files.read(covdata2)
+ self.assert_line_counts(covdata2, SUMMARY_1)
+
+ def test_debug_output_with_debug_option(self):
+ # With debug option dataio, we get debug output about reading and
+ # writing files.
+ debug = DebugControlString(options=["dataio"])
+ covdata1 = CoverageData(debug=debug)
+ covdata1.set_lines(LINES_1)
+ self.data_files.write(covdata1)
+
+ covdata2 = CoverageData(debug=debug)
+ self.data_files.read(covdata2)
+ self.assert_line_counts(covdata2, SUMMARY_1)
+
+ self.assertRegex(
+ debug.get_output(),
+ r"^Writing data to '.*\.coverage'\n"
+ r"Reading data from '.*\.coverage'\n$"
+ )
+
+ def test_debug_output_without_debug_option(self):
+ # With a debug object, but not the dataio option, we don't get debug
+ # output.
+ debug = DebugControlString(options=[])
+ covdata1 = CoverageData(debug=debug)
+ covdata1.set_lines(LINES_1)
+ self.data_files.write(covdata1)
+
+ covdata2 = CoverageData(debug=debug)
+ self.data_files.read(covdata2)
+ self.assert_line_counts(covdata2, SUMMARY_1)
+
+ self.assertEqual(debug.get_output(), "")
+
+ def test_explicit_suffix(self):
+ self.assert_doesnt_exist(".coverage.SUFFIX")
+ covdata = CoverageData()
+ covdata.set_lines(LINES_1)
+ self.data_files.write(covdata, suffix='SUFFIX')
+ self.assert_exists(".coverage.SUFFIX")
+ self.assert_doesnt_exist(".coverage")
+
+ def test_true_suffix(self):
+ self.assertEqual(glob.glob(".coverage.*"), [])
+
+ # suffix=True will make a randomly named data file.
+ covdata1 = CoverageData()
+ covdata1.set_lines(LINES_1)
+ self.data_files.write(covdata1, suffix=True)
+ self.assert_doesnt_exist(".coverage")
+ data_files1 = glob.glob(".coverage.*")
+ self.assertEqual(len(data_files1), 1)
+
+ # Another suffix=True will choose a different name.
+ covdata2 = CoverageData()
+ covdata2.set_lines(LINES_1)
+ self.data_files.write(covdata2, suffix=True)
+ self.assert_doesnt_exist(".coverage")
+ data_files2 = glob.glob(".coverage.*")
+ self.assertEqual(len(data_files2), 2)
+
+ # In addition to being different, the suffixes have the pid in them.
+ self.assertTrue(all(str(os.getpid()) in fn for fn in data_files2))
def test_combining(self):
+ self.assert_doesnt_exist(".coverage.1")
+ self.assert_doesnt_exist(".coverage.2")
+
covdata1 = CoverageData()
- covdata1.add_line_data(DATA_1)
- covdata1.write(suffix='1')
+ covdata1.set_lines(LINES_1)
+ self.data_files.write(covdata1, suffix='1')
+ self.assert_exists(".coverage.1")
+ self.assert_doesnt_exist(".coverage.2")
covdata2 = CoverageData()
- covdata2.add_line_data(DATA_2)
- covdata2.write(suffix='2')
+ covdata2.set_lines(LINES_2)
+ self.data_files.write(covdata2, suffix='2')
+ self.assert_exists(".coverage.2")
covdata3 = CoverageData()
- covdata3.combine_parallel_data()
- self.assert_summary(covdata3, SUMMARY_1_2)
+ self.data_files.combine_parallel_data(covdata3)
+ self.assert_line_counts(covdata3, SUMMARY_1_2)
self.assert_measured_files(covdata3, MEASURED_FILES_1_2)
+ self.assert_doesnt_exist(".coverage.1")
+ self.assert_doesnt_exist(".coverage.2")
def test_erasing(self):
covdata1 = CoverageData()
- covdata1.add_line_data(DATA_1)
- covdata1.write()
+ covdata1.set_lines(LINES_1)
+ self.data_files.write(covdata1)
+
covdata1.erase()
- self.assert_summary(covdata1, {})
+ self.assert_line_counts(covdata1, {})
+ self.data_files.erase()
covdata2 = CoverageData()
- covdata2.read()
- self.assert_summary(covdata2, {})
+ self.data_files.read(covdata2)
+ self.assert_line_counts(covdata2, {})
+
+ def test_erasing_parallel(self):
+ self.make_file("datafile.1")
+ self.make_file("datafile.2")
+ self.make_file(".coverage")
+ data_files = CoverageDataFiles("datafile")
+ data_files.erase(parallel=True)
+ self.assert_doesnt_exist("datafile.1")
+ self.assert_doesnt_exist("datafile.2")
+ self.assert_exists(".coverage")
+
+ def read_json_data_file(self, fname):
+ """Read a JSON data file for testing the JSON directly."""
+ with open(fname, 'r') as fdata:
+ go_away = fdata.read(len(CoverageData._GO_AWAY))
+ self.assertEqual(go_away, CoverageData._GO_AWAY)
+ return json.load(fdata)
def test_file_format(self):
- # Write with CoverageData, then read the pickle explicitly.
+ # Write with CoverageData, then read the JSON explicitly.
covdata = CoverageData()
- covdata.add_line_data(DATA_1)
- covdata.write()
+ covdata.set_lines(LINES_1)
+ self.data_files.write(covdata)
- with open(".coverage", 'rb') as fdata:
- data = pickle.load(fdata)
+ data = self.read_json_data_file(".coverage")
lines = data['lines']
self.assertCountEqual(lines.keys(), MEASURED_FILES_1)
self.assertCountEqual(lines['a.py'], A_PY_LINES_1)
self.assertCountEqual(lines['b.py'], B_PY_LINES_1)
# If not measuring branches, there's no arcs entry.
- self.assertEqual(data.get('arcs', 'not there'), 'not there')
+ self.assertNotIn('arcs', data)
+ # If no file tracers were involved, there's no file_tracers entry.
+ self.assertNotIn('file_tracers', data)
def test_file_format_with_arcs(self):
- # Write with CoverageData, then read the pickle explicitly.
+ # Write with CoverageData, then read the JSON explicitly.
covdata = CoverageData()
- covdata.add_arc_data(ARC_DATA_3)
- covdata.write()
+ covdata.set_arcs(ARCS_3)
+ self.data_files.write(covdata)
- with open(".coverage", 'rb') as fdata:
- data = pickle.load(fdata)
+ data = self.read_json_data_file(".coverage")
- self.assertCountEqual(data['lines'].keys(), [])
+ self.assertNotIn('lines', data)
arcs = data['arcs']
- self.assertCountEqual(arcs['x.py'], X_PY_ARCS_3)
- self.assertCountEqual(arcs['y.py'], Y_PY_ARCS_3)
+ self.assertCountEqual(arcs.keys(), MEASURED_FILES_3)
+ self.assertCountEqual(arcs['x.py'], map(list, X_PY_ARCS_3))
+ self.assertCountEqual(arcs['y.py'], map(list, Y_PY_ARCS_3))
+ # If no file tracers were involved, there's no file_tracers entry.
+ self.assertNotIn('file_tracers', data)
+
+ def test_writing_to_other_file(self):
+ data_files = CoverageDataFiles(".otherfile")
+ covdata = CoverageData()
+ covdata.set_lines(LINES_1)
+ data_files.write(covdata)
+ self.assert_doesnt_exist(".coverage")
+ self.assert_exists(".otherfile")
+
+ data_files.write(covdata, suffix="extra")
+ self.assert_exists(".otherfile.extra")
+ self.assert_doesnt_exist(".coverage")
def test_combining_with_aliases(self):
covdata1 = CoverageData()
- covdata1.add_line_data({
+ covdata1.set_lines({
'/home/ned/proj/src/a.py': {1: None, 2: None},
'/home/ned/proj/src/sub/b.py': {3: None},
- })
- covdata1.write(suffix='1')
+ '/home/ned/proj/src/template.html': {10: None},
+ })
+ covdata1.set_file_tracers({
+ '/home/ned/proj/src/template.html': 'html.plugin',
+ })
+ self.data_files.write(covdata1, suffix='1')
covdata2 = CoverageData()
- covdata2.add_line_data({
+ covdata2.set_lines({
r'c:\ned\test\a.py': {4: None, 5: None},
- r'c:\ned\test\sub\b.py': {6: None},
- })
- covdata2.write(suffix='2')
+ r'c:\ned\test\sub\b.py': {3: None, 6: None},
+ })
+ self.data_files.write(covdata2, suffix='2')
covdata3 = CoverageData()
aliases = PathAliases()
aliases.add("/home/ned/proj/src/", "./")
aliases.add(r"c:\ned\test", "./")
- covdata3.combine_parallel_data(aliases=aliases)
- self.assert_summary(
- covdata3, {'./a.py': 4, './sub/b.py': 2}, fullpath=True
- )
- self.assert_measured_files(covdata3, ['./a.py', './sub/b.py'])
+ self.data_files.combine_parallel_data(covdata3, aliases=aliases)
+
+ apy = canonical_filename('./a.py')
+ sub_bpy = canonical_filename('./sub/b.py')
+ template_html = canonical_filename('./template.html')
+
+ self.assert_line_counts(covdata3, {apy: 4, sub_bpy: 2, template_html: 1}, fullpath=True)
+ self.assert_measured_files(covdata3, [apy, sub_bpy, template_html])
+ self.assertEqual(covdata3.file_tracer(template_html), 'html.plugin')
+
+ def test_combining_from_different_directories(self):
+ covdata1 = CoverageData()
+ covdata1.set_lines(LINES_1)
+ os.makedirs('cov1')
+ covdata1.write_file('cov1/.coverage.1')
+
+ covdata2 = CoverageData()
+ covdata2.set_lines(LINES_2)
+ os.makedirs('cov2')
+ covdata2.write_file('cov2/.coverage.2')
+
+ # This data won't be included.
+ covdata_xxx = CoverageData()
+ covdata_xxx.set_arcs(ARCS_3)
+ covdata_xxx.write_file('.coverage.xxx')
+
+ covdata3 = CoverageData()
+ self.data_files.combine_parallel_data(covdata3, data_paths=['cov1', 'cov2'])
+
+ self.assert_line_counts(covdata3, SUMMARY_1_2)
+ self.assert_measured_files(covdata3, MEASURED_FILES_1_2)
+ self.assert_doesnt_exist("cov1/.coverage.1")
+ self.assert_doesnt_exist("cov2/.coverage.2")
+ self.assert_exists(".coverage.xxx")
+
+ def test_combining_from_files(self):
+ covdata1 = CoverageData()
+ covdata1.set_lines(LINES_1)
+ os.makedirs('cov1')
+ covdata1.write_file('cov1/.coverage.1')
+
+ covdata2 = CoverageData()
+ covdata2.set_lines(LINES_2)
+ os.makedirs('cov2')
+ covdata2.write_file('cov2/.coverage.2')
+
+ # This data won't be included.
+ covdata_xxx = CoverageData()
+ covdata_xxx.set_arcs(ARCS_3)
+ covdata_xxx.write_file('.coverage.xxx')
+ covdata_xxx.write_file('cov2/.coverage.xxx')
+
+ covdata3 = CoverageData()
+ self.data_files.combine_parallel_data(covdata3, data_paths=['cov1', 'cov2/.coverage.2'])
+
+ self.assert_line_counts(covdata3, SUMMARY_1_2)
+ self.assert_measured_files(covdata3, MEASURED_FILES_1_2)
+ self.assert_doesnt_exist("cov1/.coverage.1")
+ self.assert_doesnt_exist("cov2/.coverage.2")
+ self.assert_exists(".coverage.xxx")
+ self.assert_exists("cov2/.coverage.xxx")
+
+ def test_combining_from_nonexistent_directories(self):
+ covdata = CoverageData()
+ msg = "Couldn't combine from non-existent path 'xyzzy'"
+ with self.assertRaisesRegex(CoverageException, msg):
+ self.data_files.combine_parallel_data(covdata, data_paths=['xyzzy'])
diff --git a/tests/test_debug.py b/tests/test_debug.py
index 8dd13a57..4d9e9271 100644
--- a/tests/test_debug.py
+++ b/tests/test_debug.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 of coverage/debug.py"""
import os
@@ -61,7 +64,7 @@ class DebugTraceTest(CoverageTest):
""")
debug_out = StringIO()
- cov = coverage.coverage(debug=debug)
+ cov = coverage.Coverage(debug=debug)
cov._debug_file = debug_out
self.start_import_stop(cov, "f1")
@@ -116,7 +119,7 @@ class DebugTraceTest(CoverageTest):
out_lines = self.f1_debug_output(["sys"])
labels = """
- version coverage cover_dir pylib_dirs tracer config_files
+ version coverage cover_dirs pylib_dirs tracer config_files
configs_read data_path python platform implementation executable
cwd path environment command_line cover_match pylib_match
""".split()
diff --git a/tests/test_execfile.py b/tests/test_execfile.py
index 9c2bc7d6..2533f81d 100644
--- a/tests/test_execfile.py
+++ b/tests/test_execfile.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 coverage.execfile"""
import compileall
diff --git a/tests/test_farm.py b/tests/test_farm.py
index 92bd968a..daa83d92 100644
--- a/tests/test_farm.py
+++ b/tests/test_farm.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
+
"""Run tests in the farm sub-directory. Designed for nose."""
import difflib
@@ -14,7 +17,7 @@ from nose.plugins.skip import SkipTest
from tests.helpers import run_command
from tests.backtest import execfile # pylint: disable=redefined-builtin
-from coverage.control import _TEST_NAME_FILE
+from coverage.debug import _TEST_NAME_FILE
def test_farm(clean_only=False):
@@ -228,8 +231,8 @@ class FarmTestCase(object):
without triggering an assertion. `right_extra` means the right
directory can.
- `scrubs` is a list of pairs, regex find and replace patterns to use to
- scrub the files of unimportant differences.
+ `scrubs` is a list of pairs, regexes to find and literal strings to
+ replace them with to scrub the files of unimportant differences.
An assertion will be raised if the directories fail one of their
matches.
@@ -309,7 +312,7 @@ class FarmTestCase(object):
"""
for rgx_find, rgx_replace in scrubs:
- strdata = re.sub(rgx_find, rgx_replace, strdata)
+ strdata = re.sub(rgx_find, re.escape(rgx_replace), strdata)
return strdata
def contains(self, filename, *strlist):
diff --git a/tests/test_filereporter.py b/tests/test_filereporter.py
index 9db4c0c3..380d92d3 100644
--- a/tests/test_filereporter.py
+++ b/tests/test_filereporter.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 FileReporters"""
import os
@@ -32,9 +35,9 @@ class FileReporterTest(CoverageTest):
acu = PythonFileReporter("aa/afile.py")
bcu = PythonFileReporter("aa/bb/bfile.py")
ccu = PythonFileReporter("aa/bb/cc/cfile.py")
- self.assertEqual(acu.name, "aa/afile.py")
- self.assertEqual(bcu.name, "aa/bb/bfile.py")
- self.assertEqual(ccu.name, "aa/bb/cc/cfile.py")
+ self.assertEqual(acu.relative_filename(), "aa/afile.py")
+ self.assertEqual(bcu.relative_filename(), "aa/bb/bfile.py")
+ self.assertEqual(ccu.relative_filename(), "aa/bb/cc/cfile.py")
self.assertEqual(acu.flat_rootname(), "aa_afile_py")
self.assertEqual(bcu.flat_rootname(), "aa_bb_bfile_py")
self.assertEqual(ccu.flat_rootname(), "aa_bb_cc_cfile_py")
@@ -46,9 +49,9 @@ class FileReporterTest(CoverageTest):
acu = PythonFileReporter("aa/afile.odd.py")
bcu = PythonFileReporter("aa/bb/bfile.odd.py")
b2cu = PythonFileReporter("aa/bb.odd/bfile.py")
- self.assertEqual(acu.name, "aa/afile.odd.py")
- self.assertEqual(bcu.name, "aa/bb/bfile.odd.py")
- self.assertEqual(b2cu.name, "aa/bb.odd/bfile.py")
+ self.assertEqual(acu.relative_filename(), "aa/afile.odd.py")
+ self.assertEqual(bcu.relative_filename(), "aa/bb/bfile.odd.py")
+ self.assertEqual(b2cu.relative_filename(), "aa/bb.odd/bfile.py")
self.assertEqual(acu.flat_rootname(), "aa_afile_odd_py")
self.assertEqual(bcu.flat_rootname(), "aa_bb_bfile_odd_py")
self.assertEqual(b2cu.flat_rootname(), "aa_bb_odd_bfile_py")
@@ -64,9 +67,9 @@ class FileReporterTest(CoverageTest):
acu = PythonFileReporter(aa)
bcu = PythonFileReporter(aa.bb)
ccu = PythonFileReporter(aa.bb.cc)
- self.assertEqual(acu.name, native("aa.py"))
- self.assertEqual(bcu.name, native("aa/bb.py"))
- self.assertEqual(ccu.name, native("aa/bb/cc.py"))
+ self.assertEqual(acu.relative_filename(), native("aa.py"))
+ self.assertEqual(bcu.relative_filename(), native("aa/bb.py"))
+ self.assertEqual(ccu.relative_filename(), native("aa/bb/cc.py"))
self.assertEqual(acu.flat_rootname(), "aa_py")
self.assertEqual(bcu.flat_rootname(), "aa_bb_py")
self.assertEqual(ccu.flat_rootname(), "aa_bb_cc_py")
@@ -82,9 +85,9 @@ class FileReporterTest(CoverageTest):
acu = PythonFileReporter(aa.afile)
bcu = PythonFileReporter(aa.bb.bfile)
ccu = PythonFileReporter(aa.bb.cc.cfile)
- self.assertEqual(acu.name, native("aa/afile.py"))
- self.assertEqual(bcu.name, native("aa/bb/bfile.py"))
- self.assertEqual(ccu.name, native("aa/bb/cc/cfile.py"))
+ self.assertEqual(acu.relative_filename(), native("aa/afile.py"))
+ self.assertEqual(bcu.relative_filename(), native("aa/bb/bfile.py"))
+ self.assertEqual(ccu.relative_filename(), native("aa/bb/cc/cfile.py"))
self.assertEqual(acu.flat_rootname(), "aa_afile_py")
self.assertEqual(bcu.flat_rootname(), "aa_bb_bfile_py")
self.assertEqual(ccu.flat_rootname(), "aa_bb_cc_cfile_py")
@@ -117,4 +120,4 @@ class FileReporterTest(CoverageTest):
ecu = PythonFileReporter(egg1)
eecu = PythonFileReporter(egg1.egg1)
self.assertEqual(ecu.source(), u"")
- self.assertEqual(eecu.source().split("\n")[0], u"# My egg file!")
+ self.assertIn(u"# My egg file!", eecu.source().splitlines())
diff --git a/tests/test_files.py b/tests/test_files.py
index ae56e728..b658853a 100644
--- a/tests/test_files.py
+++ b/tests/test_files.py
@@ -1,19 +1,24 @@
+# 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 files.py"""
import os
import os.path
+from coverage import files
from coverage.files import (
- FileLocator, TreeMatcher, FnmatchMatcher, ModuleMatcher, PathAliases,
- find_python_files, abs_file
+ TreeMatcher, FnmatchMatcher, ModuleMatcher, PathAliases,
+ find_python_files, abs_file, actual_path
)
from coverage.misc import CoverageException
+from coverage import env
from tests.coveragetest import CoverageTest
-class FileLocatorTest(CoverageTest):
- """Tests of `FileLocator`."""
+class FilesTest(CoverageTest):
+ """Tests of coverage.files."""
def abs_path(self, p):
"""Return the absolute path for `p`."""
@@ -21,11 +26,11 @@ class FileLocatorTest(CoverageTest):
def test_simple(self):
self.make_file("hello.py")
- fl = FileLocator()
- self.assertEqual(fl.relative_filename("hello.py"), "hello.py")
+ files.set_relative_directory()
+ self.assertEqual(files.relative_filename("hello.py"), "hello.py")
a = self.abs_path("hello.py")
self.assertNotEqual(a, "hello.py")
- self.assertEqual(fl.relative_filename(a), "hello.py")
+ self.assertEqual(files.relative_filename(a), "hello.py")
def test_peer_directories(self):
self.make_file("sub/proj1/file1.py")
@@ -34,20 +39,20 @@ class FileLocatorTest(CoverageTest):
a2 = self.abs_path("sub/proj2/file2.py")
d = os.path.normpath("sub/proj1")
os.chdir(d)
- fl = FileLocator()
- self.assertEqual(fl.relative_filename(a1), "file1.py")
- self.assertEqual(fl.relative_filename(a2), a2)
+ files.set_relative_directory()
+ self.assertEqual(files.relative_filename(a1), "file1.py")
+ self.assertEqual(files.relative_filename(a2), a2)
def test_filepath_contains_absolute_prefix_twice(self):
# https://bitbucket.org/ned/coveragepy/issue/194
# Build a path that has two pieces matching the absolute path prefix.
# Technically, this test doesn't do that on Windows, but drive
# letters make that impractical to achieve.
- fl = FileLocator()
+ files.set_relative_directory()
d = abs_file(os.curdir)
trick = os.path.splitdrive(d)[1].lstrip(os.path.sep)
rel = os.path.join('sub', trick, 'file1.py')
- self.assertEqual(fl.relative_filename(abs_file(rel)), rel)
+ self.assertEqual(files.relative_filename(abs_file(rel)), rel)
class MatcherTest(CoverageTest):
@@ -55,11 +60,11 @@ class MatcherTest(CoverageTest):
def setUp(self):
super(MatcherTest, self).setUp()
- self.fl = FileLocator()
+ files.set_relative_directory()
def assertMatches(self, matcher, filepath, matches):
"""The `matcher` should agree with `matches` about `filepath`."""
- canonical = self.fl.canonical_filename(filepath)
+ canonical = files.canonical_filename(filepath)
self.assertEqual(
matcher.match(canonical), matches,
"File %s should have matched as %s" % (filepath, matches)
@@ -73,10 +78,9 @@ class MatcherTest(CoverageTest):
(self.make_file("sub3/file4.py"), True),
(self.make_file("sub3/file5.c"), False),
]
- fl = FileLocator()
trees = [
- fl.canonical_filename("sub"),
- fl.canonical_filename("sub3/file4.py"),
+ files.canonical_filename("sub"),
+ files.canonical_filename("sub3/file4.py"),
]
tm = TreeMatcher(trees)
self.assertEqual(tm.info(), trees)
@@ -146,34 +150,48 @@ class PathAliasesTest(CoverageTest):
run_in_temp_dir = False
+ def assert_mapped(self, aliases, inp, out):
+ """Assert that `inp` mapped through `aliases` produces `out`.
+
+ `out` is canonicalized first, since aliases always produce
+ canonicalized paths.
+
+ """
+ self.assertEqual(aliases.map(inp), files.canonical_filename(out))
+
+ def assert_not_mapped(self, aliases, inp):
+ """Assert that `inp` mapped through `aliases` is unchanged."""
+ self.assertEqual(aliases.map(inp), inp)
+
def test_noop(self):
aliases = PathAliases()
- self.assertEqual(aliases.map('/ned/home/a.py'), '/ned/home/a.py')
+ self.assert_not_mapped(aliases, '/ned/home/a.py')
def test_nomatch(self):
aliases = PathAliases()
aliases.add('/home/*/src', './mysrc')
- self.assertEqual(aliases.map('/home/foo/a.py'), '/home/foo/a.py')
+ self.assert_not_mapped(aliases, '/home/foo/a.py')
def test_wildcard(self):
aliases = PathAliases()
aliases.add('/ned/home/*/src', './mysrc')
- self.assertEqual(aliases.map('/ned/home/foo/src/a.py'), './mysrc/a.py')
+ self.assert_mapped(aliases, '/ned/home/foo/src/a.py', './mysrc/a.py')
+
aliases = PathAliases()
aliases.add('/ned/home/*/src/', './mysrc')
- self.assertEqual(aliases.map('/ned/home/foo/src/a.py'), './mysrc/a.py')
+ self.assert_mapped(aliases, '/ned/home/foo/src/a.py', './mysrc/a.py')
def test_no_accidental_match(self):
aliases = PathAliases()
aliases.add('/home/*/src', './mysrc')
- self.assertEqual(aliases.map('/home/foo/srcetc'), '/home/foo/srcetc')
+ self.assert_not_mapped(aliases, '/home/foo/srcetc')
def test_multiple_patterns(self):
aliases = PathAliases()
aliases.add('/home/*/src', './mysrc')
aliases.add('/lib/*/libsrc', './mylib')
- self.assertEqual(aliases.map('/home/foo/src/a.py'), './mysrc/a.py')
- self.assertEqual(aliases.map('/lib/foo/libsrc/a.py'), './mylib/a.py')
+ self.assert_mapped(aliases, '/home/foo/src/a.py', './mysrc/a.py')
+ self.assert_mapped(aliases, '/lib/foo/libsrc/a.py', './mylib/a.py')
def test_cant_have_wildcard_at_end(self):
aliases = PathAliases()
@@ -189,34 +207,26 @@ class PathAliasesTest(CoverageTest):
aliases = PathAliases()
aliases.add(r'c:\Zoo\boo', 'src/')
aliases.add('/home/ned$', 'src/')
- self.assertEqual(aliases.map(r'c:\Zoo\boo\foo.py'), 'src/foo.py')
- self.assertEqual(aliases.map(r'/home/ned$/foo.py'), 'src/foo.py')
+ self.assert_mapped(aliases, r'c:\Zoo\boo\foo.py', 'src/foo.py')
+ self.assert_mapped(aliases, r'/home/ned$/foo.py', 'src/foo.py')
def test_paths_are_os_corrected(self):
aliases = PathAliases()
aliases.add('/home/ned/*/src', './mysrc')
aliases.add(r'c:\ned\src', './mysrc')
- mapped = aliases.map(r'C:\Ned\src\sub\a.py')
- self.assertEqual(mapped, './mysrc/sub/a.py')
+ self.assert_mapped(aliases, r'C:\Ned\src\sub\a.py', './mysrc/sub/a.py')
aliases = PathAliases()
aliases.add('/home/ned/*/src', r'.\mysrc')
aliases.add(r'c:\ned\src', r'.\mysrc')
- mapped = aliases.map(r'/home/ned/foo/src/sub/a.py')
- self.assertEqual(mapped, r'.\mysrc\sub\a.py')
+ self.assert_mapped(aliases, r'/home/ned/foo/src/sub/a.py', r'.\mysrc\sub\a.py')
def test_leading_wildcard(self):
aliases = PathAliases()
aliases.add('*/d1', './mysrc1')
aliases.add('*/d2', './mysrc2')
- self.assertEqual(aliases.map('/foo/bar/d1/x.py'), './mysrc1/x.py')
- self.assertEqual(aliases.map('/foo/bar/d2/y.py'), './mysrc2/y.py')
-
-
-class RelativePathAliasesTest(CoverageTest):
- """Tests for coverage/files.py:PathAliases, with relative files."""
-
- run_in_temp_dir = False
+ self.assert_mapped(aliases, '/foo/bar/d1/x.py', './mysrc1/x.py')
+ self.assert_mapped(aliases, '/foo/bar/d2/y.py', './mysrc2/y.py')
def test_dot(self):
for d in ('.', '..', '../other', '~'):
@@ -227,7 +237,7 @@ class RelativePathAliasesTest(CoverageTest):
the_file = os.path.abspath(os.path.realpath(the_file))
assert '~' not in the_file # to be sure the test is pure.
- self.assertEqual(aliases.map(the_file), '/the/source/a.py')
+ self.assert_mapped(aliases, the_file, '/the/source/a.py')
class FindPythonFilesTest(CoverageTest):
@@ -248,3 +258,17 @@ class FindPythonFilesTest(CoverageTest):
"sub/ssub/__init__.py", "sub/ssub/s.py",
"sub/windows.pyw",
])
+
+
+class WindowsFileTest(CoverageTest):
+ """Windows-specific tests of file name handling."""
+
+ run_in_temp_dir = False
+
+ def setUp(self):
+ if not env.WINDOWS:
+ self.skip("Only need to run Windows tests on Windows.")
+ super(WindowsFileTest, self).setUp()
+
+ def test_actual_path(self):
+ self.assertEquals(actual_path(r'c:\Windows'), actual_path(r'C:\wINDOWS'))
diff --git a/tests/test_html.py b/tests/test_html.py
index 004ebbfb..d9ecb678 100644
--- a/tests/test_html.py
+++ b/tests/test_html.py
@@ -1,11 +1,14 @@
# -*- coding: utf-8 -*-
+# 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 that HTML generation is awesome."""
+import datetime
import os.path
import re
import coverage
-from coverage import env
import coverage.html
from coverage.misc import CoverageException, NotPython, NoSource
@@ -33,9 +36,9 @@ class HtmlTestHelpers(CoverageTest):
""")
def run_coverage(self, covargs=None, htmlargs=None):
- """Run coverage on main_file.py, and create an HTML report."""
+ """Run coverage.py on main_file.py, and create an HTML report."""
self.clean_local_file_imports()
- cov = coverage.coverage(**(covargs or {}))
+ cov = coverage.Coverage(**(covargs or {}))
self.start_import_stop(cov, "main_file")
cov.html_report(**(htmlargs or {}))
@@ -53,6 +56,21 @@ class HtmlTestHelpers(CoverageTest):
with open(filename) as f:
return f.read()
+ def get_html_index_content(self):
+ """Return the content of index.html.
+
+ Timestamps are replaced with a placeholder so that clocks don't matter.
+
+ """
+ with open("htmlcov/index.html") as f:
+ index = f.read()
+ index = re.sub(
+ r"created at \d{4}-\d{2}-\d{2} \d{2}:\d{2}",
+ r"created at YYYY-MM-DD HH:MM",
+ index,
+ )
+ return index
+
class HtmlDeltaTest(HtmlTestHelpers, CoverageTest):
"""Tests of the HTML delta speed-ups."""
@@ -60,7 +78,7 @@ class HtmlDeltaTest(HtmlTestHelpers, CoverageTest):
def setUp(self):
super(HtmlDeltaTest, self).setUp()
- # At least one of our tests monkey-patches the version of coverage,
+ # At least one of our tests monkey-patches the version of coverage.py,
# so grab it here to restore it later.
self.real_coverage_version = coverage.__version__
self.addCleanup(self.restore_coverage_version)
@@ -86,8 +104,7 @@ class HtmlDeltaTest(HtmlTestHelpers, CoverageTest):
# In this case, helper1 changes because its source is different.
self.create_initial_files()
self.run_coverage()
- with open("htmlcov/index.html") as f:
- index1 = f.read()
+ index1 = self.get_html_index_content()
self.remove_html_files()
# Now change a file and do it again
@@ -104,8 +121,7 @@ class HtmlDeltaTest(HtmlTestHelpers, CoverageTest):
self.assert_exists("htmlcov/helper1_py.html")
self.assert_doesnt_exist("htmlcov/main_file_py.html")
self.assert_doesnt_exist("htmlcov/helper2_py.html")
- with open("htmlcov/index.html") as f:
- index2 = f.read()
+ index2 = self.get_html_index_content()
self.assertMultiLineEqual(index1, index2)
def test_html_delta_from_coverage_change(self):
@@ -132,12 +148,11 @@ class HtmlDeltaTest(HtmlTestHelpers, CoverageTest):
def test_html_delta_from_settings_change(self):
# HTML generation can create only the files that have changed.
- # In this case, everything changes because the coverage settings have
- # changed.
+ # In this case, everything changes because the coverage.py settings
+ # have changed.
self.create_initial_files()
self.run_coverage(covargs=dict(omit=[]))
- with open("htmlcov/index.html") as f:
- index1 = f.read()
+ index1 = self.get_html_index_content()
self.remove_html_files()
self.run_coverage(covargs=dict(omit=['xyzzy*']))
@@ -147,18 +162,16 @@ class HtmlDeltaTest(HtmlTestHelpers, CoverageTest):
self.assert_exists("htmlcov/helper1_py.html")
self.assert_exists("htmlcov/main_file_py.html")
self.assert_exists("htmlcov/helper2_py.html")
- with open("htmlcov/index.html") as f:
- index2 = f.read()
+ index2 = self.get_html_index_content()
self.assertMultiLineEqual(index1, index2)
def test_html_delta_from_coverage_version_change(self):
# HTML generation can create only the files that have changed.
- # In this case, everything changes because the coverage version has
+ # In this case, everything changes because the coverage.py version has
# changed.
self.create_initial_files()
self.run_coverage()
- with open("htmlcov/index.html") as f:
- index1 = f.read()
+ index1 = self.get_html_index_content()
self.remove_html_files()
# "Upgrade" coverage.py!
@@ -171,8 +184,7 @@ class HtmlDeltaTest(HtmlTestHelpers, CoverageTest):
self.assert_exists("htmlcov/helper1_py.html")
self.assert_exists("htmlcov/main_file_py.html")
self.assert_exists("htmlcov/helper2_py.html")
- with open("htmlcov/index.html") as f:
- index2 = f.read()
+ index2 = self.get_html_index_content()
fixed_index2 = index2.replace("XYZZY", self.real_coverage_version)
self.assertMultiLineEqual(index1, fixed_index2)
@@ -183,8 +195,7 @@ class HtmlTitleTest(HtmlTestHelpers, CoverageTest):
def test_default_title(self):
self.create_initial_files()
self.run_coverage()
- with open("htmlcov/index.html") as f:
- index = f.read()
+ index = self.get_html_index_content()
self.assertIn("<title>Coverage report</title>", index)
self.assertIn("<h1>Coverage report:", index)
@@ -192,8 +203,7 @@ class HtmlTitleTest(HtmlTestHelpers, CoverageTest):
self.create_initial_files()
self.make_file(".coveragerc", "[html]\ntitle = Metrics & stuff!\n")
self.run_coverage()
- with open("htmlcov/index.html") as f:
- index = f.read()
+ index = self.get_html_index_content()
self.assertIn("<title>Metrics &amp; stuff!</title>", index)
self.assertIn("<h1>Metrics &amp; stuff!:", index)
@@ -203,8 +213,7 @@ class HtmlTitleTest(HtmlTestHelpers, CoverageTest):
"[html]\ntitle = «ταБЬℓσ» numbers"
)
self.run_coverage()
- with open("htmlcov/index.html") as f:
- index = f.read()
+ index = self.get_html_index_content()
self.assertIn(
"<title>&#171;&#964;&#945;&#1041;&#1068;&#8467;&#963;&#187;"
" numbers", index
@@ -218,8 +227,7 @@ class HtmlTitleTest(HtmlTestHelpers, CoverageTest):
self.create_initial_files()
self.make_file(".coveragerc", "[html]\ntitle = Good title\n")
self.run_coverage(htmlargs=dict(title="«ταБЬℓσ» & stüff!"))
- with open("htmlcov/index.html") as f:
- index = f.read()
+ index = self.get_html_index_content()
self.assertIn(
"<title>&#171;&#964;&#945;&#1041;&#1068;&#8467;&#963;&#187;"
" &amp; st&#252;ff!</title>", index
@@ -235,7 +243,7 @@ class HtmlWithUnparsableFilesTest(HtmlTestHelpers, CoverageTest):
def test_dotpy_not_python(self):
self.make_file("innocuous.py", "a = 1")
- cov = coverage.coverage()
+ cov = coverage.Coverage()
self.start_import_stop(cov, "innocuous")
self.make_file("innocuous.py", "<h1>This isn't python!</h1>")
msg = "Couldn't parse '.*innocuous.py' as Python source: .* at line 1"
@@ -244,7 +252,7 @@ class HtmlWithUnparsableFilesTest(HtmlTestHelpers, CoverageTest):
def test_dotpy_not_python_ignored(self):
self.make_file("innocuous.py", "a = 2")
- cov = coverage.coverage()
+ cov = coverage.Coverage()
self.start_import_stop(cov, "innocuous")
self.make_file("innocuous.py", "<h1>This isn't python!</h1>")
cov.html_report(ignore_errors=True)
@@ -271,7 +279,7 @@ class HtmlWithUnparsableFilesTest(HtmlTestHelpers, CoverageTest):
source = "exec(compile('','','exec'), {'__file__': 'liar.html'})"
self.make_file("liar.py", source)
self.make_file("liar.html", "{# Whoops, not python code #}")
- cov = coverage.coverage()
+ cov = coverage.Coverage()
self.start_import_stop(cov, "liar")
cov.html_report()
self.assert_exists("htmlcov/index.html")
@@ -284,14 +292,13 @@ class HtmlWithUnparsableFilesTest(HtmlTestHelpers, CoverageTest):
self.make_file("liar.py", source)
# Tokenize will raise an IndentationError if it can't dedent.
self.make_file("liar.html", "0\n 2\n 1\n")
- cov = coverage.coverage()
+ cov = coverage.Coverage()
self.start_import_stop(cov, "liar")
cov.html_report()
self.assert_exists("htmlcov/index.html")
- # TODO: enable this test, and then fix this:
- # https://bitbucket.org/ned/coveragepy/issue/351/files-with-incorrect-encoding-are-ignored
- def SKIP_THIS_decode_error(self):
+ def test_decode_error(self):
+ # https://bitbucket.org/ned/coveragepy/issue/351/files-with-incorrect-encoding-are-ignored
# imp.load_module won't load a file with an undecodable character
# in a comment, though Python will run them. So we'll change the
# file after running.
@@ -301,7 +308,7 @@ class HtmlWithUnparsableFilesTest(HtmlTestHelpers, CoverageTest):
# coding: utf8
a = 1 # Isn't this great?!
""")
- cov = coverage.coverage()
+ cov = coverage.Coverage()
self.start_import_stop(cov, "main")
# Create the undecodable version of the file. make_file is too helpful,
@@ -316,16 +323,13 @@ class HtmlWithUnparsableFilesTest(HtmlTestHelpers, CoverageTest):
cov.html_report()
html_report = self.get_html_report_content("sub/not_ascii.py")
- if env.PY2:
- expected = "# Isn&#39;t this great?&#65533;!"
- else:
- expected = "# Isn&#39;t this great?&#203;!"
+ expected = "# Isn&#39;t this great?&#65533;!"
self.assertIn(expected, html_report)
def test_formfeeds(self):
# https://bitbucket.org/ned/coveragepy/issue/360/html-reports-get-confused-by-l-in-the-code
self.make_file("formfeed.py", "line_one = 1\n\f\nline_two = 2\n")
- cov = coverage.coverage()
+ cov = coverage.Coverage()
self.start_import_stop(cov, "formfeed")
cov.html_report()
@@ -333,7 +337,7 @@ class HtmlWithUnparsableFilesTest(HtmlTestHelpers, CoverageTest):
self.assertIn("line_two", formfeed_html)
-class HtmlTest(CoverageTest):
+class HtmlTest(HtmlTestHelpers, CoverageTest):
"""Moar HTML tests."""
def test_missing_source_file_incorrect_message(self):
@@ -341,7 +345,7 @@ class HtmlTest(CoverageTest):
self.make_file("thefile.py", "import sub.another\n")
self.make_file("sub/__init__.py", "")
self.make_file("sub/another.py", "print('another')\n")
- cov = coverage.coverage()
+ cov = coverage.Coverage()
self.start_import_stop(cov, 'thefile')
os.remove("sub/another.py")
@@ -352,16 +356,39 @@ class HtmlTest(CoverageTest):
cov.html_report()
def test_extensionless_file_collides_with_extension(self):
- # It used to be that "afile" and "afile.py" would both be reported to
- # "afile.html". Now they are not.
+ # It used to be that "program" and "program.py" would both be reported
+ # to "program.html". Now they are not.
# https://bitbucket.org/ned/coveragepy/issue/69
- self.make_file("afile", "import afile\n")
- self.make_file("afile.py", "a = 1\n")
- self.run_command("coverage run afile")
+ self.make_file("program", "import program\n")
+ self.make_file("program.py", "a = 1\n")
+ self.run_command("coverage run program")
self.run_command("coverage html")
self.assert_exists("htmlcov/index.html")
- self.assert_exists("htmlcov/afile.html")
- self.assert_exists("htmlcov/afile_py.html")
+ self.assert_exists("htmlcov/program.html")
+ self.assert_exists("htmlcov/program_py.html")
+
+ def test_has_date_stamp_in_files(self):
+ self.create_initial_files()
+ self.run_coverage()
+
+ with open("htmlcov/index.html") as f:
+ self.assert_correct_timestamp(f.read())
+ with open("htmlcov/main_file_py.html") as f:
+ self.assert_correct_timestamp(f.read())
+
+ def assert_correct_timestamp(self, html):
+ """Extract the timestamp from `html`, and assert it is recent."""
+ timestamp_pat = r"created at (\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2})"
+ m = re.search(timestamp_pat, html)
+ self.assertTrue(m, "Didn't find a timestamp!")
+ timestamp = datetime.datetime(*map(int, m.groups()))
+ # The timestamp only records the minute, so the delta could be from
+ # 12:00 to 12:01:59, or two minutes.
+ self.assert_recent_datetime(
+ timestamp,
+ seconds=120,
+ msg="Timestamp is wrong: {0}".format(timestamp),
+ )
class HtmlStaticFileTest(CoverageTest):
@@ -382,7 +409,7 @@ class HtmlStaticFileTest(CoverageTest):
coverage.html.STATIC_PATH.insert(0, "static_here")
self.make_file("main.py", "print(17)")
- cov = coverage.coverage()
+ cov = coverage.Coverage()
self.start_import_stop(cov, "main")
cov.html_report()
@@ -403,7 +430,7 @@ class HtmlStaticFileTest(CoverageTest):
coverage.html.STATIC_PATH.insert(0, "static_here")
self.make_file("main.py", "print(17)")
- cov = coverage.coverage()
+ cov = coverage.Coverage()
self.start_import_stop(cov, "main")
cov.html_report()
@@ -418,7 +445,7 @@ class HtmlStaticFileTest(CoverageTest):
coverage.html.STATIC_PATH = ["/xyzzy"]
self.make_file("main.py", "print(17)")
- cov = coverage.coverage()
+ cov = coverage.Coverage()
self.start_import_stop(cov, "main")
msg = "Couldn't find static file u?'.*'"
with self.assertRaisesRegex(CoverageException, msg):
diff --git a/tests/test_misc.py b/tests/test_misc.py
index 152207b5..76194ef6 100644
--- a/tests/test_misc.py
+++ b/tests/test_misc.py
@@ -1,8 +1,11 @@
+# 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 of miscellaneous stuff."""
import sys
-from coverage.misc import Hasher, file_be_gone, overrides
+from coverage.misc import Hasher, file_be_gone
from coverage import __version__, __url__
from tests.coveragetest import CoverageTest
@@ -87,52 +90,3 @@ class SetupPyTest(CoverageTest):
self.assertGreater(len(long_description), 7)
self.assertNotEqual(long_description[0].strip(), "")
self.assertNotEqual(long_description[-1].strip(), "")
-
-
-class OverridesTest(CoverageTest):
- """Test plugins.py:overrides."""
-
- run_in_temp_dir = False
-
- def test_overrides(self):
- # pylint: disable=missing-docstring
- class SomeBase(object):
- def method1(self):
- pass
-
- def method2(self):
- pass
-
- class Derived1(SomeBase):
- def method1(self):
- pass
-
- self.assertTrue(overrides(Derived1(), "method1", SomeBase))
- self.assertFalse(overrides(Derived1(), "method2", SomeBase))
-
- class FurtherDerived1(Derived1):
- """Derive again from Derived1, inherit its method1."""
- pass
-
- self.assertTrue(overrides(FurtherDerived1(), "method1", SomeBase))
- self.assertFalse(overrides(FurtherDerived1(), "method2", SomeBase))
-
- class FurtherDerived2(Derived1):
- """Override the overridden method."""
- def method1(self):
- pass
-
- self.assertTrue(overrides(FurtherDerived2(), "method1", SomeBase))
- self.assertFalse(overrides(FurtherDerived2(), "method2", SomeBase))
-
- class Mixin(object):
- """A mixin that overrides method1."""
- def method1(self):
- pass
-
- class Derived2(Mixin, SomeBase):
- """A class that gets the method from the mixin."""
- pass
-
- self.assertTrue(overrides(Derived2(), "method1", SomeBase))
- self.assertFalse(overrides(Derived2(), "method2", SomeBase))
diff --git a/tests/test_oddball.py b/tests/test_oddball.py
index 9fdc654d..05c724b6 100644
--- a/tests/test_oddball.py
+++ b/tests/test_oddball.py
@@ -1,9 +1,12 @@
+# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
+# For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt
+
"""Oddball cases for testing coverage.py"""
-import os
import sys
import coverage
+from coverage.files import abs_file
from tests.coveragetest import CoverageTest
from tests import osinfo
@@ -107,7 +110,7 @@ class RecursionTest(CoverageTest):
i = 11
""")
- cov = coverage.coverage()
+ cov = coverage.Coverage()
self.start_import_stop(cov, "recur")
pytrace = (cov.collector.tracer_name() == "PyTracer")
@@ -209,7 +212,7 @@ class PyexpatTest(CoverageTest):
self.make_file("outer.py", "\n"*100 + "import trydom\na = 102\n")
- cov = coverage.coverage()
+ cov = coverage.Coverage()
cov.erase()
# Import the Python file, executing it.
@@ -307,9 +310,10 @@ class ExceptionTest(CoverageTest):
for callnames, lines_expected in runs:
# Make the list of functions we'll call for this test.
- calls = [getattr(sys.modules[cn], cn) for cn in callnames.split()]
+ callnames = callnames.split()
+ calls = [getattr(sys.modules[cn], cn) for cn in callnames]
- cov = coverage.coverage()
+ cov = coverage.Coverage()
cov.start()
# Call our list of functions: invoke the first, with the rest as
# an argument.
@@ -318,16 +322,13 @@ class ExceptionTest(CoverageTest):
# Clean the line data and compare to expected results.
# The filenames are absolute, so keep just the base.
- cov._harvest_data() # private! sshhh...
- lines = cov.data.line_data()
clean_lines = {}
- for f, llist in lines.items():
- # f is a path to a Python module, so we drop the '.py' to get
- # a callname.
- basename = os.path.basename(f)
- assert basename.endswith(".py")
- if basename[:-3] in callnames:
- clean_lines[basename] = llist
+ data = cov.get_data()
+ for callname in callnames:
+ filename = callname + ".py"
+ lines = data.lines(abs_file(filename))
+ clean_lines[filename] = sorted(lines)
+
self.assertEqual(clean_lines, lines_expected)
@@ -400,3 +401,35 @@ class GettraceTest(CoverageTest):
f = 12
''',
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12], "")
+
+
+class ExecTest(CoverageTest):
+ """Tests of exec."""
+ def test_correct_filename(self):
+ # https://bitbucket.org/ned/coveragepy/issues/380/code-executed-by-exec-excluded-from
+ # Bug was that exec'd files would have their lines attributed to the
+ # calling file. Make two files, both with ~30 lines, but no lines in
+ # common. Line 30 in to_exec.py was recorded as line 30 in main.py,
+ # but now it's fixed. :)
+ self.make_file("to_exec.py", """\
+ \n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
+ print("var is {0}".format(var)) # line 31
+ """)
+ self.make_file("main.py", """\
+ namespace = {'var': 17}
+ with open("to_exec.py") as to_exec_py:
+ code = compile(to_exec_py.read(), 'to_exec.py', 'exec')
+ exec(code, globals(), namespace)
+ \n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
+ print("done") # line 35
+ """)
+
+ cov = coverage.Coverage()
+ self.start_import_stop(cov, "main")
+
+ _, statements, missing, _ = cov.analysis("main.py")
+ self.assertEqual(statements, [1, 2, 3, 4, 35])
+ self.assertEqual(missing, [])
+ _, statements, missing, _ = cov.analysis("to_exec.py")
+ self.assertEqual(statements, [31])
+ self.assertEqual(missing, [])
diff --git a/tests/test_parser.py b/tests/test_parser.py
index 244d4c70..18621d15 100644
--- a/tests/test_parser.py
+++ b/tests/test_parser.py
@@ -1,17 +1,25 @@
-"""Tests for Coverage.py's code parsing."""
+# 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 coverage.py's code parsing."""
import textwrap
+
from tests.coveragetest import CoverageTest
+
+from coverage import env
from coverage.parser import PythonParser
class PythonParserTest(CoverageTest):
- """Tests for Coverage.py's Python code parsing."""
+ """Tests for coverage.py's Python code parsing."""
run_in_temp_dir = False
def parse_source(self, text):
"""Parse `text` as source, and return the `PythonParser` used."""
+ if env.PY2:
+ text = text.decode("ascii")
text = textwrap.dedent(text)
parser = PythonParser(text=text, exclude="nocover")
parser.parse_source()
@@ -34,6 +42,22 @@ class PythonParserTest(CoverageTest):
2:1, 3:1, 4:2, 5:1, 7:1, 9:1, 10:1
})
+ def test_generator_exit_counts(self):
+ # https://bitbucket.org/ned/coveragepy/issue/324/yield-in-loop-confuses-branch-coverage
+ parser = self.parse_source("""\
+ def gen(input):
+ for n in inp:
+ yield (i * 2 for i in range(n))
+
+ list(gen([1,2,3]))
+ """)
+ self.assertEqual(parser.exit_counts(), {
+ 1:1, # def -> list
+ 2:2, # for -> yield; for -> exit
+ 3:2, # yield -> for; genexp exit
+ 5:1, # list -> exit
+ })
+
def test_try_except(self):
parser = self.parse_source("""\
try:
@@ -94,10 +118,11 @@ class PythonParserTest(CoverageTest):
class ParserFileTest(CoverageTest):
- """Tests for Coverage.py's code parsing from files."""
+ """Tests for coverage.py's code parsing from files."""
def parse_file(self, filename):
"""Parse `text` as source, and return the `PythonParser` used."""
+ # pylint: disable=attribute-defined-outside-init
parser = PythonParser(filename=filename, exclude="nocover")
self.statements, self.excluded = parser.parse_source()
return parser
@@ -121,7 +146,11 @@ class ParserFileTest(CoverageTest):
fname = fname + ".py"
self.make_file(fname, text, newline=newline)
parser = self.parse_file(fname)
- self.assertEqual(parser.exit_counts(), counts)
+ self.assertEqual(
+ parser.exit_counts(),
+ counts,
+ "Wrong for %r" % fname
+ )
def test_encoding(self):
self.make_file("encoded.py", """\
diff --git a/tests/test_phystokens.py b/tests/test_phystokens.py
index 7edd6aa4..b4a106fd 100644
--- a/tests/test_phystokens.py
+++ b/tests/test_phystokens.py
@@ -1,21 +1,26 @@
-"""Tests for Coverage.py's improved tokenizer."""
+# 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 coverage.py's improved tokenizer."""
import os.path
import re
from coverage import env
from coverage.phystokens import source_token_lines, source_encoding
+from coverage.phystokens import neuter_encoding_declaration
+from coverage.python import get_python_source
from tests.coveragetest import CoverageTest
-SIMPLE = """\
+SIMPLE = u"""\
# yay!
def foo():
say('two = %d' % 2)
"""
-MIXED_WS = """\
+MIXED_WS = u"""\
def hello():
a="Hello world!"
\tb="indented"
@@ -25,7 +30,7 @@ HERE = os.path.dirname(__file__)
class PhysTokensTest(CoverageTest):
- """Tests for Coverage.py's improved tokenizer."""
+ """Tests for coverage.py's improved tokenizer."""
run_in_temp_dir = False
@@ -44,9 +49,7 @@ class PhysTokensTest(CoverageTest):
def check_file_tokenization(self, fname):
"""Use the contents of `fname` for `check_tokenization`."""
- with open(fname) as f:
- source = f.read()
- self.check_tokenization(source)
+ self.check_tokenization(get_python_source(fname))
def test_simple(self):
self.assertEqual(list(source_token_lines(SIMPLE)),
@@ -92,21 +95,27 @@ else:
DEF_ENCODING = "ascii"
+ENCODING_DECLARATION_SOURCES = [
+ # Various forms from http://www.python.org/dev/peps/pep-0263/
+ b"# coding=cp850\n\n",
+ b"#!/usr/bin/python\n# -*- coding: cp850 -*-\n",
+ b"#!/usr/bin/python\n# vim: set fileencoding=cp850:\n",
+ b"# This Python file uses this encoding: cp850\n",
+ b"# This file uses a different encoding:\n# coding: cp850\n",
+]
+
class SourceEncodingTest(CoverageTest):
"""Tests of source_encoding() for detecting encodings."""
run_in_temp_dir = False
def test_detect_source_encoding(self):
- # Various forms from http://www.python.org/dev/peps/pep-0263/
- source = b"# coding=cp850\n\n"
- self.assertEqual(source_encoding(source), 'cp850')
- source = b"#!/usr/bin/python\n# -*- coding: utf-8 -*-\n"
- self.assertEqual(source_encoding(source), 'utf-8')
- source = b"#!/usr/bin/python\n# vim: set fileencoding=utf8 :\n"
- self.assertEqual(source_encoding(source), 'utf8')
- source = b"# This Python file uses this encoding: utf-8\n"
- self.assertEqual(source_encoding(source), 'utf-8')
+ for source in ENCODING_DECLARATION_SOURCES:
+ self.assertEqual(
+ source_encoding(source),
+ 'cp850',
+ "Wrong encoding in %r" % source
+ )
def test_detect_source_encoding_not_in_comment(self):
if env.PYPY and env.PY3:
@@ -140,3 +149,19 @@ class SourceEncodingTest(CoverageTest):
source = b"\xEF\xBB\xBF# coding: cp850\n"
with self.assertRaises(SyntaxError):
source_encoding(source)
+
+
+class NeuterEncodingDeclarationTest(CoverageTest):
+ """Tests of phystokens.neuter_encoding_declaration()."""
+
+ run_in_temp_dir = False
+
+ def test_neuter_encoding_declaration(self):
+ for source in ENCODING_DECLARATION_SOURCES:
+ neutered = neuter_encoding_declaration(source.decode("ascii"))
+ neutered = neutered.encode("ascii")
+ self.assertEqual(
+ source_encoding(neutered),
+ DEF_ENCODING,
+ "Wrong encoding in %r" % neutered
+ )
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)
diff --git a/tests/test_process.py b/tests/test_process.py
index 1ae8e922..7c8b0c2d 100644
--- a/tests/test_process.py
+++ b/tests/test_process.py
@@ -1,3 +1,7 @@
+# coding: utf8
+# 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 process behavior of coverage.py."""
import glob
@@ -7,7 +11,7 @@ import sys
import textwrap
import coverage
-from coverage import env
+from coverage import env, CoverageData
from tests.coveragetest import CoverageTest
@@ -49,7 +53,8 @@ class ProcessTest(CoverageTest):
self.assert_exists(".coverage")
self.assertEqual(out, 'done\n')
- def test_combine_parallel_data(self):
+ def make_b_or_c_py(self):
+ """Create b_or_c.py, used in a few of these tests."""
self.make_file("b_or_c.py", """\
import sys
a = 1
@@ -61,9 +66,12 @@ class ProcessTest(CoverageTest):
print('done')
""")
+ def test_combine_parallel_data(self):
+ self.make_b_or_c_py()
out = self.run_command("coverage run -p b_or_c.py b")
self.assertEqual(out, 'done\n')
self.assert_doesnt_exist(".coverage")
+ self.assertEqual(self.number_of_data_files(), 1)
out = self.run_command("coverage run -p b_or_c.py c")
self.assertEqual(out, 'done\n')
@@ -83,19 +91,10 @@ class ProcessTest(CoverageTest):
# executed.
data = coverage.CoverageData()
data.read_file(".coverage")
- self.assertEqual(data.summary()['b_or_c.py'], 7)
+ self.assertEqual(data.line_counts()['b_or_c.py'], 7)
def test_combine_parallel_data_in_two_steps(self):
- self.make_file("b_or_c.py", """\
- import sys
- a = 1
- if sys.argv[1] == 'b':
- b = 1
- else:
- c = 1
- d = 1
- print('done')
- """)
+ self.make_b_or_c_py()
out = self.run_command("coverage run -p b_or_c.py b")
self.assertEqual(out, 'done\n')
@@ -107,7 +106,7 @@ class ProcessTest(CoverageTest):
self.assert_exists(".coverage")
self.assertEqual(self.number_of_data_files(), 1)
- out = self.run_command("coverage run --append -p b_or_c.py c")
+ out = self.run_command("coverage run -p b_or_c.py c")
self.assertEqual(out, 'done\n')
self.assert_exists(".coverage")
self.assertEqual(self.number_of_data_files(), 2)
@@ -123,20 +122,54 @@ class ProcessTest(CoverageTest):
# executed.
data = coverage.CoverageData()
data.read_file(".coverage")
- self.assertEqual(data.summary()['b_or_c.py'], 7)
+ self.assertEqual(data.line_counts()['b_or_c.py'], 7)
- def test_combine_with_rc(self):
- self.make_file("b_or_c.py", """\
- import sys
- a = 1
- if sys.argv[1] == 'b':
- b = 1
- else:
- c = 1
- d = 1
- print('done')
+ def test_append_data(self):
+ self.make_b_or_c_py()
+
+ out = self.run_command("coverage run b_or_c.py b")
+ self.assertEqual(out, 'done\n')
+ self.assert_exists(".coverage")
+ self.assertEqual(self.number_of_data_files(), 1)
+
+ out = self.run_command("coverage run --append b_or_c.py c")
+ self.assertEqual(out, 'done\n')
+ self.assert_exists(".coverage")
+ self.assertEqual(self.number_of_data_files(), 1)
+
+ # Read the coverage file and see that b_or_c.py has all 7 lines
+ # executed.
+ data = coverage.CoverageData()
+ data.read_file(".coverage")
+ self.assertEqual(data.line_counts()['b_or_c.py'], 7)
+
+ def test_append_data_with_different_file(self):
+ self.make_b_or_c_py()
+
+ self.make_file(".coveragerc", """\
+ [run]
+ data_file = .mycovdata
""")
+ out = self.run_command("coverage run b_or_c.py b")
+ self.assertEqual(out, 'done\n')
+ self.assert_doesnt_exist(".coverage")
+ self.assert_exists(".mycovdata")
+
+ out = self.run_command("coverage run --append b_or_c.py c")
+ self.assertEqual(out, 'done\n')
+ self.assert_doesnt_exist(".coverage")
+ self.assert_exists(".mycovdata")
+
+ # Read the coverage file and see that b_or_c.py has all 7 lines
+ # executed.
+ data = coverage.CoverageData()
+ data.read_file(".mycovdata")
+ self.assertEqual(data.line_counts()['b_or_c.py'], 7)
+
+ def test_combine_with_rc(self):
+ self.make_b_or_c_py()
+
self.make_file(".coveragerc", """\
[run]
parallel = true
@@ -165,7 +198,7 @@ class ProcessTest(CoverageTest):
# executed.
data = coverage.CoverageData()
data.read_file(".coverage")
- self.assertEqual(data.summary()['b_or_c.py'], 7)
+ self.assertEqual(data.line_counts()['b_or_c.py'], 7)
# Reporting should still work even with the .rc file
out = self.run_command("coverage report")
@@ -219,13 +252,30 @@ class ProcessTest(CoverageTest):
# files have been combined together.
data = coverage.CoverageData()
data.read_file(".coverage")
- summary = data.summary(fullpath=True)
+ summary = data.line_counts(fullpath=True)
self.assertEqual(len(summary), 1)
actual = os.path.normcase(os.path.abspath(list(summary.keys())[0]))
expected = os.path.normcase(os.path.abspath('src/x.py'))
self.assertEqual(actual, expected)
self.assertEqual(list(summary.values())[0], 6)
+ def test_erase_parallel(self):
+ self.make_file(".coveragerc", """\
+ [run]
+ data_file = data.dat
+ parallel = True
+ """)
+ self.make_file("data.dat")
+ self.make_file("data.dat.fooey")
+ self.make_file("data.dat.gooey")
+ self.make_file(".coverage")
+
+ self.run_command("coverage erase")
+ self.assert_doesnt_exist("data.dat")
+ self.assert_doesnt_exist("data.dat.fooey")
+ self.assert_doesnt_exist("data.dat.gooey")
+ self.assert_exists(".coverage")
+
def test_missing_source_file(self):
# Check what happens if the source is missing when reporting happens.
self.make_file("fleeting.py", """\
@@ -397,7 +447,7 @@ class ProcessTest(CoverageTest):
def test_coverage_run_dashm_is_like_python_dashm_with__main__207(self):
if sys.version_info < (2, 7):
- # Coverage isn't bug-for-bug compatible in the behavior of -m for
+ # Coverage.py isn't bug-for-bug compatible in the behavior of -m for
# Pythons < 2.7
self.skip("-m doesn't work the same < Python 2.7")
# https://bitbucket.org/ned/coveragepy/issue/207
@@ -447,7 +497,7 @@ class ProcessTest(CoverageTest):
# executed.
data = coverage.CoverageData()
data.read_file(".coverage")
- self.assertEqual(data.summary()['fork.py'], 9)
+ self.assertEqual(data.line_counts()['fork.py'], 9)
def test_warnings(self):
self.make_file("hello.py", """\
@@ -531,6 +581,21 @@ class ProcessTest(CoverageTest):
self.assertIn("Trace function changed", out)
+ def test_note(self):
+ self.make_file(".coveragerc", """\
+ [run]
+ data_file = mydata.dat
+ note = These are musical notes: ♫𝅗𝅥♩
+ """)
+ self.make_file("simple.py", """print('hello')""")
+ self.run_command("coverage run simple.py")
+
+ data = CoverageData()
+ data.read_file("mydata.dat")
+ infos = data.run_infos()
+ self.assertEqual(len(infos), 1)
+ self.assertEqual(infos[0]['note'], u"These are musical notes: ♫𝅗𝅥♩")
+
def test_fullcoverage(self): # pragma: not covered
if env.PY2: # This doesn't work on Python 2.
self.skip("fullcoverage doesn't work on Python 2.")
@@ -558,7 +623,7 @@ class ProcessTest(CoverageTest):
# The actual number of executed lines in os.py when it's
# imported is 120 or so. Just running os.getenv executes
# about 5.
- self.assertGreater(data.summary()['os.py'], 50)
+ self.assertGreater(data.line_counts()['os.py'], 50)
def test_deprecation_warnings(self):
# Test that coverage doesn't trigger deprecation warnings.
@@ -593,8 +658,7 @@ class ProcessTest(CoverageTest):
out = self.run_command("python run_twice.py")
self.assertEqual(
out,
- "Coverage.py warning: "
- "Module foo was previously imported, but not measured.\n"
+ "Coverage.py warning: Module foo was previously imported, but not measured.\n"
)
@@ -704,6 +768,51 @@ class FailUnderTest(CoverageTest):
self.assertEqual(st, 2)
+class FailUnderNoFilesTest(CoverageTest):
+ """Test that nothing to report results in an error exit status."""
+ def setUp(self):
+ super(FailUnderNoFilesTest, self).setUp()
+ self.make_file(".coveragerc", "[report]\nfail_under = 99\n")
+
+ def test_report(self):
+ st, out = self.run_command_status("coverage report")
+ self.assertIn('No data to report.', out)
+ self.assertEqual(st, 1)
+
+ def test_xml(self):
+ st, out = self.run_command_status("coverage xml")
+ self.assertIn('No data to report.', out)
+ self.assertEqual(st, 1)
+
+ def test_html(self):
+ st, out = self.run_command_status("coverage html")
+ self.assertIn('No data to report.', out)
+ self.assertEqual(st, 1)
+
+
+class FailUnderEmptyFilesTest(CoverageTest):
+ """Test that empty files produce the proper fail_under exit status."""
+ def setUp(self):
+ super(FailUnderEmptyFilesTest, self).setUp()
+
+ self.make_file(".coveragerc", "[report]\nfail_under = 99\n")
+ self.make_file("empty.py", "")
+ st, _ = self.run_command_status("coverage run empty.py")
+ self.assertEqual(st, 0)
+
+ def test_report(self):
+ st, _ = self.run_command_status("coverage report")
+ self.assertEqual(st, 2)
+
+ def test_xml(self):
+ st, _ = self.run_command_status("coverage xml")
+ self.assertEqual(st, 2)
+
+ def test_html(self):
+ st, _ = self.run_command_status("coverage html")
+ self.assertEqual(st, 2)
+
+
def possible_pth_dirs():
"""Produce a sequence of directories for trying to write .pth files."""
# First look through sys.path, and we find a .pth file, then it's a good
@@ -715,12 +824,12 @@ def possible_pth_dirs():
# If we're still looking, then try the Python library directory.
# https://bitbucket.org/ned/coveragepy/issue/339/pth-test-malfunctions
- import distutils.sysconfig
+ import distutils.sysconfig # pylint: disable=import-error
yield distutils.sysconfig.get_python_lib()
class ProcessCoverageMixin(object):
- """Set up a .pth file that causes all sub-processes to be coverage'd"""
+ """Set up a .pth file to coverage-measure all sub-processes."""
def setUp(self):
super(ProcessCoverageMixin, self).setUp()
@@ -775,7 +884,7 @@ class ProcessStartupTest(ProcessCoverageMixin, CoverageTest):
self.assert_exists(".mycovdata")
data = coverage.CoverageData()
data.read_file(".mycovdata")
- self.assertEqual(data.summary()['sub.py'], 2)
+ self.assertEqual(data.line_counts()['sub.py'], 2)
class ProcessStartupWithSourceTest(ProcessCoverageMixin, CoverageTest):
@@ -795,18 +904,34 @@ class ProcessStartupWithSourceTest(ProcessCoverageMixin, CoverageTest):
def assert_pth_and_source_work_together(
self, dashm, package, source
): # pragma: not covered
+ """Run the test for a particular combination of factors.
+
+ The arguments are all strings:
+
+ * `dashm`: Either "" (run the program as a file) or "-m" (run the
+ program as a module).
+
+ * `package`: Either "" (put the source at the top level) or a
+ package name to use to hold the source.
+
+ * `source`: Either "main" or "sub", which file to use as the
+ ``--source`` argument.
+
+ """
if env.METACOV:
self.skip(
"Can't test sub-process pth file suppport during metacoverage"
)
def fullname(modname):
+ """What is the full module name for `modname` for this test?"""
if package and dashm:
return '.'.join((package, modname))
else:
return modname
def path(basename):
+ """Where should `basename` be created for this test?"""
return os.path.join(package, basename)
# Main will run sub.py.
@@ -829,13 +954,11 @@ class ProcessStartupWithSourceTest(ProcessCoverageMixin, CoverageTest):
self.set_environ("COVERAGE_PROCESS_START", "coverage.ini")
if dashm:
- cmd = (sys.executable, dashm, fullname('main'))
+ cmd = "python -m %s" % fullname('main')
else:
- cmd = (sys.executable, path('main.py'))
+ cmd = "python %s" % path('main.py')
- # TODO: can we use run_command here instead of Popen?
- from subprocess import Popen
- Popen(cmd).wait()
+ self.run_command(cmd)
with open("out.txt") as f:
self.assertEqual(f.read(), "Hello, world!")
@@ -844,7 +967,7 @@ class ProcessStartupWithSourceTest(ProcessCoverageMixin, CoverageTest):
self.assert_exists(".coverage")
data = coverage.CoverageData()
data.read_file(".coverage")
- summary = data.summary()
+ summary = data.line_counts()
print(summary)
self.assertEqual(summary[source + '.py'], 2)
self.assertEqual(len(summary), 1)
diff --git a/tests/test_python.py b/tests/test_python.py
index f2c18a10..e510e786 100644
--- a/tests/test_python.py
+++ b/tests/test_python.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 of coverage/python.py"""
import os
diff --git a/tests/test_results.py b/tests/test_results.py
index 4ce048b6..54c2f6d7 100644
--- a/tests/test_results.py
+++ b/tests/test_results.py
@@ -1,11 +1,14 @@
-"""Tests for Coverage.py's results analysis."""
+# 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 coverage.py's results analysis."""
from coverage.results import Numbers
from tests.coveragetest import CoverageTest
class NumbersTest(CoverageTest):
- """Tests for Coverage.py's numeric measurement summaries."""
+ """Tests for coverage.py's numeric measurement summaries."""
run_in_temp_dir = False
diff --git a/tests/test_summary.py b/tests/test_summary.py
index c7327f1f..850f4dfd 100644
--- a/tests/test_summary.py
+++ b/tests/test_summary.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
+
"""Test text-based summary reporting for coverage.py"""
import glob
@@ -21,16 +24,20 @@ class SummaryTest(CoverageTest):
def setUp(self):
super(SummaryTest, self).setUp()
+ # Parent class saves and restores sys.path, we can just modify it.
+ sys.path.append(self.nice_file(HERE, 'modules'))
+
+ def make_mycode(self):
+ """Make the mycode.py file when needed."""
self.make_file("mycode.py", """\
import covmod1
import covmodzip1
a = 1
print('done')
""")
- # Parent class saves and restores sys.path, we can just modify it.
- sys.path.append(self.nice_file(HERE, 'modules'))
def test_report(self):
+ self.make_mycode()
out = self.run_command("coverage run mycode.py")
self.assertEqual(out, 'done\n')
report = self.report_from_command("coverage report")
@@ -38,19 +45,20 @@ class SummaryTest(CoverageTest):
# Name Stmts Miss Cover
# ------------------------------------------------------------------
# c:/ned/coverage/tests/modules/covmod1.py 2 0 100%
- # c:/ned/coverage/tests/zipmods.zip/covmodzip1.py 2 0 100%
+ # c:/ned/coverage/tests/zipmods.zip/covmodzip1.py 3 0 100%
# mycode.py 4 0 100%
# ------------------------------------------------------------------
- # TOTAL 8 0 100%
+ # TOTAL 9 0 100%
self.assertNotIn("/coverage/__init__/", report)
self.assertIn("/tests/modules/covmod1.py ", report)
self.assertIn("/tests/zipmods.zip/covmodzip1.py ", report)
self.assertIn("mycode.py ", report)
- self.assertEqual(self.last_line_squeezed(report), "TOTAL 8 0 100%")
+ self.assertEqual(self.last_line_squeezed(report), "TOTAL 9 0 100%")
def test_report_just_one(self):
# Try reporting just one module
+ self.make_mycode()
self.run_command("coverage run mycode.py")
report = self.report_from_command("coverage report mycode.py")
@@ -67,6 +75,7 @@ class SummaryTest(CoverageTest):
def test_report_wildcard(self):
# Try reporting using wildcards to get the modules.
+ self.make_mycode()
self.run_command("coverage run mycode.py")
report = self.report_from_command("coverage report my*.py")
@@ -83,10 +92,9 @@ class SummaryTest(CoverageTest):
def test_report_omitting(self):
# Try reporting while omitting some modules
+ self.make_mycode()
self.run_command("coverage run mycode.py")
- report = self.report_from_command(
- "coverage report --omit '%s/*'" % HERE
- )
+ report = self.report_from_command("coverage report --omit '%s/*'" % HERE)
# Name Stmts Miss Cover
# -------------------------------
@@ -101,6 +109,7 @@ class SummaryTest(CoverageTest):
def test_report_including(self):
# Try reporting while including some modules
+ self.make_mycode()
self.run_command("coverage run mycode.py")
report = self.report_from_command("coverage report --include=mycode*")
@@ -325,6 +334,7 @@ class SummaryTest(CoverageTest):
# We run a .py file, and when reporting, we can't parse it as Python.
# We should get an error message in the report.
+ self.make_mycode()
self.run_command("coverage run mycode.py")
self.make_file("mycode.py", "This isn't python at all!")
report = self.report_from_command("coverage report mycode.py")
@@ -333,8 +343,9 @@ class SummaryTest(CoverageTest):
# Name Stmts Miss Cover
# ----------------------------
# mycode NotPython: Couldn't parse '/tmp/test_cover/63354509363/mycode.py' as Python source: 'invalid syntax' at line 1
+ # No data to report.
- last = self.last_line_squeezed(report)
+ last = self.squeezed_lines(report)[-2]
# The actual file name varies run to run.
last = re.sub(r"parse '.*mycode.py", "parse 'mycode.py", last)
# The actual error message varies version to version
@@ -348,6 +359,7 @@ class SummaryTest(CoverageTest):
def test_dotpy_not_python_ignored(self):
# We run a .py file, and when reporting, we can't parse it as Python,
# but we've said to ignore errors, so there's no error reported.
+ self.make_mycode()
self.run_command("coverage run mycode.py")
self.make_file("mycode.py", "This isn't python at all!")
report = self.report_from_command("coverage report -i mycode.py")
@@ -355,7 +367,8 @@ class SummaryTest(CoverageTest):
# Name Stmts Miss Cover
# ----------------------------
- self.assertEqual(self.line_count(report), 2)
+ self.assertEqual(self.line_count(report), 3)
+ self.assertIn('No data to report.', report)
def test_dothtml_not_python(self):
# We run a .html file, and when reporting, we can't parse it as
@@ -370,8 +383,10 @@ class SummaryTest(CoverageTest):
# Name Stmts Miss Cover
# ----------------------------
+ # No data to report.
- self.assertEqual(self.line_count(report), 2)
+ self.assertEqual(self.line_count(report), 3)
+ self.assertIn('No data to report.', report)
def get_report(self, cov):
"""Get the report from `cov`, and canonicalize it."""
@@ -393,7 +408,7 @@ class SummaryTest(CoverageTest):
self.make_file("main.py", """\
print("y")
""")
- cov = coverage.coverage(branch=True, source=["."])
+ cov = coverage.Coverage(branch=True, source=["."])
cov.start()
import main # pragma: nested # pylint: disable=import-error,unused-variable
cov.stop() # pragma: nested
@@ -402,7 +417,7 @@ class SummaryTest(CoverageTest):
def run_TheCode_and_report_it(self):
"""A helper for the next few tests."""
- cov = coverage.coverage()
+ cov = coverage.Coverage()
cov.start()
import TheCode # pragma: nested # pylint: disable=import-error,unused-variable
cov.stop() # pragma: nested
@@ -437,7 +452,7 @@ class SummaryTest(CoverageTest):
self.make_file("mod.pyw", """\
print("In mod.pyw")
""")
- cov = coverage.coverage()
+ cov = coverage.Coverage()
cov.start()
import start # pragma: nested # pylint: disable=import-error,unused-variable
cov.stop() # pragma: nested
@@ -457,7 +472,7 @@ class SummaryTest(CoverageTest):
py_compile.compile("mod.py")
# Run the program.
- cov = coverage.coverage()
+ cov = coverage.Coverage()
cov.start()
import main # pragma: nested # pylint: disable=import-error,unused-variable
cov.stop() # pragma: nested
@@ -487,7 +502,7 @@ class SummaryTest(CoverageTest):
os.rename(pycs[0], "mod.pyc")
# Run the program.
- cov = coverage.coverage()
+ cov = coverage.Coverage()
cov.start()
import main # pragma: nested # pylint: disable=import-error,unused-variable
cov.stop() # pragma: nested
@@ -515,7 +530,7 @@ class SummaryTest2(CoverageTest):
def test_empty_files(self):
# Shows that empty files like __init__.py are listed as having zero
# statements, not one statement.
- cov = coverage.coverage()
+ cov = coverage.Coverage()
cov.start()
import usepkgs # pragma: nested # pylint: disable=import-error,unused-variable
cov.stop() # pragma: nested
@@ -544,7 +559,7 @@ class ReportingReturnValueTest(CoverageTest):
g = 7
""")
- cov = coverage.coverage()
+ cov = coverage.Coverage()
self.start_import_stop(cov, "doit")
return cov
diff --git a/tests/test_templite.py b/tests/test_templite.py
index 2b64e4e3..2f9b2dbd 100644
--- a/tests/test_templite.py
+++ b/tests/test_templite.py
@@ -1,4 +1,7 @@
# -*- coding: utf8 -*-
+# 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 coverage.templite."""
import re
diff --git a/tests/test_testing.py b/tests/test_testing.py
index 75db7e50..39c711ba 100644
--- a/tests/test_testing.py
+++ b/tests/test_testing.py
@@ -1,6 +1,10 @@
# -*- coding: utf-8 -*-
+# 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 that our test infrastructure is really working!"""
+import datetime
import os
import sys
@@ -62,6 +66,14 @@ class TempDirMixinTest(TempDirMixin, TestCase):
class CoverageTestTest(CoverageTest):
"""Test the methods in `CoverageTest`."""
+ def test_arcz_to_arcs(self):
+ self.assertEqual(self.arcz_to_arcs(".1 12 2."), [(-1, 1), (1, 2), (2, -1)])
+ self.assertEqual(self.arcz_to_arcs("-11 12 2-5"), [(-1, 1), (1, 2), (2, -5)])
+ self.assertEqual(
+ self.arcz_to_arcs("-QA CB IT Z-A"),
+ [(-26, 10), (12, 11), (18, 29), (35, -10)]
+ )
+
def test_file_exists(self):
self.make_file("whoville.txt", "We are here!")
self.assert_exists("whoville.txt")
@@ -80,6 +92,27 @@ class CoverageTestTest(CoverageTest):
with self.assertRaises(AssertionError):
self.assert_starts_with("xyz\nabc", "a")
+ def test_assert_recent_datetime(self):
+ def now_delta(seconds):
+ """Make a datetime `seconds` seconds from now."""
+ return datetime.datetime.now() + datetime.timedelta(seconds=seconds)
+
+ # Default delta is 10 seconds.
+ self.assert_recent_datetime(now_delta(0))
+ self.assert_recent_datetime(now_delta(-9))
+ with self.assertRaises(AssertionError):
+ self.assert_recent_datetime(now_delta(-11))
+ with self.assertRaises(AssertionError):
+ self.assert_recent_datetime(now_delta(1))
+
+ # Delta is settable.
+ self.assert_recent_datetime(now_delta(0), seconds=120)
+ self.assert_recent_datetime(now_delta(-100), seconds=120)
+ with self.assertRaises(AssertionError):
+ self.assert_recent_datetime(now_delta(-1000), seconds=120)
+ with self.assertRaises(AssertionError):
+ self.assert_recent_datetime(now_delta(1), seconds=120)
+
def test_sub_python_is_this_python(self):
# Try it with a Python command.
os.environ['COV_FOOBAR'] = 'XYZZY'
@@ -97,10 +130,10 @@ class CoverageTestTest(CoverageTest):
# Try it with a "coverage debug sys" command.
out = self.run_command("coverage debug sys").splitlines()
# "environment: COV_FOOBAR = XYZZY" or "COV_FOOBAR = XYZZY"
- executable = next(l for l in out if "executable:" in l)
+ executable = next(l for l in out if "executable:" in l) # pragma: part covered
executable = executable.split(":", 1)[1].strip()
self.assertTrue(same_python_executable(executable, sys.executable))
- environ = next(l for l in out if "COV_FOOBAR" in l)
+ environ = next(l for l in out if "COV_FOOBAR" in l) # pragma: part covered
_, _, environ = environ.rpartition(":")
self.assertEqual(environ.strip(), "COV_FOOBAR = XYZZY")
diff --git a/tests/test_xml.py b/tests/test_xml.py
index b9b36efb..dbf09279 100644
--- a/tests/test_xml.py
+++ b/tests/test_xml.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 XML reports from coverage.py."""
import os
@@ -21,7 +24,7 @@ class XmlTestHelpers(CoverageTest):
self.make_file("sub/__init__.py")
self.make_file("sub/doit.py", "print('doit!')")
self.make_file("main.py", "import sub.doit")
- cov = coverage.coverage()
+ cov = coverage.Coverage()
self.start_import_stop(cov, "main")
return cov
@@ -30,7 +33,7 @@ class XmlTestHelpers(CoverageTest):
Makes `width` directories, named d0 .. d{width-1}. Each directory has
__init__.py, and `width` files, named f0.py .. f{width-1}.py. Each
- directory also has `width` subdirectories, in the same fashion, until
+ directory also has `width` sub-directories, in the same fashion, until
a depth of `depth` is reached.
"""
@@ -88,7 +91,7 @@ class XmlReportTest(XmlTestHelpers, CoverageTest):
# Written while investigating a bug, might as well keep it.
# https://bitbucket.org/ned/coveragepy/issue/208
self.make_file("innocuous.py", "a = 4")
- cov = coverage.coverage()
+ cov = coverage.Coverage()
self.start_import_stop(cov, "innocuous")
os.remove("innocuous.py")
cov.xml_report(ignore_errors=True)
@@ -120,7 +123,7 @@ class XmlReportTest(XmlTestHelpers, CoverageTest):
# Used to raise a zero division error:
# https://bitbucket.org/ned/coveragepy/issue/250
self.make_file("empty.py", "")
- cov = coverage.coverage()
+ cov = coverage.Coverage()
empty = self.start_import_stop(cov, "empty")
cov.xml_report([empty], outfile="-")
xml = self.stdout()
@@ -160,7 +163,7 @@ class XmlPackageStructureTest(XmlTestHelpers, CoverageTest):
self.make_file("main.py", """\
from d0.d0 import f0
""")
- cov = coverage.coverage(source=".")
+ cov = coverage.Coverage(source=".")
self.start_import_stop(cov, "main")
self.assert_package_and_class_tags(cov, """\
<package name=".">
@@ -178,7 +181,7 @@ class XmlPackageStructureTest(XmlTestHelpers, CoverageTest):
self.make_file("main.py", """\
from d0.d0 import f0
""")
- cov = coverage.coverage(source=".")
+ cov = coverage.Coverage(source=".")
self.start_import_stop(cov, "main")
cov.config["xml:package_depth"] = 1
diff --git a/tests/try_execfile.py b/tests/try_execfile.py
index e0b79b48..70905071 100644
--- a/tests/try_execfile.py
+++ b/tests/try_execfile.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
+
"""Test file for run_python_file.
This file is executed two ways::
diff --git a/tox.ini b/tox.ini
index adf94c9d..7e55bc42 100644
--- a/tox.ini
+++ b/tox.ini
@@ -1,10 +1,9 @@
-# Tox (http://tox.testrun.org/) is a tool for running tests
-# in multiple virtualenvs. This configuration file will run the
-# test suite on all supported python versions. To use it, "pip install tox"
-# and then run "tox" from this directory.
+# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
+# For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt
[tox]
-envlist = py26, py27, py33, py34, py35, pypy24, pypy3_24
+envlist = py26, py27, py33, py34, py35, pypy24, pypy26, pypy3_24
+skip_missing_interpreters = True
[testenv]
commands =
@@ -25,19 +24,24 @@ commands =
deps =
nose
mock
+ PyContracts
py26: unittest2
py26,py27: gevent
py26,py27: eventlet
py26,py27,py33,py34,py35: greenlet
setenv =
- pypy24,pypy3_24: COVERAGE_NO_EXTENSION=no C extensions under PyPy
+ pypy24,pypy26,pypy3_24: COVERAGE_NO_EXTENSION=no C extensions under PyPy
usedevelop = True
+passenv = *
[testenv:pypy24]
basepython = pypy2.4
+[testenv:pypy26]
+basepython = pypy2.6
+
[testenv:pypy3_24]
basepython = pypy3-2.4
@@ -47,3 +51,4 @@ basepython = pypy3-2.4
# E265: block comment should start with '# '
# The rest are the default ignored warnings.
ignore = E265,E123,E133,E226,E241,E242
+max-line-length = 100
diff --git a/tox_wheels.ini b/tox_wheels.ini
new file mode 100644
index 00000000..691ca5b1
--- /dev/null
+++ b/tox_wheels.ini
@@ -0,0 +1,14 @@
+# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
+# For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt
+
+[tox]
+envlist = py26, py27, py33, py34, py35
+toxworkdir = {toxinidir}/.tox_kits
+
+[testenv]
+commands =
+ {envpython} setup.py bdist_wheel {posargs}
+
+deps =
+ wheel
+ setuptools >= 6.0.0
diff --git a/tox_winkits.ini b/tox_winkits.ini
index 54e8779d..85b318ed 100644
--- a/tox_winkits.ini
+++ b/tox_winkits.ini
@@ -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
+
[tox]
envlist = py26, py27, py33, py34
toxworkdir = {toxinidir}/.tox_kits