diff -Nru python-coverage-3.4/AUTHORS.txt python-coverage-3.6/AUTHORS.txt --- python-coverage-3.4/AUTHORS.txt 2010-08-08 13:36:52.000000000 +0000 +++ python-coverage-3.6/AUTHORS.txt 2012-11-22 03:45:08.000000000 +0000 @@ -3,28 +3,39 @@ Other contributions have been made by: +Marc Abramowitz Chris Adams +Geoff Bache +Julian Berman +Titus Brown +Brett Cannon +Pablo Carballo +Guillaume Chazarain +David Christian +Marcus Cobden Danek Duvall -Ross Lawley Ben Finney +Martin Fuzzey +Imri Goldberg Bill Hart Christian Heimes -Detlev Offenbach -George Song -David Christian -Imri Goldberg -Patrick Mezard +Devin Jeanpierre +Ross Lawley Edward Loper -Guillaume Chazarain -Titus Brown +Sandra Martocchia +Patrick Mezard Noel O'Boyle +Detlev Offenbach +JT Olds +George Paci Catherine Proulx +Brandon Rhodes +Adi Roiban +Greg Rogers +George Song +David Stanek Joseph Tate Sigve Tjora Mark van der Wal -Geoff Bache -Martin Fuzzey -Greg Rogers -Christoph Zwerschke Zooko Wilcox-O'Hearn -David Stanek +Christoph Zwerschke diff -Nru python-coverage-3.4/CHANGES.txt python-coverage-3.6/CHANGES.txt --- python-coverage-3.4/CHANGES.txt 2010-09-19 20:33:38.000000000 +0000 +++ python-coverage-3.6/CHANGES.txt 2013-01-05 22:33:27.000000000 +0000 @@ -2,6 +2,384 @@ Change history for Coverage.py ------------------------------ +Version 3.6 --- 5 January 2013 +------------------------------ + +- Added a page to the docs about troublesome situations, closing `issue 226`_, + and added some info to the TODO file, closing `issue 227`_. + +.. _issue 226: https://bitbucket.org/ned/coveragepy/issue/226/make-readme-section-to-describe-when +.. _issue 227: https://bitbucket.org/ned/coveragepy/issue/227/update-todo + + +Version 3.6b3 --- 29 December 2012 +---------------------------------- + +- Beta 2 broke the nose plugin. It's fixed again, closing `issue 224`_. + +.. _issue 224: https://bitbucket.org/ned/coveragepy/issue/224/36b2-breaks-nosexcover + + +Version 3.6b2 --- 23 December 2012 +---------------------------------- + +- Coverage.py runs on Python 2.3 and 2.4 again. It was broken in 3.6b1. + +- The C extension is optionally compiled using a different more widely-used + technique, taking another stab at fixing `issue 80`_ once and for all. + +- Combining data files would create entries for phantom files if used with + ``source`` and path aliases. It no longer does. + +- ``debug sys`` now shows the configuration file path that was read. + +- If an oddly-behaved package claims that code came from an empty-string + filename, coverage.py no longer associates it with the directory name, + fixing `issue 221`_. + +.. _issue 80: https://bitbucket.org/ned/coveragepy/issue/80/is-there-a-duck-typing-way-to-know-we-cant +.. _issue 221: https://bitbucket.org/ned/coveragepy/issue/221/coveragepy-incompatible-with-pyratemp + + +Version 3.6b1 --- 28 November 2012 +---------------------------------- + +- Wildcards in ``include=`` and ``omit=`` arguments were not handled properly + in reporting functions, though they were when running. Now they are handled + uniformly, closing `issue 143`_ and `issue 163`_. **NOTE**: it is possible + that your configurations may now be incorrect. If you use ``include`` or + ``omit`` during reporting, whether on the command line, through the API, or + in a configuration file, please check carefully that you were not relying on + the old broken behavior. + +- The **report**, **html**, and **xml** commands now accept a ``--fail-under`` + switch that indicates in the exit status whether the coverage percentage was + less than a particular value. Closes `issue 139`_. + +- The reporting functions coverage.report(), coverage.html_report(), and + coverage.xml_report() now all return a float, the total percentage covered + measurement. + +- The HTML report's title can now be set in the configuration file, with the + ``--title`` switch on the command line, or via the API. + +- 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 + didn't work. Now it does. + +- The XML report now consistently uses filenames for the filename attribute, + rather than sometimes using module names. Fixes `issue 67`_. + Thanks, Marcus Cobden. + +- Coverage percentage metrics are now computed slightly differently under + branch coverage. This means that completely unexecuted files will now + correctly have 0% coverage, fixing `issue 156`_. This also means that your + total coverage numbers will generally now be lower if you are measuring + branch coverage. + +- When installing, now in addition to creating a "coverage" command, two new + aliases are also installed. A "coverage2" or "coverage3" command will be + created, depending on whether you are installing in Python 2.x or 3.x. + A "coverage-X.Y" command will also be created corresponding to your specific + version of Python. Closes `issue 111`_. + +- The coverage.py installer no longer tries to bootstrap setuptools or + Distribute. You must have one of them installed first, as `issue 202`_ + recommended. + +- The coverage.py kit now includes docs (closing `issue 137`_) and tests. + +- On Windows, files are now reported in their correct case, fixing `issue 89`_ + and `issue 203`_. + +- If a file is missing during reporting, the path shown in the error message + is now correct, rather than an incorrect path in the current directory. + Fixes `issue 60`_. + +- Running an HTML report in Python 3 in the same directory as an old Python 2 + HTML report would fail with a UnicodeDecodeError. This issue (`issue 193`_) + is now fixed. + +- Fixed yet another error trying to parse non-Python files as Python, this + time an IndentationError, closing `issue 82`_ for the fourth time... + +- If `coverage xml` fails because there is no data to report, it used to + create a zero-length XML file. Now it doesn't, fixing `issue 210`_. + +- 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`_. + +- 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, + fixing `issue 214`_, though this will take some time to be repackaged by the + operating systems. + +- Docstrings for the legacy singleton methods are more helpful. Thanks Marius + Gedminas. Closes `issue 205`_. + +- The pydoc tool can now show docmentation for the class `coverage.coverage`. + Closes `issue 206`_. + +- Added a page to the docs about contributing to coverage.py, closing + `issue 171`_. + +- When coverage.py ended unsuccessfully, it may have reported odd errors like + ``'NoneType' object has no attribute 'isabs'``. It no longer does, + so kiss `issue 153`_ goodbye. + +.. _issue 60: https://bitbucket.org/ned/coveragepy/issue/60/incorrect-path-to-orphaned-pyc-files +.. _issue 67: https://bitbucket.org/ned/coveragepy/issue/67/xml-report-filenames-may-be-generated +.. _issue 82: https://bitbucket.org/ned/coveragepy/issue/82/tokenerror-when-generating-html-report +.. _issue 89: https://bitbucket.org/ned/coveragepy/issue/89/on-windows-all-packages-are-reported-in +.. _issue 97: https://bitbucket.org/ned/coveragepy/issue/97/allow-environment-variables-to-be +.. _issue 100: https://bitbucket.org/ned/coveragepy/issue/100/source-directive-doesnt-work-for-packages +.. _issue 111: https://bitbucket.org/ned/coveragepy/issue/111/when-installing-coverage-with-pip-not +.. _issue 137: https://bitbucket.org/ned/coveragepy/issue/137/provide-docs-with-source-distribution +.. _issue 139: https://bitbucket.org/ned/coveragepy/issue/139/easy-check-for-a-certain-coverage-in-tests +.. _issue 143: https://bitbucket.org/ned/coveragepy/issue/143/omit-doesnt-seem-to-work-in-coverage +.. _issue 153: https://bitbucket.org/ned/coveragepy/issue/153/non-existent-filename-triggers +.. _issue 156: https://bitbucket.org/ned/coveragepy/issue/156/a-completely-unexecuted-file-shows-14 +.. _issue 163: https://bitbucket.org/ned/coveragepy/issue/163/problem-with-include-and-omit-filename +.. _issue 171: https://bitbucket.org/ned/coveragepy/issue/171/how-to-contribute-and-run-tests +.. _issue 193: https://bitbucket.org/ned/coveragepy/issue/193/unicodedecodeerror-on-htmlpy +.. _issue 201: https://bitbucket.org/ned/coveragepy/issue/201/coverage-using-django-14-with-pydb-on +.. _issue 202: https://bitbucket.org/ned/coveragepy/issue/202/get-rid-of-ez_setuppy-and +.. _issue 203: https://bitbucket.org/ned/coveragepy/issue/203/duplicate-filenames-reported-when-filename +.. _issue 205: https://bitbucket.org/ned/coveragepy/issue/205/make-pydoc-coverage-more-friendly +.. _issue 206: https://bitbucket.org/ned/coveragepy/issue/206/pydoc-coveragecoverage-fails-with-an-error +.. _issue 210: https://bitbucket.org/ned/coveragepy/issue/210/if-theres-no-coverage-data-coverage-xml +.. _issue 214: https://bitbucket.org/ned/coveragepy/issue/214/coveragepy-measures-itself-on-precise + + +Version 3.5.3 --- 29 September 2012 +----------------------------------- + +- Line numbers in the HTML report line up better with the source lines, fixing + `issue 197`_, thanks Marius Gedminas. + +- When specifying a directory as the source= option, the directory itself no + longer needs to have a ``__init__.py`` file, though its subdirectories do, to + be considered as source files. + +- Files encoded as UTF-8 with a BOM are now properly handled, fixing + `issue 179`_. Thanks, Pablo Carballo. + +- Fixed more cases of non-Python files being reported as Python source, and + then not being able to parse them as Python. Closes `issue 82`_ (again). + Thanks, Julian Berman. + +- Fixed memory leaks under Python 3, thanks, Brett Cannon. Closes `issue 147`_. + +- Optimized .pyo files may not have been handled correctly, `issue 195`_. + Thanks, Marius Gedminas. + +- Certain unusually named file paths could have been mangled during reporting, + `issue 194`_. Thanks, Marius Gedminas. + +- Try to do a better job of the impossible task of detecting when we can't + build the C extension, fixing `issue 183`_. + +- Testing is now done with `tox`_, thanks, Marc Abramowitz. + +.. _issue 82: https://bitbucket.org/ned/coveragepy/issue/82/tokenerror-when-generating-html-report +.. _issue 147: https://bitbucket.org/ned/coveragepy/issue/147/massive-memory-usage-by-ctracer +.. _issue 179: https://bitbucket.org/ned/coveragepy/issue/179/htmlreporter-fails-when-source-file-is +.. _issue 183: https://bitbucket.org/ned/coveragepy/issue/183/install-fails-for-python-23 +.. _issue 194: https://bitbucket.org/ned/coveragepy/issue/194/filelocatorrelative_filename-could-mangle +.. _issue 195: https://bitbucket.org/ned/coveragepy/issue/195/pyo-file-handling-in-codeunit +.. _issue 197: https://bitbucket.org/ned/coveragepy/issue/197/line-numbers-in-html-report-do-not-align +.. _tox: http://tox.readthedocs.org/ + + + +Version 3.5.2 --- 4 May 2012 +---------------------------- + +No changes since 3.5.2.b1 + + +Version 3.5.2b1 --- 29 April 2012 +--------------------------------- + +- The HTML report has slightly tweaked controls: the buttons at the top of + 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. + +- 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 + properly. This fixes `issue 157`_. + +- Backup files left behind by editors are no longer collected by the source= + option, fixing `issue 168`_. + +- If a file doesn't parse properly as Python, we don't report it as an error + if the filename seems like maybe it wasn't meant to be Python. This is a + pragmatic fix for `issue 82`_. + +- The ``-m`` switch on ``coverage report``, which includes missing line numbers + in the summary report, can now be specifed as ``show_missing`` in the + config file. Closes `issue 173`_. + +- When running a module with ``coverage run -m ``, certain details + of the execution environment weren't the same as for + ``python -m ``. This had the unfortunate side-effect of making + ``coverage run -m unittest discover`` not work if you had tests in a + directory named "test". This fixes `issue 155`_. + +- Now the exit status of your product code is properly used as the process + status when running ``python -m coverage run ...``. Thanks, JT Olds. + +- When installing into pypy, we no longer attempt (and fail) to compile + the C tracer function, closing `issue 166`_. + +.. _issue 82: https://bitbucket.org/ned/coveragepy/issue/82/tokenerror-when-generating-html-report +.. _issue 155: https://bitbucket.org/ned/coveragepy/issue/155/cant-use-coverage-run-m-unittest-discover +.. _issue 157: https://bitbucket.org/ned/coveragepy/issue/157/chokes-on-source-files-with-non-utf-8 +.. _issue 166: https://bitbucket.org/ned/coveragepy/issue/166/dont-try-to-compile-c-extension-on-pypy +.. _issue 168: https://bitbucket.org/ned/coveragepy/issue/168/dont-be-alarmed-by-emacs-droppings +.. _issue 173: https://bitbucket.org/ned/coveragepy/issue/173/theres-no-way-to-specify-show-missing-in + + +Version 3.5.1 --- 23 September 2011 +----------------------------------- + +- The ``[paths]`` feature unfortunately didn't work in real world situations + where you wanted to, you know, report on the combined data. Now all paths + stored in the combined file are canonicalized properly. + + +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`_. + +- for-else constructs are understood better, and don't cause erroneous partial + branch warnings. Fixes `issue 122`_. + +- Branch coverage for ``with`` statements is improved, fixing `issue 128`_. + +- The number of partial branches reported on the HTML summary page was + different than the number reported on the individual file pages. This is + now fixed. + +- An explicit include directive to measure files in the Python installation + wouldn't work because of the standard library exclusion. Now the include + directive takes precendence, and the files will be measured. Fixes + `issue 138`_. + +- The HTML report now handles Unicode characters in Python source files + properly. This fixes `issue 124`_ and `issue 144`_. Thanks, Devin + Jeanpierre. + +- 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. + See the coverage/fullcoverage directory if you are interested. + +.. _issue 17: http://bitbucket.org/ned/coveragepy/issue/17/support-combining-coverage-data-from +.. _issue 122: http://bitbucket.org/ned/coveragepy/issue/122/for-else-always-reports-missing-branch +.. _issue 124: http://bitbucket.org/ned/coveragepy/issue/124/no-arbitrary-unicode-in-html-reports-in +.. _issue 128: http://bitbucket.org/ned/coveragepy/issue/128/branch-coverage-of-with-statement-in-27 +.. _issue 138: http://bitbucket.org/ned/coveragepy/issue/138/include-should-take-precedence-over-is +.. _issue 144: http://bitbucket.org/ned/coveragepy/issue/144/failure-generating-html-output-for + + +Version 3.5 --- 29 June 2011 +---------------------------- + +- The HTML report hotkeys now behave slightly differently when the current + chunk isn't visible at all: a chunk on the screen will be selected, + instead of the old behavior of jumping to the literal next chunk. + The hotkeys now work in Google Chrome. Thanks, Guido van Rossum. + + +Version 3.5b1 --- 5 June 2011 +----------------------------- + +- The HTML report now has hotkeys. Try ``n``, ``s``, ``m``, ``x``, ``b``, + ``p``, and ``c`` on the overview page to change the column sorting. + On a file page, ``r``, ``m``, ``x``, and ``p`` toggle the run, missing, + excluded, and partial line markings. You can navigate the highlighted + sections of code by using the ``j`` and ``k`` keys for next and previous. + The ``1`` (one) key jumps to the first highlighted section in the file, + and ``0`` (zero) scrolls to the top of the file. + +- The ``--omit`` and ``--include`` switches now interpret their values more + usefully. If the value starts with a wildcard character, it is used as-is. + If it does not, it is interpreted relative to the current directory. + Closes `issue 121`_. + +- Partial branch warnings can now be pragma'd away. The configuration option + ``partial_branches`` is a list of regular expressions. Lines matching any of + those expressions will never be marked as a partial branch. In addition, + there's a built-in list of regular expressions marking statements which should + never be marked as partial. This list includes ``while True:``, ``while 1:``, + ``if 1:``, and ``if 0:``. + +- The ``coverage()`` constructor accepts single strings for the ``omit=`` and + ``include=`` arguments, adapting to a common error in programmatic use. + +- Modules can now be run directly using ``coverage run -m modulename``, to + mirror Python's ``-m`` flag. Closes `issue 95`_, thanks, Brandon Rhodes. + +- ``coverage run`` didn't emulate Python accurately in one small detail: the + current directory inserted into ``sys.path`` was relative rather than + absolute. This is now fixed. + +- HTML reporting is now incremental: a record is kept of the data that + produced the HTML reports, and only files whose data has changed will + be generated. This should make most HTML reporting faster. + +- Pathological code execution could disable the trace function behind our + backs, leading to incorrect code measurement. Now if this happens, + coverage.py will issue a warning, at least alerting you to the problem. + Closes `issue 93`_. Thanks to Marius Gedminas for the idea. + +- The C-based trace function now behaves properly when saved and restored + with ``sys.gettrace()`` and ``sys.settrace()``. This fixes `issue 125`_ + and `issue 123`_. Thanks, Devin Jeanpierre. + +- Source files are now opened with Python 3.2's ``tokenize.open()`` where + possible, to get the best handling of Python source files with encodings. + Closes `issue 107`_, thanks, Brett Cannon. + +- Syntax errors in supposed Python files can now be ignored during reporting + with the ``-i`` switch just like other source errors. Closes `issue 115`_. + +- Installation from source now succeeds on machines without a C compiler, + closing `issue 80`_. + +- Coverage.py can now be run directly from a working tree by specifying + the directory name to python: ``python coverage_py_working_dir run ...``. + Thanks, Brett Cannon. + +- A little bit of Jython support: `coverage run` can now measure Jython + execution by adapting when $py.class files are traced. Thanks, Adi Roiban. + Jython still doesn't provide the Python libraries needed to make + coverage reporting work, unfortunately. + +- Internally, files are now closed explicitly, fixing `issue 104`_. Thanks, + Brett Cannon. + +.. _issue 80: https://bitbucket.org/ned/coveragepy/issue/80/is-there-a-duck-typing-way-to-know-we-cant +.. _issue 93: http://bitbucket.org/ned/coveragepy/issue/93/copying-a-mock-object-breaks-coverage +.. _issue 95: https://bitbucket.org/ned/coveragepy/issue/95/run-subcommand-should-take-a-module-name +.. _issue 104: https://bitbucket.org/ned/coveragepy/issue/104/explicitly-close-files +.. _issue 107: https://bitbucket.org/ned/coveragepy/issue/107/codeparser-not-opening-source-files-with +.. _issue 115: https://bitbucket.org/ned/coveragepy/issue/115/fail-gracefully-when-reporting-on-file +.. _issue 121: https://bitbucket.org/ned/coveragepy/issue/121/filename-patterns-are-applied-stupidly +.. _issue 123: https://bitbucket.org/ned/coveragepy/issue/123/pyeval_settrace-used-in-way-that-breaks +.. _issue 125: https://bitbucket.org/ned/coveragepy/issue/125/coverage-removes-decoratortoolss-tracing + Version 3.4 --- 19 September 2010 --------------------------------- @@ -92,6 +470,9 @@ 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, + Brett Cannon. + - 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. @@ -100,7 +481,7 @@ - Jinja HTML templates compile into Python code using the HTML filename, which confused coverage.py. Now these files are no longer traced, fixing - `issue 82`. + `issue 82`_. - Source files can have more than one dot in them (foo.test.py), and will be treated properly while reporting. Fixes `issue 46`_. @@ -229,7 +610,7 @@ - Fixed some problems syntax coloring sources with line continuations and source with tabs: `issue 30`_ and `issue 31`_. -- The --omit option now works much better than before, fixing `issue 14` and +- The --omit option now works much better than before, fixing `issue 14`_ and `issue 33`_. Thanks, Danek Duvall. .. _issue 14: http://bitbucket.org/ned/coveragepy/issue/14 @@ -301,17 +682,17 @@ ----------------------------- - Removed the recursion limit in the tracer function. Previously, code that - ran more than 500 frames deep would crash. Fixed `issue 9`. + ran more than 500 frames deep would crash. Fixed `issue 9`_. - Fixed a bizarre problem involving pyexpat, whereby lines following XML parser - invocations could be overlooked. Fixed `issue 10`. + invocations could be overlooked. Fixed `issue 10`_. - On Python 2.3, coverage.py could mis-measure code with exceptions being 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`. + `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 @@ -329,7 +710,7 @@ excluded the old way. - Tabs are now properly converted in HTML reports. Previously indentation was - lost. Fixed `issue 6`. + lost. Fixed `issue 6`_. - Nested modules now get a proper flat_rootname. Thanks, Christian Heimes. diff -Nru python-coverage-3.4/MANIFEST.in python-coverage-3.6/MANIFEST.in --- python-coverage-3.4/MANIFEST.in 2010-05-30 04:26:49.000000000 +0000 +++ python-coverage-3.6/MANIFEST.in 2012-11-18 04:32:29.000000000 +0000 @@ -1,11 +1,16 @@ # MANIFEST.in file for coverage.py +recursive-include coverage/htmlfiles * +recursive-include coverage/fullcoverage * + include coverage.egg-info/*.* -include coverage/*.py -include coverage/htmlfiles/*.* -include distribute_setup.py -include ez_setup.py include setup.py include README.txt include CHANGES.txt include AUTHORS.txt -prune test +include requirements.txt +include igor.py +include tox.ini + +recursive-include test * +recursive-include doc *.rst +global-exclude *.pyc diff -Nru python-coverage-3.4/PKG-INFO python-coverage-3.6/PKG-INFO --- python-coverage-3.4/PKG-INFO 2010-09-19 21:02:59.000000000 +0000 +++ python-coverage-3.6/PKG-INFO 2013-01-06 00:59:54.000000000 +0000 @@ -1,6 +1,6 @@ -Metadata-Version: 1.0 +Metadata-Version: 1.1 Name: coverage -Version: 3.4 +Version: 3.6 Summary: Code coverage measurement for Python Home-page: http://nedbatchelder.com/code/coverage Author: Ned Batchelder and others @@ -10,18 +10,21 @@ the code analysis tools and tracing hooks provided in the Python standard library to determine which lines are executable, and which have been executed. - Coverage.py runs on Pythons 2.3 through 3.2. + Coverage.py runs on Pythons 2.3 through 3.3, and PyPy 1.9. Documentation is at `nedbatchelder.com `_. Code repository and issue tracker are at `bitbucket.org `_. - New in 3.2: Branch coverage! + New in 3.6: ``--fail-under``, and >20 bugs closed. - New in 3.3: .coveragerc files. + New in 3.5: Branch coverage exclusions, keyboard shortcuts in HTML report. New in 3.4: Better control over source to measure, and unexecuted files can be reported. + New in 3.3: .coveragerc files. + + New in 3.2: Branch coverage! Keywords: code coverage testing Platform: UNKNOWN Classifier: Environment :: Console diff -Nru python-coverage-3.4/coverage/__init__.py python-coverage-3.6/coverage/__init__.py --- python-coverage-3.4/coverage/__init__.py 2010-09-19 20:33:09.000000000 +0000 +++ python-coverage-3.6/coverage/__init__.py 2012-11-12 15:54:13.000000000 +0000 @@ -5,19 +5,13 @@ """ -__version__ = "3.4" # see detailed history in CHANGES.txt - -__url__ = "http://nedbatchelder.com/code/coverage" -if 'b' in __version__: - # For beta, use a version-specific URL. - __url__ += "/" + __version__ +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 - # 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 @@ -36,12 +30,34 @@ called. """ + # Disable pylint msg W0612, because a bunch of variables look unused, but + # they're accessed via locals(). + # pylint: disable=W0612 + 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 @@ -57,10 +73,26 @@ 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. +import encodings.utf_8 + +# Because of the "from coverage.control import fooey" lines at the top of the +# file, there's an entry for coverage.coverage in sys.modules, mapped to None. +# This makes some inspection tools (like pydoc) unable to find the class +# coverage.coverage. So remove that entry. +import sys +try: + del sys.modules['coverage.coverage'] +except KeyError: + pass + + # COPYRIGHT AND LICENSE # # Copyright 2001 Gareth Rees. All rights reserved. -# Copyright 2004-2010 Ned Batchelder. All rights reserved. +# Copyright 2004-2012 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 diff -Nru python-coverage-3.4/coverage/__main__.py python-coverage-3.6/coverage/__main__.py --- python-coverage-3.4/coverage/__main__.py 2010-09-02 02:14:33.000000000 +0000 +++ python-coverage-3.6/coverage/__main__.py 2012-11-06 03:04:44.000000000 +0000 @@ -1,3 +1,4 @@ -"""Coverage.py's main entrypoint.""" +"""Coverage.py's main entry point.""" +import sys from coverage.cmdline import main -main() +sys.exit(main()) diff -Nru python-coverage-3.4/coverage/annotate.py python-coverage-3.6/coverage/annotate.py --- python-coverage-3.4/coverage/annotate.py 2010-09-03 00:47:10.000000000 +0000 +++ python-coverage-3.6/coverage/annotate.py 2012-04-20 17:26:17.000000000 +0000 @@ -26,20 +26,20 @@ """ - def __init__(self, coverage, ignore_errors=False): - super(AnnotateReporter, self).__init__(coverage, ignore_errors) + def __init__(self, coverage, config): + super(AnnotateReporter, self).__init__(coverage, config) self.directory = None blank_re = re.compile(r"\s*(#|$)") else_re = re.compile(r"\s*else\s*:\s*(#|$)") - def report(self, morfs, config, directory=None): + def report(self, morfs, directory=None): """Run the report. See `coverage.report()` for arguments. """ - self.report_files(self.annotate_file, morfs, config, directory) + self.report_files(self.annotate_file, morfs, directory) def annotate_file(self, cu, analysis): """Annotate a single file. diff -Nru python-coverage-3.4/coverage/backward.py python-coverage-3.6/coverage/backward.py --- python-coverage-3.4/coverage/backward.py 2010-05-30 04:26:50.000000000 +0000 +++ python-coverage-3.6/coverage/backward.py 2012-12-20 12:02:31.000000000 +0000 @@ -1,12 +1,12 @@ """Add things to old Pythons so I can pretend they are newer.""" # This file does lots of tricky stuff, so disable a bunch of lintisms. -# pylint: disable-msg=F0401,W0611,W0622 +# pylint: disable=F0401,W0611,W0622 # F0401: Unable to import blah # W0611: Unused import blah # W0622: Redefining built-in blah -import os, sys +import os, re, sys # Python 2.3 doesn't have `set` try: @@ -24,6 +24,22 @@ lst.sort() return lst +# rpartition is new in 2.5 +try: + "".rpartition +except AttributeError: + def rpartition(s, sep): + """Implement s.rpartition(sep) for old Pythons.""" + i = s.rfind(sep) + if i == -1: + return ('', '', s) + else: + return (s[:i], sep, s[i+len(sep):]) +else: + def rpartition(s, sep): + """A common interface for new Pythons.""" + return s.rpartition(sep) + # Pythons 2 and 3 differ on where to get StringIO try: from cStringIO import StringIO @@ -49,6 +65,16 @@ except NameError: range = range +# A function to iterate listlessly over a dict's items. +if "iteritems" in dir({}): + def iitems(d): + """Produce the items from dict `d`.""" + return d.iteritems() +else: + def iitems(d): + """Produce the items from dict `d`.""" + return d.items() + # Exec is a statement in Py2, a function in Py3 if sys.version_info >= (3, 0): def exec_code_object(code, global_map): @@ -66,8 +92,56 @@ ) ) -# ConfigParser was renamed to the more-standard configparser +# Reading Python source and interpreting the coding comment is a big deal. +if sys.version_info >= (3, 0): + # Python 3.2 provides `tokenize.open`, the best way to open source files. + import tokenize + try: + open_source = tokenize.open # pylint: disable=E1101 + except AttributeError: + from io import TextIOWrapper + detect_encoding = tokenize.detect_encoding # pylint: disable=E1101 + # Copied from the 3.2 stdlib: + def open_source(fname): + """Open a file in read only mode using the encoding detected by + detect_encoding(). + """ + buffer = open(fname, 'rb') + encoding, _ = detect_encoding(buffer.readline) + buffer.seek(0) + text = TextIOWrapper(buffer, encoding, line_buffering=True) + text.mode = 'r' + return text +else: + def open_source(fname): + """Open a source file the best way.""" + return open(fname, "rU") + + +# Python 3.x is picky about bytes and strings, so provide methods to +# get them right, and make them no-ops in 2.x +if sys.version_info >= (3, 0): + def to_bytes(s): + """Convert string `s` to bytes.""" + return s.encode('utf8') + + def to_string(b): + """Convert bytes `b` to a string.""" + return b.decode('utf8') + +else: + def to_bytes(s): + """Convert string `s` to bytes (no-op in 2.x).""" + return s + + def to_string(b): + """Convert bytes `b` to a string (no-op in 2.x).""" + return b + +# Md5 is available in different places. try: - import configparser + import hashlib + md5 = hashlib.md5 except ImportError: - import ConfigParser as configparser + import md5 + md5 = md5.new diff -Nru python-coverage-3.4/coverage/bytecode.py python-coverage-3.6/coverage/bytecode.py --- python-coverage-3.4/coverage/bytecode.py 2010-05-30 04:26:50.000000000 +0000 +++ python-coverage-3.6/coverage/bytecode.py 2012-11-10 20:19:12.000000000 +0000 @@ -5,10 +5,19 @@ class ByteCode(object): """A single bytecode.""" def __init__(self): + # The offset of this bytecode in the code object. self.offset = -1 + + # The opcode, defined in the `opcode` module. self.op = -1 + + # The argument, a small integer, whose meaning depends on the opcode. self.arg = -1 + + # The offset in the code object of the next bytecode. self.next_offset = -1 + + # The offset to jump to. self.jump_to = -1 @@ -18,6 +27,7 @@ Returns `ByteCode` objects. """ + # pylint: disable=R0924 def __init__(self, code): self.code = code self.offset = 0 diff -Nru python-coverage-3.4/coverage/cmdline.py python-coverage-3.6/coverage/cmdline.py --- python-coverage-3.4/coverage/cmdline.py 2010-09-18 22:59:32.000000000 +0000 +++ python-coverage-3.6/coverage/cmdline.py 2012-11-23 04:27:01.000000000 +0000 @@ -1,10 +1,10 @@ """Command-line support for Coverage.""" -import optparse, re, sys, traceback +import optparse, sys, traceback -from coverage.backward import sorted # pylint: disable-msg=W0622 -from coverage.execfile import run_python_file -from coverage.misc import CoverageException, ExceptionDuringRun +from coverage.backward import sorted # pylint: disable=W0622 +from coverage.execfile import run_python_file, run_python_module +from coverage.misc import CoverageException, ExceptionDuringRun, NoSource class Opts(object): @@ -20,10 +20,13 @@ help="Measure branch coverage in addition to statement coverage." ) directory = optparse.make_option( - '-d', '--directory', action='store', - metavar="DIR", + '-d', '--directory', action='store', metavar="DIR", help="Write the output files to DIR." ) + fail_under = optparse.make_option( + '', '--fail-under', action='store', metavar="MIN", type="int", + help="Exit with a status of 2 if the total coverage is less than MIN." + ) help = optparse.make_option( '-h', '--help', action='store_true', help="Get help on this command." @@ -71,6 +74,11 @@ ".coverage data file name to simplify collecting data from " "many processes." ) + module = optparse.make_option( + '-m', '--module', action='store_true', + help=" is an importable Python module, not a script path, " + "to be run as 'python -m' would run it." + ) rcfile = optparse.make_option( '', '--rcfile', action='store', help="Specify configuration file. Defaults to '.coveragerc'" @@ -84,6 +92,10 @@ help="Use a simpler but slower trace method. Try this if you get " "seemingly impossible results!" ) + title = optparse.make_option( + '', '--title', action='store', metavar="TITLE", + help="A text string to use as the title on the HTML." + ) version = optparse.make_option( '', '--version', action='store_true', help="Display version information and exit." @@ -106,16 +118,19 @@ actions=[], branch=None, directory=None, + fail_under=None, help=None, ignore_errors=None, include=None, omit=None, parallel_mode=None, + module=None, pylib=None, rcfile=True, show_missing=None, source=None, timid=None, + title=None, erase_first=None, version=None, ) @@ -267,9 +282,11 @@ 'html': CmdOptionParser("html", [ Opts.directory, + Opts.fail_under, Opts.ignore_errors, Opts.omit, Opts.include, + Opts.title, ] + GLOBAL_ARGS, usage = "[options] [modules]", description = "Create an HTML report of the coverage of the files. " @@ -279,6 +296,7 @@ 'report': CmdOptionParser("report", [ + Opts.fail_under, Opts.ignore_errors, Opts.omit, Opts.include, @@ -294,6 +312,7 @@ Opts.branch, Opts.pylib, Opts.parallel_mode, + Opts.module, Opts.timid, Opts.source, Opts.omit, @@ -307,26 +326,27 @@ 'xml': CmdOptionParser("xml", [ + Opts.fail_under, Opts.ignore_errors, Opts.omit, Opts.include, Opts.output_xml, ] + GLOBAL_ARGS, cmd = "xml", - defaults = {'outfile': 'coverage.xml'}, usage = "[options] [modules]", description = "Generate an XML report of coverage results." ), } -OK, ERR = 0, 1 +OK, ERR, FAIL_UNDER = 0, 1, 2 class CoverageScript(object): """The command-line interface to Coverage.""" - def __init__(self, _covpkg=None, _run_python_file=None, _help_fn=None): + def __init__(self, _covpkg=None, _run_python_file=None, + _run_python_module=None, _help_fn=None): # _covpkg is for dependency injection, so we can test this code. if _covpkg: self.covpkg = _covpkg @@ -334,32 +354,14 @@ import coverage self.covpkg = coverage - # _run_python_file is for dependency injection also. + # For dependency injection: self.run_python_file = _run_python_file or run_python_file - - # _help_fn is for dependency injection. + self.run_python_module = _run_python_module or run_python_module self.help_fn = _help_fn or self.help + self.classic = False self.coverage = None - def help(self, error=None, topic=None, parser=None): - """Display an error message, or the named topic.""" - assert error or topic or parser - if error: - print(error) - print("Use 'coverage help' for help.") - elif parser: - print(parser.format_help().strip()) - else: - # Parse out the topic we want from HELP_TOPICS - topic_list = re.split("(?m)^=+ (\w+) =+$", HELP_TOPICS) - topics = dict(zip(topic_list[1::2], topic_list[2::2])) - help_msg = topics.get(topic, '').strip() - if help_msg: - print(help_msg % self.covpkg.__dict__) - else: - print("Don't know topic %r" % topic) - def command_line(self, argv): """The bulk of the command line interface to Coverage. @@ -369,15 +371,14 @@ """ # Collect the command-line options. - if not argv: self.help_fn(topic='minimum_help') return OK # The command syntax we parse depends on the first argument. Classic # syntax always starts with an option. - classic = argv[0].startswith('-') - if classic: + self.classic = argv[0].startswith('-') + if self.classic: parser = ClassicOptionParser() else: parser = CMDS.get(argv[0]) @@ -391,58 +392,12 @@ if not ok: return ERR - # Handle help. - if options.help: - if classic: - self.help_fn(topic='help') - else: - self.help_fn(parser=parser) - return OK - - if "help" in options.actions: - if args: - for a in args: - parser = CMDS.get(a) - if parser: - self.help_fn(parser=parser) - else: - self.help_fn(topic=a) - else: - self.help_fn(topic='help') - return OK - - # Handle version. - if options.version: - self.help_fn(topic='version') + # Handle help and version. + if self.do_help(options, args, parser): return OK # Check for conflicts and problems in the options. - for i in ['erase', 'execute']: - for j in ['annotate', 'html', 'report', 'combine']: - if (i in options.actions) and (j in options.actions): - self.help_fn("You can't specify the '%s' and '%s' " - "options at the same time." % (i, j)) - return ERR - - if not options.actions: - self.help_fn( - "You must specify at least one of -e, -x, -c, -r, -a, or -b." - ) - return ERR - args_allowed = ( - 'execute' in options.actions or - 'annotate' in options.actions or - 'html' in options.actions or - 'debug' in options.actions or - 'report' in options.actions or - 'xml' in options.actions - ) - if not args_allowed and args: - self.help_fn("Unexpected arguments: %s" % " ".join(args)) - return ERR - - if 'execute' in options.actions and not args: - self.help_fn("Nothing to do.") + if not self.args_ok(options, args): return ERR # Listify the list options. @@ -463,38 +418,7 @@ ) if 'debug' in options.actions: - if not args: - self.help_fn("What information would you like: data, sys?") - return ERR - for info in args: - if info == 'sys': - print("-- sys ----------------------------------------") - for label, info in self.coverage.sysinfo(): - if info == []: - info = "-none-" - if isinstance(info, list): - print("%15s:" % label) - for e in info: - print("%15s %s" % ("", e)) - else: - print("%15s: %s" % (label, info)) - elif info == 'data': - print("-- data ---------------------------------------") - self.coverage.load() - 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: - filenames = sorted(summary.keys()) - print("\n%d files:" % len(filenames)) - for f in filenames: - print("%s: %d lines" % (f, summary[f])) - else: - print("No data collected") - else: - self.help_fn("Don't know what you mean by %r" % info) - return ERR - return OK + return self.do_debug(args) if 'erase' in options.actions or options.erase_first: self.coverage.erase() @@ -502,13 +426,7 @@ self.coverage.load() if 'execute' in options.actions: - # Run the script. - self.coverage.start() - try: - self.run_python_file(args[0], args) - finally: - self.coverage.stop() - self.coverage.save() + self.do_execute(options, args) if 'combine' in options.actions: self.coverage.combine() @@ -523,18 +441,166 @@ ) if 'report' in options.actions: - self.coverage.report( + total = self.coverage.report( show_missing=options.show_missing, **report_args) if 'annotate' in options.actions: self.coverage.annotate( directory=options.directory, **report_args) if 'html' in options.actions: - self.coverage.html_report( - directory=options.directory, **report_args) + total = self.coverage.html_report( + directory=options.directory, title=options.title, + **report_args) if 'xml' in options.actions: outfile = options.outfile - self.coverage.xml_report(outfile=outfile, **report_args) + total = self.coverage.xml_report(outfile=outfile, **report_args) + + if options.fail_under is not None: + if total >= options.fail_under: + return OK + else: + return FAIL_UNDER + else: + return OK + def help(self, error=None, topic=None, parser=None): + """Display an error message, or the named topic.""" + assert error or topic or parser + if error: + print(error) + print("Use 'coverage help' for help.") + elif parser: + print(parser.format_help().strip()) + else: + help_msg = HELP_TOPICS.get(topic, '').strip() + if help_msg: + print(help_msg % self.covpkg.__dict__) + else: + print("Don't know topic %r" % topic) + + def do_help(self, options, args, parser): + """Deal with help requests. + + Return True if it handled the request, False if not. + + """ + # Handle help. + if options.help: + if self.classic: + self.help_fn(topic='help') + else: + self.help_fn(parser=parser) + return True + + if "help" in options.actions: + if args: + for a in args: + parser = CMDS.get(a) + if parser: + self.help_fn(parser=parser) + else: + self.help_fn(topic=a) + else: + self.help_fn(topic='help') + return True + + # Handle version. + if options.version: + self.help_fn(topic='version') + return True + + return False + + def args_ok(self, options, args): + """Check for conflicts and problems in the options. + + Returns True if everything is ok, or False if not. + + """ + for i in ['erase', 'execute']: + for j in ['annotate', 'html', 'report', 'combine']: + if (i in options.actions) and (j in options.actions): + self.help_fn("You can't specify the '%s' and '%s' " + "options at the same time." % (i, j)) + return False + + if not options.actions: + self.help_fn( + "You must specify at least one of -e, -x, -c, -r, -a, or -b." + ) + return False + args_allowed = ( + 'execute' in options.actions or + 'annotate' in options.actions or + 'html' in options.actions or + 'debug' in options.actions or + 'report' in options.actions or + 'xml' in options.actions + ) + if not args_allowed and args: + self.help_fn("Unexpected arguments: %s" % " ".join(args)) + return False + + if 'execute' in options.actions and not args: + self.help_fn("Nothing to do.") + return False + + return True + + def do_execute(self, options, args): + """Implementation of 'coverage run'.""" + + # Run the script. + self.coverage.start() + code_ran = True + try: + try: + if options.module: + self.run_python_module(args[0], args) + else: + self.run_python_file(args[0], args) + except NoSource: + code_ran = False + raise + finally: + self.coverage.stop() + if code_ran: + self.coverage.save() + + 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': + print("-- sys ----------------------------------------") + for label, info in self.coverage.sysinfo(): + if info == []: + info = "-none-" + if isinstance(info, list): + prefix = "%15s:" % label + for e in info: + print("%16s %s" % (prefix, e)) + prefix = "" + else: + print("%15s: %s" % (label, info)) + elif info == 'data': + print("-- data ---------------------------------------") + self.coverage.load() + 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: + filenames = sorted(summary.keys()) + print("\n%d files:" % len(filenames)) + for f in filenames: + print("%s: %d lines" % (f, summary[f])) + else: + print("No data collected") + else: + self.help_fn("Don't know what you mean by %r" % info) + return ERR return OK @@ -552,10 +618,10 @@ return s.split(',') -HELP_TOPICS = r""" - -== classic ==================================================================== -Coverage.py version %(__version__)s +HELP_TOPICS = { +# ------------------------- +'classic': +r"""Coverage.py version %(__version__)s Measure, collect, and report on code coverage in Python programs. Usage: @@ -599,8 +665,9 @@ Coverage data is saved in the file .coverage by default. Set the COVERAGE_FILE environment variable to save it somewhere else. - -== help ======================================================================= +""", +# ------------------------- +'help': """\ Coverage.py, version %(__version__)s Measure, collect, and report on code coverage in Python programs. @@ -619,20 +686,22 @@ Use "coverage help " for detailed help on any command. Use "coverage help classic" for help on older command syntax. For more information, see %(__url__)s - -== minimum_help =============================================================== +""", +# ------------------------- +'minimum_help': """\ Code coverage for Python. Use 'coverage help' for help. - -== version ==================================================================== +""", +# ------------------------- +'version': """\ Coverage.py, version %(__version__)s. %(__url__)s - -""" +""", +} def main(argv=None): - """The main entrypoint to Coverage. + """The main entry point to Coverage. - This is installed as the script entrypoint. + This is installed as the script entry point. """ if argv is None: diff -Nru python-coverage-3.4/coverage/codeunit.py python-coverage-3.6/coverage/codeunit.py --- python-coverage-3.4/coverage/codeunit.py 2010-08-28 17:46:22.000000000 +0000 +++ python-coverage-3.6/coverage/codeunit.py 2012-12-30 19:34:22.000000000 +0000 @@ -2,7 +2,7 @@ import glob, os -from coverage.backward import string_class, StringIO +from coverage.backward import open_source, string_class, StringIO from coverage.misc import CoverageException @@ -52,8 +52,10 @@ else: f = morf # .pyc files should always refer to a .py instead. - if f.endswith('.pyc'): + if f.endswith('.pyc') or f.endswith('.pyo'): f = f[:-1] + elif f.endswith('$py.class'): # Jython + f = f[:-9] + ".py" self.filename = self.file_locator.canonical_filename(f) if hasattr(morf, '__name__'): @@ -77,12 +79,18 @@ # Annoying comparison operators. Py3k wants __lt__ etc, and Py2k needs all # of them defined. - def __lt__(self, other): return self.name < other.name - def __le__(self, other): return self.name <= other.name - def __eq__(self, other): return self.name == other.name - def __ne__(self, other): return self.name != other.name - def __gt__(self, other): return self.name > other.name - def __ge__(self, other): return self.name >= other.name + def __lt__(self, other): + return self.name < other.name + def __le__(self, other): + return self.name <= other.name + def __eq__(self, other): + return self.name == other.name + def __ne__(self, other): + return self.name != other.name + def __gt__(self, other): + return self.name > other.name + def __ge__(self, other): + return self.name >= other.name def flat_rootname(self): """A base for a flat filename to correspond to this code unit. @@ -104,7 +112,7 @@ """Return an open file for reading the source of the code unit.""" if os.path.exists(self.filename): # A regular text file: open it. - return open(self.filename) + return open_source(self.filename) # Maybe it's in a zip file? source = self.file_locator.get_zip_data(self.filename) @@ -113,5 +121,25 @@ # Couldn't find source. raise CoverageException( - "No source for code %r." % self.filename + "No source for code '%s'." % self.filename ) + + def should_be_python(self): + """Does it seem like this file should contain Python? + + This is used to decide if a file reported as part of the exection of + a program was really likely to have contained Python in the first + place. + + """ + # Get the file extension. + _, ext = os.path.splitext(self.filename) + + # Anything named *.py* should be Python. + if ext.startswith('.py'): + return True + # A file with no extension should be Python. + if not ext: + return True + # Everything else is probably not Python. + return False diff -Nru python-coverage-3.4/coverage/collector.py python-coverage-3.6/coverage/collector.py --- python-coverage-3.4/coverage/collector.py 2010-08-26 13:28:03.000000000 +0000 +++ python-coverage-3.6/coverage/collector.py 2012-12-21 14:40:42.000000000 +0000 @@ -1,13 +1,24 @@ """Raw data collector for Coverage.""" -import sys, threading +import os, sys, threading try: # Use the C extension code when we can, for speed. - from coverage.tracer import Tracer + from coverage.tracer import CTracer # pylint: disable=F0401,E0611 except ImportError: # Couldn't import the C extension, maybe it isn't built. - Tracer = None + if os.getenv('COVERAGE_TEST_TRACER') == 'c': + # During testing, we use the COVERAGE_TEST_TRACER env var to indicate + # that we've fiddled with the environment to test this fallback code. + # If we thought we had a C tracer, but couldn't import it, then exit + # quickly and clearly instead of dribbling confusing errors. I'm using + # sys.exit here instead of an exception because an 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 PyTracer(object): @@ -33,6 +44,7 @@ self.data = None self.should_trace = None self.should_trace_cache = None + self.warn = None self.cur_file_data = None self.last_line = 0 self.data_stack = [] @@ -43,8 +55,9 @@ def _trace(self, frame, event, arg_unused): """The trace function passed to sys.settrace.""" - #print "trace event: %s %r @%d" % ( - # event, frame.f_code.co_filename, frame.f_lineno) + #print("trace event: %s %r @%d" % ( + # event, frame.f_code.co_filename, frame.f_lineno), + # file=sys.stderr) if self.last_exc_back: if frame == self.last_exc_back: @@ -64,6 +77,8 @@ if tracename is None: tracename = self.should_trace(filename, frame) self.should_trace_cache[filename] = tracename + #print("called, stack is %d deep, tracename is %r" % ( + # len(self.data_stack), tracename)) if tracename: if tracename not in self.data: self.data[tracename] = {} @@ -77,10 +92,10 @@ # Record an executed line. if self.cur_file_data is not None: if self.arcs: - #print "lin", self.last_line, frame.f_lineno + #print("lin", self.last_line, frame.f_lineno) self.cur_file_data[(self.last_line, frame.f_lineno)] = None else: - #print "lin", frame.f_lineno + #print("lin", frame.f_lineno) self.cur_file_data[frame.f_lineno] = None self.last_line = frame.f_lineno elif event == 'return': @@ -89,8 +104,9 @@ self.cur_file_data[(self.last_line, -first)] = None # Leaving this function, pop the filename stack. self.cur_file_data, self.last_line = self.data_stack.pop() + #print("returned, stack is %d deep" % (len(self.data_stack))) elif event == 'exception': - #print "exc", self.last_line, frame.f_lineno + #print("exc", self.last_line, frame.f_lineno) self.last_exc_back = frame.f_back self.last_exc_firstlineno = frame.f_code.co_firstlineno return self._trace @@ -106,6 +122,13 @@ def stop(self): """Stop this Tracer.""" + if hasattr(sys, "gettrace") and self.warn: + if sys.gettrace() != self._trace: + msg = "Trace function changed, measurement is likely wrong: %r" + self.warn(msg % (sys.gettrace(),)) + #--debug + #from coverage.misc import short_stack + #self.warn(msg % (sys.gettrace()))#, short_stack())) sys.settrace(None) def get_stats(self): @@ -134,7 +157,7 @@ # the top, and resumed when they become the top again. _collectors = [] - def __init__(self, should_trace, timid, branch): + def __init__(self, should_trace, timid, branch, warn): """Create a collector. `should_trace` is a function, taking a filename, and returning a @@ -150,8 +173,12 @@ collecting data on which statements followed each other (arcs). Use `get_arc_data` to get the arc data. + `warn` is a warning function, taking a single string message argument, + to be used if a warning needs to be issued. + """ self.should_trace = should_trace + self.warn = warn self.branch = branch self.reset() @@ -161,7 +188,7 @@ else: # Being fast: use the C Tracer if it is available, else the Python # trace function. - self._trace_class = Tracer or PyTracer + self._trace_class = CTracer or PyTracer def __repr__(self): return "" % id(self) @@ -191,6 +218,7 @@ tracer.arcs = self.branch tracer.should_trace = self.should_trace tracer.should_trace_cache = self.should_trace_cache + tracer.warn = self.warn fn = tracer.start() self.tracers.append(tracer) return fn @@ -219,9 +247,29 @@ if self._collectors: self._collectors[-1].pause() self._collectors.append(self) - #print >>sys.stderr, "Started: %r" % self._collectors + #print("Started: %r" % self._collectors, file=sys.stderr) + + # Check to see whether we had a fullcoverage tracer installed. + traces0 = [] + if hasattr(sys, "gettrace"): + fn0 = sys.gettrace() + if fn0: + tracer0 = getattr(fn0, '__self__', None) + if tracer0: + traces0 = getattr(tracer0, 'traces', []) + # Install the tracer on this thread. - self._start_tracer() + fn = self._start_tracer() + + for args in traces0: + (frame, event, arg), lineno = args + try: + fn(frame, event, arg, lineno=lineno) + except TypeError: + raise Exception( + "fullcoverage must be run with the C trace function." + ) + # Install our installation tracer in threading, to jump start other # threads. threading.settrace(self._installation_trace) diff -Nru python-coverage-3.4/coverage/config.py python-coverage-3.6/coverage/config.py --- python-coverage-3.4/coverage/config.py 2010-08-24 03:14:13.000000000 +0000 +++ python-coverage-3.6/coverage/config.py 2012-12-02 18:40:27.000000000 +0000 @@ -1,7 +1,94 @@ """Config file for coverage.py""" -import os -from coverage.backward import configparser # pylint: disable-msg=W0622 +import os, re, sys +from coverage.backward import string_class, iitems + +# In py3, # ConfigParser was renamed to the more-standard configparser +try: + import configparser # pylint: disable=F0401 +except ImportError: + import ConfigParser as configparser + + +class HandyConfigParser(configparser.RawConfigParser): + """Our specialization of ConfigParser.""" + + def read(self, filename): + """Read a filename as UTF-8 configuration data.""" + kwargs = {} + if sys.version_info >= (3, 2): + kwargs['encoding'] = "utf-8" + return configparser.RawConfigParser.read(self, filename, **kwargs) + + def get(self, *args, **kwargs): + v = configparser.RawConfigParser.get(self, *args, **kwargs) + def dollar_replace(m): + """Called for each $replacement.""" + # Only one of the groups will have matched, just get its text. + word = [w for w in m.groups() if w is not None][0] + if word == "$": + return "$" + else: + return os.environ.get(word, '') + + dollar_pattern = r"""(?x) # Use extended regex syntax + \$(?: # A dollar sign, then + (?P\w+) | # a plain word, + {(?P\w+)} | # or a {-wrapped word, + (?P[$]) # or a dollar sign. + ) + """ + v = re.sub(dollar_pattern, dollar_replace, v) + return v + + def getlist(self, section, option): + """Read a list of strings. + + The value of `section` and `option` is treated as a comma- and newline- + separated list of strings. Each value is stripped of whitespace. + + Returns the list of strings. + + """ + value_list = self.get(section, option) + values = [] + for value_line in value_list.split('\n'): + for value in value_line.split(','): + value = value.strip() + if value: + values.append(value) + return values + + def getlinelist(self, section, option): + """Read a list of full-line strings. + + The value of `section` and `option` is treated as a newline-separated + list of strings. Each value is stripped of whitespace. + + Returns the list of strings. + + """ + value_list = self.get(section, option) + return list(filter(None, value_list.split('\n'))) + + +# The default line exclusion regexes +DEFAULT_EXCLUDE = [ + '(?i)# *pragma[: ]*no *cover', + ] + +# The default partial branch regexes, to be modified by the user. +DEFAULT_PARTIAL = [ + '(?i)# *pragma[: ]*no *branch', + ] + +# The default partial branch regexes, based on Python semantics. +# These are any Python branching constructs that can't actually execute all +# their branches. +DEFAULT_PARTIAL_ALWAYS = [ + 'while (True|1|False|0):', + 'if (True|1|False|0):', + ] class CoverageConfig(object): @@ -11,9 +98,12 @@ operation of coverage.py. """ - def __init__(self): """Initialize the configuration attributes to their defaults.""" + # Metadata about the config. + self.attempted_config_files = [] + self.config_files = [] + # Defaults for [run] self.branch = False self.cover_pylib = False @@ -23,18 +113,26 @@ self.source = None # Defaults for [report] - self.exclude_list = ['(?i)# *pragma[: ]*no *cover'] + self.exclude_list = DEFAULT_EXCLUDE[:] self.ignore_errors = False - self.omit = None self.include = None + self.omit = None + self.partial_list = DEFAULT_PARTIAL[:] + self.partial_always_list = DEFAULT_PARTIAL_ALWAYS[:] self.precision = 0 + self.show_missing = False # Defaults for [html] self.html_dir = "htmlcov" + self.extra_css = None + self.html_title = "Coverage report" # Defaults for [xml] self.xml_output = "coverage.xml" + # Defaults for [paths] + self.paths = {} + def from_environment(self, env_var): """Read configuration from the `env_var` environment variable.""" # Timidity: for nose users, read an environment variable. This is a @@ -44,75 +142,70 @@ if env: self.timid = ('--timid' in env) + MUST_BE_LIST = ["omit", "include"] + def from_args(self, **kwargs): """Read config values from `kwargs`.""" - for k, v in kwargs.items(): + for k, v in iitems(kwargs): if v is not None: + if k in self.MUST_BE_LIST and isinstance(v, string_class): + v = [v] setattr(self, k, v) - def from_file(self, *files): - """Read configuration from .rc files. + def from_file(self, filename): + """Read configuration from a .rc file. - Each argument in `files` is a file name to read. + `filename` is a file name to read. """ - cp = configparser.RawConfigParser() - cp.read(files) + self.attempted_config_files.append(filename) + + cp = HandyConfigParser() + files_read = cp.read(filename) + if files_read is not None: # return value changed in 2.4 + self.config_files.extend(files_read) + + for option_spec in self.CONFIG_FILE_OPTIONS: + self.set_attr_from_config_option(cp, *option_spec) + + # [paths] is special + if cp.has_section('paths'): + for option in cp.options('paths'): + self.paths[option] = cp.getlist('paths', option) + CONFIG_FILE_OPTIONS = [ # [run] - if cp.has_option('run', 'branch'): - self.branch = cp.getboolean('run', 'branch') - if cp.has_option('run', 'cover_pylib'): - self.cover_pylib = cp.getboolean('run', 'cover_pylib') - if cp.has_option('run', 'data_file'): - self.data_file = cp.get('run', 'data_file') - if cp.has_option('run', 'parallel'): - self.parallel = cp.getboolean('run', 'parallel') - if cp.has_option('run', 'timid'): - self.timid = cp.getboolean('run', 'timid') - if cp.has_option('run', 'source'): - self.source = self.get_list(cp, 'run', 'source') - if cp.has_option('run', 'omit'): - self.omit = self.get_list(cp, 'run', 'omit') - if cp.has_option('run', 'include'): - self.include = self.get_list(cp, 'run', 'include') + ('branch', 'run:branch', 'boolean'), + ('cover_pylib', 'run:cover_pylib', 'boolean'), + ('data_file', 'run:data_file'), + ('include', 'run:include', 'list'), + ('omit', 'run:omit', 'list'), + ('parallel', 'run:parallel', 'boolean'), + ('source', 'run:source', 'list'), + ('timid', 'run:timid', 'boolean'), # [report] - if cp.has_option('report', 'exclude_lines'): - # exclude_lines is a list of lines, leave out the blank ones. - exclude_list = cp.get('report', 'exclude_lines') - self.exclude_list = list(filter(None, exclude_list.split('\n'))) - if cp.has_option('report', 'ignore_errors'): - self.ignore_errors = cp.getboolean('report', 'ignore_errors') - if cp.has_option('report', 'omit'): - self.omit = self.get_list(cp, 'report', 'omit') - if cp.has_option('report', 'include'): - self.include = self.get_list(cp, 'report', 'include') - if cp.has_option('report', 'precision'): - self.precision = cp.getint('report', 'precision') + ('exclude_list', 'report:exclude_lines', 'linelist'), + ('ignore_errors', 'report:ignore_errors', 'boolean'), + ('include', 'report:include', 'list'), + ('omit', 'report:omit', 'list'), + ('partial_list', 'report:partial_branches', 'linelist'), + ('partial_always_list', 'report:partial_branches_always', 'linelist'), + ('precision', 'report:precision', 'int'), + ('show_missing', 'report:show_missing', 'boolean'), # [html] - if cp.has_option('html', 'directory'): - self.html_dir = cp.get('html', 'directory') + ('html_dir', 'html:directory'), + ('extra_css', 'html:extra_css'), + ('html_title', 'html:title'), # [xml] - if cp.has_option('xml', 'output'): - self.xml_output = cp.get('xml', 'output') - - def get_list(self, cp, section, option): - """Read a list of strings from the ConfigParser `cp`. - - The value of `section` and `option` is treated as a comma- and newline- - separated list of strings. Each value is stripped of whitespace. - - Returns the list of strings. + ('xml_output', 'xml:output'), + ] - """ - value_list = cp.get(section, option) - values = [] - for value_line in value_list.split('\n'): - for value in value_line.split(','): - value = value.strip() - if value: - values.append(value) - return values + 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_) + setattr(self, attr, method(section, option)) diff -Nru python-coverage-3.4/coverage/control.py python-coverage-3.6/coverage/control.py --- python-coverage-3.4/coverage/control.py 2010-09-12 19:30:57.000000000 +0000 +++ python-coverage-3.6/coverage/control.py 2012-12-29 15:29:19.000000000 +0000 @@ -3,21 +3,22 @@ import atexit, os, random, socket, sys from coverage.annotate import AnnotateReporter -from coverage.backward import string_class +from coverage.backward import string_class, iitems from coverage.codeunit import code_unit_factory, CodeUnit from coverage.collector import Collector from coverage.config import CoverageConfig from coverage.data import CoverageData from coverage.files import FileLocator, TreeMatcher, FnmatchMatcher -from coverage.files import find_python_files +from coverage.files import PathAliases, find_python_files, prep_patterns from coverage.html import HtmlReporter -from coverage.misc import CoverageException, bool_or_none +from coverage.misc import CoverageException, bool_or_none, join_regex +from coverage.misc import file_be_gone from coverage.results import Analysis, Numbers from coverage.summary import SummaryReporter from coverage.xmlreport import XmlReporter class coverage(object): - """Programmatic access to Coverage. + """Programmatic access to coverage.py. To use:: @@ -25,7 +26,7 @@ cov = coverage() cov.start() - #.. blah blah (run your code) blah blah .. + #.. call your code .. cov.stop() cov.html_report(directory='covhtml') @@ -64,11 +65,15 @@ measured. `include` and `omit` are lists of filename patterns. Files that match - `include` will be measured, files that match `omit` will not. + `include` will be measured, files that match `omit` will not. Each + will also accept a single string argument. """ from coverage import __version__ + # A record of all the warnings that have been issued. + self._warnings = [] + # Build our configuration from a number of sources: # 1: defaults: self.config = CoverageConfig() @@ -99,10 +104,11 @@ ) self.auto_data = auto_data - self.atexit_registered = False - self.exclude_re = "" - self._compile_exclude() + # _exclude_re is a dict mapping exclusion list names to compiled + # regexes. + self._exclude_re = {} + self._exclude_regex_stale() self.file_locator = FileLocator() @@ -115,12 +121,12 @@ else: self.source_pkgs.append(src) - self.omit = self._abs_files(self.config.omit) - self.include = self._abs_files(self.config.include) + self.omit = prep_patterns(self.config.omit) + self.include = prep_patterns(self.config.include) self.collector = Collector( self._should_trace, timid=self.config.timid, - branch=self.config.branch + branch=self.config.branch, warn=self._warn ) # Suffixes are a bit tricky. We want to use the data suffix only when @@ -154,7 +160,7 @@ # we've imported, and take all the different ones. for m in (atexit, os, random, socket): if hasattr(m, "__file__"): - m_dir = self._canonical_dir(m.__file__) + m_dir = self._canonical_dir(m) if m_dir not in self.pylib_dirs: self.pylib_dirs.append(m_dir) @@ -167,29 +173,32 @@ self.pylib_match = self.cover_match = None self.include_match = self.omit_match = None - # Only _harvest_data once per measurement cycle. - self._harvested = False - # Set the reporting precision. Numbers.set_precision(self.config.precision) - # When tearing down the coverage object, modules can become None. - # Saving the modules as object attributes avoids problems, but it is - # quite ad-hoc which modules need to be saved and which references - # need to use the object attributes. - self.socket = socket - self.os = os - self.random = random - - def _canonical_dir(self, f): - """Return the canonical directory of the file `f`.""" - return os.path.split(self.file_locator.canonical_filename(f))[0] + # Is it ok for no data to be collected? + self._warn_no_data = True + self._warn_unimported_source = True + + # State machine variables: + # Have we started collecting and not stopped it? + self._started = False + # Have we measured some data and not harvested it? + self._measured = False + + atexit.register(self._atexit) + + def _canonical_dir(self, morf): + """Return the canonical directory of the module or file `morf`.""" + return os.path.split(CodeUnit(morf, self.file_locator).filename)[0] def _source_for_file(self, filename): """Return the source file for `filename`.""" if not filename.endswith(".py"): if filename[-4:-1] == ".py": filename = filename[:-1] + elif filename.endswith("$py.class"): # jython + filename = filename[:-9] + ".py" return filename def _should_trace(self, filename, frame): @@ -202,7 +211,8 @@ should not. """ - if os is None: + if not filename: + # Empty string is pretty useless return False if filename.startswith('<'): @@ -212,33 +222,34 @@ # can't do anything with the data later anyway. return False - if filename.endswith(".html"): - # Jinja and maybe other templating systems compile templates into - # Python code, but use the template filename as the filename in - # the compiled code. Of course, those filenames are useless later - # so don't bother collecting. TODO: How should we really separate - # out good file extensions from bad? - return False - self._check_for_packages() # Compiled Python files have two filenames: frame.f_code.co_filename is - # the filename at the time the .pyc was compiled. The second name - # is __file__, which is where the .pyc was actually loaded from. Since + # the filename at the time the .pyc was compiled. The second name is + # __file__, which is where the .pyc was actually loaded from. Since # .pyc files can be moved after compilation (for example, by being # installed), we look for __file__ in the frame and prefer it to the # co_filename value. dunder_file = frame.f_globals.get('__file__') if dunder_file: filename = self._source_for_file(dunder_file) + + # Jython reports the .class file to the tracer, use the source file. + if filename.endswith("$py.class"): + filename = filename[:-9] + ".py" + canonical = self.file_locator.canonical_filename(filename) - # If the user specified source, then that's authoritative about what to - # measure. If they didn't, then we have to exclude the stdlib and - # coverage.py directories. + # If the user specified source or include, then that's authoritative + # about the outer bound of what to measure and we don't have to apply + # any canned exclusions. If they didn't, then we have to exclude the + # stdlib and coverage.py directories. if self.source_match: if not self.source_match.match(canonical): return False + elif self.include_match: + if not self.include_match.match(canonical): + return False else: # If we aren't supposed to trace installed code, then check if this # is near the Python standard library and skip it if so. @@ -250,9 +261,7 @@ if self.cover_match and self.cover_match.match(canonical): return False - # Check the file against the include and omit patterns. - if self.include_match and not self.include_match.match(canonical): - return False + # Check the file against the omit pattern. if self.omit_match and self.omit_match.match(canonical): return False @@ -261,7 +270,7 @@ # To log what should_trace returns, change this to "if 1:" if 0: _real_should_trace = _should_trace - def _should_trace(self, filename, frame): # pylint: disable-msg=E0102 + def _should_trace(self, filename, frame): # pylint: disable=E0102 """A logging decorator around the real _should_trace function.""" ret = self._real_should_trace(filename, frame) print("should_trace: %r -> %r" % (filename, ret)) @@ -269,12 +278,8 @@ def _warn(self, msg): """Use `msg` as a warning.""" - sys.stderr.write("Coverage.py warning: " + msg + "\n") - - def _abs_files(self, files): - """Return a list of absolute file names for the names in `files`.""" - files = files or [] - return [self.file_locator.abs_file(f) for f in files] + self._warnings.append(msg) + sys.stderr.write("Coverage.py warning: %s\n" % msg) def _check_for_packages(self): """Update the source_match matcher with latest imported packages.""" @@ -295,17 +300,23 @@ try: pkg_file = mod.__file__ except AttributeError: - self._warn("Module %s has no python source." % pkg) + pkg_file = None else: d, f = os.path.split(pkg_file) - if f.startswith('__init__.'): + if f.startswith('__init__'): # This is actually a package, return the directory. pkg_file = d else: pkg_file = self._source_for_file(pkg_file) pkg_file = self.file_locator.canonical_filename(pkg_file) + if not os.path.exists(pkg_file): + pkg_file = None + + if pkg_file: self.source.append(pkg_file) self.source_match.add(pkg_file) + else: + self._warn("Module %s has no Python source." % pkg) for pkg in found: self.source_pkgs.remove(pkg) @@ -324,17 +335,21 @@ self.data.read() def start(self): - """Start measuring code coverage.""" + """Start measuring code coverage. + + Coverage measurement actually occurs in functions called after `start` + is invoked. Statements in the same scope as `start` won't be measured. + + Once you invoke `start`, you must also call `stop` eventually, or your + process might not shut down cleanly. + + """ if self.run_suffix: # Calling start() means we're running code, so use the run_suffix # as the data_suffix when we eventually save the data. self.data_suffix = self.run_suffix if self.auto_data: self.load() - # Save coverage data when Python exits. - if not self.atexit_registered: - atexit.register(self.save) - self.atexit_registered = True # Create the matchers we need for _should_trace if self.source or self.source_pkgs: @@ -349,13 +364,21 @@ if self.omit: self.omit_match = FnmatchMatcher(self.omit) - self._harvested = False self.collector.start() + self._started = True + self._measured = True def stop(self): """Stop measuring code coverage.""" + self._started = False self.collector.stop() - self._harvest_data() + + def _atexit(self): + """Clean up on process shutdown.""" + if self._started: + self.stop() + if self.auto_data: + self.save() def erase(self): """Erase previously-collected coverage data. @@ -367,30 +390,49 @@ self.collector.reset() self.data.erase() - def clear_exclude(self): + def clear_exclude(self, which='exclude'): """Clear the exclude list.""" - self.config.exclude_list = [] - self.exclude_re = "" + setattr(self.config, which + "_list", []) + self._exclude_regex_stale() - def exclude(self, regex): + def exclude(self, regex, which='exclude'): """Exclude source lines from execution consideration. - `regex` is a regular expression. Lines matching this expression are - not considered executable when reporting code coverage. A list of - regexes is maintained; this function adds a new regex to the list. - Matching any of the regexes excludes a source line. - - """ - self.config.exclude_list.append(regex) - self._compile_exclude() - - def _compile_exclude(self): - """Build the internal usable form of the exclude list.""" - self.exclude_re = "(" + ")|(".join(self.config.exclude_list) + ")" - - def get_exclude_list(self): - """Return the list of excluded regex patterns.""" - return self.config.exclude_list + A number of lists of regular expressions are maintained. Each list + selects lines that are treated differently during reporting. + + `which` determines which list is modified. The "exclude" list selects + lines that are not considered executable at all. The "partial" list + indicates lines with branches that are not taken. + + `regex` is a regular expression. The regex is added to the specified + list. If any of the regexes in the list is found in a line, the line + is marked for special treatment during reporting. + + """ + excl_list = getattr(self.config, which + "_list") + excl_list.append(regex) + self._exclude_regex_stale() + + def _exclude_regex_stale(self): + """Drop all the compiled exclusion regexes, a list was modified.""" + self._exclude_re.clear() + + def _exclude_regex(self, which): + """Return a compiled regex for the given exclusion list.""" + if which not in self._exclude_re: + excl_list = getattr(self.config, which + "_list") + self._exclude_re[which] = join_regex(excl_list) + return self._exclude_re[which] + + def get_exclude_list(self, which='exclude'): + """Return a list of excluded regex patterns. + + `which` indicates which list is desired. See `exclude` for the lists + that are available, and their meaning. + + """ + return getattr(self.config, which + "_list") def save(self): """Save the collected coverage data to the data file.""" @@ -400,9 +442,15 @@ # 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. - data_suffix = "%s.%s.%06d" % ( - self.socket.gethostname(), self.os.getpid(), - self.random.randint(0, 99999) + extra = "" + if _TEST_NAME_FILE: + f = open(_TEST_NAME_FILE) + test_name = f.read() + f.close() + extra = "." + test_name + data_suffix = "%s%s.%s.%06d" % ( + socket.gethostname(), extra, os.getpid(), + random.randint(0, 999999) ) self._harvest_data() @@ -416,7 +464,14 @@ current measurements. """ - self.data.combine_parallel_data() + aliases = None + if self.config.paths: + aliases = PathAliases(self.file_locator) + 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): """Get the collected data and reset the collector. @@ -424,27 +479,29 @@ Also warn about various problems collecting data. """ - if not self._harvested: + if self._measured: self.data.add_line_data(self.collector.get_line_data()) self.data.add_arc_data(self.collector.get_arc_data()) self.collector.reset() # If there are still entries in the source_pkgs list, then we never # encountered those packages. - for pkg in self.source_pkgs: - self._warn("Source module %s was never encountered." % pkg) + if self._warn_unimported_source: + for pkg in self.source_pkgs: + self._warn("Module %s was never imported." % pkg) # Find out if we got any data. summary = self.data.summary() - if not summary: + if not summary 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) self.data.touch_file(py_file) - self._harvested = True + self._measured = False # Backward compatibility with version 1. def analysis(self, morf): @@ -481,13 +538,14 @@ Returns an `Analysis` object. """ + self._harvest_data() if not isinstance(it, CodeUnit): it = code_unit_factory(it, self.file_locator)[0] return Analysis(self, it) def report(self, morfs=None, show_missing=True, ignore_errors=None, - file=None, # pylint: disable-msg=W0622 + file=None, # pylint: disable=W0622 omit=None, include=None ): """Write a summary report to `file`. @@ -499,14 +557,16 @@ match those patterns will be included in the report. Modules matching `omit` will not be included in the report. + Returns a float, the total percentage covered. + """ + self._harvest_data() self.config.from_args( - ignore_errors=ignore_errors, omit=omit, include=include - ) - reporter = SummaryReporter( - self, show_missing, self.config.ignore_errors + ignore_errors=ignore_errors, omit=omit, include=include, + show_missing=show_missing, ) - reporter.report(morfs, outfile=file, config=self.config) + reporter = SummaryReporter(self, self.config) + return reporter.report(morfs, outfile=file) def annotate(self, morfs=None, directory=None, ignore_errors=None, omit=None, include=None): @@ -520,25 +580,39 @@ See `coverage.report()` for other arguments. """ + self._harvest_data() self.config.from_args( ignore_errors=ignore_errors, omit=omit, include=include ) - reporter = AnnotateReporter(self, self.config.ignore_errors) - reporter.report(morfs, config=self.config, directory=directory) + reporter = AnnotateReporter(self, self.config) + reporter.report(morfs, directory=directory) def html_report(self, morfs=None, directory=None, ignore_errors=None, - omit=None, include=None): + omit=None, include=None, extra_css=None, title=None): """Generate an HTML report. + The HTML is written to `directory`. The file "index.html" is the + overview starting point, with links to more detailed pages for + individual modules. + + `extra_css` is a path to a file of other CSS to apply on the page. + It will be copied into the HTML directory. + + `title` is a text string (not HTML) to use as the title of the HTML + report. + See `coverage.report()` for other arguments. + Returns a float, the total percentage covered. + """ + self._harvest_data() self.config.from_args( ignore_errors=ignore_errors, omit=omit, include=include, - html_dir=directory, + html_dir=directory, extra_css=extra_css, html_title=title, ) - reporter = HtmlReporter(self, self.config.ignore_errors) - reporter.report(morfs, config=self.config) + reporter = HtmlReporter(self, self.config) + return reporter.report(morfs) def xml_report(self, morfs=None, outfile=None, ignore_errors=None, omit=None, include=None): @@ -551,12 +625,16 @@ See `coverage.report()` for other arguments. + Returns a float, the total percentage covered. + """ + self._harvest_data() self.config.from_args( ignore_errors=ignore_errors, omit=omit, include=include, xml_output=outfile, ) file_to_close = None + delete_file = False if self.config.xml_output: if self.config.xml_output == '-': outfile = sys.stdout @@ -564,11 +642,17 @@ outfile = open(self.config.xml_output, "w") file_to_close = outfile try: - reporter = XmlReporter(self, self.config.ignore_errors) - reporter.report(morfs, outfile=outfile, config=self.config) + try: + reporter = XmlReporter(self, self.config) + return reporter.report(morfs, outfile=outfile) + except CoverageException: + delete_file = True + raise finally: if file_to_close: file_to_close.close() + if delete_file: + file_be_gone(self.config.xml_output) def sysinfo(self): """Return a list of (key, value) pairs showing internal information.""" @@ -576,20 +660,29 @@ import coverage as covmod import platform, re + try: + implementation = platform.python_implementation() + except AttributeError: + implementation = "unknown" + info = [ ('version', covmod.__version__), ('coverage', covmod.__file__), ('cover_dir', self.cover_dir), ('pylib_dirs', self.pylib_dirs), ('tracer', self.collector.tracer_name()), + ('config_files', self.config.attempted_config_files), + ('configs_read', self.config.config_files), ('data_path', self.data.filename), ('python', sys.version.replace('\n', '')), ('platform', platform.platform()), + ('implementation', implementation), + ('executable', sys.executable), ('cwd', os.getcwd()), ('path', sys.path), ('environment', [ - ("%s = %s" % (k, v)) for k, v in os.environ.items() - if re.search("^COV|^PY", k) + ("%s = %s" % (k, v)) for k, v in iitems(os.environ) + if re.search(r"^COV|^PY", k) ]), ] return info @@ -618,7 +711,10 @@ cps = os.environ.get("COVERAGE_PROCESS_START") if cps: cov = coverage(config_file=cps, auto_data=True) - if os.environ.get("COVERAGE_COVERAGE"): - # Measuring coverage within coverage.py takes yet more trickery. - cov.cover_dir = "Please measure coverage.py!" cov.start() + cov._warn_no_data = False + cov._warn_unimported_source = False + + +# A hack for debugging testing in subprocesses. +_TEST_NAME_FILE = "" #"/tmp/covtest.txt" diff -Nru python-coverage-3.4/coverage/data.py python-coverage-3.6/coverage/data.py --- python-coverage-3.4/coverage/data.py 2010-09-04 03:17:49.000000000 +0000 +++ python-coverage-3.6/coverage/data.py 2012-11-17 01:25:13.000000000 +0000 @@ -2,7 +2,9 @@ import os -from coverage.backward import pickle, sorted # pylint: disable-msg=W0622 +from coverage.backward import iitems, pickle, sorted # pylint: disable=W0622 +from coverage.files import PathAliases +from coverage.misc import file_be_gone class CoverageData(object): @@ -59,10 +61,6 @@ # self.arcs = {} - self.os = os - self.sorted = sorted - self.pickle = pickle - def usefile(self, use_file=True): """Set whether or not to use a disk file for data.""" self.use_file = use_file @@ -92,21 +90,21 @@ def erase(self): """Erase the data, both in this object, and from its file storage.""" if self.use_file: - if self.filename and os.path.exists(self.filename): - os.remove(self.filename) + if self.filename: + file_be_gone(self.filename) self.lines = {} self.arcs = {} def line_data(self): """Return the map from filenames to lists of line numbers executed.""" return dict( - [(f, self.sorted(lmap.keys())) for f, lmap in self.lines.items()] + [(f, sorted(lmap.keys())) for f, lmap in iitems(self.lines)] ) def arc_data(self): """Return the map from filenames to lists of line number pairs.""" return dict( - [(f, self.sorted(amap.keys())) for f, amap in self.arcs.items()] + [(f, sorted(amap.keys())) for f, amap in iitems(self.arcs)] ) def write_file(self, filename): @@ -126,7 +124,7 @@ # Write the pickle to the file. fdata = open(filename, 'wb') try: - self.pickle.dump(data, fdata, 2) + pickle.dump(data, fdata, 2) finally: fdata.close() @@ -158,33 +156,39 @@ # Unpack the 'lines' item. lines = dict([ (f, dict.fromkeys(linenos, None)) - for f, linenos in data.get('lines', {}).items() + for f, linenos in iitems(data.get('lines', {})) ]) # Unpack the 'arcs' item. arcs = dict([ (f, dict.fromkeys(arcpairs, None)) - for f, arcpairs in data.get('arcs', {}).items() + for f, arcpairs in iitems(data.get('arcs', {})) ]) except Exception: pass return lines, arcs - def combine_parallel_data(self): + def combine_parallel_data(self, aliases=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. + """ + 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 = self._read_file(full_path) - for filename, file_data in new_lines.items(): + for filename, file_data in iitems(new_lines): + filename = aliases.map(filename) self.lines.setdefault(filename, {}).update(file_data) - for filename, file_data in new_arcs.items(): + for filename, file_data in iitems(new_arcs): + filename = aliases.map(filename) self.arcs.setdefault(filename, {}).update(file_data) if f != local: os.remove(full_path) @@ -195,7 +199,7 @@ `line_data` is { filename: { lineno: None, ... }, ...} """ - for filename, linenos in line_data.items(): + for filename, linenos in iitems(line_data): self.lines.setdefault(filename, {}).update(linenos) def add_arc_data(self, arc_data): @@ -204,7 +208,7 @@ `arc_data` is { filename: { (l1,l2): None, ... }, ...} """ - for filename, arcs in arc_data.items(): + for filename, arcs in iitems(arc_data): self.arcs.setdefault(filename, {}).update(arcs) def touch_file(self, filename): @@ -228,6 +232,11 @@ """A map containing all the arcs executed in `filename`.""" return self.arcs.get(filename) or {} + 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)) + def summary(self, fullpath=False): """Return a dict summarizing the coverage data. @@ -240,8 +249,8 @@ if fullpath: filename_fn = lambda f: f else: - filename_fn = self.os.path.basename - for filename, lines in self.lines.items(): + filename_fn = os.path.basename + for filename, lines in iitems(self.lines): summ[filename_fn(filename)] = len(lines) return summ diff -Nru python-coverage-3.4/coverage/execfile.py python-coverage-3.6/coverage/execfile.py --- python-coverage-3.4/coverage/execfile.py 2010-05-30 04:26:50.000000000 +0000 +++ python-coverage-3.6/coverage/execfile.py 2012-11-11 18:26:06.000000000 +0000 @@ -2,7 +2,7 @@ import imp, os, sys -from coverage.backward import exec_code_object +from coverage.backward import exec_code_object, open_source from coverage.misc import NoSource, ExceptionDuringRun @@ -14,12 +14,68 @@ BUILTINS = sys.modules['builtins'] -def run_python_file(filename, args): +def rsplit1(s, sep): + """The same as s.rsplit(sep, 1), but works in 2.3""" + parts = s.split(sep) + return sep.join(parts[:-1]), parts[-1] + + +def run_python_module(modulename, args): + """Run a python module, as though with ``python -m name args...``. + + `modulename` is the name of the module, possibly a dot-separated name. + `args` is the argument array to present as sys.argv, including the first + element naming the module being executed. + + """ + openfile = None + glo, loc = globals(), locals() + try: + try: + # Search for the module - inside its parent package, if any - using + # standard import mechanics. + if '.' in modulename: + packagename, name = rsplit1(modulename, '.') + package = __import__(packagename, glo, loc, ['__path__']) + searchpath = package.__path__ + else: + packagename, name = None, modulename + searchpath = None # "top-level search" in imp.find_module() + openfile, pathname, _ = imp.find_module(name, searchpath) + + # Complain if this is a magic non-file module. + if openfile is None and pathname is None: + raise NoSource( + "module does not live in a file: %r" % modulename + ) + + # If `modulename` is actually a package, not a mere module, then we + # pretend to be Python 2.7 and try running its __main__.py script. + if openfile is None: + packagename = modulename + name = '__main__' + package = __import__(packagename, glo, loc, ['__path__']) + searchpath = package.__path__ + openfile, pathname, _ = imp.find_module(name, searchpath) + except ImportError: + _, err, _ = sys.exc_info() + raise NoSource(str(err)) + finally: + if openfile: + openfile.close() + + # Finally, hand the file off to run_python_file for execution. + args[0] = pathname + run_python_file(pathname, args, package=packagename) + + +def run_python_file(filename, args, package=None): """Run a python file as if it were the main program on the command line. `filename` is the path to the file to execute, it need not be a .py file. `args` is the argument array to present as sys.argv, including the first - element representing the file being executed. + element naming the file being executed. `package` is the name of the + enclosing package, if any. """ # Create a module to serve as __main__ @@ -27,24 +83,34 @@ main_mod = imp.new_module('__main__') sys.modules['__main__'] = main_mod main_mod.__file__ = filename + if package: + main_mod.__package__ = package main_mod.__builtins__ = BUILTINS # Set sys.argv and the first path element properly. old_argv = sys.argv old_path0 = sys.path[0] sys.argv = args - sys.path[0] = os.path.dirname(filename) + if package: + sys.path[0] = '' + else: + sys.path[0] = os.path.abspath(os.path.dirname(filename)) try: # Open the source file. try: - source = open(filename, 'rU').read() + source_file = open_source(filename) except IOError: raise NoSource("No file to run: %r" % filename) + try: + source = source_file.read() + finally: + source_file.close() + # We have the source. `compile` still needs the last line to be clean, # so make sure it is, then compile a code object from it. - if source[-1] != '\n': + if not source or source[-1] != '\n': source += '\n' code = compile(source, filename, "exec") diff -Nru python-coverage-3.4/coverage/files.py python-coverage-3.6/coverage/files.py --- python-coverage-3.4/coverage/files.py 2010-08-31 02:48:29.000000000 +0000 +++ python-coverage-3.6/coverage/files.py 2012-11-24 02:55:37.000000000 +0000 @@ -1,22 +1,20 @@ """File wrangling.""" -import fnmatch, os, sys +from coverage.backward import to_string +from coverage.misc import CoverageException +import fnmatch, os, os.path, re, sys class FileLocator(object): """Understand how filenames work.""" def __init__(self): # The absolute path to our current directory. - self.relative_dir = self.abs_file(os.curdir) + os.sep + 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 abs_file(self, filename): - """Return the absolute normalized form of `filename`.""" - return os.path.normcase(os.path.abspath(os.path.realpath(filename))) - def relative_filename(self, filename): """Return the relative form of `filename`. @@ -24,8 +22,9 @@ `FileLocator` was constructed. """ - if filename.startswith(self.relative_dir): - filename = filename.replace(self.relative_dir, "") + fnorm = os.path.normcase(filename) + if fnorm.startswith(self.relative_dir): + filename = filename[len(self.relative_dir):] return filename def canonical_filename(self, filename): @@ -35,19 +34,15 @@ """ if filename not in self.canonical_filename_cache: - f = filename - if os.path.isabs(f) and not os.path.exists(f): - if self.get_zip_data(f) is None: - f = os.path.basename(f) - if not os.path.isabs(f): + if not os.path.isabs(filename): for path in [os.curdir] + sys.path: if path is None: continue - g = os.path.join(path, f) - if os.path.exists(g): - f = g + f = os.path.join(path, filename) + if os.path.exists(f): + filename = f break - cf = self.abs_file(f) + cf = abs_file(filename) self.canonical_filename_cache[filename] = cf return self.canonical_filename_cache[filename] @@ -72,12 +67,76 @@ data = zi.get_data(parts[1]) except IOError: continue - if sys.version_info >= (3, 0): - data = data.decode('utf8') # TODO: How to do this properly? - return data + return to_string(data) return None +if sys.platform == 'win32': + + 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] + + head, tail = os.path.split(path) + if not tail: + actpath = head + elif not head: + actpath = tail + else: + head = actual_path(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 + 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 + return actpath + + actual_path.cache = {} + actual_path.list_cache = {} + +else: + def actual_path(filename): + """The actual path for non-Windows platforms.""" + return filename + +def abs_file(filename): + """Return the absolute normalized form of `filename`.""" + path = os.path.abspath(os.path.realpath(filename)) + path = actual_path(path) + return path + + +def prep_patterns(patterns): + """Prepare the file patterns for use in a `FnmatchMatcher`. + + If a pattern starts with a wildcard, it is used as a pattern + as-is. If it does not start with a wildcard, then it is made + absolute with the current directory. + + If `patterns` is None, an empty list is returned. + + """ + patterns = patterns or [] + prepped = [] + for p in patterns or []: + if p.startswith("*") or p.startswith("?"): + prepped.append(p) + else: + prepped.append(abs_file(p)) + return prepped + + class TreeMatcher(object): """A matcher for files in a tree.""" def __init__(self, directories): @@ -119,14 +178,112 @@ return False +def sep(s): + """Find the path separator used in this string, or os.sep if none.""" + sep_match = re.search(r"[\\/]", s) + if sep_match: + the_sep = sep_match.group(0) + else: + the_sep = os.sep + return the_sep + + +class PathAliases(object): + """A collection of aliases for paths. + + When combining data files from remote machines, often the paths to source + code are different, for example, due to OS differences, or because of + serialized checkouts on continuous integration machines. + + 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): + self.aliases = [] + self.locator = locator + + def add(self, pattern, result): + """Add the `pattern`/`result` pair to the list of aliases. + + `pattern` is an `fnmatch`-style pattern. `result` is a simple + string. When mapping paths, if a path starts with a match against + `pattern`, then that match is replaced with `result`. This models + isomorphic source trees being rooted at different places on two + different machines. + + `pattern` can't end with a wildcard component, since that would + match an entire tree, and not just its root. + + """ + # The pattern can't end with a wildcard component. + pattern = pattern.rstrip(r"\/") + if pattern.endswith("*"): + raise CoverageException("Pattern must not end with wildcards.") + pattern_sep = sep(pattern) + pattern += pattern_sep + + # Make a regex from the pattern. fnmatch always adds a \Z or $ to + # match the whole string, which we don't want. + regex_pat = fnmatch.translate(pattern).replace(r'\Z(', '(') + if regex_pat.endswith("$"): + regex_pat = regex_pat[:-1] + # We want */a/b.py to match on Windows to, so change slash to match + # either separator. + regex_pat = regex_pat.replace(r"\/", r"[\\/]") + # We want case-insensitive matching, so add that flag. + regex = re.compile(r"(?i)" + regex_pat) + + # Normalize the result: it must end with a path separator. + result_sep = sep(result) + result = result.rstrip(r"\/") + result_sep + self.aliases.append((regex, result, pattern_sep, result_sep)) + + def map(self, path): + """Map `path` through the aliases. + + `path` is checked against all of the patterns. The first pattern to + match is used to replace the root of the path with the result root. + Only one pattern is ever used. If no patterns match, `path` is + returned unchanged. + + The separator style in the result is made to match that of the result + in the alias. + + """ + for regex, result, pattern_sep, result_sep in self.aliases: + m = regex.match(path) + if m: + 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) + return new + return path + + def find_python_files(dirname): - """Yield all of the importable Python files in `dirname`, recursively.""" - for dirpath, dirnames, filenames in os.walk(dirname, topdown=True): - if '__init__.py' not in filenames: + """Yield all of the importable Python files in `dirname`, recursively. + + To be importable, the files have to be in a directory with a __init__.py, + except for `dirname` itself, which isn't required to have one. The + assumption is that `dirname` was specified directly, so the user knows + best, but subdirectories are checked for a __init__.py to be sure we only + find the importable files. + + """ + for i, (dirpath, dirnames, filenames) in enumerate(os.walk(dirname)): + if i > 0 and '__init__.py' not in filenames: # If a directory doesn't have __init__.py, then it isn't # importable and neither are its files del dirnames[:] continue for filename in filenames: - if fnmatch.fnmatch(filename, "*.py"): + # We're only interested in files that look like reasonable Python + # files: Must end with .py, and must not have certain funny + # characters that probably mean they are editor junk. + if re.match(r"^[^.#~!$@%^&*()+=,]+\.py$", filename): yield os.path.join(dirpath, filename) diff -Nru python-coverage-3.4/coverage/fullcoverage/encodings.py python-coverage-3.6/coverage/fullcoverage/encodings.py --- python-coverage-3.4/coverage/fullcoverage/encodings.py 1970-01-01 00:00:00.000000000 +0000 +++ python-coverage-3.6/coverage/fullcoverage/encodings.py 2012-11-11 04:52:58.000000000 +0000 @@ -0,0 +1,57 @@ +"""Imposter encodings module that installs a coverage-style tracer. + +This is NOT the encodings module; it is an imposter that sets up tracing +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 +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. + +""" + +import sys + +class FullCoverageTracer(object): + def __init__(self): + # `traces` is a list of trace events. Frames are tricky: the same + # frame object is used for a whole scope, with new line numbers + # written into it. So in one scope, all the frame objects are the + # same object, and will eventually all will point to the last line + # executed. So we keep the line numbers alongside the frames. + # The list looks like: + # + # traces = [ + # ((frame, event, arg), lineno), ... + # ] + # + self.traces = [] + + def fullcoverage_trace(self, *args): + frame, event, arg = args + self.traces.append((args, frame.f_lineno)) + return self.fullcoverage_trace + +sys.settrace(FullCoverageTracer().fullcoverage_trace) + +# In coverage/files.py is actual_filename(), which uses glob.glob. I don't +# understand why, but that use of glob borks everything if fullcoverage is in +# effect. So here we make an ugly hail-mary pass to switch off glob.glob over +# there. This means when using fullcoverage, Windows path names will not be +# their actual case. + +#sys.fullcoverage = True + +# Finally, remove our own directory from sys.path; remove ourselves from +# sys.modules; and re-import "encodings", which will be the real package +# this time. Note that the delete from sys.modules dictionary has to +# happen last, since all of the symbols in this module will become None +# at that exact moment, including "sys". + +parentdir = max(filter(__file__.startswith, sys.path), key=len) +sys.path.remove(parentdir) +del sys.modules['encodings'] +import encodings diff -Nru python-coverage-3.4/coverage/html.py python-coverage-3.6/coverage/html.py --- python-coverage-3.4/coverage/html.py 2010-09-08 15:47:48.000000000 +0000 +++ python-coverage-3.6/coverage/html.py 2012-11-19 23:44:27.000000000 +0000 @@ -1,16 +1,18 @@ """HTML reporting for Coverage.""" -import os, re, shutil +import os, re, shutil, sys -from coverage import __url__, __version__ # pylint: disable-msg=W0611 -from coverage.misc import CoverageException -from coverage.phystokens import source_token_lines +import coverage +from coverage.backward import pickle +from coverage.misc import CoverageException, Hasher +from coverage.phystokens import source_token_lines, source_encoding from coverage.report import Reporter +from coverage.results import Numbers from coverage.templite import Templite # Disable pylint msg W0612, because a bunch of variables look unused, but # they're accessed in a Templite context via locals(). -# pylint: disable-msg=W0612 +# pylint: disable=W0612 def data_filename(fname): """Return the path to a data file of ours.""" @@ -18,31 +20,74 @@ def data(fname): """Return the contents of a data file of ours.""" - return open(data_filename(fname)).read() + data_file = open(data_filename(fname)) + try: + return data_file.read() + finally: + data_file.close() class HtmlReporter(Reporter): """HTML reporting.""" - def __init__(self, coverage, ignore_errors=False): - super(HtmlReporter, self).__init__(coverage, ignore_errors) + # These files will be copied from the htmlfiles dir to the output dir. + STATIC_FILES = [ + "style.css", + "jquery-1.4.3.min.js", + "jquery.hotkeys.js", + "jquery.isonscreen.js", + "jquery.tablesorter.min.js", + "coverage_html.js", + "keybd_closed.png", + "keybd_open.png", + ] + + def __init__(self, cov, config): + super(HtmlReporter, self).__init__(cov, config) self.directory = None - self.source_tmpl = Templite(data("htmlfiles/pyfile.html"), globals()) + self.template_globals = { + 'escape': escape, + 'title': self.config.html_title, + '__url__': coverage.__url__, + '__version__': coverage.__version__, + } + self.source_tmpl = Templite( + data("htmlfiles/pyfile.html"), self.template_globals + ) + + self.coverage = cov self.files = [] - self.arcs = coverage.data.has_arcs() + self.arcs = self.coverage.data.has_arcs() + self.status = HtmlStatus() + self.extra_css = None + self.totals = Numbers() - def report(self, morfs, config=None): + def report(self, morfs): """Generate an HTML report for `morfs`. - `morfs` is a list of modules or filenames. `config` is a - CoverageConfig instance. + `morfs` is a list of modules or filenames. """ - assert config.html_dir, "must provide a directory for html reporting" + assert self.config.html_dir, "must give a directory for html reporting" + + # Read the status data. + self.status.read(self.config.html_dir) + + # Check that this run used the same settings as the last run. + m = Hasher() + m.update(self.config) + these_settings = m.digest() + if self.status.settings_hash() != these_settings: + self.status.reset() + self.status.set_settings_hash(these_settings) + + # The user may have extra CSS they want copied. + if self.config.extra_css: + self.extra_css = os.path.basename(self.config.extra_css) # Process all the files. - self.report_files(self.html_file, morfs, config, config.html_dir) + self.report_files(self.html_file, morfs, self.config.html_dir) if not self.files: raise CoverageException("No data to report.") @@ -50,25 +95,73 @@ # Write the index file. self.index_file() - # Create the once-per-directory files. - for static in [ - "style.css", "coverage_html.js", - "jquery-1.3.2.min.js", "jquery.tablesorter.min.js" - ]: + self.make_local_static_report_files() + + return self.totals.pc_covered + + def make_local_static_report_files(self): + """Make local instances of static files for HTML report.""" + # The files we provide must always be copied. + for static in self.STATIC_FILES: shutil.copyfile( data_filename("htmlfiles/" + static), os.path.join(self.directory, static) ) + # The user may have extra CSS they want copied. + if self.extra_css: + shutil.copyfile( + self.config.extra_css, + os.path.join(self.directory, self.extra_css) + ) + + def write_html(self, fname, html): + """Write `html` to `fname`, properly encoded.""" + fout = open(fname, "wb") + try: + fout.write(html.encode('ascii', 'xmlcharrefreplace')) + finally: + fout.close() + + def file_hash(self, source, cu): + """Compute a hash that changes if the file needs to be re-reported.""" + m = Hasher() + m.update(source) + self.coverage.data.add_to_hash(cu.filename, m) + return m.digest() + def html_file(self, cu, analysis): """Generate an HTML file for one source file.""" + source_file = cu.source_file() + try: + source = source_file.read() + finally: + source_file.close() + + # Find out if the file on disk is already correct. + flat_rootname = cu.flat_rootname() + this_hash = self.file_hash(source, cu) + that_hash = self.status.file_hash(flat_rootname) + if this_hash == that_hash: + # Nothing has changed to require the file to be reported again. + self.files.append(self.status.index_info(flat_rootname)) + return + + self.status.set_file_hash(flat_rootname, this_hash) + + # If need be, determine the encoding of the source file. We use it + # later to properly write the HTML. + if sys.version_info < (3, 0): + encoding = source_encoding(source) + # Some UTF8 files have the dreaded UTF8 BOM. If so, junk it. + if encoding.startswith("utf-8") and source[:3] == "\xef\xbb\xbf": + source = source[3:] + encoding = "utf-8" - source = cu.source_file().read() - + # Get the numbers for this file. nums = analysis.numbers missing_branch_arcs = analysis.missing_branch_arcs() - n_par = 0 # accumulated below. arcs = self.arcs # These classes determine which lines are highlighted by default. @@ -93,7 +186,6 @@ line_class.append(c_mis) elif self.arcs and lineno in missing_branch_arcs: line_class.append(c_par) - n_par += 1 annlines = [] for b in missing_branch_arcs[lineno]: if b < 0: @@ -128,33 +220,125 @@ }) # Write the HTML page for this file. - html_filename = cu.flat_rootname() + ".html" + html_filename = flat_rootname + ".html" html_path = os.path.join(self.directory, html_filename) + extra_css = self.extra_css + html = spaceless(self.source_tmpl.render(locals())) - fhtml = open(html_path, 'w') - fhtml.write(html) - fhtml.close() + if sys.version_info < (3, 0): + html = html.decode(encoding) + self.write_html(html_path, html) # Save this file's information for the index file. - self.files.append({ + index_info = { 'nums': nums, - 'par': n_par, 'html_filename': html_filename, - 'cu': cu, - }) + 'name': cu.name, + } + self.files.append(index_info) + self.status.set_index_info(flat_rootname, index_info) def index_file(self): """Write the index.html file for this report.""" - index_tmpl = Templite(data("htmlfiles/index.html"), globals()) + index_tmpl = Templite( + data("htmlfiles/index.html"), self.template_globals + ) files = self.files arcs = self.arcs - totals = sum([f['nums'] for f in files]) + self.totals = totals = sum([f['nums'] for f in files]) + extra_css = self.extra_css - fhtml = open(os.path.join(self.directory, "index.html"), "w") - fhtml.write(index_tmpl.render(locals())) - fhtml.close() + html = index_tmpl.render(locals()) + if sys.version_info < (3, 0): + html = html.decode("utf-8") + self.write_html( + os.path.join(self.directory, "index.html"), + html + ) + + # Write the latest hashes for next time. + self.status.write(self.directory) + + +class HtmlStatus(object): + """The status information we keep to support incremental reporting.""" + + STATUS_FILE = "status.dat" + STATUS_FORMAT = 1 + + def __init__(self): + self.reset() + + def reset(self): + """Initialize to empty.""" + self.settings = '' + self.files = {} + + def read(self, directory): + """Read the last status in `directory`.""" + usable = False + try: + status_file = os.path.join(directory, self.STATUS_FILE) + fstatus = open(status_file, "rb") + try: + status = pickle.load(fstatus) + finally: + fstatus.close() + except (IOError, ValueError): + usable = False + else: + usable = True + if status['format'] != self.STATUS_FORMAT: + usable = False + elif status['version'] != coverage.__version__: + usable = False + + if usable: + self.files = status['files'] + self.settings = status['settings'] + else: + self.reset() + + def write(self, directory): + """Write the current status to `directory`.""" + status_file = os.path.join(directory, self.STATUS_FILE) + status = { + 'format': self.STATUS_FORMAT, + 'version': coverage.__version__, + 'settings': self.settings, + 'files': self.files, + } + fout = open(status_file, "wb") + try: + pickle.dump(status, fout) + finally: + fout.close() + + def settings_hash(self): + """Get the hash of the coverage.py settings.""" + return self.settings + + def set_settings_hash(self, settings): + """Set the hash of the coverage.py settings.""" + self.settings = settings + + def file_hash(self, fname): + """Get the hash of `fname`'s contents.""" + return self.files.get(fname, {}).get('hash', '') + + def set_file_hash(self, fname, val): + """Set the hash of `fname`'s contents.""" + self.files.setdefault(fname, {})['hash'] = val + + def index_info(self, fname): + """Get the information for index.html for `fname`.""" + return self.files.get(fname, {}).get('index', {}) + + def set_index_info(self, fname, info): + """Set the information for index.html for `fname`.""" + self.files.setdefault(fname, {})['index'] = info # Helpers for templates and generating HTML @@ -179,5 +363,5 @@ Get rid of some. """ - html = re.sub(">\s+

\n

\s+

\n

-1) { - cookies = document.cookie.split(";"); - for (var i=0; i < cookies.length; i++) { - parts = cookies[i].split("=") + var cookies = document.cookie.split(";"); + for (i = 0; i < cookies.length; i++) { + var parts = cookies[i].split("="); - if ($.trim(parts[0]) == cookie_name && parts[1]) { + if ($.trim(parts[0]) === cookie_name && parts[1]) { sort_list = eval("[[" + parts[1] + "]]"); break; } @@ -25,8 +61,8 @@ id: "persistentSort", // Format is called by the widget before displaying: - format: function(table) { - if (table.config.sortList.length == 0 && sort_list.length > 0) { + format: function (table) { + if (table.config.sortList.length === 0 && sort_list.length > 0) { // This table hasn't been sorted before - we'll use // our stored settings: $(table).trigger('sorton', [sort_list]); @@ -42,11 +78,11 @@ // Configure our tablesorter to handle the variable number of // columns produced depending on report options: - var headers = {}; + var headers = []; var col_count = $("table.index > thead > tr > th").length; headers[0] = { sorter: 'text' }; - for (var i = 1; i < col_count-1; i++) { + for (i = 1; i < col_count-1; i++) { headers[i] = { sorter: 'digit' }; } headers[col_count-1] = { sorter: 'percent' }; @@ -57,23 +93,45 @@ headers: headers }); + coverage.assign_shortkeys(); + coverage.wire_up_help_panel(); + // Watch for page unload events so we can save the final sort settings: - $(window).unload(function() { - document.cookie = cookie_name + "=" + sort_list.toString() + "; path=/" + $(window).unload(function () { + document.cookie = cookie_name + "=" + sort_list.toString() + "; path=/"; }); -} +}; // -- pyfile stuff -- -function pyfile_ready($) { +coverage.pyfile_ready = function ($) { // If we're directed to a particular line number, highlight the line. var frag = location.hash; - if (frag.length > 2 && frag[1] == 'n') { + if (frag.length > 2 && frag[1] === 'n') { $(frag).addClass('highlight'); + coverage.set_sel(parseInt(frag.substr(2), 10)); + } + else { + coverage.set_sel(0); } -} -function toggle_lines(btn, cls) { + $(document) + .bind('keydown', 'j', coverage.to_next_chunk_nicely) + .bind('keydown', 'k', coverage.to_prev_chunk_nicely) + .bind('keydown', '0', coverage.to_top) + .bind('keydown', '1', coverage.to_first_chunk) + ; + + $(".button_toggle_run").click(function (evt) {coverage.toggle_lines(evt.target, "run");}); + $(".button_toggle_exc").click(function (evt) {coverage.toggle_lines(evt.target, "exc");}); + $(".button_toggle_mis").click(function (evt) {coverage.toggle_lines(evt.target, "mis");}); + $(".button_toggle_par").click(function (evt) {coverage.toggle_lines(evt.target, "par");}); + + coverage.assign_shortkeys(); + coverage.wire_up_help_panel(); +}; + +coverage.toggle_lines = function (btn, cls) { btn = $(btn); var hide = "hide_"+cls; if (btn.hasClass(hide)) { @@ -84,4 +142,235 @@ $("#source ."+cls).addClass(hide); btn.addClass(hide); } -} +}; + +// Return the nth line div. +coverage.line_elt = function (n) { + return $("#t" + n); +}; + +// Return the nth line number div. +coverage.num_elt = function (n) { + return $("#n" + n); +}; + +// Return the container of all the code. +coverage.code_container = function () { + return $(".linenos"); +}; + +// Set the selection. b and e are line numbers. +coverage.set_sel = function (b, e) { + // The first line selected. + coverage.sel_begin = b; + // The next line not selected. + coverage.sel_end = (e === undefined) ? b+1 : e; +}; + +coverage.to_top = function () { + coverage.set_sel(0, 1); + coverage.scroll_window(0); +}; + +coverage.to_first_chunk = function () { + coverage.set_sel(0, 1); + coverage.to_next_chunk(); +}; + +coverage.is_transparent = function (color) { + // Different browsers return different colors for "none". + return color === "transparent" || color === "rgba(0, 0, 0, 0)"; +}; + +coverage.to_next_chunk = function () { + var c = coverage; + + // Find the start of the next colored chunk. + var probe = c.sel_end; + while (true) { + var probe_line = c.line_elt(probe); + if (probe_line.length === 0) { + return; + } + var color = probe_line.css("background-color"); + if (!c.is_transparent(color)) { + break; + } + probe++; + } + + // There's a next chunk, `probe` points to it. + var begin = probe; + + // Find the end of this chunk. + var next_color = color; + while (next_color === color) { + probe++; + probe_line = c.line_elt(probe); + next_color = probe_line.css("background-color"); + } + c.set_sel(begin, probe); + c.show_selection(); +}; + +coverage.to_prev_chunk = function () { + var c = coverage; + + // Find the end of the prev colored chunk. + var probe = c.sel_begin-1; + var probe_line = c.line_elt(probe); + if (probe_line.length === 0) { + return; + } + var color = probe_line.css("background-color"); + while (probe > 0 && c.is_transparent(color)) { + probe--; + probe_line = c.line_elt(probe); + if (probe_line.length === 0) { + return; + } + color = probe_line.css("background-color"); + } + + // There's a prev chunk, `probe` points to its last line. + var end = probe+1; + + // Find the beginning of this chunk. + var prev_color = color; + while (prev_color === color) { + probe--; + probe_line = c.line_elt(probe); + prev_color = probe_line.css("background-color"); + } + c.set_sel(probe+1, end); + c.show_selection(); +}; + +// Return the line number of the line nearest pixel position pos +coverage.line_at_pos = function (pos) { + var l1 = coverage.line_elt(1), + l2 = coverage.line_elt(2), + result; + if (l1.length && l2.length) { + var l1_top = l1.offset().top, + line_height = l2.offset().top - l1_top, + nlines = (pos - l1_top) / line_height; + if (nlines < 1) { + result = 1; + } + else { + result = Math.ceil(nlines); + } + } + else { + result = 1; + } + return result; +}; + +// Returns 0, 1, or 2: how many of the two ends of the selection are on +// the screen right now? +coverage.selection_ends_on_screen = function () { + if (coverage.sel_begin === 0) { + return 0; + } + + var top = coverage.line_elt(coverage.sel_begin); + var next = coverage.line_elt(coverage.sel_end-1); + + return ( + (top.isOnScreen() ? 1 : 0) + + (next.isOnScreen() ? 1 : 0) + ); +}; + +coverage.to_next_chunk_nicely = function () { + coverage.finish_scrolling(); + if (coverage.selection_ends_on_screen() === 0) { + // The selection is entirely off the screen: select the top line on + // the screen. + var win = $(window); + coverage.select_line_or_chunk(coverage.line_at_pos(win.scrollTop())); + } + coverage.to_next_chunk(); +}; + +coverage.to_prev_chunk_nicely = function () { + coverage.finish_scrolling(); + if (coverage.selection_ends_on_screen() === 0) { + var win = $(window); + coverage.select_line_or_chunk(coverage.line_at_pos(win.scrollTop() + win.height())); + } + coverage.to_prev_chunk(); +}; + +// Select line number lineno, or if it is in a colored chunk, select the +// entire chunk +coverage.select_line_or_chunk = function (lineno) { + var c = coverage; + var probe_line = c.line_elt(lineno); + if (probe_line.length === 0) { + return; + } + var the_color = probe_line.css("background-color"); + if (!c.is_transparent(the_color)) { + // The line is in a highlighted chunk. + // Search backward for the first line. + var probe = lineno; + var color = the_color; + while (probe > 0 && color === the_color) { + probe--; + probe_line = c.line_elt(probe); + if (probe_line.length === 0) { + break; + } + color = probe_line.css("background-color"); + } + var begin = probe + 1; + + // Search forward for the last line. + probe = lineno; + color = the_color; + while (color === the_color) { + probe++; + probe_line = c.line_elt(probe); + color = probe_line.css("background-color"); + } + + coverage.set_sel(begin, probe); + } + else { + coverage.set_sel(lineno); + } +}; + +coverage.show_selection = function () { + var c = coverage; + + // Highlight the lines in the chunk + c.code_container().find(".highlight").removeClass("highlight"); + for (var probe = c.sel_begin; probe > 0 && probe < c.sel_end; probe++) { + c.num_elt(probe).addClass("highlight"); + } + + c.scroll_to_selection(); +}; + +coverage.scroll_to_selection = function () { + // Scroll the page if the chunk isn't fully visible. + if (coverage.selection_ends_on_screen() < 2) { + // Need to move the page. The html,body trick makes it scroll in all + // browsers, got it from http://stackoverflow.com/questions/3042651 + var top = coverage.line_elt(coverage.sel_begin); + var top_pos = parseInt(top.offset().top, 10); + coverage.scroll_window(top_pos - 30); + } +}; + +coverage.scroll_window = function (to_pos) { + $("html,body").animate({scrollTop: to_pos}, 200); +}; + +coverage.finish_scrolling = function () { + $("html,body").stop(true, true); +}; diff -Nru python-coverage-3.4/coverage/htmlfiles/index.html python-coverage-3.6/coverage/htmlfiles/index.html --- python-coverage-3.4/coverage/htmlfiles/index.html 2010-08-22 23:34:55.000000000 +0000 +++ python-coverage-3.6/coverage/htmlfiles/index.html 2012-11-20 01:51:47.000000000 +0000 @@ -2,22 +2,45 @@ - Coverage report + {{ title|escape }} - + {% if extra_css %} + + {% endif %} + +

+ +
+ +

Hot-keys on this page

+
+

+ n + s + m + x + {% if arcs %} + b + p + {% endif %} + c   change column sorting +

@@ -26,15 +49,15 @@ {# The title='' attr doesn't work in Safari. #} - Module - statements - missing - excluded + Module + statements + missing + excluded {% if arcs %} - branches - partial + branches + partial {% endif %} - coverage + coverage {# HTML syntax requires thead, tfoot, tbody #} @@ -46,7 +69,7 @@ {{totals.n_excluded}} {% if arcs %} {{totals.n_branches}} - {{totals.n_missing_branches}} + {{totals.n_partial_branches}} {% endif %} {{totals.pc_covered_str}}% @@ -54,13 +77,13 @@ {% for file in files %} - {{file.cu.name}} + {{file.name}} {{file.nums.n_statements}} {{file.nums.n_missing}} {{file.nums.n_excluded}} {% if arcs %} {{file.nums.n_branches}} - {{file.nums.n_missing_branches}} + {{file.nums.n_partial_branches}} {% endif %} {{file.nums.pc_covered_str}}% diff -Nru python-coverage-3.4/coverage/htmlfiles/jquery-1.3.2.min.js python-coverage-3.6/coverage/htmlfiles/jquery-1.3.2.min.js --- python-coverage-3.4/coverage/htmlfiles/jquery-1.3.2.min.js 2010-05-15 14:40:25.000000000 +0000 +++ python-coverage-3.6/coverage/htmlfiles/jquery-1.3.2.min.js 1970-01-01 00:00:00.000000000 +0000 @@ -1,19 +0,0 @@ -/* - * jQuery JavaScript Library v1.3.2 - * http://jquery.com/ - * - * Copyright (c) 2009 John Resig - * Dual licensed under the MIT and GPL licenses. - * http://docs.jquery.com/License - * - * Date: 2009-02-19 17:34:21 -0500 (Thu, 19 Feb 2009) - * Revision: 6246 - */ -(function(){var l=this,g,y=l.jQuery,p=l.$,o=l.jQuery=l.$=function(E,F){return new o.fn.init(E,F)},D=/^[^<]*(<(.|\s)+>)[^>]*$|^#([\w-]+)$/,f=/^.[^:#\[\.,]*$/;o.fn=o.prototype={init:function(E,H){E=E||document;if(E.nodeType){this[0]=E;this.length=1;this.context=E;return this}if(typeof E==="string"){var G=D.exec(E);if(G&&(G[1]||!H)){if(G[1]){E=o.clean([G[1]],H)}else{var I=document.getElementById(G[3]);if(I&&I.id!=G[3]){return o().find(E)}var F=o(I||[]);F.context=document;F.selector=E;return F}}else{return o(H).find(E)}}else{if(o.isFunction(E)){return o(document).ready(E)}}if(E.selector&&E.context){this.selector=E.selector;this.context=E.context}return this.setArray(o.isArray(E)?E:o.makeArray(E))},selector:"",jquery:"1.3.2",size:function(){return this.length},get:function(E){return E===g?Array.prototype.slice.call(this):this[E]},pushStack:function(F,H,E){var G=o(F);G.prevObject=this;G.context=this.context;if(H==="find"){G.selector=this.selector+(this.selector?" ":"")+E}else{if(H){G.selector=this.selector+"."+H+"("+E+")"}}return G},setArray:function(E){this.length=0;Array.prototype.push.apply(this,E);return this},each:function(F,E){return o.each(this,F,E)},index:function(E){return o.inArray(E&&E.jquery?E[0]:E,this)},attr:function(F,H,G){var E=F;if(typeof F==="string"){if(H===g){return this[0]&&o[G||"attr"](this[0],F)}else{E={};E[F]=H}}return this.each(function(I){for(F in E){o.attr(G?this.style:this,F,o.prop(this,E[F],G,I,F))}})},css:function(E,F){if((E=="width"||E=="height")&&parseFloat(F)<0){F=g}return this.attr(E,F,"curCSS")},text:function(F){if(typeof F!=="object"&&F!=null){return this.empty().append((this[0]&&this[0].ownerDocument||document).createTextNode(F))}var E="";o.each(F||this,function(){o.each(this.childNodes,function(){if(this.nodeType!=8){E+=this.nodeType!=1?this.nodeValue:o.fn.text([this])}})});return E},wrapAll:function(E){if(this[0]){var F=o(E,this[0].ownerDocument).clone();if(this[0].parentNode){F.insertBefore(this[0])}F.map(function(){var G=this;while(G.firstChild){G=G.firstChild}return G}).append(this)}return this},wrapInner:function(E){return this.each(function(){o(this).contents().wrapAll(E)})},wrap:function(E){return this.each(function(){o(this).wrapAll(E)})},append:function(){return this.domManip(arguments,true,function(E){if(this.nodeType==1){this.appendChild(E)}})},prepend:function(){return this.domManip(arguments,true,function(E){if(this.nodeType==1){this.insertBefore(E,this.firstChild)}})},before:function(){return this.domManip(arguments,false,function(E){this.parentNode.insertBefore(E,this)})},after:function(){return this.domManip(arguments,false,function(E){this.parentNode.insertBefore(E,this.nextSibling)})},end:function(){return this.prevObject||o([])},push:[].push,sort:[].sort,splice:[].splice,find:function(E){if(this.length===1){var F=this.pushStack([],"find",E);F.length=0;o.find(E,this[0],F);return F}else{return this.pushStack(o.unique(o.map(this,function(G){return o.find(E,G)})),"find",E)}},clone:function(G){var E=this.map(function(){if(!o.support.noCloneEvent&&!o.isXMLDoc(this)){var I=this.outerHTML;if(!I){var J=this.ownerDocument.createElement("div");J.appendChild(this.cloneNode(true));I=J.innerHTML}return o.clean([I.replace(/ jQuery\d+="(?:\d+|null)"/g,"").replace(/^\s*/,"")])[0]}else{return this.cloneNode(true)}});if(G===true){var H=this.find("*").andSelf(),F=0;E.find("*").andSelf().each(function(){if(this.nodeName!==H[F].nodeName){return}var I=o.data(H[F],"events");for(var K in I){for(var J in I[K]){o.event.add(this,K,I[K][J],I[K][J].data)}}F++})}return E},filter:function(E){return this.pushStack(o.isFunction(E)&&o.grep(this,function(G,F){return E.call(G,F)})||o.multiFilter(E,o.grep(this,function(F){return F.nodeType===1})),"filter",E)},closest:function(E){var G=o.expr.match.POS.test(E)?o(E):null,F=0;return this.map(function(){var H=this;while(H&&H.ownerDocument){if(G?G.index(H)>-1:o(H).is(E)){o.data(H,"closest",F);return H}H=H.parentNode;F++}})},not:function(E){if(typeof E==="string"){if(f.test(E)){return this.pushStack(o.multiFilter(E,this,true),"not",E)}else{E=o.multiFilter(E,this)}}var F=E.length&&E[E.length-1]!==g&&!E.nodeType;return this.filter(function(){return F?o.inArray(this,E)<0:this!=E})},add:function(E){return this.pushStack(o.unique(o.merge(this.get(),typeof E==="string"?o(E):o.makeArray(E))))},is:function(E){return !!E&&o.multiFilter(E,this).length>0},hasClass:function(E){return !!E&&this.is("."+E)},val:function(K){if(K===g){var E=this[0];if(E){if(o.nodeName(E,"option")){return(E.attributes.value||{}).specified?E.value:E.text}if(o.nodeName(E,"select")){var I=E.selectedIndex,L=[],M=E.options,H=E.type=="select-one";if(I<0){return null}for(var F=H?I:0,J=H?I+1:M.length;F=0||o.inArray(this.name,K)>=0)}else{if(o.nodeName(this,"select")){var N=o.makeArray(K);o("option",this).each(function(){this.selected=(o.inArray(this.value,N)>=0||o.inArray(this.text,N)>=0)});if(!N.length){this.selectedIndex=-1}}else{this.value=K}}})},html:function(E){return E===g?(this[0]?this[0].innerHTML.replace(/ jQuery\d+="(?:\d+|null)"/g,""):null):this.empty().append(E)},replaceWith:function(E){return this.after(E).remove()},eq:function(E){return this.slice(E,+E+1)},slice:function(){return this.pushStack(Array.prototype.slice.apply(this,arguments),"slice",Array.prototype.slice.call(arguments).join(","))},map:function(E){return this.pushStack(o.map(this,function(G,F){return E.call(G,F,G)}))},andSelf:function(){return this.add(this.prevObject)},domManip:function(J,M,L){if(this[0]){var I=(this[0].ownerDocument||this[0]).createDocumentFragment(),F=o.clean(J,(this[0].ownerDocument||this[0]),I),H=I.firstChild;if(H){for(var G=0,E=this.length;G1||G>0?I.cloneNode(true):I)}}if(F){o.each(F,z)}}return this;function K(N,O){return M&&o.nodeName(N,"table")&&o.nodeName(O,"tr")?(N.getElementsByTagName("tbody")[0]||N.appendChild(N.ownerDocument.createElement("tbody"))):N}}};o.fn.init.prototype=o.fn;function z(E,F){if(F.src){o.ajax({url:F.src,async:false,dataType:"script"})}else{o.globalEval(F.text||F.textContent||F.innerHTML||"")}if(F.parentNode){F.parentNode.removeChild(F)}}function e(){return +new Date}o.extend=o.fn.extend=function(){var J=arguments[0]||{},H=1,I=arguments.length,E=false,G;if(typeof J==="boolean"){E=J;J=arguments[1]||{};H=2}if(typeof J!=="object"&&!o.isFunction(J)){J={}}if(I==H){J=this;--H}for(;H-1}},swap:function(H,G,I){var E={};for(var F in G){E[F]=H.style[F];H.style[F]=G[F]}I.call(H);for(var F in G){H.style[F]=E[F]}},css:function(H,F,J,E){if(F=="width"||F=="height"){var L,G={position:"absolute",visibility:"hidden",display:"block"},K=F=="width"?["Left","Right"]:["Top","Bottom"];function I(){L=F=="width"?H.offsetWidth:H.offsetHeight;if(E==="border"){return}o.each(K,function(){if(!E){L-=parseFloat(o.curCSS(H,"padding"+this,true))||0}if(E==="margin"){L+=parseFloat(o.curCSS(H,"margin"+this,true))||0}else{L-=parseFloat(o.curCSS(H,"border"+this+"Width",true))||0}})}if(H.offsetWidth!==0){I()}else{o.swap(H,G,I)}return Math.max(0,Math.round(L))}return o.curCSS(H,F,J)},curCSS:function(I,F,G){var L,E=I.style;if(F=="opacity"&&!o.support.opacity){L=o.attr(E,"opacity");return L==""?"1":L}if(F.match(/float/i)){F=w}if(!G&&E&&E[F]){L=E[F]}else{if(q.getComputedStyle){if(F.match(/float/i)){F="float"}F=F.replace(/([A-Z])/g,"-$1").toLowerCase();var M=q.getComputedStyle(I,null);if(M){L=M.getPropertyValue(F)}if(F=="opacity"&&L==""){L="1"}}else{if(I.currentStyle){var J=F.replace(/\-(\w)/g,function(N,O){return O.toUpperCase()});L=I.currentStyle[F]||I.currentStyle[J];if(!/^\d+(px)?$/i.test(L)&&/^\d/.test(L)){var H=E.left,K=I.runtimeStyle.left;I.runtimeStyle.left=I.currentStyle.left;E.left=L||0;L=E.pixelLeft+"px";E.left=H;I.runtimeStyle.left=K}}}}return L},clean:function(F,K,I){K=K||document;if(typeof K.createElement==="undefined"){K=K.ownerDocument||K[0]&&K[0].ownerDocument||document}if(!I&&F.length===1&&typeof F[0]==="string"){var H=/^<(\w+)\s*\/?>$/.exec(F[0]);if(H){return[K.createElement(H[1])]}}var G=[],E=[],L=K.createElement("div");o.each(F,function(P,S){if(typeof S==="number"){S+=""}if(!S){return}if(typeof S==="string"){S=S.replace(/(<(\w+)[^>]*?)\/>/g,function(U,V,T){return T.match(/^(abbr|br|col|img|input|link|meta|param|hr|area|embed)$/i)?U:V+">"});var O=S.replace(/^\s+/,"").substring(0,10).toLowerCase();var Q=!O.indexOf("",""]||!O.indexOf("",""]||O.match(/^<(thead|tbody|tfoot|colg|cap)/)&&[1,"","
"]||!O.indexOf("",""]||(!O.indexOf("",""]||!O.indexOf("",""]||!o.support.htmlSerialize&&[1,"div
","
"]||[0,"",""];L.innerHTML=Q[1]+S+Q[2];while(Q[0]--){L=L.lastChild}if(!o.support.tbody){var R=/"&&!R?L.childNodes:[];for(var M=N.length-1;M>=0;--M){if(o.nodeName(N[M],"tbody")&&!N[M].childNodes.length){N[M].parentNode.removeChild(N[M])}}}if(!o.support.leadingWhitespace&&/^\s/.test(S)){L.insertBefore(K.createTextNode(S.match(/^\s*/)[0]),L.firstChild)}S=o.makeArray(L.childNodes)}if(S.nodeType){G.push(S)}else{G=o.merge(G,S)}});if(I){for(var J=0;G[J];J++){if(o.nodeName(G[J],"script")&&(!G[J].type||G[J].type.toLowerCase()==="text/javascript")){E.push(G[J].parentNode?G[J].parentNode.removeChild(G[J]):G[J])}else{if(G[J].nodeType===1){G.splice.apply(G,[J+1,0].concat(o.makeArray(G[J].getElementsByTagName("script"))))}I.appendChild(G[J])}}return E}return G},attr:function(J,G,K){if(!J||J.nodeType==3||J.nodeType==8){return g}var H=!o.isXMLDoc(J),L=K!==g;G=H&&o.props[G]||G;if(J.tagName){var F=/href|src|style/.test(G);if(G=="selected"&&J.parentNode){J.parentNode.selectedIndex}if(G in J&&H&&!F){if(L){if(G=="type"&&o.nodeName(J,"input")&&J.parentNode){throw"type property can't be changed"}J[G]=K}if(o.nodeName(J,"form")&&J.getAttributeNode(G)){return J.getAttributeNode(G).nodeValue}if(G=="tabIndex"){var I=J.getAttributeNode("tabIndex");return I&&I.specified?I.value:J.nodeName.match(/(button|input|object|select|textarea)/i)?0:J.nodeName.match(/^(a|area)$/i)&&J.href?0:g}return J[G]}if(!o.support.style&&H&&G=="style"){return o.attr(J.style,"cssText",K)}if(L){J.setAttribute(G,""+K)}var E=!o.support.hrefNormalized&&H&&F?J.getAttribute(G,2):J.getAttribute(G);return E===null?g:E}if(!o.support.opacity&&G=="opacity"){if(L){J.zoom=1;J.filter=(J.filter||"").replace(/alpha\([^)]*\)/,"")+(parseInt(K)+""=="NaN"?"":"alpha(opacity="+K*100+")")}return J.filter&&J.filter.indexOf("opacity=")>=0?(parseFloat(J.filter.match(/opacity=([^)]*)/)[1])/100)+"":""}G=G.replace(/-([a-z])/ig,function(M,N){return N.toUpperCase()});if(L){J[G]=K}return J[G]},trim:function(E){return(E||"").replace(/^\s+|\s+$/g,"")},makeArray:function(G){var E=[];if(G!=null){var F=G.length;if(F==null||typeof G==="string"||o.isFunction(G)||G.setInterval){E[0]=G}else{while(F){E[--F]=G[F]}}}return E},inArray:function(G,H){for(var E=0,F=H.length;E0?this.clone(true):this).get();o.fn[F].apply(o(L[K]),I);J=J.concat(I)}return this.pushStack(J,E,G)}});o.each({removeAttr:function(E){o.attr(this,E,"");if(this.nodeType==1){this.removeAttribute(E)}},addClass:function(E){o.className.add(this,E)},removeClass:function(E){o.className.remove(this,E)},toggleClass:function(F,E){if(typeof E!=="boolean"){E=!o.className.has(this,F)}o.className[E?"add":"remove"](this,F)},remove:function(E){if(!E||o.filter(E,[this]).length){o("*",this).add([this]).each(function(){o.event.remove(this);o.removeData(this)});if(this.parentNode){this.parentNode.removeChild(this)}}},empty:function(){o(this).children().remove();while(this.firstChild){this.removeChild(this.firstChild)}}},function(E,F){o.fn[E]=function(){return this.each(F,arguments)}});function j(E,F){return E[0]&&parseInt(o.curCSS(E[0],F,true),10)||0}var h="jQuery"+e(),v=0,A={};o.extend({cache:{},data:function(F,E,G){F=F==l?A:F;var H=F[h];if(!H){H=F[h]=++v}if(E&&!o.cache[H]){o.cache[H]={}}if(G!==g){o.cache[H][E]=G}return E?o.cache[H][E]:H},removeData:function(F,E){F=F==l?A:F;var H=F[h];if(E){if(o.cache[H]){delete o.cache[H][E];E="";for(E in o.cache[H]){break}if(!E){o.removeData(F)}}}else{try{delete F[h]}catch(G){if(F.removeAttribute){F.removeAttribute(h)}}delete o.cache[H]}},queue:function(F,E,H){if(F){E=(E||"fx")+"queue";var G=o.data(F,E);if(!G||o.isArray(H)){G=o.data(F,E,o.makeArray(H))}else{if(H){G.push(H)}}}return G},dequeue:function(H,G){var E=o.queue(H,G),F=E.shift();if(!G||G==="fx"){F=E[0]}if(F!==g){F.call(H)}}});o.fn.extend({data:function(E,G){var H=E.split(".");H[1]=H[1]?"."+H[1]:"";if(G===g){var F=this.triggerHandler("getData"+H[1]+"!",[H[0]]);if(F===g&&this.length){F=o.data(this[0],E)}return F===g&&H[1]?this.data(H[0]):F}else{return this.trigger("setData"+H[1]+"!",[H[0],G]).each(function(){o.data(this,E,G)})}},removeData:function(E){return this.each(function(){o.removeData(this,E)})},queue:function(E,F){if(typeof E!=="string"){F=E;E="fx"}if(F===g){return o.queue(this[0],E)}return this.each(function(){var G=o.queue(this,E,F);if(E=="fx"&&G.length==1){G[0].call(this)}})},dequeue:function(E){return this.each(function(){o.dequeue(this,E)})}}); -/* - * Sizzle CSS Selector Engine - v0.9.3 - * Copyright 2009, The Dojo Foundation - * Released under the MIT, BSD, and GPL Licenses. - * More information: http://sizzlejs.com/ - */ -(function(){var R=/((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^[\]]*\]|['"][^'"]*['"]|[^[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?/g,L=0,H=Object.prototype.toString;var F=function(Y,U,ab,ac){ab=ab||[];U=U||document;if(U.nodeType!==1&&U.nodeType!==9){return[]}if(!Y||typeof Y!=="string"){return ab}var Z=[],W,af,ai,T,ad,V,X=true;R.lastIndex=0;while((W=R.exec(Y))!==null){Z.push(W[1]);if(W[2]){V=RegExp.rightContext;break}}if(Z.length>1&&M.exec(Y)){if(Z.length===2&&I.relative[Z[0]]){af=J(Z[0]+Z[1],U)}else{af=I.relative[Z[0]]?[U]:F(Z.shift(),U);while(Z.length){Y=Z.shift();if(I.relative[Y]){Y+=Z.shift()}af=J(Y,af)}}}else{var ae=ac?{expr:Z.pop(),set:E(ac)}:F.find(Z.pop(),Z.length===1&&U.parentNode?U.parentNode:U,Q(U));af=F.filter(ae.expr,ae.set);if(Z.length>0){ai=E(af)}else{X=false}while(Z.length){var ah=Z.pop(),ag=ah;if(!I.relative[ah]){ah=""}else{ag=Z.pop()}if(ag==null){ag=U}I.relative[ah](ai,ag,Q(U))}}if(!ai){ai=af}if(!ai){throw"Syntax error, unrecognized expression: "+(ah||Y)}if(H.call(ai)==="[object Array]"){if(!X){ab.push.apply(ab,ai)}else{if(U.nodeType===1){for(var aa=0;ai[aa]!=null;aa++){if(ai[aa]&&(ai[aa]===true||ai[aa].nodeType===1&&K(U,ai[aa]))){ab.push(af[aa])}}}else{for(var aa=0;ai[aa]!=null;aa++){if(ai[aa]&&ai[aa].nodeType===1){ab.push(af[aa])}}}}}else{E(ai,ab)}if(V){F(V,U,ab,ac);if(G){hasDuplicate=false;ab.sort(G);if(hasDuplicate){for(var aa=1;aa":function(Z,U,aa){var X=typeof U==="string";if(X&&!/\W/.test(U)){U=aa?U:U.toUpperCase();for(var V=0,T=Z.length;V=0)){if(!V){T.push(Y)}}else{if(V){U[X]=false}}}}return false},ID:function(T){return T[1].replace(/\\/g,"")},TAG:function(U,T){for(var V=0;T[V]===false;V++){}return T[V]&&Q(T[V])?U[1]:U[1].toUpperCase()},CHILD:function(T){if(T[1]=="nth"){var U=/(-?)(\d*)n((?:\+|-)?\d*)/.exec(T[2]=="even"&&"2n"||T[2]=="odd"&&"2n+1"||!/\D/.test(T[2])&&"0n+"+T[2]||T[2]);T[2]=(U[1]+(U[2]||1))-0;T[3]=U[3]-0}T[0]=L++;return T},ATTR:function(X,U,V,T,Y,Z){var W=X[1].replace(/\\/g,"");if(!Z&&I.attrMap[W]){X[1]=I.attrMap[W]}if(X[2]==="~="){X[4]=" "+X[4]+" "}return X},PSEUDO:function(X,U,V,T,Y){if(X[1]==="not"){if(X[3].match(R).length>1||/^\w/.test(X[3])){X[3]=F(X[3],null,null,U)}else{var W=F.filter(X[3],U,V,true^Y);if(!V){T.push.apply(T,W)}return false}}else{if(I.match.POS.test(X[0])||I.match.CHILD.test(X[0])){return true}}return X},POS:function(T){T.unshift(true);return T}},filters:{enabled:function(T){return T.disabled===false&&T.type!=="hidden"},disabled:function(T){return T.disabled===true},checked:function(T){return T.checked===true},selected:function(T){T.parentNode.selectedIndex;return T.selected===true},parent:function(T){return !!T.firstChild},empty:function(T){return !T.firstChild},has:function(V,U,T){return !!F(T[3],V).length},header:function(T){return/h\d/i.test(T.nodeName)},text:function(T){return"text"===T.type},radio:function(T){return"radio"===T.type},checkbox:function(T){return"checkbox"===T.type},file:function(T){return"file"===T.type},password:function(T){return"password"===T.type},submit:function(T){return"submit"===T.type},image:function(T){return"image"===T.type},reset:function(T){return"reset"===T.type},button:function(T){return"button"===T.type||T.nodeName.toUpperCase()==="BUTTON"},input:function(T){return/input|select|textarea|button/i.test(T.nodeName)}},setFilters:{first:function(U,T){return T===0},last:function(V,U,T,W){return U===W.length-1},even:function(U,T){return T%2===0},odd:function(U,T){return T%2===1},lt:function(V,U,T){return UT[3]-0},nth:function(V,U,T){return T[3]-0==U},eq:function(V,U,T){return T[3]-0==U}},filter:{PSEUDO:function(Z,V,W,aa){var U=V[1],X=I.filters[U];if(X){return X(Z,W,V,aa)}else{if(U==="contains"){return(Z.textContent||Z.innerText||"").indexOf(V[3])>=0}else{if(U==="not"){var Y=V[3];for(var W=0,T=Y.length;W=0)}}},ID:function(U,T){return U.nodeType===1&&U.getAttribute("id")===T},TAG:function(U,T){return(T==="*"&&U.nodeType===1)||U.nodeName===T},CLASS:function(U,T){return(" "+(U.className||U.getAttribute("class"))+" ").indexOf(T)>-1},ATTR:function(Y,W){var V=W[1],T=I.attrHandle[V]?I.attrHandle[V](Y):Y[V]!=null?Y[V]:Y.getAttribute(V),Z=T+"",X=W[2],U=W[4];return T==null?X==="!=":X==="="?Z===U:X==="*="?Z.indexOf(U)>=0:X==="~="?(" "+Z+" ").indexOf(U)>=0:!U?Z&&T!==false:X==="!="?Z!=U:X==="^="?Z.indexOf(U)===0:X==="$="?Z.substr(Z.length-U.length)===U:X==="|="?Z===U||Z.substr(0,U.length+1)===U+"-":false},POS:function(X,U,V,Y){var T=U[2],W=I.setFilters[T];if(W){return W(X,V,U,Y)}}}};var M=I.match.POS;for(var O in I.match){I.match[O]=RegExp(I.match[O].source+/(?![^\[]*\])(?![^\(]*\))/.source)}var E=function(U,T){U=Array.prototype.slice.call(U);if(T){T.push.apply(T,U);return T}return U};try{Array.prototype.slice.call(document.documentElement.childNodes)}catch(N){E=function(X,W){var U=W||[];if(H.call(X)==="[object Array]"){Array.prototype.push.apply(U,X)}else{if(typeof X.length==="number"){for(var V=0,T=X.length;V";var T=document.documentElement;T.insertBefore(U,T.firstChild);if(!!document.getElementById(V)){I.find.ID=function(X,Y,Z){if(typeof Y.getElementById!=="undefined"&&!Z){var W=Y.getElementById(X[1]);return W?W.id===X[1]||typeof W.getAttributeNode!=="undefined"&&W.getAttributeNode("id").nodeValue===X[1]?[W]:g:[]}};I.filter.ID=function(Y,W){var X=typeof Y.getAttributeNode!=="undefined"&&Y.getAttributeNode("id");return Y.nodeType===1&&X&&X.nodeValue===W}}T.removeChild(U)})();(function(){var T=document.createElement("div");T.appendChild(document.createComment(""));if(T.getElementsByTagName("*").length>0){I.find.TAG=function(U,Y){var X=Y.getElementsByTagName(U[1]);if(U[1]==="*"){var W=[];for(var V=0;X[V];V++){if(X[V].nodeType===1){W.push(X[V])}}X=W}return X}}T.innerHTML="";if(T.firstChild&&typeof T.firstChild.getAttribute!=="undefined"&&T.firstChild.getAttribute("href")!=="#"){I.attrHandle.href=function(U){return U.getAttribute("href",2)}}})();if(document.querySelectorAll){(function(){var T=F,U=document.createElement("div");U.innerHTML="

";if(U.querySelectorAll&&U.querySelectorAll(".TEST").length===0){return}F=function(Y,X,V,W){X=X||document;if(!W&&X.nodeType===9&&!Q(X)){try{return E(X.querySelectorAll(Y),V)}catch(Z){}}return T(Y,X,V,W)};F.find=T.find;F.filter=T.filter;F.selectors=T.selectors;F.matches=T.matches})()}if(document.getElementsByClassName&&document.documentElement.getElementsByClassName){(function(){var T=document.createElement("div");T.innerHTML="
";if(T.getElementsByClassName("e").length===0){return}T.lastChild.className="e";if(T.getElementsByClassName("e").length===1){return}I.order.splice(1,0,"CLASS");I.find.CLASS=function(U,V,W){if(typeof V.getElementsByClassName!=="undefined"&&!W){return V.getElementsByClassName(U[1])}}})()}function P(U,Z,Y,ad,aa,ac){var ab=U=="previousSibling"&&!ac;for(var W=0,V=ad.length;W0){X=T;break}}}T=T[U]}ad[W]=X}}}var K=document.compareDocumentPosition?function(U,T){return U.compareDocumentPosition(T)&16}:function(U,T){return U!==T&&(U.contains?U.contains(T):true)};var Q=function(T){return T.nodeType===9&&T.documentElement.nodeName!=="HTML"||!!T.ownerDocument&&Q(T.ownerDocument)};var J=function(T,aa){var W=[],X="",Y,V=aa.nodeType?[aa]:aa;while((Y=I.match.PSEUDO.exec(T))){X+=Y[0];T=T.replace(I.match.PSEUDO,"")}T=I.relative[T]?T+"*":T;for(var Z=0,U=V.length;Z0||T.offsetHeight>0};F.selectors.filters.animated=function(T){return o.grep(o.timers,function(U){return T===U.elem}).length};o.multiFilter=function(V,T,U){if(U){V=":not("+V+")"}return F.matches(V,T)};o.dir=function(V,U){var T=[],W=V[U];while(W&&W!=document){if(W.nodeType==1){T.push(W)}W=W[U]}return T};o.nth=function(X,T,V,W){T=T||1;var U=0;for(;X;X=X[V]){if(X.nodeType==1&&++U==T){break}}return X};o.sibling=function(V,U){var T=[];for(;V;V=V.nextSibling){if(V.nodeType==1&&V!=U){T.push(V)}}return T};return;l.Sizzle=F})();o.event={add:function(I,F,H,K){if(I.nodeType==3||I.nodeType==8){return}if(I.setInterval&&I!=l){I=l}if(!H.guid){H.guid=this.guid++}if(K!==g){var G=H;H=this.proxy(G);H.data=K}var E=o.data(I,"events")||o.data(I,"events",{}),J=o.data(I,"handle")||o.data(I,"handle",function(){return typeof o!=="undefined"&&!o.event.triggered?o.event.handle.apply(arguments.callee.elem,arguments):g});J.elem=I;o.each(F.split(/\s+/),function(M,N){var O=N.split(".");N=O.shift();H.type=O.slice().sort().join(".");var L=E[N];if(o.event.specialAll[N]){o.event.specialAll[N].setup.call(I,K,O)}if(!L){L=E[N]={};if(!o.event.special[N]||o.event.special[N].setup.call(I,K,O)===false){if(I.addEventListener){I.addEventListener(N,J,false)}else{if(I.attachEvent){I.attachEvent("on"+N,J)}}}}L[H.guid]=H;o.event.global[N]=true});I=null},guid:1,global:{},remove:function(K,H,J){if(K.nodeType==3||K.nodeType==8){return}var G=o.data(K,"events"),F,E;if(G){if(H===g||(typeof H==="string"&&H.charAt(0)==".")){for(var I in G){this.remove(K,I+(H||""))}}else{if(H.type){J=H.handler;H=H.type}o.each(H.split(/\s+/),function(M,O){var Q=O.split(".");O=Q.shift();var N=RegExp("(^|\\.)"+Q.slice().sort().join(".*\\.")+"(\\.|$)");if(G[O]){if(J){delete G[O][J.guid]}else{for(var P in G[O]){if(N.test(G[O][P].type)){delete G[O][P]}}}if(o.event.specialAll[O]){o.event.specialAll[O].teardown.call(K,Q)}for(F in G[O]){break}if(!F){if(!o.event.special[O]||o.event.special[O].teardown.call(K,Q)===false){if(K.removeEventListener){K.removeEventListener(O,o.data(K,"handle"),false)}else{if(K.detachEvent){K.detachEvent("on"+O,o.data(K,"handle"))}}}F=null;delete G[O]}}})}for(F in G){break}if(!F){var L=o.data(K,"handle");if(L){L.elem=null}o.removeData(K,"events");o.removeData(K,"handle")}}},trigger:function(I,K,H,E){var G=I.type||I;if(!E){I=typeof I==="object"?I[h]?I:o.extend(o.Event(G),I):o.Event(G);if(G.indexOf("!")>=0){I.type=G=G.slice(0,-1);I.exclusive=true}if(!H){I.stopPropagation();if(this.global[G]){o.each(o.cache,function(){if(this.events&&this.events[G]){o.event.trigger(I,K,this.handle.elem)}})}}if(!H||H.nodeType==3||H.nodeType==8){return g}I.result=g;I.target=H;K=o.makeArray(K);K.unshift(I)}I.currentTarget=H;var J=o.data(H,"handle");if(J){J.apply(H,K)}if((!H[G]||(o.nodeName(H,"a")&&G=="click"))&&H["on"+G]&&H["on"+G].apply(H,K)===false){I.result=false}if(!E&&H[G]&&!I.isDefaultPrevented()&&!(o.nodeName(H,"a")&&G=="click")){this.triggered=true;try{H[G]()}catch(L){}}this.triggered=false;if(!I.isPropagationStopped()){var F=H.parentNode||H.ownerDocument;if(F){o.event.trigger(I,K,F,true)}}},handle:function(K){var J,E;K=arguments[0]=o.event.fix(K||l.event);K.currentTarget=this;var L=K.type.split(".");K.type=L.shift();J=!L.length&&!K.exclusive;var I=RegExp("(^|\\.)"+L.slice().sort().join(".*\\.")+"(\\.|$)");E=(o.data(this,"events")||{})[K.type];for(var G in E){var H=E[G];if(J||I.test(H.type)){K.handler=H;K.data=H.data;var F=H.apply(this,arguments);if(F!==g){K.result=F;if(F===false){K.preventDefault();K.stopPropagation()}}if(K.isImmediatePropagationStopped()){break}}}},props:"altKey attrChange attrName bubbles button cancelable charCode clientX clientY ctrlKey currentTarget data detail eventPhase fromElement handler keyCode metaKey newValue originalTarget pageX pageY prevValue relatedNode relatedTarget screenX screenY shiftKey srcElement target toElement view wheelDelta which".split(" "),fix:function(H){if(H[h]){return H}var F=H;H=o.Event(F);for(var G=this.props.length,J;G;){J=this.props[--G];H[J]=F[J]}if(!H.target){H.target=H.srcElement||document}if(H.target.nodeType==3){H.target=H.target.parentNode}if(!H.relatedTarget&&H.fromElement){H.relatedTarget=H.fromElement==H.target?H.toElement:H.fromElement}if(H.pageX==null&&H.clientX!=null){var I=document.documentElement,E=document.body;H.pageX=H.clientX+(I&&I.scrollLeft||E&&E.scrollLeft||0)-(I.clientLeft||0);H.pageY=H.clientY+(I&&I.scrollTop||E&&E.scrollTop||0)-(I.clientTop||0)}if(!H.which&&((H.charCode||H.charCode===0)?H.charCode:H.keyCode)){H.which=H.charCode||H.keyCode}if(!H.metaKey&&H.ctrlKey){H.metaKey=H.ctrlKey}if(!H.which&&H.button){H.which=(H.button&1?1:(H.button&2?3:(H.button&4?2:0)))}return H},proxy:function(F,E){E=E||function(){return F.apply(this,arguments)};E.guid=F.guid=F.guid||E.guid||this.guid++;return E},special:{ready:{setup:B,teardown:function(){}}},specialAll:{live:{setup:function(E,F){o.event.add(this,F[0],c)},teardown:function(G){if(G.length){var E=0,F=RegExp("(^|\\.)"+G[0]+"(\\.|$)");o.each((o.data(this,"events").live||{}),function(){if(F.test(this.type)){E++}});if(E<1){o.event.remove(this,G[0],c)}}}}}};o.Event=function(E){if(!this.preventDefault){return new o.Event(E)}if(E&&E.type){this.originalEvent=E;this.type=E.type}else{this.type=E}this.timeStamp=e();this[h]=true};function k(){return false}function u(){return true}o.Event.prototype={preventDefault:function(){this.isDefaultPrevented=u;var E=this.originalEvent;if(!E){return}if(E.preventDefault){E.preventDefault()}E.returnValue=false},stopPropagation:function(){this.isPropagationStopped=u;var E=this.originalEvent;if(!E){return}if(E.stopPropagation){E.stopPropagation()}E.cancelBubble=true},stopImmediatePropagation:function(){this.isImmediatePropagationStopped=u;this.stopPropagation()},isDefaultPrevented:k,isPropagationStopped:k,isImmediatePropagationStopped:k};var a=function(F){var E=F.relatedTarget;while(E&&E!=this){try{E=E.parentNode}catch(G){E=this}}if(E!=this){F.type=F.data;o.event.handle.apply(this,arguments)}};o.each({mouseover:"mouseenter",mouseout:"mouseleave"},function(F,E){o.event.special[E]={setup:function(){o.event.add(this,F,a,E)},teardown:function(){o.event.remove(this,F,a)}}});o.fn.extend({bind:function(F,G,E){return F=="unload"?this.one(F,G,E):this.each(function(){o.event.add(this,F,E||G,E&&G)})},one:function(G,H,F){var E=o.event.proxy(F||H,function(I){o(this).unbind(I,E);return(F||H).apply(this,arguments)});return this.each(function(){o.event.add(this,G,E,F&&H)})},unbind:function(F,E){return this.each(function(){o.event.remove(this,F,E)})},trigger:function(E,F){return this.each(function(){o.event.trigger(E,F,this)})},triggerHandler:function(E,G){if(this[0]){var F=o.Event(E);F.preventDefault();F.stopPropagation();o.event.trigger(F,G,this[0]);return F.result}},toggle:function(G){var E=arguments,F=1;while(F=0){var E=G.slice(I,G.length);G=G.slice(0,I)}var H="GET";if(J){if(o.isFunction(J)){K=J;J=null}else{if(typeof J==="object"){J=o.param(J);H="POST"}}}var F=this;o.ajax({url:G,type:H,dataType:"html",data:J,complete:function(M,L){if(L=="success"||L=="notmodified"){F.html(E?o("
").append(M.responseText.replace(//g,"")).find(E):M.responseText)}if(K){F.each(K,[M.responseText,L,M])}}});return this},serialize:function(){return o.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?o.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||/select|textarea/i.test(this.nodeName)||/text|hidden|password|search/i.test(this.type))}).map(function(E,F){var G=o(this).val();return G==null?null:o.isArray(G)?o.map(G,function(I,H){return{name:F.name,value:I}}):{name:F.name,value:G}}).get()}});o.each("ajaxStart,ajaxStop,ajaxComplete,ajaxError,ajaxSuccess,ajaxSend".split(","),function(E,F){o.fn[F]=function(G){return this.bind(F,G)}});var r=e();o.extend({get:function(E,G,H,F){if(o.isFunction(G)){H=G;G=null}return o.ajax({type:"GET",url:E,data:G,success:H,dataType:F})},getScript:function(E,F){return o.get(E,null,F,"script")},getJSON:function(E,F,G){return o.get(E,F,G,"json")},post:function(E,G,H,F){if(o.isFunction(G)){H=G;G={}}return o.ajax({type:"POST",url:E,data:G,success:H,dataType:F})},ajaxSetup:function(E){o.extend(o.ajaxSettings,E)},ajaxSettings:{url:location.href,global:true,type:"GET",contentType:"application/x-www-form-urlencoded",processData:true,async:true,xhr:function(){return l.ActiveXObject?new ActiveXObject("Microsoft.XMLHTTP"):new XMLHttpRequest()},accepts:{xml:"application/xml, text/xml",html:"text/html",script:"text/javascript, application/javascript",json:"application/json, text/javascript",text:"text/plain",_default:"*/*"}},lastModified:{},ajax:function(M){M=o.extend(true,M,o.extend(true,{},o.ajaxSettings,M));var W,F=/=\?(&|$)/g,R,V,G=M.type.toUpperCase();if(M.data&&M.processData&&typeof M.data!=="string"){M.data=o.param(M.data)}if(M.dataType=="jsonp"){if(G=="GET"){if(!M.url.match(F)){M.url+=(M.url.match(/\?/)?"&":"?")+(M.jsonp||"callback")+"=?"}}else{if(!M.data||!M.data.match(F)){M.data=(M.data?M.data+"&":"")+(M.jsonp||"callback")+"=?"}}M.dataType="json"}if(M.dataType=="json"&&(M.data&&M.data.match(F)||M.url.match(F))){W="jsonp"+r++;if(M.data){M.data=(M.data+"").replace(F,"="+W+"$1")}M.url=M.url.replace(F,"="+W+"$1");M.dataType="script";l[W]=function(X){V=X;I();L();l[W]=g;try{delete l[W]}catch(Y){}if(H){H.removeChild(T)}}}if(M.dataType=="script"&&M.cache==null){M.cache=false}if(M.cache===false&&G=="GET"){var E=e();var U=M.url.replace(/(\?|&)_=.*?(&|$)/,"$1_="+E+"$2");M.url=U+((U==M.url)?(M.url.match(/\?/)?"&":"?")+"_="+E:"")}if(M.data&&G=="GET"){M.url+=(M.url.match(/\?/)?"&":"?")+M.data;M.data=null}if(M.global&&!o.active++){o.event.trigger("ajaxStart")}var Q=/^(\w+:)?\/\/([^\/?#]+)/.exec(M.url);if(M.dataType=="script"&&G=="GET"&&Q&&(Q[1]&&Q[1]!=location.protocol||Q[2]!=location.host)){var H=document.getElementsByTagName("head")[0];var T=document.createElement("script");T.src=M.url;if(M.scriptCharset){T.charset=M.scriptCharset}if(!W){var O=false;T.onload=T.onreadystatechange=function(){if(!O&&(!this.readyState||this.readyState=="loaded"||this.readyState=="complete")){O=true;I();L();T.onload=T.onreadystatechange=null;H.removeChild(T)}}}H.appendChild(T);return g}var K=false;var J=M.xhr();if(M.username){J.open(G,M.url,M.async,M.username,M.password)}else{J.open(G,M.url,M.async)}try{if(M.data){J.setRequestHeader("Content-Type",M.contentType)}if(M.ifModified){J.setRequestHeader("If-Modified-Since",o.lastModified[M.url]||"Thu, 01 Jan 1970 00:00:00 GMT")}J.setRequestHeader("X-Requested-With","XMLHttpRequest");J.setRequestHeader("Accept",M.dataType&&M.accepts[M.dataType]?M.accepts[M.dataType]+", */*":M.accepts._default)}catch(S){}if(M.beforeSend&&M.beforeSend(J,M)===false){if(M.global&&!--o.active){o.event.trigger("ajaxStop")}J.abort();return false}if(M.global){o.event.trigger("ajaxSend",[J,M])}var N=function(X){if(J.readyState==0){if(P){clearInterval(P);P=null;if(M.global&&!--o.active){o.event.trigger("ajaxStop")}}}else{if(!K&&J&&(J.readyState==4||X=="timeout")){K=true;if(P){clearInterval(P);P=null}R=X=="timeout"?"timeout":!o.httpSuccess(J)?"error":M.ifModified&&o.httpNotModified(J,M.url)?"notmodified":"success";if(R=="success"){try{V=o.httpData(J,M.dataType,M)}catch(Z){R="parsererror"}}if(R=="success"){var Y;try{Y=J.getResponseHeader("Last-Modified")}catch(Z){}if(M.ifModified&&Y){o.lastModified[M.url]=Y}if(!W){I()}}else{o.handleError(M,J,R)}L();if(X){J.abort()}if(M.async){J=null}}}};if(M.async){var P=setInterval(N,13);if(M.timeout>0){setTimeout(function(){if(J&&!K){N("timeout")}},M.timeout)}}try{J.send(M.data)}catch(S){o.handleError(M,J,null,S)}if(!M.async){N()}function I(){if(M.success){M.success(V,R)}if(M.global){o.event.trigger("ajaxSuccess",[J,M])}}function L(){if(M.complete){M.complete(J,R)}if(M.global){o.event.trigger("ajaxComplete",[J,M])}if(M.global&&!--o.active){o.event.trigger("ajaxStop")}}return J},handleError:function(F,H,E,G){if(F.error){F.error(H,E,G)}if(F.global){o.event.trigger("ajaxError",[H,F,G])}},active:0,httpSuccess:function(F){try{return !F.status&&location.protocol=="file:"||(F.status>=200&&F.status<300)||F.status==304||F.status==1223}catch(E){}return false},httpNotModified:function(G,E){try{var H=G.getResponseHeader("Last-Modified");return G.status==304||H==o.lastModified[E]}catch(F){}return false},httpData:function(J,H,G){var F=J.getResponseHeader("content-type"),E=H=="xml"||!H&&F&&F.indexOf("xml")>=0,I=E?J.responseXML:J.responseText;if(E&&I.documentElement.tagName=="parsererror"){throw"parsererror"}if(G&&G.dataFilter){I=G.dataFilter(I,H)}if(typeof I==="string"){if(H=="script"){o.globalEval(I)}if(H=="json"){I=l["eval"]("("+I+")")}}return I},param:function(E){var G=[];function H(I,J){G[G.length]=encodeURIComponent(I)+"="+encodeURIComponent(J)}if(o.isArray(E)||E.jquery){o.each(E,function(){H(this.name,this.value)})}else{for(var F in E){if(o.isArray(E[F])){o.each(E[F],function(){H(F,this)})}else{H(F,o.isFunction(E[F])?E[F]():E[F])}}}return G.join("&").replace(/%20/g,"+")}});var m={},n,d=[["height","marginTop","marginBottom","paddingTop","paddingBottom"],["width","marginLeft","marginRight","paddingLeft","paddingRight"],["opacity"]];function t(F,E){var G={};o.each(d.concat.apply([],d.slice(0,E)),function(){G[this]=F});return G}o.fn.extend({show:function(J,L){if(J){return this.animate(t("show",3),J,L)}else{for(var H=0,F=this.length;H").appendTo("body");K=I.css("display");if(K==="none"){K="block"}I.remove();m[G]=K}o.data(this[H],"olddisplay",K)}}for(var H=0,F=this.length;H=0;H--){if(G[H].elem==this){if(E){G[H](true)}G.splice(H,1)}}});if(!E){this.dequeue()}return this}});o.each({slideDown:t("show",1),slideUp:t("hide",1),slideToggle:t("toggle",1),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"}},function(E,F){o.fn[E]=function(G,H){return this.animate(F,G,H)}});o.extend({speed:function(G,H,F){var E=typeof G==="object"?G:{complete:F||!F&&H||o.isFunction(G)&&G,duration:G,easing:F&&H||H&&!o.isFunction(H)&&H};E.duration=o.fx.off?0:typeof E.duration==="number"?E.duration:o.fx.speeds[E.duration]||o.fx.speeds._default;E.old=E.complete;E.complete=function(){if(E.queue!==false){o(this).dequeue()}if(o.isFunction(E.old)){E.old.call(this)}};return E},easing:{linear:function(G,H,E,F){return E+F*G},swing:function(G,H,E,F){return((-Math.cos(G*Math.PI)/2)+0.5)*F+E}},timers:[],fx:function(F,E,G){this.options=E;this.elem=F;this.prop=G;if(!E.orig){E.orig={}}}});o.fx.prototype={update:function(){if(this.options.step){this.options.step.call(this.elem,this.now,this)}(o.fx.step[this.prop]||o.fx.step._default)(this);if((this.prop=="height"||this.prop=="width")&&this.elem.style){this.elem.style.display="block"}},cur:function(F){if(this.elem[this.prop]!=null&&(!this.elem.style||this.elem.style[this.prop]==null)){return this.elem[this.prop]}var E=parseFloat(o.css(this.elem,this.prop,F));return E&&E>-10000?E:parseFloat(o.curCSS(this.elem,this.prop))||0},custom:function(I,H,G){this.startTime=e();this.start=I;this.end=H;this.unit=G||this.unit||"px";this.now=this.start;this.pos=this.state=0;var E=this;function F(J){return E.step(J)}F.elem=this.elem;if(F()&&o.timers.push(F)&&!n){n=setInterval(function(){var K=o.timers;for(var J=0;J=this.options.duration+this.startTime){this.now=this.end;this.pos=this.state=1;this.update();this.options.curAnim[this.prop]=true;var E=true;for(var F in this.options.curAnim){if(this.options.curAnim[F]!==true){E=false}}if(E){if(this.options.display!=null){this.elem.style.overflow=this.options.overflow;this.elem.style.display=this.options.display;if(o.css(this.elem,"display")=="none"){this.elem.style.display="block"}}if(this.options.hide){o(this.elem).hide()}if(this.options.hide||this.options.show){for(var I in this.options.curAnim){o.attr(this.elem.style,I,this.options.orig[I])}}this.options.complete.call(this.elem)}return false}else{var J=G-this.startTime;this.state=J/this.options.duration;this.pos=o.easing[this.options.easing||(o.easing.swing?"swing":"linear")](this.state,J,0,1,this.options.duration);this.now=this.start+((this.end-this.start)*this.pos);this.update()}return true}};o.extend(o.fx,{speeds:{slow:600,fast:200,_default:400},step:{opacity:function(E){o.attr(E.elem.style,"opacity",E.now)},_default:function(E){if(E.elem.style&&E.elem.style[E.prop]!=null){E.elem.style[E.prop]=E.now+E.unit}else{E.elem[E.prop]=E.now}}}});if(document.documentElement.getBoundingClientRect){o.fn.offset=function(){if(!this[0]){return{top:0,left:0}}if(this[0]===this[0].ownerDocument.body){return o.offset.bodyOffset(this[0])}var G=this[0].getBoundingClientRect(),J=this[0].ownerDocument,F=J.body,E=J.documentElement,L=E.clientTop||F.clientTop||0,K=E.clientLeft||F.clientLeft||0,I=G.top+(self.pageYOffset||o.boxModel&&E.scrollTop||F.scrollTop)-L,H=G.left+(self.pageXOffset||o.boxModel&&E.scrollLeft||F.scrollLeft)-K;return{top:I,left:H}}}else{o.fn.offset=function(){if(!this[0]){return{top:0,left:0}}if(this[0]===this[0].ownerDocument.body){return o.offset.bodyOffset(this[0])}o.offset.initialized||o.offset.initialize();var J=this[0],G=J.offsetParent,F=J,O=J.ownerDocument,M,H=O.documentElement,K=O.body,L=O.defaultView,E=L.getComputedStyle(J,null),N=J.offsetTop,I=J.offsetLeft;while((J=J.parentNode)&&J!==K&&J!==H){M=L.getComputedStyle(J,null);N-=J.scrollTop,I-=J.scrollLeft;if(J===G){N+=J.offsetTop,I+=J.offsetLeft;if(o.offset.doesNotAddBorder&&!(o.offset.doesAddBorderForTableAndCells&&/^t(able|d|h)$/i.test(J.tagName))){N+=parseInt(M.borderTopWidth,10)||0,I+=parseInt(M.borderLeftWidth,10)||0}F=G,G=J.offsetParent}if(o.offset.subtractsBorderForOverflowNotVisible&&M.overflow!=="visible"){N+=parseInt(M.borderTopWidth,10)||0,I+=parseInt(M.borderLeftWidth,10)||0}E=M}if(E.position==="relative"||E.position==="static"){N+=K.offsetTop,I+=K.offsetLeft}if(E.position==="fixed"){N+=Math.max(H.scrollTop,K.scrollTop),I+=Math.max(H.scrollLeft,K.scrollLeft)}return{top:N,left:I}}}o.offset={initialize:function(){if(this.initialized){return}var L=document.body,F=document.createElement("div"),H,G,N,I,M,E,J=L.style.marginTop,K='
';M={position:"absolute",top:0,left:0,margin:0,border:0,width:"1px",height:"1px",visibility:"hidden"};for(E in M){F.style[E]=M[E]}F.innerHTML=K;L.insertBefore(F,L.firstChild);H=F.firstChild,G=H.firstChild,I=H.nextSibling.firstChild.firstChild;this.doesNotAddBorder=(G.offsetTop!==5);this.doesAddBorderForTableAndCells=(I.offsetTop===5);H.style.overflow="hidden",H.style.position="relative";this.subtractsBorderForOverflowNotVisible=(G.offsetTop===-5);L.style.marginTop="1px";this.doesNotIncludeMarginInBodyOffset=(L.offsetTop===0);L.style.marginTop=J;L.removeChild(F);this.initialized=true},bodyOffset:function(E){o.offset.initialized||o.offset.initialize();var G=E.offsetTop,F=E.offsetLeft;if(o.offset.doesNotIncludeMarginInBodyOffset){G+=parseInt(o.curCSS(E,"marginTop",true),10)||0,F+=parseInt(o.curCSS(E,"marginLeft",true),10)||0}return{top:G,left:F}}};o.fn.extend({position:function(){var I=0,H=0,F;if(this[0]){var G=this.offsetParent(),J=this.offset(),E=/^body|html$/i.test(G[0].tagName)?{top:0,left:0}:G.offset();J.top-=j(this,"marginTop");J.left-=j(this,"marginLeft");E.top+=j(G,"borderTopWidth");E.left+=j(G,"borderLeftWidth");F={top:J.top-E.top,left:J.left-E.left}}return F},offsetParent:function(){var E=this[0].offsetParent||document.body;while(E&&(!/^body|html$/i.test(E.tagName)&&o.css(E,"position")=="static")){E=E.offsetParent}return o(E)}});o.each(["Left","Top"],function(F,E){var G="scroll"+E;o.fn[G]=function(H){if(!this[0]){return null}return H!==g?this.each(function(){this==l||this==document?l.scrollTo(!F?H:o(l).scrollLeft(),F?H:o(l).scrollTop()):this[G]=H}):this[0]==l||this[0]==document?self[F?"pageYOffset":"pageXOffset"]||o.boxModel&&document.documentElement[G]||document.body[G]:this[0][G]}});o.each(["Height","Width"],function(I,G){var E=I?"Left":"Top",H=I?"Right":"Bottom",F=G.toLowerCase();o.fn["inner"+G]=function(){return this[0]?o.css(this[0],F,false,"padding"):null};o.fn["outer"+G]=function(K){return this[0]?o.css(this[0],F,false,K?"margin":"border"):null};var J=G.toLowerCase();o.fn[J]=function(K){return this[0]==l?document.compatMode=="CSS1Compat"&&document.documentElement["client"+G]||document.body["client"+G]:this[0]==document?Math.max(document.documentElement["client"+G],document.body["scroll"+G],document.documentElement["scroll"+G],document.body["offset"+G],document.documentElement["offset"+G]):K===g?(this.length?o.css(this[0],J):null):this.css(J,typeof K==="string"?K:K+"px")}})})(); \ No newline at end of file diff -Nru python-coverage-3.4/coverage/htmlfiles/jquery-1.4.3.min.js python-coverage-3.6/coverage/htmlfiles/jquery-1.4.3.min.js --- python-coverage-3.4/coverage/htmlfiles/jquery-1.4.3.min.js 1970-01-01 00:00:00.000000000 +0000 +++ python-coverage-3.6/coverage/htmlfiles/jquery-1.4.3.min.js 2010-11-07 13:57:01.000000000 +0000 @@ -0,0 +1,166 @@ +/*! + * jQuery JavaScript Library v1.4.3 + * http://jquery.com/ + * + * Copyright 2010, John Resig + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * Includes Sizzle.js + * http://sizzlejs.com/ + * Copyright 2010, The Dojo Foundation + * Released under the MIT, BSD, and GPL Licenses. + * + * Date: Thu Oct 14 23:10:06 2010 -0400 + */ +(function(E,A){function U(){return false}function ba(){return true}function ja(a,b,d){d[0].type=a;return c.event.handle.apply(b,d)}function Ga(a){var b,d,e=[],f=[],h,k,l,n,s,v,B,D;k=c.data(this,this.nodeType?"events":"__events__");if(typeof k==="function")k=k.events;if(!(a.liveFired===this||!k||!k.live||a.button&&a.type==="click")){if(a.namespace)D=RegExp("(^|\\.)"+a.namespace.split(".").join("\\.(?:.*\\.)?")+"(\\.|$)");a.liveFired=this;var H=k.live.slice(0);for(n=0;nd)break;a.currentTarget=f.elem;a.data=f.handleObj.data; +a.handleObj=f.handleObj;D=f.handleObj.origHandler.apply(f.elem,arguments);if(D===false||a.isPropagationStopped()){d=f.level;if(D===false)b=false}}return b}}function Y(a,b){return(a&&a!=="*"?a+".":"")+b.replace(Ha,"`").replace(Ia,"&")}function ka(a,b,d){if(c.isFunction(b))return c.grep(a,function(f,h){return!!b.call(f,h,f)===d});else if(b.nodeType)return c.grep(a,function(f){return f===b===d});else if(typeof b==="string"){var e=c.grep(a,function(f){return f.nodeType===1});if(Ja.test(b))return c.filter(b, +e,!d);else b=c.filter(b,e)}return c.grep(a,function(f){return c.inArray(f,b)>=0===d})}function la(a,b){var d=0;b.each(function(){if(this.nodeName===(a[d]&&a[d].nodeName)){var e=c.data(a[d++]),f=c.data(this,e);if(e=e&&e.events){delete f.handle;f.events={};for(var h in e)for(var k in e[h])c.event.add(this,h,e[h][k],e[h][k].data)}}})}function Ka(a,b){b.src?c.ajax({url:b.src,async:false,dataType:"script"}):c.globalEval(b.text||b.textContent||b.innerHTML||"");b.parentNode&&b.parentNode.removeChild(b)} +function ma(a,b,d){var e=b==="width"?a.offsetWidth:a.offsetHeight;if(d==="border")return e;c.each(b==="width"?La:Ma,function(){d||(e-=parseFloat(c.css(a,"padding"+this))||0);if(d==="margin")e+=parseFloat(c.css(a,"margin"+this))||0;else e-=parseFloat(c.css(a,"border"+this+"Width"))||0});return e}function ca(a,b,d,e){if(c.isArray(b)&&b.length)c.each(b,function(f,h){d||Na.test(a)?e(a,h):ca(a+"["+(typeof h==="object"||c.isArray(h)?f:"")+"]",h,d,e)});else if(!d&&b!=null&&typeof b==="object")c.isEmptyObject(b)? +e(a,""):c.each(b,function(f,h){ca(a+"["+f+"]",h,d,e)});else e(a,b)}function S(a,b){var d={};c.each(na.concat.apply([],na.slice(0,b)),function(){d[this]=a});return d}function oa(a){if(!da[a]){var b=c("<"+a+">").appendTo("body"),d=b.css("display");b.remove();if(d==="none"||d==="")d="block";da[a]=d}return da[a]}function ea(a){return c.isWindow(a)?a:a.nodeType===9?a.defaultView||a.parentWindow:false}var u=E.document,c=function(){function a(){if(!b.isReady){try{u.documentElement.doScroll("left")}catch(i){setTimeout(a, +1);return}b.ready()}}var b=function(i,r){return new b.fn.init(i,r)},d=E.jQuery,e=E.$,f,h=/^(?:[^<]*(<[\w\W]+>)[^>]*$|#([\w\-]+)$)/,k=/\S/,l=/^\s+/,n=/\s+$/,s=/\W/,v=/\d/,B=/^<(\w+)\s*\/?>(?:<\/\1>)?$/,D=/^[\],:{}\s]*$/,H=/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,w=/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,G=/(?:^|:|,)(?:\s*\[)+/g,M=/(webkit)[ \/]([\w.]+)/,g=/(opera)(?:.*version)?[ \/]([\w.]+)/,j=/(msie) ([\w.]+)/,o=/(mozilla)(?:.*? rv:([\w.]+))?/,m=navigator.userAgent,p=false, +q=[],t,x=Object.prototype.toString,C=Object.prototype.hasOwnProperty,P=Array.prototype.push,N=Array.prototype.slice,R=String.prototype.trim,Q=Array.prototype.indexOf,L={};b.fn=b.prototype={init:function(i,r){var y,z,F;if(!i)return this;if(i.nodeType){this.context=this[0]=i;this.length=1;return this}if(i==="body"&&!r&&u.body){this.context=u;this[0]=u.body;this.selector="body";this.length=1;return this}if(typeof i==="string")if((y=h.exec(i))&&(y[1]||!r))if(y[1]){F=r?r.ownerDocument||r:u;if(z=B.exec(i))if(b.isPlainObject(r)){i= +[u.createElement(z[1])];b.fn.attr.call(i,r,true)}else i=[F.createElement(z[1])];else{z=b.buildFragment([y[1]],[F]);i=(z.cacheable?z.fragment.cloneNode(true):z.fragment).childNodes}return b.merge(this,i)}else{if((z=u.getElementById(y[2]))&&z.parentNode){if(z.id!==y[2])return f.find(i);this.length=1;this[0]=z}this.context=u;this.selector=i;return this}else if(!r&&!s.test(i)){this.selector=i;this.context=u;i=u.getElementsByTagName(i);return b.merge(this,i)}else return!r||r.jquery?(r||f).find(i):b(r).find(i); +else if(b.isFunction(i))return f.ready(i);if(i.selector!==A){this.selector=i.selector;this.context=i.context}return b.makeArray(i,this)},selector:"",jquery:"1.4.3",length:0,size:function(){return this.length},toArray:function(){return N.call(this,0)},get:function(i){return i==null?this.toArray():i<0?this.slice(i)[0]:this[i]},pushStack:function(i,r,y){var z=b();b.isArray(i)?P.apply(z,i):b.merge(z,i);z.prevObject=this;z.context=this.context;if(r==="find")z.selector=this.selector+(this.selector?" ": +"")+y;else if(r)z.selector=this.selector+"."+r+"("+y+")";return z},each:function(i,r){return b.each(this,i,r)},ready:function(i){b.bindReady();if(b.isReady)i.call(u,b);else q&&q.push(i);return this},eq:function(i){return i===-1?this.slice(i):this.slice(i,+i+1)},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},slice:function(){return this.pushStack(N.apply(this,arguments),"slice",N.call(arguments).join(","))},map:function(i){return this.pushStack(b.map(this,function(r,y){return i.call(r, +y,r)}))},end:function(){return this.prevObject||b(null)},push:P,sort:[].sort,splice:[].splice};b.fn.init.prototype=b.fn;b.extend=b.fn.extend=function(){var i=arguments[0]||{},r=1,y=arguments.length,z=false,F,I,K,J,fa;if(typeof i==="boolean"){z=i;i=arguments[1]||{};r=2}if(typeof i!=="object"&&!b.isFunction(i))i={};if(y===r){i=this;--r}for(;r0)){if(q){for(var r=0;i=q[r++];)i.call(u,b);q=null}b.fn.triggerHandler&&b(u).triggerHandler("ready")}}},bindReady:function(){if(!p){p=true;if(u.readyState==="complete")return setTimeout(b.ready, +1);if(u.addEventListener){u.addEventListener("DOMContentLoaded",t,false);E.addEventListener("load",b.ready,false)}else if(u.attachEvent){u.attachEvent("onreadystatechange",t);E.attachEvent("onload",b.ready);var i=false;try{i=E.frameElement==null}catch(r){}u.documentElement.doScroll&&i&&a()}}},isFunction:function(i){return b.type(i)==="function"},isArray:Array.isArray||function(i){return b.type(i)==="array"},isWindow:function(i){return i&&typeof i==="object"&&"setInterval"in i},isNaN:function(i){return i== +null||!v.test(i)||isNaN(i)},type:function(i){return i==null?String(i):L[x.call(i)]||"object"},isPlainObject:function(i){if(!i||b.type(i)!=="object"||i.nodeType||b.isWindow(i))return false;if(i.constructor&&!C.call(i,"constructor")&&!C.call(i.constructor.prototype,"isPrototypeOf"))return false;for(var r in i);return r===A||C.call(i,r)},isEmptyObject:function(i){for(var r in i)return false;return true},error:function(i){throw i;},parseJSON:function(i){if(typeof i!=="string"||!i)return null;i=b.trim(i); +if(D.test(i.replace(H,"@").replace(w,"]").replace(G,"")))return E.JSON&&E.JSON.parse?E.JSON.parse(i):(new Function("return "+i))();else b.error("Invalid JSON: "+i)},noop:function(){},globalEval:function(i){if(i&&k.test(i)){var r=u.getElementsByTagName("head")[0]||u.documentElement,y=u.createElement("script");y.type="text/javascript";if(b.support.scriptEval)y.appendChild(u.createTextNode(i));else y.text=i;r.insertBefore(y,r.firstChild);r.removeChild(y)}},nodeName:function(i,r){return i.nodeName&&i.nodeName.toUpperCase()=== +r.toUpperCase()},each:function(i,r,y){var z,F=0,I=i.length,K=I===A||b.isFunction(i);if(y)if(K)for(z in i){if(r.apply(i[z],y)===false)break}else for(;F";a=u.createDocumentFragment();a.appendChild(d.firstChild);c.support.checkClone=a.cloneNode(true).cloneNode(true).lastChild.checked;c(function(){var s=u.createElement("div"); +s.style.width=s.style.paddingLeft="1px";u.body.appendChild(s);c.boxModel=c.support.boxModel=s.offsetWidth===2;if("zoom"in s.style){s.style.display="inline";s.style.zoom=1;c.support.inlineBlockNeedsLayout=s.offsetWidth===2;s.style.display="";s.innerHTML="
";c.support.shrinkWrapBlocks=s.offsetWidth!==2}s.innerHTML="
t
";var v=s.getElementsByTagName("td");c.support.reliableHiddenOffsets=v[0].offsetHeight=== +0;v[0].style.display="";v[1].style.display="none";c.support.reliableHiddenOffsets=c.support.reliableHiddenOffsets&&v[0].offsetHeight===0;s.innerHTML="";u.body.removeChild(s).style.display="none"});a=function(s){var v=u.createElement("div");s="on"+s;var B=s in v;if(!B){v.setAttribute(s,"return;");B=typeof v[s]==="function"}return B};c.support.submitBubbles=a("submit");c.support.changeBubbles=a("change");a=b=d=f=h=null}})();c.props={"for":"htmlFor","class":"className",readonly:"readOnly",maxlength:"maxLength", +cellspacing:"cellSpacing",rowspan:"rowSpan",colspan:"colSpan",tabindex:"tabIndex",usemap:"useMap",frameborder:"frameBorder"};var pa={},Oa=/^(?:\{.*\}|\[.*\])$/;c.extend({cache:{},uuid:0,expando:"jQuery"+c.now(),noData:{embed:true,object:"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000",applet:true},data:function(a,b,d){if(c.acceptData(a)){a=a==E?pa:a;var e=a.nodeType,f=e?a[c.expando]:null,h=c.cache;if(!(e&&!f&&typeof b==="string"&&d===A)){if(e)f||(a[c.expando]=f=++c.uuid);else h=a;if(typeof b==="object")if(e)h[f]= +c.extend(h[f],b);else c.extend(h,b);else if(e&&!h[f])h[f]={};a=e?h[f]:h;if(d!==A)a[b]=d;return typeof b==="string"?a[b]:a}}},removeData:function(a,b){if(c.acceptData(a)){a=a==E?pa:a;var d=a.nodeType,e=d?a[c.expando]:a,f=c.cache,h=d?f[e]:e;if(b){if(h){delete h[b];d&&c.isEmptyObject(h)&&c.removeData(a)}}else if(d&&c.support.deleteExpando)delete a[c.expando];else if(a.removeAttribute)a.removeAttribute(c.expando);else if(d)delete f[e];else for(var k in a)delete a[k]}},acceptData:function(a){if(a.nodeName){var b= +c.noData[a.nodeName.toLowerCase()];if(b)return!(b===true||a.getAttribute("classid")!==b)}return true}});c.fn.extend({data:function(a,b){if(typeof a==="undefined")return this.length?c.data(this[0]):null;else if(typeof a==="object")return this.each(function(){c.data(this,a)});var d=a.split(".");d[1]=d[1]?"."+d[1]:"";if(b===A){var e=this.triggerHandler("getData"+d[1]+"!",[d[0]]);if(e===A&&this.length){e=c.data(this[0],a);if(e===A&&this[0].nodeType===1){e=this[0].getAttribute("data-"+a);if(typeof e=== +"string")try{e=e==="true"?true:e==="false"?false:e==="null"?null:!c.isNaN(e)?parseFloat(e):Oa.test(e)?c.parseJSON(e):e}catch(f){}else e=A}}return e===A&&d[1]?this.data(d[0]):e}else return this.each(function(){var h=c(this),k=[d[0],b];h.triggerHandler("setData"+d[1]+"!",k);c.data(this,a,b);h.triggerHandler("changeData"+d[1]+"!",k)})},removeData:function(a){return this.each(function(){c.removeData(this,a)})}});c.extend({queue:function(a,b,d){if(a){b=(b||"fx")+"queue";var e=c.data(a,b);if(!d)return e|| +[];if(!e||c.isArray(d))e=c.data(a,b,c.makeArray(d));else e.push(d);return e}},dequeue:function(a,b){b=b||"fx";var d=c.queue(a,b),e=d.shift();if(e==="inprogress")e=d.shift();if(e){b==="fx"&&d.unshift("inprogress");e.call(a,function(){c.dequeue(a,b)})}}});c.fn.extend({queue:function(a,b){if(typeof a!=="string"){b=a;a="fx"}if(b===A)return c.queue(this[0],a);return this.each(function(){var d=c.queue(this,a,b);a==="fx"&&d[0]!=="inprogress"&&c.dequeue(this,a)})},dequeue:function(a){return this.each(function(){c.dequeue(this, +a)})},delay:function(a,b){a=c.fx?c.fx.speeds[a]||a:a;b=b||"fx";return this.queue(b,function(){var d=this;setTimeout(function(){c.dequeue(d,b)},a)})},clearQueue:function(a){return this.queue(a||"fx",[])}});var qa=/[\n\t]/g,ga=/\s+/,Pa=/\r/g,Qa=/^(?:href|src|style)$/,Ra=/^(?:button|input)$/i,Sa=/^(?:button|input|object|select|textarea)$/i,Ta=/^a(?:rea)?$/i,ra=/^(?:radio|checkbox)$/i;c.fn.extend({attr:function(a,b){return c.access(this,a,b,true,c.attr)},removeAttr:function(a){return this.each(function(){c.attr(this, +a,"");this.nodeType===1&&this.removeAttribute(a)})},addClass:function(a){if(c.isFunction(a))return this.each(function(s){var v=c(this);v.addClass(a.call(this,s,v.attr("class")))});if(a&&typeof a==="string")for(var b=(a||"").split(ga),d=0,e=this.length;d-1)return true;return false}, +val:function(a){if(!arguments.length){var b=this[0];if(b){if(c.nodeName(b,"option")){var d=b.attributes.value;return!d||d.specified?b.value:b.text}if(c.nodeName(b,"select")){var e=b.selectedIndex;d=[];var f=b.options;b=b.type==="select-one";if(e<0)return null;var h=b?e:0;for(e=b?e+1:f.length;h=0;else if(c.nodeName(this,"select")){var B=c.makeArray(v);c("option",this).each(function(){this.selected= +c.inArray(c(this).val(),B)>=0});if(!B.length)this.selectedIndex=-1}else this.value=v}})}});c.extend({attrFn:{val:true,css:true,html:true,text:true,data:true,width:true,height:true,offset:true},attr:function(a,b,d,e){if(!a||a.nodeType===3||a.nodeType===8)return A;if(e&&b in c.attrFn)return c(a)[b](d);e=a.nodeType!==1||!c.isXMLDoc(a);var f=d!==A;b=e&&c.props[b]||b;if(a.nodeType===1){var h=Qa.test(b);if((b in a||a[b]!==A)&&e&&!h){if(f){b==="type"&&Ra.test(a.nodeName)&&a.parentNode&&c.error("type property can't be changed"); +if(d===null)a.nodeType===1&&a.removeAttribute(b);else a[b]=d}if(c.nodeName(a,"form")&&a.getAttributeNode(b))return a.getAttributeNode(b).nodeValue;if(b==="tabIndex")return(b=a.getAttributeNode("tabIndex"))&&b.specified?b.value:Sa.test(a.nodeName)||Ta.test(a.nodeName)&&a.href?0:A;return a[b]}if(!c.support.style&&e&&b==="style"){if(f)a.style.cssText=""+d;return a.style.cssText}f&&a.setAttribute(b,""+d);if(!a.attributes[b]&&a.hasAttribute&&!a.hasAttribute(b))return A;a=!c.support.hrefNormalized&&e&& +h?a.getAttribute(b,2):a.getAttribute(b);return a===null?A:a}}});var X=/\.(.*)$/,ha=/^(?:textarea|input|select)$/i,Ha=/\./g,Ia=/ /g,Ua=/[^\w\s.|`]/g,Va=function(a){return a.replace(Ua,"\\$&")},sa={focusin:0,focusout:0};c.event={add:function(a,b,d,e){if(!(a.nodeType===3||a.nodeType===8)){if(c.isWindow(a)&&a!==E&&!a.frameElement)a=E;if(d===false)d=U;var f,h;if(d.handler){f=d;d=f.handler}if(!d.guid)d.guid=c.guid++;if(h=c.data(a)){var k=a.nodeType?"events":"__events__",l=h[k],n=h.handle;if(typeof l=== +"function"){n=l.handle;l=l.events}else if(!l){a.nodeType||(h[k]=h=function(){});h.events=l={}}if(!n)h.handle=n=function(){return typeof c!=="undefined"&&!c.event.triggered?c.event.handle.apply(n.elem,arguments):A};n.elem=a;b=b.split(" ");for(var s=0,v;k=b[s++];){h=f?c.extend({},f):{handler:d,data:e};if(k.indexOf(".")>-1){v=k.split(".");k=v.shift();h.namespace=v.slice(0).sort().join(".")}else{v=[];h.namespace=""}h.type=k;if(!h.guid)h.guid=d.guid;var B=l[k],D=c.event.special[k]||{};if(!B){B=l[k]=[]; +if(!D.setup||D.setup.call(a,e,v,n)===false)if(a.addEventListener)a.addEventListener(k,n,false);else a.attachEvent&&a.attachEvent("on"+k,n)}if(D.add){D.add.call(a,h);if(!h.handler.guid)h.handler.guid=d.guid}B.push(h);c.event.global[k]=true}a=null}}},global:{},remove:function(a,b,d,e){if(!(a.nodeType===3||a.nodeType===8)){if(d===false)d=U;var f,h,k=0,l,n,s,v,B,D,H=a.nodeType?"events":"__events__",w=c.data(a),G=w&&w[H];if(w&&G){if(typeof G==="function"){w=G;G=G.events}if(b&&b.type){d=b.handler;b=b.type}if(!b|| +typeof b==="string"&&b.charAt(0)==="."){b=b||"";for(f in G)c.event.remove(a,f+b)}else{for(b=b.split(" ");f=b[k++];){v=f;l=f.indexOf(".")<0;n=[];if(!l){n=f.split(".");f=n.shift();s=RegExp("(^|\\.)"+c.map(n.slice(0).sort(),Va).join("\\.(?:.*\\.)?")+"(\\.|$)")}if(B=G[f])if(d){v=c.event.special[f]||{};for(h=e||0;h=0){a.type= +f=f.slice(0,-1);a.exclusive=true}if(!d){a.stopPropagation();c.event.global[f]&&c.each(c.cache,function(){this.events&&this.events[f]&&c.event.trigger(a,b,this.handle.elem)})}if(!d||d.nodeType===3||d.nodeType===8)return A;a.result=A;a.target=d;b=c.makeArray(b);b.unshift(a)}a.currentTarget=d;(e=d.nodeType?c.data(d,"handle"):(c.data(d,"__events__")||{}).handle)&&e.apply(d,b);e=d.parentNode||d.ownerDocument;try{if(!(d&&d.nodeName&&c.noData[d.nodeName.toLowerCase()]))if(d["on"+f]&&d["on"+f].apply(d,b)=== +false){a.result=false;a.preventDefault()}}catch(h){}if(!a.isPropagationStopped()&&e)c.event.trigger(a,b,e,true);else if(!a.isDefaultPrevented()){e=a.target;var k,l=f.replace(X,""),n=c.nodeName(e,"a")&&l==="click",s=c.event.special[l]||{};if((!s._default||s._default.call(d,a)===false)&&!n&&!(e&&e.nodeName&&c.noData[e.nodeName.toLowerCase()])){try{if(e[l]){if(k=e["on"+l])e["on"+l]=null;c.event.triggered=true;e[l]()}}catch(v){}if(k)e["on"+l]=k;c.event.triggered=false}}},handle:function(a){var b,d,e; +d=[];var f,h=c.makeArray(arguments);a=h[0]=c.event.fix(a||E.event);a.currentTarget=this;b=a.type.indexOf(".")<0&&!a.exclusive;if(!b){e=a.type.split(".");a.type=e.shift();d=e.slice(0).sort();e=RegExp("(^|\\.)"+d.join("\\.(?:.*\\.)?")+"(\\.|$)")}a.namespace=a.namespace||d.join(".");f=c.data(this,this.nodeType?"events":"__events__");if(typeof f==="function")f=f.events;d=(f||{})[a.type];if(f&&d){d=d.slice(0);f=0;for(var k=d.length;f-1?c.map(a.options,function(e){return e.selected}).join("-"):"";else if(a.nodeName.toLowerCase()==="select")d=a.selectedIndex;return d},Z=function(a,b){var d=a.target,e,f;if(!(!ha.test(d.nodeName)||d.readOnly)){e=c.data(d,"_change_data");f=va(d);if(a.type!=="focusout"||d.type!=="radio")c.data(d,"_change_data",f);if(!(e===A||f===e))if(e!=null||f){a.type="change";a.liveFired= +A;return c.event.trigger(a,b,d)}}};c.event.special.change={filters:{focusout:Z,beforedeactivate:Z,click:function(a){var b=a.target,d=b.type;if(d==="radio"||d==="checkbox"||b.nodeName.toLowerCase()==="select")return Z.call(this,a)},keydown:function(a){var b=a.target,d=b.type;if(a.keyCode===13&&b.nodeName.toLowerCase()!=="textarea"||a.keyCode===32&&(d==="checkbox"||d==="radio")||d==="select-multiple")return Z.call(this,a)},beforeactivate:function(a){a=a.target;c.data(a,"_change_data",va(a))}},setup:function(){if(this.type=== +"file")return false;for(var a in V)c.event.add(this,a+".specialChange",V[a]);return ha.test(this.nodeName)},teardown:function(){c.event.remove(this,".specialChange");return ha.test(this.nodeName)}};V=c.event.special.change.filters;V.focus=V.beforeactivate}u.addEventListener&&c.each({focus:"focusin",blur:"focusout"},function(a,b){function d(e){e=c.event.fix(e);e.type=b;return c.event.trigger(e,null,e.target)}c.event.special[b]={setup:function(){sa[b]++===0&&u.addEventListener(a,d,true)},teardown:function(){--sa[b]=== +0&&u.removeEventListener(a,d,true)}}});c.each(["bind","one"],function(a,b){c.fn[b]=function(d,e,f){if(typeof d==="object"){for(var h in d)this[b](h,e,d[h],f);return this}if(c.isFunction(e)||e===false){f=e;e=A}var k=b==="one"?c.proxy(f,function(n){c(this).unbind(n,k);return f.apply(this,arguments)}):f;if(d==="unload"&&b!=="one")this.one(d,e,f);else{h=0;for(var l=this.length;h0?this.bind(b,d,e):this.trigger(b)};if(c.attrFn)c.attrFn[b]=true});E.attachEvent&&!E.addEventListener&&c(E).bind("unload",function(){for(var a in c.cache)if(c.cache[a].handle)try{c.event.remove(c.cache[a].handle.elem)}catch(b){}}); +(function(){function a(g,j,o,m,p,q){p=0;for(var t=m.length;p0){C=x;break}}x=x[g]}m[p]=C}}}var d=/((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^\[\]]*\]|['"][^'"]*['"]|[^\[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,e=0,f=Object.prototype.toString,h=false,k=true;[0,0].sort(function(){k=false;return 0});var l=function(g,j,o,m){o=o||[];var p=j=j||u;if(j.nodeType!==1&&j.nodeType!==9)return[];if(!g||typeof g!=="string")return o;var q=[],t,x,C,P,N=true,R=l.isXML(j),Q=g,L;do{d.exec("");if(t=d.exec(Q)){Q=t[3];q.push(t[1]);if(t[2]){P=t[3]; +break}}}while(t);if(q.length>1&&s.exec(g))if(q.length===2&&n.relative[q[0]])x=M(q[0]+q[1],j);else for(x=n.relative[q[0]]?[j]:l(q.shift(),j);q.length;){g=q.shift();if(n.relative[g])g+=q.shift();x=M(g,x)}else{if(!m&&q.length>1&&j.nodeType===9&&!R&&n.match.ID.test(q[0])&&!n.match.ID.test(q[q.length-1])){t=l.find(q.shift(),j,R);j=t.expr?l.filter(t.expr,t.set)[0]:t.set[0]}if(j){t=m?{expr:q.pop(),set:D(m)}:l.find(q.pop(),q.length===1&&(q[0]==="~"||q[0]==="+")&&j.parentNode?j.parentNode:j,R);x=t.expr?l.filter(t.expr, +t.set):t.set;if(q.length>0)C=D(x);else N=false;for(;q.length;){t=L=q.pop();if(n.relative[L])t=q.pop();else L="";if(t==null)t=j;n.relative[L](C,t,R)}}else C=[]}C||(C=x);C||l.error(L||g);if(f.call(C)==="[object Array]")if(N)if(j&&j.nodeType===1)for(g=0;C[g]!=null;g++){if(C[g]&&(C[g]===true||C[g].nodeType===1&&l.contains(j,C[g])))o.push(x[g])}else for(g=0;C[g]!=null;g++)C[g]&&C[g].nodeType===1&&o.push(x[g]);else o.push.apply(o,C);else D(C,o);if(P){l(P,p,o,m);l.uniqueSort(o)}return o};l.uniqueSort=function(g){if(w){h= +k;g.sort(w);if(h)for(var j=1;j0};l.find=function(g,j,o){var m;if(!g)return[];for(var p=0,q=n.order.length;p":function(g,j){var o=typeof j==="string",m,p=0,q=g.length;if(o&&!/\W/.test(j))for(j=j.toLowerCase();p=0))o||m.push(t);else if(o)j[q]=false;return false},ID:function(g){return g[1].replace(/\\/g,"")},TAG:function(g){return g[1].toLowerCase()},CHILD:function(g){if(g[1]==="nth"){var j=/(-?)(\d*)n((?:\+|-)?\d*)/.exec(g[2]==="even"&&"2n"||g[2]==="odd"&&"2n+1"||!/\D/.test(g[2])&&"0n+"+g[2]||g[2]);g[2]=j[1]+(j[2]||1)-0;g[3]=j[3]-0}g[0]=e++;return g},ATTR:function(g,j,o, +m,p,q){j=g[1].replace(/\\/g,"");if(!q&&n.attrMap[j])g[1]=n.attrMap[j];if(g[2]==="~=")g[4]=" "+g[4]+" ";return g},PSEUDO:function(g,j,o,m,p){if(g[1]==="not")if((d.exec(g[3])||"").length>1||/^\w/.test(g[3]))g[3]=l(g[3],null,null,j);else{g=l.filter(g[3],j,o,true^p);o||m.push.apply(m,g);return false}else if(n.match.POS.test(g[0])||n.match.CHILD.test(g[0]))return true;return g},POS:function(g){g.unshift(true);return g}},filters:{enabled:function(g){return g.disabled===false&&g.type!=="hidden"},disabled:function(g){return g.disabled=== +true},checked:function(g){return g.checked===true},selected:function(g){return g.selected===true},parent:function(g){return!!g.firstChild},empty:function(g){return!g.firstChild},has:function(g,j,o){return!!l(o[3],g).length},header:function(g){return/h\d/i.test(g.nodeName)},text:function(g){return"text"===g.type},radio:function(g){return"radio"===g.type},checkbox:function(g){return"checkbox"===g.type},file:function(g){return"file"===g.type},password:function(g){return"password"===g.type},submit:function(g){return"submit"=== +g.type},image:function(g){return"image"===g.type},reset:function(g){return"reset"===g.type},button:function(g){return"button"===g.type||g.nodeName.toLowerCase()==="button"},input:function(g){return/input|select|textarea|button/i.test(g.nodeName)}},setFilters:{first:function(g,j){return j===0},last:function(g,j,o,m){return j===m.length-1},even:function(g,j){return j%2===0},odd:function(g,j){return j%2===1},lt:function(g,j,o){return jo[3]-0},nth:function(g,j,o){return o[3]- +0===j},eq:function(g,j,o){return o[3]-0===j}},filter:{PSEUDO:function(g,j,o,m){var p=j[1],q=n.filters[p];if(q)return q(g,o,j,m);else if(p==="contains")return(g.textContent||g.innerText||l.getText([g])||"").indexOf(j[3])>=0;else if(p==="not"){j=j[3];o=0;for(m=j.length;o=0}},ID:function(g,j){return g.nodeType===1&&g.getAttribute("id")===j},TAG:function(g,j){return j==="*"&&g.nodeType===1||g.nodeName.toLowerCase()=== +j},CLASS:function(g,j){return(" "+(g.className||g.getAttribute("class"))+" ").indexOf(j)>-1},ATTR:function(g,j){var o=j[1];o=n.attrHandle[o]?n.attrHandle[o](g):g[o]!=null?g[o]:g.getAttribute(o);var m=o+"",p=j[2],q=j[4];return o==null?p==="!=":p==="="?m===q:p==="*="?m.indexOf(q)>=0:p==="~="?(" "+m+" ").indexOf(q)>=0:!q?m&&o!==false:p==="!="?m!==q:p==="^="?m.indexOf(q)===0:p==="$="?m.substr(m.length-q.length)===q:p==="|="?m===q||m.substr(0,q.length+1)===q+"-":false},POS:function(g,j,o,m){var p=n.setFilters[j[2]]; +if(p)return p(g,o,j,m)}}},s=n.match.POS,v=function(g,j){return"\\"+(j-0+1)},B;for(B in n.match){n.match[B]=RegExp(n.match[B].source+/(?![^\[]*\])(?![^\(]*\))/.source);n.leftMatch[B]=RegExp(/(^(?:.|\r|\n)*?)/.source+n.match[B].source.replace(/\\(\d+)/g,v))}var D=function(g,j){g=Array.prototype.slice.call(g,0);if(j){j.push.apply(j,g);return j}return g};try{Array.prototype.slice.call(u.documentElement.childNodes,0)}catch(H){D=function(g,j){var o=j||[],m=0;if(f.call(g)==="[object Array]")Array.prototype.push.apply(o, +g);else if(typeof g.length==="number")for(var p=g.length;m";var o=u.documentElement;o.insertBefore(g,o.firstChild);if(u.getElementById(j)){n.find.ID=function(m,p,q){if(typeof p.getElementById!=="undefined"&&!q)return(p=p.getElementById(m[1]))?p.id===m[1]||typeof p.getAttributeNode!=="undefined"&&p.getAttributeNode("id").nodeValue===m[1]?[p]:A:[]};n.filter.ID=function(m,p){var q=typeof m.getAttributeNode!=="undefined"&&m.getAttributeNode("id");return m.nodeType===1&&q&&q.nodeValue===p}}o.removeChild(g); +o=g=null})();(function(){var g=u.createElement("div");g.appendChild(u.createComment(""));if(g.getElementsByTagName("*").length>0)n.find.TAG=function(j,o){var m=o.getElementsByTagName(j[1]);if(j[1]==="*"){for(var p=[],q=0;m[q];q++)m[q].nodeType===1&&p.push(m[q]);m=p}return m};g.innerHTML="";if(g.firstChild&&typeof g.firstChild.getAttribute!=="undefined"&&g.firstChild.getAttribute("href")!=="#")n.attrHandle.href=function(j){return j.getAttribute("href",2)};g=null})();u.querySelectorAll&& +function(){var g=l,j=u.createElement("div");j.innerHTML="

";if(!(j.querySelectorAll&&j.querySelectorAll(".TEST").length===0)){l=function(m,p,q,t){p=p||u;if(!t&&!l.isXML(p))if(p.nodeType===9)try{return D(p.querySelectorAll(m),q)}catch(x){}else if(p.nodeType===1&&p.nodeName.toLowerCase()!=="object"){var C=p.id,P=p.id="__sizzle__";try{return D(p.querySelectorAll("#"+P+" "+m),q)}catch(N){}finally{if(C)p.id=C;else p.removeAttribute("id")}}return g(m,p,q,t)};for(var o in g)l[o]=g[o]; +j=null}}();(function(){var g=u.documentElement,j=g.matchesSelector||g.mozMatchesSelector||g.webkitMatchesSelector||g.msMatchesSelector,o=false;try{j.call(u.documentElement,":sizzle")}catch(m){o=true}if(j)l.matchesSelector=function(p,q){try{if(o||!n.match.PSEUDO.test(q))return j.call(p,q)}catch(t){}return l(q,null,null,[p]).length>0}})();(function(){var g=u.createElement("div");g.innerHTML="
";if(!(!g.getElementsByClassName||g.getElementsByClassName("e").length=== +0)){g.lastChild.className="e";if(g.getElementsByClassName("e").length!==1){n.order.splice(1,0,"CLASS");n.find.CLASS=function(j,o,m){if(typeof o.getElementsByClassName!=="undefined"&&!m)return o.getElementsByClassName(j[1])};g=null}}})();l.contains=u.documentElement.contains?function(g,j){return g!==j&&(g.contains?g.contains(j):true)}:function(g,j){return!!(g.compareDocumentPosition(j)&16)};l.isXML=function(g){return(g=(g?g.ownerDocument||g:0).documentElement)?g.nodeName!=="HTML":false};var M=function(g, +j){for(var o=[],m="",p,q=j.nodeType?[j]:j;p=n.match.PSEUDO.exec(g);){m+=p[0];g=g.replace(n.match.PSEUDO,"")}g=n.relative[g]?g+"*":g;p=0;for(var t=q.length;p0)for(var h=d;h0},closest:function(a, +b){var d=[],e,f,h=this[0];if(c.isArray(a)){var k={},l,n=1;if(h&&a.length){e=0;for(f=a.length;e-1:c(h).is(e))d.push({selector:l,elem:h,level:n})}h=h.parentNode;n++}}return d}k=$a.test(a)?c(a,b||this.context):null;e=0;for(f=this.length;e-1:c.find.matchesSelector(h,a)){d.push(h);break}else{h=h.parentNode;if(!h|| +!h.ownerDocument||h===b)break}d=d.length>1?c.unique(d):d;return this.pushStack(d,"closest",a)},index:function(a){if(!a||typeof a==="string")return c.inArray(this[0],a?c(a):this.parent().children());return c.inArray(a.jquery?a[0]:a,this)},add:function(a,b){var d=typeof a==="string"?c(a,b||this.context):c.makeArray(a),e=c.merge(this.get(),d);return this.pushStack(!d[0]||!d[0].parentNode||d[0].parentNode.nodeType===11||!e[0]||!e[0].parentNode||e[0].parentNode.nodeType===11?e:c.unique(e))},andSelf:function(){return this.add(this.prevObject)}}); +c.each({parent:function(a){return(a=a.parentNode)&&a.nodeType!==11?a:null},parents:function(a){return c.dir(a,"parentNode")},parentsUntil:function(a,b,d){return c.dir(a,"parentNode",d)},next:function(a){return c.nth(a,2,"nextSibling")},prev:function(a){return c.nth(a,2,"previousSibling")},nextAll:function(a){return c.dir(a,"nextSibling")},prevAll:function(a){return c.dir(a,"previousSibling")},nextUntil:function(a,b,d){return c.dir(a,"nextSibling",d)},prevUntil:function(a,b,d){return c.dir(a,"previousSibling", +d)},siblings:function(a){return c.sibling(a.parentNode.firstChild,a)},children:function(a){return c.sibling(a.firstChild)},contents:function(a){return c.nodeName(a,"iframe")?a.contentDocument||a.contentWindow.document:c.makeArray(a.childNodes)}},function(a,b){c.fn[a]=function(d,e){var f=c.map(this,b,d);Wa.test(a)||(e=d);if(e&&typeof e==="string")f=c.filter(e,f);f=this.length>1?c.unique(f):f;if((this.length>1||Ya.test(e))&&Xa.test(a))f=f.reverse();return this.pushStack(f,a,Za.call(arguments).join(","))}}); +c.extend({filter:function(a,b,d){if(d)a=":not("+a+")";return b.length===1?c.find.matchesSelector(b[0],a)?[b[0]]:[]:c.find.matches(a,b)},dir:function(a,b,d){var e=[];for(a=a[b];a&&a.nodeType!==9&&(d===A||a.nodeType!==1||!c(a).is(d));){a.nodeType===1&&e.push(a);a=a[b]}return e},nth:function(a,b,d){b=b||1;for(var e=0;a;a=a[d])if(a.nodeType===1&&++e===b)break;return a},sibling:function(a,b){for(var d=[];a;a=a.nextSibling)a.nodeType===1&&a!==b&&d.push(a);return d}});var xa=/ jQuery\d+="(?:\d+|null)"/g, +$=/^\s+/,ya=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/ig,za=/<([\w:]+)/,ab=/\s]+\/)>/g,O={option:[1,""],legend:[1,"
","
"],thead:[1,"","
"],tr:[2,"","
"],td:[3,"","
"],col:[2,"","
"], +area:[1,"",""],_default:[0,"",""]};O.optgroup=O.option;O.tbody=O.tfoot=O.colgroup=O.caption=O.thead;O.th=O.td;if(!c.support.htmlSerialize)O._default=[1,"div
","
"];c.fn.extend({text:function(a){if(c.isFunction(a))return this.each(function(b){var d=c(this);d.text(a.call(this,b,d.text()))});if(typeof a!=="object"&&a!==A)return this.empty().append((this[0]&&this[0].ownerDocument||u).createTextNode(a));return c.text(this)},wrapAll:function(a){if(c.isFunction(a))return this.each(function(d){c(this).wrapAll(a.call(this, +d))});if(this[0]){var b=c(a,this[0].ownerDocument).eq(0).clone(true);this[0].parentNode&&b.insertBefore(this[0]);b.map(function(){for(var d=this;d.firstChild&&d.firstChild.nodeType===1;)d=d.firstChild;return d}).append(this)}return this},wrapInner:function(a){if(c.isFunction(a))return this.each(function(b){c(this).wrapInner(a.call(this,b))});return this.each(function(){var b=c(this),d=b.contents();d.length?d.wrapAll(a):b.append(a)})},wrap:function(a){return this.each(function(){c(this).wrapAll(a)})}, +unwrap:function(){return this.parent().each(function(){c.nodeName(this,"body")||c(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,true,function(a){this.nodeType===1&&this.appendChild(a)})},prepend:function(){return this.domManip(arguments,true,function(a){this.nodeType===1&&this.insertBefore(a,this.firstChild)})},before:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,false,function(b){this.parentNode.insertBefore(b,this)});else if(arguments.length){var a= +c(arguments[0]);a.push.apply(a,this.toArray());return this.pushStack(a,"before",arguments)}},after:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,false,function(b){this.parentNode.insertBefore(b,this.nextSibling)});else if(arguments.length){var a=this.pushStack(this,"after",arguments);a.push.apply(a,c(arguments[0]).toArray());return a}},remove:function(a,b){for(var d=0,e;(e=this[d])!=null;d++)if(!a||c.filter(a,[e]).length){if(!b&&e.nodeType===1){c.cleanData(e.getElementsByTagName("*")); +c.cleanData([e])}e.parentNode&&e.parentNode.removeChild(e)}return this},empty:function(){for(var a=0,b;(b=this[a])!=null;a++)for(b.nodeType===1&&c.cleanData(b.getElementsByTagName("*"));b.firstChild;)b.removeChild(b.firstChild);return this},clone:function(a){var b=this.map(function(){if(!c.support.noCloneEvent&&!c.isXMLDoc(this)){var d=this.outerHTML,e=this.ownerDocument;if(!d){d=e.createElement("div");d.appendChild(this.cloneNode(true));d=d.innerHTML}return c.clean([d.replace(xa,"").replace(cb,'="$1">').replace($, +"")],e)[0]}else return this.cloneNode(true)});if(a===true){la(this,b);la(this.find("*"),b.find("*"))}return b},html:function(a){if(a===A)return this[0]&&this[0].nodeType===1?this[0].innerHTML.replace(xa,""):null;else if(typeof a==="string"&&!Aa.test(a)&&(c.support.leadingWhitespace||!$.test(a))&&!O[(za.exec(a)||["",""])[1].toLowerCase()]){a=a.replace(ya,"<$1>");try{for(var b=0,d=this.length;b0||e.cacheable||this.length>1?l.cloneNode(true):l)}k.length&&c.each(k,Ka)}return this}});c.buildFragment=function(a,b,d){var e,f,h;b=b&&b[0]?b[0].ownerDocument||b[0]:u;if(a.length===1&&typeof a[0]==="string"&&a[0].length<512&&b===u&&!Aa.test(a[0])&&(c.support.checkClone|| +!Ba.test(a[0]))){f=true;if(h=c.fragments[a[0]])if(h!==1)e=h}if(!e){e=b.createDocumentFragment();c.clean(a,b,e,d)}if(f)c.fragments[a[0]]=h?e:1;return{fragment:e,cacheable:f}};c.fragments={};c.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(a,b){c.fn[a]=function(d){var e=[];d=c(d);var f=this.length===1&&this[0].parentNode;if(f&&f.nodeType===11&&f.childNodes.length===1&&d.length===1){d[b](this[0]);return this}else{f=0;for(var h= +d.length;f0?this.clone(true):this).get();c(d[f])[b](k);e=e.concat(k)}return this.pushStack(e,a,d.selector)}}});c.extend({clean:function(a,b,d,e){b=b||u;if(typeof b.createElement==="undefined")b=b.ownerDocument||b[0]&&b[0].ownerDocument||u;for(var f=[],h=0,k;(k=a[h])!=null;h++){if(typeof k==="number")k+="";if(k){if(typeof k==="string"&&!bb.test(k))k=b.createTextNode(k);else if(typeof k==="string"){k=k.replace(ya,"<$1>");var l=(za.exec(k)||["",""])[1].toLowerCase(),n=O[l]||O._default, +s=n[0],v=b.createElement("div");for(v.innerHTML=n[1]+k+n[2];s--;)v=v.lastChild;if(!c.support.tbody){s=ab.test(k);l=l==="table"&&!s?v.firstChild&&v.firstChild.childNodes:n[1]===""&&!s?v.childNodes:[];for(n=l.length-1;n>=0;--n)c.nodeName(l[n],"tbody")&&!l[n].childNodes.length&&l[n].parentNode.removeChild(l[n])}!c.support.leadingWhitespace&&$.test(k)&&v.insertBefore(b.createTextNode($.exec(k)[0]),v.firstChild);k=v.childNodes}if(k.nodeType)f.push(k);else f=c.merge(f,k)}}if(d)for(h=0;f[h];h++)if(e&& +c.nodeName(f[h],"script")&&(!f[h].type||f[h].type.toLowerCase()==="text/javascript"))e.push(f[h].parentNode?f[h].parentNode.removeChild(f[h]):f[h]);else{f[h].nodeType===1&&f.splice.apply(f,[h+1,0].concat(c.makeArray(f[h].getElementsByTagName("script"))));d.appendChild(f[h])}return f},cleanData:function(a){for(var b,d,e=c.cache,f=c.event.special,h=c.support.deleteExpando,k=0,l;(l=a[k])!=null;k++)if(!(l.nodeName&&c.noData[l.nodeName.toLowerCase()]))if(d=l[c.expando]){if((b=e[d])&&b.events)for(var n in b.events)f[n]? +c.event.remove(l,n):c.removeEvent(l,n,b.handle);if(h)delete l[c.expando];else l.removeAttribute&&l.removeAttribute(c.expando);delete e[d]}}});var Ca=/alpha\([^)]*\)/i,db=/opacity=([^)]*)/,eb=/-([a-z])/ig,fb=/([A-Z])/g,Da=/^-?\d+(?:px)?$/i,gb=/^-?\d/,hb={position:"absolute",visibility:"hidden",display:"block"},La=["Left","Right"],Ma=["Top","Bottom"],W,ib=u.defaultView&&u.defaultView.getComputedStyle,jb=function(a,b){return b.toUpperCase()};c.fn.css=function(a,b){if(arguments.length===2&&b===A)return this; +return c.access(this,a,b,true,function(d,e,f){return f!==A?c.style(d,e,f):c.css(d,e)})};c.extend({cssHooks:{opacity:{get:function(a,b){if(b){var d=W(a,"opacity","opacity");return d===""?"1":d}else return a.style.opacity}}},cssNumber:{zIndex:true,fontWeight:true,opacity:true,zoom:true,lineHeight:true},cssProps:{"float":c.support.cssFloat?"cssFloat":"styleFloat"},style:function(a,b,d,e){if(!(!a||a.nodeType===3||a.nodeType===8||!a.style)){var f,h=c.camelCase(b),k=a.style,l=c.cssHooks[h];b=c.cssProps[h]|| +h;if(d!==A){if(!(typeof d==="number"&&isNaN(d)||d==null)){if(typeof d==="number"&&!c.cssNumber[h])d+="px";if(!l||!("set"in l)||(d=l.set(a,d))!==A)try{k[b]=d}catch(n){}}}else{if(l&&"get"in l&&(f=l.get(a,false,e))!==A)return f;return k[b]}}},css:function(a,b,d){var e,f=c.camelCase(b),h=c.cssHooks[f];b=c.cssProps[f]||f;if(h&&"get"in h&&(e=h.get(a,true,d))!==A)return e;else if(W)return W(a,b,f)},swap:function(a,b,d){var e={},f;for(f in b){e[f]=a.style[f];a.style[f]=b[f]}d.call(a);for(f in b)a.style[f]= +e[f]},camelCase:function(a){return a.replace(eb,jb)}});c.curCSS=c.css;c.each(["height","width"],function(a,b){c.cssHooks[b]={get:function(d,e,f){var h;if(e){if(d.offsetWidth!==0)h=ma(d,b,f);else c.swap(d,hb,function(){h=ma(d,b,f)});return h+"px"}},set:function(d,e){if(Da.test(e)){e=parseFloat(e);if(e>=0)return e+"px"}else return e}}});if(!c.support.opacity)c.cssHooks.opacity={get:function(a,b){return db.test((b&&a.currentStyle?a.currentStyle.filter:a.style.filter)||"")?parseFloat(RegExp.$1)/100+"": +b?"1":""},set:function(a,b){var d=a.style;d.zoom=1;var e=c.isNaN(b)?"":"alpha(opacity="+b*100+")",f=d.filter||"";d.filter=Ca.test(f)?f.replace(Ca,e):d.filter+" "+e}};if(ib)W=function(a,b,d){var e;d=d.replace(fb,"-$1").toLowerCase();if(!(b=a.ownerDocument.defaultView))return A;if(b=b.getComputedStyle(a,null)){e=b.getPropertyValue(d);if(e===""&&!c.contains(a.ownerDocument.documentElement,a))e=c.style(a,d)}return e};else if(u.documentElement.currentStyle)W=function(a,b){var d,e,f=a.currentStyle&&a.currentStyle[b], +h=a.style;if(!Da.test(f)&&gb.test(f)){d=h.left;e=a.runtimeStyle.left;a.runtimeStyle.left=a.currentStyle.left;h.left=b==="fontSize"?"1em":f||0;f=h.pixelLeft+"px";h.left=d;a.runtimeStyle.left=e}return f};if(c.expr&&c.expr.filters){c.expr.filters.hidden=function(a){var b=a.offsetHeight;return a.offsetWidth===0&&b===0||!c.support.reliableHiddenOffsets&&(a.style.display||c.css(a,"display"))==="none"};c.expr.filters.visible=function(a){return!c.expr.filters.hidden(a)}}var kb=c.now(),lb=/)<[^<]*)*<\/script>/gi, +mb=/^(?:select|textarea)/i,nb=/^(?:color|date|datetime|email|hidden|month|number|password|range|search|tel|text|time|url|week)$/i,ob=/^(?:GET|HEAD|DELETE)$/,Na=/\[\]$/,T=/\=\?(&|$)/,ia=/\?/,pb=/([?&])_=[^&]*/,qb=/^(\w+:)?\/\/([^\/?#]+)/,rb=/%20/g,sb=/#.*$/,Ea=c.fn.load;c.fn.extend({load:function(a,b,d){if(typeof a!=="string"&&Ea)return Ea.apply(this,arguments);else if(!this.length)return this;var e=a.indexOf(" ");if(e>=0){var f=a.slice(e,a.length);a=a.slice(0,e)}e="GET";if(b)if(c.isFunction(b)){d= +b;b=null}else if(typeof b==="object"){b=c.param(b,c.ajaxSettings.traditional);e="POST"}var h=this;c.ajax({url:a,type:e,dataType:"html",data:b,complete:function(k,l){if(l==="success"||l==="notmodified")h.html(f?c("
").append(k.responseText.replace(lb,"")).find(f):k.responseText);d&&h.each(d,[k.responseText,l,k])}});return this},serialize:function(){return c.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?c.makeArray(this.elements):this}).filter(function(){return this.name&& +!this.disabled&&(this.checked||mb.test(this.nodeName)||nb.test(this.type))}).map(function(a,b){var d=c(this).val();return d==null?null:c.isArray(d)?c.map(d,function(e){return{name:b.name,value:e}}):{name:b.name,value:d}}).get()}});c.each("ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "),function(a,b){c.fn[b]=function(d){return this.bind(b,d)}});c.extend({get:function(a,b,d,e){if(c.isFunction(b)){e=e||d;d=b;b=null}return c.ajax({type:"GET",url:a,data:b,success:d,dataType:e})}, +getScript:function(a,b){return c.get(a,null,b,"script")},getJSON:function(a,b,d){return c.get(a,b,d,"json")},post:function(a,b,d,e){if(c.isFunction(b)){e=e||d;d=b;b={}}return c.ajax({type:"POST",url:a,data:b,success:d,dataType:e})},ajaxSetup:function(a){c.extend(c.ajaxSettings,a)},ajaxSettings:{url:location.href,global:true,type:"GET",contentType:"application/x-www-form-urlencoded",processData:true,async:true,xhr:function(){return new E.XMLHttpRequest},accepts:{xml:"application/xml, text/xml",html:"text/html", +script:"text/javascript, application/javascript",json:"application/json, text/javascript",text:"text/plain",_default:"*/*"}},ajax:function(a){var b=c.extend(true,{},c.ajaxSettings,a),d,e,f,h=b.type.toUpperCase(),k=ob.test(h);b.url=b.url.replace(sb,"");b.context=a&&a.context!=null?a.context:b;if(b.data&&b.processData&&typeof b.data!=="string")b.data=c.param(b.data,b.traditional);if(b.dataType==="jsonp"){if(h==="GET")T.test(b.url)||(b.url+=(ia.test(b.url)?"&":"?")+(b.jsonp||"callback")+"=?");else if(!b.data|| +!T.test(b.data))b.data=(b.data?b.data+"&":"")+(b.jsonp||"callback")+"=?";b.dataType="json"}if(b.dataType==="json"&&(b.data&&T.test(b.data)||T.test(b.url))){d=b.jsonpCallback||"jsonp"+kb++;if(b.data)b.data=(b.data+"").replace(T,"="+d+"$1");b.url=b.url.replace(T,"="+d+"$1");b.dataType="script";var l=E[d];E[d]=function(m){f=m;c.handleSuccess(b,w,e,f);c.handleComplete(b,w,e,f);if(c.isFunction(l))l(m);else{E[d]=A;try{delete E[d]}catch(p){}}v&&v.removeChild(B)}}if(b.dataType==="script"&&b.cache===null)b.cache= +false;if(b.cache===false&&h==="GET"){var n=c.now(),s=b.url.replace(pb,"$1_="+n);b.url=s+(s===b.url?(ia.test(b.url)?"&":"?")+"_="+n:"")}if(b.data&&h==="GET")b.url+=(ia.test(b.url)?"&":"?")+b.data;b.global&&c.active++===0&&c.event.trigger("ajaxStart");n=(n=qb.exec(b.url))&&(n[1]&&n[1]!==location.protocol||n[2]!==location.host);if(b.dataType==="script"&&h==="GET"&&n){var v=u.getElementsByTagName("head")[0]||u.documentElement,B=u.createElement("script");if(b.scriptCharset)B.charset=b.scriptCharset;B.src= +b.url;if(!d){var D=false;B.onload=B.onreadystatechange=function(){if(!D&&(!this.readyState||this.readyState==="loaded"||this.readyState==="complete")){D=true;c.handleSuccess(b,w,e,f);c.handleComplete(b,w,e,f);B.onload=B.onreadystatechange=null;v&&B.parentNode&&v.removeChild(B)}}}v.insertBefore(B,v.firstChild);return A}var H=false,w=b.xhr();if(w){b.username?w.open(h,b.url,b.async,b.username,b.password):w.open(h,b.url,b.async);try{if(b.data!=null&&!k||a&&a.contentType)w.setRequestHeader("Content-Type", +b.contentType);if(b.ifModified){c.lastModified[b.url]&&w.setRequestHeader("If-Modified-Since",c.lastModified[b.url]);c.etag[b.url]&&w.setRequestHeader("If-None-Match",c.etag[b.url])}n||w.setRequestHeader("X-Requested-With","XMLHttpRequest");w.setRequestHeader("Accept",b.dataType&&b.accepts[b.dataType]?b.accepts[b.dataType]+", */*; q=0.01":b.accepts._default)}catch(G){}if(b.beforeSend&&b.beforeSend.call(b.context,w,b)===false){b.global&&c.active--===1&&c.event.trigger("ajaxStop");w.abort();return false}b.global&& +c.triggerGlobal(b,"ajaxSend",[w,b]);var M=w.onreadystatechange=function(m){if(!w||w.readyState===0||m==="abort"){H||c.handleComplete(b,w,e,f);H=true;if(w)w.onreadystatechange=c.noop}else if(!H&&w&&(w.readyState===4||m==="timeout")){H=true;w.onreadystatechange=c.noop;e=m==="timeout"?"timeout":!c.httpSuccess(w)?"error":b.ifModified&&c.httpNotModified(w,b.url)?"notmodified":"success";var p;if(e==="success")try{f=c.httpData(w,b.dataType,b)}catch(q){e="parsererror";p=q}if(e==="success"||e==="notmodified")d|| +c.handleSuccess(b,w,e,f);else c.handleError(b,w,e,p);d||c.handleComplete(b,w,e,f);m==="timeout"&&w.abort();if(b.async)w=null}};try{var g=w.abort;w.abort=function(){w&&g.call&&g.call(w);M("abort")}}catch(j){}b.async&&b.timeout>0&&setTimeout(function(){w&&!H&&M("timeout")},b.timeout);try{w.send(k||b.data==null?null:b.data)}catch(o){c.handleError(b,w,null,o);c.handleComplete(b,w,e,f)}b.async||M();return w}},param:function(a,b){var d=[],e=function(h,k){k=c.isFunction(k)?k():k;d[d.length]=encodeURIComponent(h)+ +"="+encodeURIComponent(k)};if(b===A)b=c.ajaxSettings.traditional;if(c.isArray(a)||a.jquery)c.each(a,function(){e(this.name,this.value)});else for(var f in a)ca(f,a[f],b,e);return d.join("&").replace(rb,"+")}});c.extend({active:0,lastModified:{},etag:{},handleError:function(a,b,d,e){a.error&&a.error.call(a.context,b,d,e);a.global&&c.triggerGlobal(a,"ajaxError",[b,a,e])},handleSuccess:function(a,b,d,e){a.success&&a.success.call(a.context,e,d,b);a.global&&c.triggerGlobal(a,"ajaxSuccess",[b,a])},handleComplete:function(a, +b,d){a.complete&&a.complete.call(a.context,b,d);a.global&&c.triggerGlobal(a,"ajaxComplete",[b,a]);a.global&&c.active--===1&&c.event.trigger("ajaxStop")},triggerGlobal:function(a,b,d){(a.context&&a.context.url==null?c(a.context):c.event).trigger(b,d)},httpSuccess:function(a){try{return!a.status&&location.protocol==="file:"||a.status>=200&&a.status<300||a.status===304||a.status===1223}catch(b){}return false},httpNotModified:function(a,b){var d=a.getResponseHeader("Last-Modified"),e=a.getResponseHeader("Etag"); +if(d)c.lastModified[b]=d;if(e)c.etag[b]=e;return a.status===304},httpData:function(a,b,d){var e=a.getResponseHeader("content-type")||"",f=b==="xml"||!b&&e.indexOf("xml")>=0;a=f?a.responseXML:a.responseText;f&&a.documentElement.nodeName==="parsererror"&&c.error("parsererror");if(d&&d.dataFilter)a=d.dataFilter(a,b);if(typeof a==="string")if(b==="json"||!b&&e.indexOf("json")>=0)a=c.parseJSON(a);else if(b==="script"||!b&&e.indexOf("javascript")>=0)c.globalEval(a);return a}});if(E.ActiveXObject)c.ajaxSettings.xhr= +function(){if(E.location.protocol!=="file:")try{return new E.XMLHttpRequest}catch(a){}try{return new E.ActiveXObject("Microsoft.XMLHTTP")}catch(b){}};c.support.ajax=!!c.ajaxSettings.xhr();var da={},tb=/^(?:toggle|show|hide)$/,ub=/^([+\-]=)?([\d+.\-]+)(.*)$/,aa,na=[["height","marginTop","marginBottom","paddingTop","paddingBottom"],["width","marginLeft","marginRight","paddingLeft","paddingRight"],["opacity"]];c.fn.extend({show:function(a,b,d){if(a||a===0)return this.animate(S("show",3),a,b,d);else{a= +0;for(b=this.length;a=0;e--)if(d[e].elem===this){b&&d[e](true);d.splice(e,1)}});b||this.dequeue();return this}});c.each({slideDown:S("show",1),slideUp:S("hide",1),slideToggle:S("toggle",1),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"}},function(a,b){c.fn[a]=function(d,e,f){return this.animate(b, +d,e,f)}});c.extend({speed:function(a,b,d){var e=a&&typeof a==="object"?c.extend({},a):{complete:d||!d&&b||c.isFunction(a)&&a,duration:a,easing:d&&b||b&&!c.isFunction(b)&&b};e.duration=c.fx.off?0:typeof e.duration==="number"?e.duration:e.duration in c.fx.speeds?c.fx.speeds[e.duration]:c.fx.speeds._default;e.old=e.complete;e.complete=function(){e.queue!==false&&c(this).dequeue();c.isFunction(e.old)&&e.old.call(this)};return e},easing:{linear:function(a,b,d,e){return d+e*a},swing:function(a,b,d,e){return(-Math.cos(a* +Math.PI)/2+0.5)*e+d}},timers:[],fx:function(a,b,d){this.options=b;this.elem=a;this.prop=d;if(!b.orig)b.orig={}}});c.fx.prototype={update:function(){this.options.step&&this.options.step.call(this.elem,this.now,this);(c.fx.step[this.prop]||c.fx.step._default)(this)},cur:function(){if(this.elem[this.prop]!=null&&(!this.elem.style||this.elem.style[this.prop]==null))return this.elem[this.prop];var a=parseFloat(c.css(this.elem,this.prop));return a&&a>-1E4?a:0},custom:function(a,b,d){function e(h){return f.step(h)} +this.startTime=c.now();this.start=a;this.end=b;this.unit=d||this.unit||"px";this.now=this.start;this.pos=this.state=0;var f=this;a=c.fx;e.elem=this.elem;if(e()&&c.timers.push(e)&&!aa)aa=setInterval(a.tick,a.interval)},show:function(){this.options.orig[this.prop]=c.style(this.elem,this.prop);this.options.show=true;this.custom(this.prop==="width"||this.prop==="height"?1:0,this.cur());c(this.elem).show()},hide:function(){this.options.orig[this.prop]=c.style(this.elem,this.prop);this.options.hide=true; +this.custom(this.cur(),0)},step:function(a){var b=c.now(),d=true;if(a||b>=this.options.duration+this.startTime){this.now=this.end;this.pos=this.state=1;this.update();this.options.curAnim[this.prop]=true;for(var e in this.options.curAnim)if(this.options.curAnim[e]!==true)d=false;if(d){if(this.options.overflow!=null&&!c.support.shrinkWrapBlocks){var f=this.elem,h=this.options;c.each(["","X","Y"],function(l,n){f.style["overflow"+n]=h.overflow[l]})}this.options.hide&&c(this.elem).hide();if(this.options.hide|| +this.options.show)for(var k in this.options.curAnim)c.style(this.elem,k,this.options.orig[k]);this.options.complete.call(this.elem)}return false}else{a=b-this.startTime;this.state=a/this.options.duration;b=this.options.easing||(c.easing.swing?"swing":"linear");this.pos=c.easing[this.options.specialEasing&&this.options.specialEasing[this.prop]||b](this.state,a,0,1,this.options.duration);this.now=this.start+(this.end-this.start)*this.pos;this.update()}return true}};c.extend(c.fx,{tick:function(){for(var a= +c.timers,b=0;b-1;e={};var s={};if(n)s=f.position();k=n?s.top:parseInt(k,10)||0;l=n?s.left:parseInt(l,10)||0;if(c.isFunction(b))b=b.call(a,d,h);if(b.top!=null)e.top=b.top-h.top+k;if(b.left!=null)e.left=b.left-h.left+l;"using"in b?b.using.call(a, +e):f.css(e)}};c.fn.extend({position:function(){if(!this[0])return null;var a=this[0],b=this.offsetParent(),d=this.offset(),e=Fa.test(b[0].nodeName)?{top:0,left:0}:b.offset();d.top-=parseFloat(c.css(a,"marginTop"))||0;d.left-=parseFloat(c.css(a,"marginLeft"))||0;e.top+=parseFloat(c.css(b[0],"borderTopWidth"))||0;e.left+=parseFloat(c.css(b[0],"borderLeftWidth"))||0;return{top:d.top-e.top,left:d.left-e.left}},offsetParent:function(){return this.map(function(){for(var a=this.offsetParent||u.body;a&&!Fa.test(a.nodeName)&& +c.css(a,"position")==="static";)a=a.offsetParent;return a})}});c.each(["Left","Top"],function(a,b){var d="scroll"+b;c.fn[d]=function(e){var f=this[0],h;if(!f)return null;if(e!==A)return this.each(function(){if(h=ea(this))h.scrollTo(!a?e:c(h).scrollLeft(),a?e:c(h).scrollTop());else this[d]=e});else return(h=ea(f))?"pageXOffset"in h?h[a?"pageYOffset":"pageXOffset"]:c.support.boxModel&&h.document.documentElement[d]||h.document.body[d]:f[d]}});c.each(["Height","Width"],function(a,b){var d=b.toLowerCase(); +c.fn["inner"+b]=function(){return this[0]?parseFloat(c.css(this[0],d,"padding")):null};c.fn["outer"+b]=function(e){return this[0]?parseFloat(c.css(this[0],d,e?"margin":"border")):null};c.fn[d]=function(e){var f=this[0];if(!f)return e==null?null:this;if(c.isFunction(e))return this.each(function(h){var k=c(this);k[d](e.call(this,h,k[d]()))});return c.isWindow(f)?f.document.compatMode==="CSS1Compat"&&f.document.documentElement["client"+b]||f.document.body["client"+b]:f.nodeType===9?Math.max(f.documentElement["client"+ +b],f.body["scroll"+b],f.documentElement["scroll"+b],f.body["offset"+b],f.documentElement["offset"+b]):e===A?parseFloat(c.css(f,d)):this.css(d,typeof e==="string"?e:e+"px")}})})(window); diff -Nru python-coverage-3.4/coverage/htmlfiles/jquery.hotkeys.js python-coverage-3.6/coverage/htmlfiles/jquery.hotkeys.js --- python-coverage-3.4/coverage/htmlfiles/jquery.hotkeys.js 1970-01-01 00:00:00.000000000 +0000 +++ python-coverage-3.6/coverage/htmlfiles/jquery.hotkeys.js 2011-01-15 19:10:34.000000000 +0000 @@ -0,0 +1,99 @@ +/* + * jQuery Hotkeys Plugin + * Copyright 2010, John Resig + * Dual licensed under the MIT or GPL Version 2 licenses. + * + * Based upon the plugin by Tzury Bar Yochay: + * http://github.com/tzuryby/hotkeys + * + * Original idea by: + * Binny V A, http://www.openjs.com/scripts/events/keyboard_shortcuts/ +*/ + +(function(jQuery){ + + jQuery.hotkeys = { + version: "0.8", + + specialKeys: { + 8: "backspace", 9: "tab", 13: "return", 16: "shift", 17: "ctrl", 18: "alt", 19: "pause", + 20: "capslock", 27: "esc", 32: "space", 33: "pageup", 34: "pagedown", 35: "end", 36: "home", + 37: "left", 38: "up", 39: "right", 40: "down", 45: "insert", 46: "del", + 96: "0", 97: "1", 98: "2", 99: "3", 100: "4", 101: "5", 102: "6", 103: "7", + 104: "8", 105: "9", 106: "*", 107: "+", 109: "-", 110: ".", 111 : "/", + 112: "f1", 113: "f2", 114: "f3", 115: "f4", 116: "f5", 117: "f6", 118: "f7", 119: "f8", + 120: "f9", 121: "f10", 122: "f11", 123: "f12", 144: "numlock", 145: "scroll", 191: "/", 224: "meta" + }, + + shiftNums: { + "`": "~", "1": "!", "2": "@", "3": "#", "4": "$", "5": "%", "6": "^", "7": "&", + "8": "*", "9": "(", "0": ")", "-": "_", "=": "+", ";": ": ", "'": "\"", ",": "<", + ".": ">", "/": "?", "\\": "|" + } + }; + + function keyHandler( handleObj ) { + // Only care when a possible input has been specified + if ( typeof handleObj.data !== "string" ) { + return; + } + + var origHandler = handleObj.handler, + keys = handleObj.data.toLowerCase().split(" "); + + handleObj.handler = function( event ) { + // Don't fire in text-accepting inputs that we didn't directly bind to + if ( this !== event.target && (/textarea|select/i.test( event.target.nodeName ) || + event.target.type === "text") ) { + return; + } + + // Keypress represents characters, not special keys + var special = event.type !== "keypress" && jQuery.hotkeys.specialKeys[ event.which ], + character = String.fromCharCode( event.which ).toLowerCase(), + key, modif = "", possible = {}; + + // check combinations (alt|ctrl|shift+anything) + if ( event.altKey && special !== "alt" ) { + modif += "alt+"; + } + + if ( event.ctrlKey && special !== "ctrl" ) { + modif += "ctrl+"; + } + + // TODO: Need to make sure this works consistently across platforms + if ( event.metaKey && !event.ctrlKey && special !== "meta" ) { + modif += "meta+"; + } + + if ( event.shiftKey && special !== "shift" ) { + modif += "shift+"; + } + + if ( special ) { + possible[ modif + special ] = true; + + } else { + possible[ modif + character ] = true; + possible[ modif + jQuery.hotkeys.shiftNums[ character ] ] = true; + + // "$" can be triggered as "Shift+4" or "Shift+$" or just "$" + if ( modif === "shift+" ) { + possible[ jQuery.hotkeys.shiftNums[ character ] ] = true; + } + } + + for ( var i = 0, l = keys.length; i < l; i++ ) { + if ( possible[ keys[i] ] ) { + return origHandler.apply( this, arguments ); + } + } + }; + } + + jQuery.each([ "keydown", "keyup", "keypress" ], function() { + jQuery.event.special[ this ] = { add: keyHandler }; + }); + +})( jQuery ); diff -Nru python-coverage-3.4/coverage/htmlfiles/jquery.isonscreen.js python-coverage-3.6/coverage/htmlfiles/jquery.isonscreen.js --- python-coverage-3.4/coverage/htmlfiles/jquery.isonscreen.js 1970-01-01 00:00:00.000000000 +0000 +++ python-coverage-3.6/coverage/htmlfiles/jquery.isonscreen.js 2011-04-17 20:36:08.000000000 +0000 @@ -0,0 +1,53 @@ +/* Copyright (c) 2010 + * @author Laurence Wheway + * Dual licensed under the MIT (http://www.opensource.org/licenses/mit-license.php) + * and GPL (http://www.opensource.org/licenses/gpl-license.php) licenses. + * + * @version 1.2.0 + */ +(function($) { + jQuery.extend({ + isOnScreen: function(box, container) { + //ensure numbers come in as intgers (not strings) and remove 'px' is it's there + for(var i in box){box[i] = parseFloat(box[i])}; + for(var i in container){container[i] = parseFloat(container[i])}; + + if(!container){ + container = { + left: $(window).scrollLeft(), + top: $(window).scrollTop(), + width: $(window).width(), + height: $(window).height() + } + } + + if( box.left+box.width-container.left > 0 && + box.left < container.width+container.left && + box.top+box.height-container.top > 0 && + box.top < container.height+container.top + ) return true; + return false; + } + }) + + + jQuery.fn.isOnScreen = function (container) { + for(var i in container){container[i] = parseFloat(container[i])}; + + if(!container){ + container = { + left: $(window).scrollLeft(), + top: $(window).scrollTop(), + width: $(window).width(), + height: $(window).height() + } + } + + if( $(this).offset().left+$(this).width()-container.left > 0 && + $(this).offset().left < container.width+container.left && + $(this).offset().top+$(this).height()-container.top > 0 && + $(this).offset().top < container.height+container.top + ) return true; + return false; + } +})(jQuery); Binary files /tmp/ADivlKEjnf/python-coverage-3.4/coverage/htmlfiles/keybd_closed.png and /tmp/IBWRQkcXpK/python-coverage-3.6/coverage/htmlfiles/keybd_closed.png differ Binary files /tmp/ADivlKEjnf/python-coverage-3.4/coverage/htmlfiles/keybd_open.png and /tmp/IBWRQkcXpK/python-coverage-3.6/coverage/htmlfiles/keybd_open.png differ diff -Nru python-coverage-3.4/coverage/htmlfiles/pyfile.html python-coverage-3.6/coverage/htmlfiles/pyfile.html --- python-coverage-3.4/coverage/htmlfiles/pyfile.html 2010-08-22 23:34:36.000000000 +0000 +++ python-coverage-3.6/coverage/htmlfiles/pyfile.html 2012-11-20 01:51:55.000000000 +0000 @@ -7,10 +7,15 @@ Coverage for {{cu.name|escape}}: {{nums.pc_covered_str}}% - + {% if extra_css %} + + {% endif %} + + + @@ -20,18 +25,42 @@

Coverage for {{cu.name|escape}} : {{nums.pc_covered_str}}%

+

- {{nums.n_statements}} statements - {{nums.n_executed}} run - {{nums.n_missing}} missing - {{nums.n_excluded}} excluded + {{nums.n_statements}} statements   + {{nums.n_executed}} run + {{nums.n_missing}} missing + {{nums.n_excluded}} excluded {% if arcs %} - {{n_par}} partial + {{nums.n_partial_branches}} partial {% endif %}

+
+ +

Hot-keys on this page

+
+

+ r + m + x + p   toggle line displays +

+

+ j + k   next/prev highlighted chunk +

+

+ 0   (zero) top of page +

+

+ 1   (one) first highlighted chunk +

+
+
+
diff -Nru python-coverage-3.4/coverage/htmlfiles/style.css python-coverage-3.6/coverage/htmlfiles/style.css --- python-coverage-3.4/coverage/htmlfiles/style.css 2010-08-08 12:26:30.000000000 +0000 +++ python-coverage-3.6/coverage/htmlfiles/style.css 2012-09-26 13:27:55.000000000 +0000 @@ -24,8 +24,8 @@ /* Set base font size to 12/16 */ p { - font-size: .75em; /* 12/16 */ - line-height: 1.3333em; /* 16/12 */ + font-size: .75em; /* 12/16 */ + line-height: 1.33333333em; /* 16/12 */ } table { @@ -102,6 +102,76 @@ border-color: #999 #ccc #ccc #999; } +.stats span.run { + background: #ddffdd; +} +.stats span.exc { + background: #eeeeee; +} +.stats span.mis { + background: #ffdddd; +} +.stats span.hide_run { + background: #eeffee; +} +.stats span.hide_exc { + background: #f5f5f5; +} +.stats span.hide_mis { + background: #ffeeee; +} +.stats span.par { + background: #ffffaa; +} +.stats span.hide_par { + background: #ffffcc; +} + +/* Help panel */ +#keyboard_icon { + float: right; + cursor: pointer; +} + +.help_panel { + position: absolute; + background: #ffc; + padding: .5em; + border: 1px solid #883; + display: none; +} + +#indexfile .help_panel { + width: 20em; height: 4em; +} + +#pyfile .help_panel { + width: 16em; height: 8em; +} + +.help_panel .legend { + font-style: italic; + margin-bottom: 1em; +} + +#panel_icon { + float: right; + cursor: pointer; +} + +.keyhelp { + margin: .75em; +} + +.keyhelp .key { + border: 1px solid black; + border-color: #888 #333 #333 #888; + padding: .1em .35em; + font-family: monospace; + font-weight: bold; + background: #eee; +} + /* Source file styles */ .linenos p { text-align: right; diff -Nru python-coverage-3.4/coverage/misc.py python-coverage-3.6/coverage/misc.py --- python-coverage-3.4/coverage/misc.py 2010-05-30 04:26:50.000000000 +0000 +++ python-coverage-3.6/coverage/misc.py 2012-12-22 14:53:12.000000000 +0000 @@ -1,5 +1,14 @@ """Miscellaneous stuff for Coverage.""" +import errno +import inspect +import os +import sys + +from coverage.backward import md5, sorted # pylint: disable=W0622 +from coverage.backward import string_class, to_bytes + + def nice_pair(pair): """Make a nice string representation of a pair of numbers. @@ -45,6 +54,12 @@ return ret +def short_stack(): + """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. @@ -68,12 +83,71 @@ return bool(b) +def join_regex(regexes): + """Combine a list of regexes into one that matches any of them.""" + if len(regexes) > 1: + return "(" + ")|(".join(regexes) + ")" + elif regexes: + return regexes[0] + else: + return "" + + +def file_be_gone(path): + """Remove a file, and don't get annoyed if it doesn't exist.""" + try: + os.remove(path) + except OSError: + _, e, _ = sys.exc_info() + if e.errno != errno.ENOENT: + raise + + +class Hasher(object): + """Hashes Python data into md5.""" + def __init__(self): + self.md5 = md5() + + def update(self, v): + """Add `v` to the hash, recursively if needed.""" + self.md5.update(to_bytes(str(type(v)))) + if isinstance(v, string_class): + self.md5.update(to_bytes(v)) + elif isinstance(v, (int, float)): + self.update(str(v)) + elif isinstance(v, (tuple, list)): + for e in v: + self.update(e) + elif isinstance(v, dict): + keys = v.keys() + for k in sorted(keys): + self.update(k) + self.update(v[k]) + else: + for k in dir(v): + if k.startswith('__'): + continue + a = getattr(v, k) + if inspect.isroutine(a): + continue + self.update(k) + self.update(a) + + def digest(self): + """Retrieve the digest of the hash.""" + return self.md5.digest() + + class CoverageException(Exception): """An exception specific to Coverage.""" pass class NoSource(CoverageException): - """Used to indicate we couldn't find the source for a module.""" + """We couldn't find the source for a module.""" + pass + +class NotPython(CoverageException): + """A source file turned out not to be parsable Python.""" pass class ExceptionDuringRun(CoverageException): diff -Nru python-coverage-3.4/coverage/parser.py python-coverage-3.6/coverage/parser.py --- python-coverage-3.4/coverage/parser.py 2010-09-04 20:33:27.000000000 +0000 +++ python-coverage-3.6/coverage/parser.py 2012-12-01 21:58:38.000000000 +0000 @@ -1,10 +1,12 @@ """Code parsing for Coverage.""" -import glob, opcode, os, re, sys, token, tokenize +import opcode, re, sys, token, tokenize -from coverage.backward import set, sorted, StringIO # pylint: disable-msg=W0622 +from coverage.backward import set, sorted, StringIO # pylint: disable=W0622 +from coverage.backward import open_source from coverage.bytecode import ByteCodes, CodeObjects -from coverage.misc import nice_pair, CoverageException, NoSource, expensive +from coverage.misc import nice_pair, expensive, join_regex +from coverage.misc import CoverageException, NoSource, NotPython class CodeParser(object): @@ -13,7 +15,7 @@ def __init__(self, text=None, filename=None, exclude=None): """ Source can be provided as `text`, the text itself, or `filename`, from - which text will be read. Excluded lines are those that match + which the text will be read. Excluded lines are those that match `exclude`, a regex. """ @@ -22,15 +24,20 @@ self.text = text if not self.text: try: - sourcef = open(self.filename, 'rU') - self.text = sourcef.read() - sourcef.close() + sourcef = open_source(self.filename) + try: + self.text = sourcef.read() + finally: + sourcef.close() except IOError: _, err, _ = sys.exc_info() raise NoSource( - "No source for code: %r: %s" % (self.filename, err) + "No source for code: '%s': %s" % (self.filename, err) ) - self.text = self.text.replace('\r\n', '\n') + + # Scrap the BOM if it exists. + if self.text and ord(self.text[0]) == 0xfeff: + self.text = self.text[1:] self.exclude = exclude @@ -65,6 +72,21 @@ return self._byte_parser byte_parser = property(_get_byte_parser) + def lines_matching(self, *regexes): + """Find the lines matching one of a list of regexes. + + Returns a set of line numbers, the lines that contain a match for one + of the regexes in `regexes`. The entire line needn't match, just a + part of it. + + """ + regex_c = re.compile(join_regex(regexes)) + matches = set() + for i, ltext in enumerate(self.lines): + if regex_c.search(ltext): + matches.add(i+1) + return matches + def _raw_parse(self): """Parse the source to find the interesting facts about its lines. @@ -73,10 +95,7 @@ """ # Find lines which match an exclusion pattern. if self.exclude: - re_exclude = re.compile(self.exclude) - for i, ltext in enumerate(self.lines): - if re_exclude.search(ltext): - self.excluded.add(i+1) + self.excluded = self.lines_matching(self.exclude) # Tokenize, to find excluded suites, to find docstrings, and to find # multi-line statements. @@ -89,7 +108,7 @@ tokgen = tokenize.generate_tokens(StringIO(self.text).readline) for toktype, ttext, (slineno, _), (elineno, _), ltext in tokgen: - if self.show_tokens: # pragma: no cover + if self.show_tokens: # pragma: not covered print("%10s %5s %-20r %r" % ( tokenize.tok_name.get(toktype, toktype), nice_pair((slineno, elineno)), ttext, ltext @@ -184,7 +203,15 @@ statements. """ - self._raw_parse() + try: + self._raw_parse() + except (tokenize.TokenError, IndentationError): + _, tokerr, _ = sys.exc_info() + msg, lineno = tokerr.args + raise NotPython( + "Couldn't parse '%s' as Python source: '%s' at %s" % + (self.filename, msg, lineno) + ) excluded_lines = self.first_lines(self.excluded) ignore = excluded_lines + list(self.docstrings) @@ -282,7 +309,7 @@ OPS_POP_BLOCK = _opcode_set('POP_BLOCK') # Opcodes that have a jump destination, but aren't really a jump. -OPS_NO_JUMP = _opcode_set('SETUP_EXCEPT', 'SETUP_FINALLY') +OPS_NO_JUMP = OPS_PUSH_BLOCK # Individual opcodes we need below. OP_BREAK_LOOP = _opcode('BREAK_LOOP') @@ -299,12 +326,16 @@ def __init__(self, code=None, text=None, filename=None): if code: self.code = code + self.text = text else: if not text: assert filename, "If no code or text, need a filename" - sourcef = open(filename, 'rU') - text = sourcef.read() - sourcef.close() + sourcef = open_source(filename) + try: + text = sourcef.read() + finally: + sourcef.close() + self.text = text try: # Python 2.3 and 2.4 don't like partial last lines, so be sure @@ -312,7 +343,7 @@ self.code = compile(text + '\n', filename, "exec") except SyntaxError: _, synerr, _ = sys.exc_info() - raise CoverageException( + raise NotPython( "Couldn't parse '%s' as Python source: '%s' at line %d" % (filename, synerr.msg, synerr.lineno) ) @@ -333,7 +364,8 @@ The iteration includes `self` as its first value. """ - return map(lambda c: ByteParser(code=c), CodeObjects(self.code)) + children = CodeObjects(self.code) + return [ByteParser(code=c, text=self.text) for c in children] # Getting numbers from the lnotab value changed in Py3.0. if sys.version_info >= (3, 0): @@ -385,18 +417,6 @@ stmts.add(l) return stmts - def _disassemble(self): # pragma: no cover - """Disassemble code, for ad-hoc experimenting.""" - - import dis - - for bp in self.child_parsers(): - print("\n%s: " % bp.code) - dis.dis(bp.code) - print("Bytes lines: %r" % bp._bytes_lines()) - - print("") - def _split_into_chunks(self): """Split the code object into a list of `Chunk` objects. @@ -509,7 +529,7 @@ chunks.append(chunk) # Give all the chunks a length. - chunks[-1].length = bc.next_offset - chunks[-1].byte + chunks[-1].length = bc.next_offset - chunks[-1].byte # pylint: disable=W0631,C0301 for i in range(len(chunks)-1): chunks[i].length = chunks[i+1].byte - chunks[i].byte @@ -556,7 +576,7 @@ else: # No chunk for this byte! raise Exception("Couldn't find chunk @ %d" % byte) - byte_chunks[byte] = ch + byte_chunks[byte] = ch # pylint: disable=W0631 if ch.line: lines.add(ch.line) @@ -640,144 +660,3 @@ return "<%d+%d @%d %r>" % ( self.byte, self.length, self.line, list(self.exits) ) - - -class AdHocMain(object): # pragma: no cover - """An ad-hoc main for code parsing experiments.""" - - def main(self, args): - """A main function for trying the code from the command line.""" - - from optparse import OptionParser - - parser = OptionParser() - parser.add_option( - "-c", action="store_true", dest="chunks", - help="Show basic block chunks" - ) - parser.add_option( - "-d", action="store_true", dest="dis", - help="Disassemble" - ) - parser.add_option( - "-R", action="store_true", dest="recursive", - help="Recurse to find source files" - ) - parser.add_option( - "-s", action="store_true", dest="source", - help="Show analyzed source" - ) - parser.add_option( - "-t", action="store_true", dest="tokens", - help="Show tokens" - ) - - options, args = parser.parse_args() - if options.recursive: - if args: - root = args[0] - else: - root = "." - for root, _, _ in os.walk(root): - for f in glob.glob(root + "/*.py"): - self.adhoc_one_file(options, f) - else: - self.adhoc_one_file(options, args[0]) - - def adhoc_one_file(self, options, filename): - """Process just one file.""" - - if options.dis or options.chunks: - try: - bp = ByteParser(filename=filename) - except CoverageException: - _, err, _ = sys.exc_info() - print("%s" % (err,)) - return - - if options.dis: - print("Main code:") - bp._disassemble() - - if options.chunks: - chunks = bp._all_chunks() - if options.recursive: - print("%6d: %s" % (len(chunks), filename)) - else: - print("Chunks: %r" % chunks) - arcs = bp._all_arcs() - print("Arcs: %r" % sorted(arcs)) - - if options.source or options.tokens: - cp = CodeParser(filename=filename, exclude=r"no\s*cover") - cp.show_tokens = options.tokens - cp._raw_parse() - - if options.source: - if options.chunks: - arc_width, arc_chars = self.arc_ascii_art(arcs) - else: - arc_width, arc_chars = 0, {} - - exit_counts = cp.exit_counts() - - for i, ltext in enumerate(cp.lines): - lineno = i+1 - m0 = m1 = m2 = m3 = a = ' ' - if lineno in cp.statement_starts: - m0 = '-' - exits = exit_counts.get(lineno, 0) - if exits > 1: - m1 = str(exits) - if lineno in cp.docstrings: - m2 = '"' - if lineno in cp.classdefs: - m2 = 'C' - if lineno in cp.excluded: - m3 = 'x' - a = arc_chars.get(lineno, '').ljust(arc_width) - print("%4d %s%s%s%s%s %s" % - (lineno, m0, m1, m2, m3, a, ltext) - ) - - def arc_ascii_art(self, arcs): - """Draw arcs as ascii art. - - Returns a width of characters needed to draw all the arcs, and a - dictionary mapping line numbers to ascii strings to draw for that line. - - """ - arc_chars = {} - for lfrom, lto in sorted(arcs): - if lfrom < 0: - arc_chars[lto] = arc_chars.get(lto, '') + 'v' - elif lto < 0: - arc_chars[lfrom] = arc_chars.get(lfrom, '') + '^' - else: - if lfrom == lto - 1: - # Don't show obvious arcs. - continue - if lfrom < lto: - l1, l2 = lfrom, lto - else: - l1, l2 = lto, lfrom - w = max([len(arc_chars.get(l, '')) for l in range(l1, l2+1)]) - for l in range(l1, l2+1): - if l == lfrom: - ch = '<' - elif l == lto: - ch = '>' - else: - ch = '|' - arc_chars[l] = arc_chars.get(l, '').ljust(w) + ch - arc_width = 0 - - if arc_chars: - arc_width = max([len(a) for a in arc_chars.values()]) - else: - arc_width = 0 - - return arc_width, arc_chars - -if __name__ == '__main__': - AdHocMain().main(sys.argv[1:]) diff -Nru python-coverage-3.4/coverage/phystokens.py python-coverage-3.6/coverage/phystokens.py --- python-coverage-3.4/coverage/phystokens.py 2010-08-08 12:26:30.000000000 +0000 +++ python-coverage-3.6/coverage/phystokens.py 2012-11-10 20:19:12.000000000 +0000 @@ -1,7 +1,7 @@ """Better tokenizing for coverage.py.""" -import keyword, re, token, tokenize -from coverage.backward import StringIO # pylint: disable-msg=W0622 +import codecs, keyword, re, sys, token, tokenize +from coverage.backward import StringIO # pylint: disable=W0622 def phys_tokens(toks): """Return all physical tokens, even line continuations. @@ -106,3 +106,101 @@ if line: yield line + +def source_encoding(source): + """Determine the encoding for `source` (a string), according to PEP 263. + + Returns a string, the name of the encoding. + + """ + # Note: this function should never be called on Python 3, since py3 has + # built-in tools to do this. + assert sys.version_info < (3, 0) + + # This is mostly code adapted from Py3.2's tokenize module. + + cookie_re = re.compile(r"coding[:=]\s*([-\w.]+)") + + # Do this so the detect_encode code we copied will work. + readline = iter(source.splitlines()).next + + def _get_normal_name(orig_enc): + """Imitates get_normal_name in tokenizer.c.""" + # Only care about the first 12 characters. + enc = orig_enc[:12].lower().replace("_", "-") + if re.match(r"^utf-8($|-)", enc): + return "utf-8" + if re.match(r"^(latin-1|iso-8859-1|iso-latin-1)($|-)", enc): + return "iso-8859-1" + return orig_enc + + # From detect_encode(): + # It detects the encoding from the presence of a utf-8 bom or an encoding + # cookie as specified in pep-0263. If both a bom and a cookie are present, + # but disagree, a SyntaxError will be raised. If the encoding cookie is an + # invalid charset, raise a SyntaxError. Note that if a utf-8 bom is found, + # 'utf-8-sig' is returned. + + # If no encoding is specified, then the default will be returned. The + # default varied with version. + + if sys.version_info <= (2, 4): + default = 'iso-8859-1' + else: + default = 'ascii' + + bom_found = False + encoding = None + + def read_or_stop(): + """Get the next source line, or ''.""" + try: + return readline() + except StopIteration: + return '' + + def find_cookie(line): + """Find an encoding cookie in `line`.""" + try: + line_string = line.decode('ascii') + except UnicodeDecodeError: + return None + + matches = cookie_re.findall(line_string) + if not matches: + return None + encoding = _get_normal_name(matches[0]) + try: + codec = codecs.lookup(encoding) + except LookupError: + # This behaviour mimics the Python interpreter + raise SyntaxError("unknown encoding: " + encoding) + + if bom_found: + if codec.name != 'utf-8': + # This behaviour mimics the Python interpreter + raise SyntaxError('encoding problem: utf-8') + encoding += '-sig' + return encoding + + first = read_or_stop() + if first.startswith(codecs.BOM_UTF8): + bom_found = True + first = first[3:] + default = 'utf-8-sig' + if not first: + return default + + encoding = find_cookie(first) + if encoding: + return encoding + + second = read_or_stop() + if not second: + return default + + encoding = find_cookie(second) + if encoding: + return encoding + + return default diff -Nru python-coverage-3.4/coverage/report.py python-coverage-3.6/coverage/report.py --- python-coverage-3.4/coverage/report.py 2010-09-04 03:17:57.000000000 +0000 +++ python-coverage-3.6/coverage/report.py 2012-10-29 01:22:36.000000000 +0000 @@ -2,20 +2,21 @@ import fnmatch, os from coverage.codeunit import code_unit_factory -from coverage.misc import CoverageException, NoSource +from coverage.files import prep_patterns +from coverage.misc import CoverageException, NoSource, NotPython class Reporter(object): """A base class for all reporters.""" - def __init__(self, coverage, ignore_errors=False): + def __init__(self, coverage, config): """Create a reporter. - `coverage` is the coverage instance. `ignore_errors` controls how - skittish the reporter will be during file processing. + `coverage` is the coverage instance. `config` is an instance of + CoverageConfig, for controlling all sorts of behavior. """ self.coverage = coverage - self.ignore_errors = ignore_errors + self.config = config # The code units to report on. Set by find_code_units. self.code_units = [] @@ -24,19 +25,18 @@ # classes. self.directory = None - def find_code_units(self, morfs, config): + def find_code_units(self, morfs): """Find the code units we'll report on. - `morfs` is a list of modules or filenames. `config` is a - CoverageConfig instance. + `morfs` is a list of modules or filenames. """ morfs = morfs or self.coverage.data.measured_files() file_locator = self.coverage.file_locator self.code_units = code_unit_factory(morfs, file_locator) - if config.include: - patterns = [file_locator.abs_file(p) for p in config.include] + if self.config.include: + patterns = prep_patterns(self.config.include) filtered = [] for cu in self.code_units: for pattern in patterns: @@ -45,8 +45,8 @@ break self.code_units = filtered - if config.omit: - patterns = [file_locator.abs_file(p) for p in config.omit] + if self.config.omit: + patterns = prep_patterns(self.config.omit) filtered = [] for cu in self.code_units: for pattern in patterns: @@ -58,15 +58,19 @@ self.code_units.sort() - def report_files(self, report_fn, morfs, config, directory=None): + def report_files(self, report_fn, morfs, directory=None): """Run a reporting function on a number of morfs. - `report_fn` is called for each relative morf in `morfs`. + `report_fn` is called for each relative morf in `morfs`. It is called + as:: - `config` is a CoverageConfig instance. + report_fn(code_unit, analysis) + + where `code_unit` is the `CodeUnit` for the morf, and `analysis` is + the `Analysis` for the morf. """ - self.find_code_units(morfs, config) + self.find_code_units(morfs) if not self.code_units: raise CoverageException("No data to report.") @@ -79,5 +83,10 @@ try: report_fn(cu, self.coverage._analyze(cu)) except NoSource: - if not self.ignore_errors: + if not self.config.ignore_errors: + raise + except NotPython: + # Only report errors for .py files, and only if we didn't + # explicitly suppress those errors. + if cu.should_be_python() and not self.config.ignore_errors: raise diff -Nru python-coverage-3.4/coverage/results.py python-coverage-3.6/coverage/results.py --- python-coverage-3.4/coverage/results.py 2010-09-04 17:47:27.000000000 +0000 +++ python-coverage-3.6/coverage/results.py 2012-11-24 03:16:44.000000000 +0000 @@ -2,8 +2,8 @@ import os -from coverage.backward import set, sorted # pylint: disable-msg=W0622 -from coverage.misc import format_lines, NoSource +from coverage.backward import iitems, set, sorted # pylint: disable=W0622 +from coverage.misc import format_lines, join_regex, NoSource from coverage.parser import CodeParser @@ -21,11 +21,11 @@ if not os.path.exists(self.filename): source = self.coverage.file_locator.get_zip_data(self.filename) if not source: - raise NoSource("No source for code: %r" % self.filename) + raise NoSource("No source for code: '%s'" % self.filename) self.parser = CodeParser( text=source, filename=self.filename, - exclude=self.coverage.exclude_re + exclude=self.coverage._exclude_regex('exclude') ) self.statements, self.excluded = self.parser.parse_source() @@ -35,11 +35,19 @@ self.missing = sorted(set(self.statements) - set(exec1)) if self.coverage.data.has_arcs(): + self.no_branch = self.parser.lines_matching( + join_regex(self.coverage.config.partial_list), + join_regex(self.coverage.config.partial_always_list) + ) n_branches = self.total_branches() mba = self.missing_branch_arcs() - n_missing_branches = sum([len(v) for v in mba.values()]) + n_partial_branches = sum( + [len(v) for k,v in iitems(mba) if k not in self.missing] + ) + n_missing_branches = sum([len(v) for k,v in iitems(mba)]) else: - n_branches = n_missing_branches = 0 + n_branches = n_partial_branches = n_missing_branches = 0 + self.no_branch = set() self.numbers = Numbers( n_files=1, @@ -47,6 +55,7 @@ n_excluded=len(self.excluded), n_missing=len(self.missing), n_branches=n_branches, + n_partial_branches=n_partial_branches, n_missing_branches=n_missing_branches, ) @@ -64,7 +73,8 @@ def arc_possibilities(self): """Returns a sorted list of the arcs in the code.""" - return self.parser.arcs() + arcs = self.parser.arcs() + return arcs def arcs_executed(self): """Returns a sorted list of the arcs actually executed in the code.""" @@ -77,7 +87,11 @@ """Returns a sorted list of the arcs in the code not executed.""" possible = self.arc_possibilities() executed = self.arcs_executed() - missing = [p for p in possible if p not in executed] + missing = [ + p for p in possible + if p not in executed + and p[0] not in self.no_branch + ] return sorted(missing) def arcs_unpredicted(self): @@ -89,14 +103,15 @@ # trouble, and here is where it's the least burden to remove them. unpredicted = [ e for e in executed - if e not in possible and e[0] != e[1] + if e not in possible + and e[0] != e[1] ] return sorted(unpredicted) def branch_lines(self): """Returns a list of line numbers that have more than one exit.""" exit_counts = self.parser.exit_counts() - return [l1 for l1,count in exit_counts.items() if count > 1] + return [l1 for l1,count in iitems(exit_counts) if count > 1] def total_branches(self): """How many total branches are there?""" @@ -153,13 +168,14 @@ _near100 = 99.0 def __init__(self, n_files=0, n_statements=0, n_excluded=0, n_missing=0, - n_branches=0, n_missing_branches=0 + n_branches=0, n_partial_branches=0, n_missing_branches=0 ): self.n_files = n_files self.n_statements = n_statements self.n_excluded = n_excluded self.n_missing = n_missing self.n_branches = n_branches + self.n_partial_branches = n_partial_branches self.n_missing_branches = n_missing_branches def set_precision(cls, precision): @@ -193,8 +209,9 @@ def _get_pc_covered_str(self): """Returns the percent covered, as a string, without a percent sign. - The important thing here is that "0" only be returned when it's truly - zero, and "100" only be returned when it's truly 100. + Note that "0" is only returned when the value is truly zero, and "100" + is only returned when the value is truly 100. Rounding can never + result in either "0" or "100". """ pc = self.pc_covered @@ -222,12 +239,16 @@ nums.n_excluded = self.n_excluded + other.n_excluded nums.n_missing = self.n_missing + other.n_missing nums.n_branches = self.n_branches + other.n_branches - nums.n_missing_branches = (self.n_missing_branches + - other.n_missing_branches) + nums.n_partial_branches = ( + self.n_partial_branches + other.n_partial_branches + ) + nums.n_missing_branches = ( + self.n_missing_branches + other.n_missing_branches + ) return nums def __radd__(self, other): # Implementing 0+Numbers allows us to sum() a list of Numbers. if other == 0: return self - raise NotImplemented + return NotImplemented diff -Nru python-coverage-3.4/coverage/summary.py python-coverage-3.6/coverage/summary.py --- python-coverage-3.4/coverage/summary.py 2010-09-03 01:40:49.000000000 +0000 +++ python-coverage-3.6/coverage/summary.py 2012-12-01 21:58:38.000000000 +0000 @@ -4,24 +4,23 @@ from coverage.report import Reporter from coverage.results import Numbers +from coverage.misc import NotPython class SummaryReporter(Reporter): """A reporter for writing the summary report.""" - def __init__(self, coverage, show_missing=True, ignore_errors=False): - super(SummaryReporter, self).__init__(coverage, ignore_errors) - self.show_missing = show_missing + def __init__(self, coverage, config): + super(SummaryReporter, self).__init__(coverage, config) self.branches = coverage.data.has_arcs() - def report(self, morfs, outfile=None, config=None): + def report(self, morfs, outfile=None): """Writes a report summarizing coverage statistics per module. - `outfile` is a file object to write the summary to. `config` is a - CoverageConfig instance. + `outfile` is a file object to write the summary to. """ - self.find_code_units(morfs, config) + self.find_code_units(morfs) # Prepare the formatting strings max_name = max([len(cu.name) for cu in self.code_units] + [5]) @@ -30,12 +29,12 @@ header = (fmt_name % "Name") + " Stmts Miss" fmt_coverage = fmt_name + "%6d %6d" if self.branches: - header += " Branch BrPart" + header += " Branch BrMiss" fmt_coverage += " %6d %6d" width100 = Numbers.pc_str_width() header += "%*s" % (width100+4, "Cover") fmt_coverage += "%%%ds%%%%" % (width100+3,) - if self.show_missing: + if self.config.show_missing: header += " Missing" fmt_coverage += " %s" rule = "-" * len(header) + "\n" @@ -59,15 +58,19 @@ if self.branches: args += (nums.n_branches, nums.n_missing_branches) args += (nums.pc_covered_str,) - if self.show_missing: + if self.config.show_missing: args += (analysis.missing_formatted(),) outfile.write(fmt_coverage % args) total += nums - except KeyboardInterrupt: # pragma: no cover + except KeyboardInterrupt: # pragma: not covered raise except: - if not self.ignore_errors: + report_it = not self.config.ignore_errors + if report_it: typ, msg = sys.exc_info()[:2] + if typ is NotPython and not cu.should_be_python(): + report_it = False + if report_it: outfile.write(fmt_err % (cu.name, typ.__name__, msg)) if total.n_files > 1: @@ -76,6 +79,8 @@ if self.branches: args += (total.n_branches, total.n_missing_branches) args += (total.pc_covered_str,) - if self.show_missing: + if self.config.show_missing: args += ("",) outfile.write(fmt_coverage % args) + + return total.pc_covered diff -Nru python-coverage-3.4/coverage/tracer.c python-coverage-3.6/coverage/tracer.c --- python-coverage-3.4/coverage/tracer.c 2010-08-26 13:29:59.000000000 +0000 +++ python-coverage-3.6/coverage/tracer.c 2012-11-04 12:35:53.000000000 +0000 @@ -27,7 +27,8 @@ #define MyText_Type PyUnicode_Type #define MyText_Check(o) PyUnicode_Check(o) -#define MyText_AS_STRING(o) PyBytes_AS_STRING(PyUnicode_AsASCIIString(o)) +#define MyText_AS_BYTES(o) PyUnicode_AsASCIIString(o) +#define MyText_AS_STRING(o) PyBytes_AS_STRING(o) #define MyInt_FromLong(l) PyLong_FromLong(l) #define MyType_HEAD_INIT PyVarObject_HEAD_INIT(NULL, 0) @@ -36,6 +37,7 @@ #define MyText_Type PyString_Type #define MyText_Check(o) PyString_Check(o) +#define MyText_AS_BYTES(o) (Py_INCREF(o), o) #define MyText_AS_STRING(o) PyString_AS_STRING(o) #define MyInt_FromLong(l) PyInt_FromLong(l) @@ -56,13 +58,14 @@ int last_line; } DataStackEntry; -/* The Tracer type. */ +/* The CTracer type. */ typedef struct { PyObject_HEAD /* Python objects manipulated directly by the Collector class. */ PyObject * should_trace; + PyObject * warn; PyObject * data; PyObject * should_trace_cache; PyObject * arcs; @@ -114,12 +117,12 @@ unsigned int errors; } stats; #endif /* COLLECT_STATS */ -} Tracer; +} CTracer; #define STACK_DELTA 100 static int -Tracer_init(Tracer *self, PyObject *args_unused, PyObject *kwds_unused) +CTracer_init(CTracer *self, PyObject *args_unused, PyObject *kwds_unused) { #if COLLECT_STATS self->stats.calls = 0; @@ -134,6 +137,7 @@ #endif /* COLLECT_STATS */ self->should_trace = NULL; + self->warn = NULL; self->data = NULL; self->should_trace_cache = NULL; self->arcs = NULL; @@ -159,13 +163,14 @@ } static void -Tracer_dealloc(Tracer *self) +CTracer_dealloc(CTracer *self) { if (self->started) { PyEval_SetTrace(NULL, NULL); } Py_XDECREF(self->should_trace); + Py_XDECREF(self->warn); Py_XDECREF(self->data); Py_XDECREF(self->should_trace_cache); @@ -204,7 +209,9 @@ printf(" "); } if (filename) { - printf(" %s", MyText_AS_STRING(filename)); + PyObject *ascii = MyText_AS_BYTES(filename); + printf(" %s", MyText_AS_STRING(ascii)); + Py_DECREF(ascii); } if (msg) { printf(" %s", msg); @@ -224,14 +231,12 @@ /* Record a pair of integers in self->cur_file_data. */ static int -Tracer_record_pair(Tracer *self, int l1, int l2) +CTracer_record_pair(CTracer *self, int l1, int l2) { int ret = RET_OK; - PyObject * t = PyTuple_New(2); + PyObject * t = Py_BuildValue("(ii)", l1, l2); if (t != NULL) { - PyTuple_SET_ITEM(t, 0, MyInt_FromLong(l1)); - PyTuple_SET_ITEM(t, 1, MyInt_FromLong(l2)); if (PyDict_SetItem(self->cur_file_data, t, Py_None) < 0) { STATS( self->stats.errors++; ) ret = RET_ERROR; @@ -249,22 +254,29 @@ * The Trace Function */ static int -Tracer_trace(Tracer *self, PyFrameObject *frame, int what, PyObject *arg_unused) +CTracer_trace(CTracer *self, PyFrameObject *frame, int what, PyObject *arg_unused) { int ret = RET_OK; PyObject * filename = NULL; PyObject * tracename = NULL; + #if WHAT_LOG || TRACE_LOG + PyObject * ascii = NULL; + #endif #if WHAT_LOG if (what <= sizeof(what_sym)/sizeof(const char *)) { - printf("trace: %s @ %s %d\n", what_sym[what], MyText_AS_STRING(frame->f_code->co_filename), frame->f_lineno); + ascii = MyText_AS_BYTES(frame->f_code->co_filename); + printf("trace: %s @ %s %d\n", what_sym[what], MyText_AS_STRING(ascii), frame->f_lineno); + Py_DECREF(ascii); } #endif #if TRACE_LOG - if (strstr(MyText_AS_STRING(frame->f_code->co_filename), start_file) && frame->f_lineno == start_line) { + ascii = MyText_AS_BYTES(frame->f_code->co_filename); + if (strstr(MyText_AS_STRING(ascii), start_file) && frame->f_lineno == start_line) { logging = 1; } + Py_DECREF(ascii); #endif /* See below for details on missing-return detection. */ @@ -283,7 +295,7 @@ STATS( self->stats.missed_returns++; ) if (self->depth >= 0) { if (self->tracing_arcs && self->cur_file_data) { - if (Tracer_record_pair(self, self->last_line, -self->last_exc_firstlineno) < 0) { + if (CTracer_record_pair(self, self->last_line, -self->last_exc_firstlineno) < 0) { return RET_ERROR; } } @@ -362,6 +374,9 @@ } } self->cur_file_data = file_data; + /* Make the frame right in case settrace(gettrace()) happens. */ + Py_INCREF(self); + frame->f_trace = (PyObject*)self; SHOWLOG(self->depth, frame->f_lineno, filename, "traced"); } else { @@ -380,7 +395,7 @@ if (self->depth >= 0) { if (self->tracing_arcs && self->cur_file_data) { int first = frame->f_code->co_firstlineno; - if (Tracer_record_pair(self, self->last_line, -first) < 0) { + if (CTracer_record_pair(self, self->last_line, -first) < 0) { return RET_ERROR; } } @@ -400,7 +415,7 @@ /* We're tracing in this frame: record something. */ if (self->tracing_arcs) { /* Tracing arcs: key is (last_line,this_line). */ - if (Tracer_record_pair(self, self->last_line, frame->f_lineno) < 0) { + if (CTracer_record_pair(self, self->last_line, frame->f_lineno) < 0) { return RET_ERROR; } } @@ -452,57 +467,98 @@ } /* - * A sys.settrace-compatible function that invokes our C trace function. + * Python has two ways to set the trace function: sys.settrace(fn), which + * takes a Python callable, and PyEval_SetTrace(func, obj), which takes + * a C function and a Python object. The way these work together is that + * sys.settrace(pyfn) calls PyEval_SetTrace(builtin_func, pyfn), using the + * Python callable as the object in PyEval_SetTrace. So sys.gettrace() + * simply returns the Python object used as the second argument to + * PyEval_SetTrace. So sys.gettrace() will return our self parameter, which + * means it must be callable to be used in sys.settrace(). + * + * So we make our self callable, equivalent to invoking our trace function. + * + * To help with the process of replaying stored frames, this function has an + * optional keyword argument: + * + * def CTracer_call(frame, event, arg, lineno=0) + * + * If provided, the lineno argument is used as the line number, and the + * frame's f_lineno member is ignored. */ static PyObject * -Tracer_pytrace(Tracer *self, PyObject *args) +CTracer_call(CTracer *self, PyObject *args, PyObject *kwds) { PyFrameObject *frame; PyObject *what_str; - PyObject *arg_unused; + PyObject *arg; + int lineno = 0; int what; + int orig_lineno; + PyObject *ret = NULL; + static char *what_names[] = { "call", "exception", "line", "return", "c_call", "c_exception", "c_return", NULL }; - if (!PyArg_ParseTuple(args, "O!O!O:Tracer_pytrace", - &PyFrame_Type, &frame, &MyText_Type, &what_str, &arg_unused)) { + #if WHAT_LOG + printf("pytrace\n"); + #endif + + static char *kwlist[] = {"frame", "event", "arg", "lineno", NULL}; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "O!O!O|i:Tracer_call", kwlist, + &PyFrame_Type, &frame, &MyText_Type, &what_str, &arg, &lineno)) { goto done; } /* In Python, the what argument is a string, we need to find an int for the C function. */ for (what = 0; what_names[what]; what++) { - if (!strcmp(MyText_AS_STRING(what_str), what_names[what])) { + PyObject *ascii = MyText_AS_BYTES(what_str); + int should_break = !strcmp(MyText_AS_STRING(ascii), what_names[what]); + Py_DECREF(ascii); + if (should_break) { break; } } + /* Save off the frame's lineno, and use the forced one, if provided. */ + orig_lineno = frame->f_lineno; + if (lineno > 0) { + frame->f_lineno = lineno; + } + /* Invoke the C function, and return ourselves. */ - if (Tracer_trace(self, frame, what, arg_unused) == RET_OK) { - return PyObject_GetAttrString((PyObject*)self, "pytrace"); + if (CTracer_trace(self, frame, what, arg) == RET_OK) { + Py_INCREF(self); + ret = (PyObject *)self; } + /* Clean up. */ + frame->f_lineno = orig_lineno; + done: - return NULL; + return ret; } static PyObject * -Tracer_start(Tracer *self, PyObject *args_unused) +CTracer_start(CTracer *self, PyObject *args_unused) { - PyEval_SetTrace((Py_tracefunc)Tracer_trace, (PyObject*)self); + PyEval_SetTrace((Py_tracefunc)CTracer_trace, (PyObject*)self); self->started = 1; self->tracing_arcs = self->arcs && PyObject_IsTrue(self->arcs); self->last_line = -1; /* start() returns a trace function usable with sys.settrace() */ - return PyObject_GetAttrString((PyObject*)self, "pytrace"); + Py_INCREF(self); + return (PyObject *)self; } static PyObject * -Tracer_stop(Tracer *self, PyObject *args_unused) +CTracer_stop(CTracer *self, PyObject *args_unused) { if (self->started) { PyEval_SetTrace(NULL, NULL); @@ -513,7 +569,7 @@ } static PyObject * -Tracer_get_stats(Tracer *self) +CTracer_get_stats(CTracer *self) { #if COLLECT_STATS return Py_BuildValue( @@ -535,46 +591,46 @@ } static PyMemberDef -Tracer_members[] = { - { "should_trace", T_OBJECT, offsetof(Tracer, should_trace), 0, +CTracer_members[] = { + { "should_trace", T_OBJECT, offsetof(CTracer, should_trace), 0, PyDoc_STR("Function indicating whether to trace a file.") }, - { "data", T_OBJECT, offsetof(Tracer, data), 0, + { "warn", T_OBJECT, offsetof(CTracer, warn), 0, + PyDoc_STR("Function for issuing warnings.") }, + + { "data", T_OBJECT, offsetof(CTracer, data), 0, PyDoc_STR("The raw dictionary of trace data.") }, - { "should_trace_cache", T_OBJECT, offsetof(Tracer, should_trace_cache), 0, + { "should_trace_cache", T_OBJECT, offsetof(CTracer, should_trace_cache), 0, PyDoc_STR("Dictionary caching should_trace results.") }, - { "arcs", T_OBJECT, offsetof(Tracer, arcs), 0, + { "arcs", T_OBJECT, offsetof(CTracer, arcs), 0, PyDoc_STR("Should we trace arcs, or just lines?") }, { NULL } }; static PyMethodDef -Tracer_methods[] = { - { "pytrace", (PyCFunction) Tracer_pytrace, METH_VARARGS, - PyDoc_STR("A trace function compatible with sys.settrace()") }, - - { "start", (PyCFunction) Tracer_start, METH_VARARGS, +CTracer_methods[] = { + { "start", (PyCFunction) CTracer_start, METH_VARARGS, PyDoc_STR("Start the tracer") }, - { "stop", (PyCFunction) Tracer_stop, METH_VARARGS, + { "stop", (PyCFunction) CTracer_stop, METH_VARARGS, PyDoc_STR("Stop the tracer") }, - { "get_stats", (PyCFunction) Tracer_get_stats, METH_VARARGS, + { "get_stats", (PyCFunction) CTracer_get_stats, METH_VARARGS, PyDoc_STR("Get statistics about the tracing") }, { NULL } }; static PyTypeObject -TracerType = { +CTracerType = { MyType_HEAD_INIT - "coverage.Tracer", /*tp_name*/ - sizeof(Tracer), /*tp_basicsize*/ + "coverage.CTracer", /*tp_name*/ + sizeof(CTracer), /*tp_basicsize*/ 0, /*tp_itemsize*/ - (destructor)Tracer_dealloc, /*tp_dealloc*/ + (destructor)CTracer_dealloc, /*tp_dealloc*/ 0, /*tp_print*/ 0, /*tp_getattr*/ 0, /*tp_setattr*/ @@ -584,28 +640,28 @@ 0, /*tp_as_sequence*/ 0, /*tp_as_mapping*/ 0, /*tp_hash */ - 0, /*tp_call*/ + (ternaryfunc)CTracer_call, /*tp_call*/ 0, /*tp_str*/ 0, /*tp_getattro*/ 0, /*tp_setattro*/ 0, /*tp_as_buffer*/ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/ - "Tracer objects", /* tp_doc */ + "CTracer objects", /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ 0, /* tp_iter */ 0, /* tp_iternext */ - Tracer_methods, /* tp_methods */ - Tracer_members, /* tp_members */ + CTracer_methods, /* tp_methods */ + CTracer_members, /* tp_members */ 0, /* tp_getset */ 0, /* tp_base */ 0, /* tp_dict */ 0, /* tp_descr_get */ 0, /* tp_descr_set */ 0, /* tp_dictoffset */ - (initproc)Tracer_init, /* tp_init */ + (initproc)CTracer_init, /* tp_init */ 0, /* tp_alloc */ 0, /* tp_new */ }; @@ -638,14 +694,14 @@ return NULL; } - TracerType.tp_new = PyType_GenericNew; - if (PyType_Ready(&TracerType) < 0) { + CTracerType.tp_new = PyType_GenericNew; + if (PyType_Ready(&CTracerType) < 0) { Py_DECREF(mod); return NULL; } - Py_INCREF(&TracerType); - PyModule_AddObject(mod, "Tracer", (PyObject *)&TracerType); + Py_INCREF(&CTracerType); + PyModule_AddObject(mod, "CTracer", (PyObject *)&CTracerType); return mod; } @@ -662,13 +718,13 @@ return; } - TracerType.tp_new = PyType_GenericNew; - if (PyType_Ready(&TracerType) < 0) { + CTracerType.tp_new = PyType_GenericNew; + if (PyType_Ready(&CTracerType) < 0) { return; } - Py_INCREF(&TracerType); - PyModule_AddObject(mod, "Tracer", (PyObject *)&TracerType); + Py_INCREF(&CTracerType); + PyModule_AddObject(mod, "CTracer", (PyObject *)&CTracerType); } #endif /* Py3k */ diff -Nru python-coverage-3.4/coverage/version.py python-coverage-3.6/coverage/version.py --- python-coverage-3.4/coverage/version.py 1970-01-01 00:00:00.000000000 +0000 +++ python-coverage-3.6/coverage/version.py 2013-01-05 22:22:35.000000000 +0000 @@ -0,0 +1,9 @@ +"""The version and URL for coverage.py""" +# This file is exec'ed in setup.py, don't import anything! + +__version__ = "3.6" # see detailed history in CHANGES.txt + +__url__ = "http://nedbatchelder.com/code/coverage" +if max(__version__).isalpha(): + # For pre-releases, use a version-specific URL. + __url__ += "/" + __version__ diff -Nru python-coverage-3.4/coverage/xmlreport.py python-coverage-3.6/coverage/xmlreport.py --- python-coverage-3.4/coverage/xmlreport.py 2010-09-11 23:06:41.000000000 +0000 +++ python-coverage-3.6/coverage/xmlreport.py 2012-12-20 12:02:31.000000000 +0000 @@ -4,7 +4,7 @@ import xml.dom.minidom from coverage import __url__, __version__ -from coverage.backward import sorted # pylint: disable-msg=W0622 +from coverage.backward import sorted, rpartition # pylint: disable=W0622 from coverage.report import Reporter def rate(hit, num): @@ -15,20 +15,19 @@ class XmlReporter(Reporter): """A reporter for writing Cobertura-style XML coverage results.""" - def __init__(self, coverage, ignore_errors=False): - super(XmlReporter, self).__init__(coverage, ignore_errors) + def __init__(self, coverage, config): + super(XmlReporter, self).__init__(coverage, config) self.packages = None self.xml_out = None self.arcs = coverage.data.has_arcs() - def report(self, morfs, outfile=None, config=None): + def report(self, morfs, outfile=None): """Generate a Cobertura-compatible XML report for `morfs`. `morfs` is a list of modules or filenames. - `outfile` is a file object to write the XML to. `config` is a - CoverageConfig instance. + `outfile` is a file object to write the XML to. """ # Initial setup. @@ -54,7 +53,7 @@ # Call xml_file for each file in the data. self.packages = {} - self.report_files(self.xml_file, morfs, config) + self.report_files(self.xml_file, morfs) lnum_tot, lhits_tot = 0, 0 bnum_tot, bhits_tot = 0, 0 @@ -85,14 +84,18 @@ # Use the DOM to write the output file. outfile.write(self.xml_out.toprettyxml()) + # Return the total percentage. + return 100.0 * (lhits_tot + bhits_tot) / (lnum_tot + bnum_tot) + def xml_file(self, cu, analysis): """Add to the XML report for a single file.""" # Create the 'lines' and 'package' XML elements, which # are populated later. Note that a package == a directory. - dirname, fname = os.path.split(cu.name) - dirname = dirname or '.' - package = self.packages.setdefault(dirname, [ {}, 0, 0, 0, 0 ]) + package_name = rpartition(cu.name, ".")[0] + className = cu.name + + package = self.packages.setdefault(package_name, [{}, 0, 0, 0, 0]) xclass = self.xml_out.createElement("class") @@ -100,10 +103,10 @@ xlines = self.xml_out.createElement("lines") xclass.appendChild(xlines) - className = fname.replace('.', '_') + xclass.setAttribute("name", className) - ext = os.path.splitext(cu.filename)[1] - xclass.setAttribute("filename", cu.name + ext) + filename = cu.file_locator.relative_filename(cu.filename) + xclass.setAttribute("filename", filename.replace("\\", "/")) xclass.setAttribute("complexity", "0") branch_stats = analysis.branch_stats() @@ -115,7 +118,7 @@ # Q: can we get info about the number of times a statement is # executed? If so, that should be recorded here. - xline.setAttribute("hits", str(int(not line in analysis.missing))) + xline.setAttribute("hits", str(int(line not in analysis.missing))) if self.arcs: if line in branch_stats: diff -Nru python-coverage-3.4/coverage.egg-info/PKG-INFO python-coverage-3.6/coverage.egg-info/PKG-INFO --- python-coverage-3.4/coverage.egg-info/PKG-INFO 2010-09-19 21:02:58.000000000 +0000 +++ python-coverage-3.6/coverage.egg-info/PKG-INFO 2013-01-06 00:59:49.000000000 +0000 @@ -1,6 +1,6 @@ -Metadata-Version: 1.0 +Metadata-Version: 1.1 Name: coverage -Version: 3.4 +Version: 3.6 Summary: Code coverage measurement for Python Home-page: http://nedbatchelder.com/code/coverage Author: Ned Batchelder and others @@ -10,18 +10,21 @@ the code analysis tools and tracing hooks provided in the Python standard library to determine which lines are executable, and which have been executed. - Coverage.py runs on Pythons 2.3 through 3.2. + Coverage.py runs on Pythons 2.3 through 3.3, and PyPy 1.9. Documentation is at `nedbatchelder.com `_. Code repository and issue tracker are at `bitbucket.org `_. - New in 3.2: Branch coverage! + New in 3.6: ``--fail-under``, and >20 bugs closed. - New in 3.3: .coveragerc files. + New in 3.5: Branch coverage exclusions, keyboard shortcuts in HTML report. New in 3.4: Better control over source to measure, and unexecuted files can be reported. + New in 3.3: .coveragerc files. + + New in 3.2: Branch coverage! Keywords: code coverage testing Platform: UNKNOWN Classifier: Environment :: Console diff -Nru python-coverage-3.4/coverage.egg-info/SOURCES.txt python-coverage-3.6/coverage.egg-info/SOURCES.txt --- python-coverage-3.4/coverage.egg-info/SOURCES.txt 2010-09-19 21:02:59.000000000 +0000 +++ python-coverage-3.6/coverage.egg-info/SOURCES.txt 2013-01-06 00:59:51.000000000 +0000 @@ -2,9 +2,11 @@ CHANGES.txt MANIFEST.in README.txt -distribute_setup.py -ez_setup.py +igor.py +requirements.txt setup.py +test_old.sh +tox.ini coverage/__init__.py coverage/__main__.py coverage/annotate.py @@ -27,6 +29,7 @@ coverage/summary.py coverage/templite.py coverage/tracer.c +coverage/version.py coverage/xmlreport.py coverage.egg-info/PKG-INFO coverage.egg-info/SOURCES.txt @@ -34,9 +37,201 @@ coverage.egg-info/entry_points.txt coverage.egg-info/not-zip-safe coverage.egg-info/top_level.txt +coverage/fullcoverage/encodings.py coverage/htmlfiles/coverage_html.js coverage/htmlfiles/index.html -coverage/htmlfiles/jquery-1.3.2.min.js +coverage/htmlfiles/jquery-1.4.3.min.js +coverage/htmlfiles/jquery.hotkeys.js +coverage/htmlfiles/jquery.isonscreen.js coverage/htmlfiles/jquery.tablesorter.min.js +coverage/htmlfiles/keybd_closed.png +coverage/htmlfiles/keybd_open.png coverage/htmlfiles/pyfile.html -coverage/htmlfiles/style.css \ No newline at end of file +coverage/htmlfiles/style.css +doc/api.rst +doc/branch.rst +doc/changes.rst +doc/cmd.rst +doc/config.rst +doc/contributing.rst +doc/excluding.rst +doc/faq.rst +doc/index.rst +doc/install.rst +doc/source.rst +doc/subprocess.rst +doc/trouble.rst +test/__init__.py +test/backtest.py +test/backunittest.py +test/coveragetest.py +test/covmodzip1.py +test/osinfo.py +test/stress_phystoken.tok +test/stress_phystoken_dos.tok +test/test_api.py +test/test_arcs.py +test/test_cmdline.py +test/test_codeunit.py +test/test_config.py +test/test_coverage.py +test/test_data.py +test/test_execfile.py +test/test_farm.py +test/test_files.py +test/test_html.py +test/test_misc.py +test/test_oddball.py +test/test_parser.py +test/test_phystokens.py +test/test_process.py +test/test_results.py +test/test_summary.py +test/test_templite.py +test/test_testing.py +test/test_xml.py +test/try_execfile.py +test/eggsrc/setup.py +test/eggsrc/egg1/__init__.py +test/eggsrc/egg1/egg1.py +test/farm/annotate/annotate_dir.py +test/farm/annotate/run.py +test/farm/annotate/run_multi.py +test/farm/annotate/gold/white.py,cover +test/farm/annotate/gold_anno_dir/a___init__.py,cover +test/farm/annotate/gold_anno_dir/a_a.py,cover +test/farm/annotate/gold_anno_dir/b___init__.py,cover +test/farm/annotate/gold_anno_dir/b_b.py,cover +test/farm/annotate/gold_anno_dir/multi.py,cover +test/farm/annotate/gold_multi/multi.py,cover +test/farm/annotate/gold_multi/a/__init__.py,cover +test/farm/annotate/gold_multi/a/a.py,cover +test/farm/annotate/gold_multi/b/__init__.py,cover +test/farm/annotate/gold_multi/b/b.py,cover +test/farm/annotate/gold_v24/white.py,cover +test/farm/annotate/src/multi.py +test/farm/annotate/src/white.py +test/farm/annotate/src/a/__init__.py +test/farm/annotate/src/a/a.py +test/farm/annotate/src/b/__init__.py +test/farm/annotate/src/b/b.py +test/farm/html/run_a.py +test/farm/html/run_a_xml_1.py +test/farm/html/run_a_xml_2.py +test/farm/html/run_b_branch.py +test/farm/html/run_bom.py +test/farm/html/run_isolatin1.py +test/farm/html/run_omit_1.py +test/farm/html/run_omit_2.py +test/farm/html/run_omit_3.py +test/farm/html/run_omit_4.py +test/farm/html/run_omit_5.py +test/farm/html/run_other.py +test/farm/html/run_partial.py +test/farm/html/run_styled.py +test/farm/html/run_tabbed.py +test/farm/html/run_unicode.py +test/farm/html/run_y_xml_branch.py +test/farm/html/gold_a/a.html +test/farm/html/gold_a/index.html +test/farm/html/gold_b_branch/b.html +test/farm/html/gold_b_branch/index.html +test/farm/html/gold_bom/bom.html +test/farm/html/gold_bom/index.html +test/farm/html/gold_isolatin1/index.html +test/farm/html/gold_isolatin1/isolatin1.html +test/farm/html/gold_omit_1/index.html +test/farm/html/gold_omit_1/m1.html +test/farm/html/gold_omit_1/m2.html +test/farm/html/gold_omit_1/m3.html +test/farm/html/gold_omit_1/main.html +test/farm/html/gold_omit_2/index.html +test/farm/html/gold_omit_2/m2.html +test/farm/html/gold_omit_2/m3.html +test/farm/html/gold_omit_2/main.html +test/farm/html/gold_omit_3/index.html +test/farm/html/gold_omit_3/m3.html +test/farm/html/gold_omit_3/main.html +test/farm/html/gold_omit_4/index.html +test/farm/html/gold_omit_4/m1.html +test/farm/html/gold_omit_4/m3.html +test/farm/html/gold_omit_4/main.html +test/farm/html/gold_omit_5/index.html +test/farm/html/gold_omit_5/m1.html +test/farm/html/gold_omit_5/main.html +test/farm/html/gold_other/blah_blah_other.html +test/farm/html/gold_other/here.html +test/farm/html/gold_other/index.html +test/farm/html/gold_partial/index.html +test/farm/html/gold_partial/partial.html +test/farm/html/gold_styled/a.html +test/farm/html/gold_styled/extra.css +test/farm/html/gold_styled/index.html +test/farm/html/gold_styled/style.css +test/farm/html/gold_unicode/index.html +test/farm/html/gold_unicode/unicode.html +test/farm/html/gold_x_xml/coverage.xml +test/farm/html/gold_y_xml_branch/coverage.xml +test/farm/html/othersrc/other.py +test/farm/html/src/a.py +test/farm/html/src/b.py +test/farm/html/src/bom.py +test/farm/html/src/coverage.xml +test/farm/html/src/extra.css +test/farm/html/src/here.py +test/farm/html/src/isolatin1.py +test/farm/html/src/m1.py +test/farm/html/src/m2.py +test/farm/html/src/m3.py +test/farm/html/src/main.py +test/farm/html/src/omit4.ini +test/farm/html/src/omit5.ini +test/farm/html/src/partial.py +test/farm/html/src/run_a_xml_2.ini +test/farm/html/src/tabbed.py +test/farm/html/src/unicode.py +test/farm/html/src/y.py +test/farm/run/run_chdir.py +test/farm/run/run_timid.py +test/farm/run/run_xxx.py +test/farm/run/src/chdir.py +test/farm/run/src/showtrace.py +test/farm/run/src/xxx +test/farm/run/src/subdir/placeholder +test/js/index.html +test/js/tests.js +test/modules/covmod1.py +test/modules/runmod1.py +test/modules/usepkgs.py +test/modules/aa/__init__.py +test/modules/aa/afile.odd.py +test/modules/aa/afile.py +test/modules/aa/zfile.py +test/modules/aa/bb/__init__.py +test/modules/aa/bb/bfile.odd.py +test/modules/aa/bb/bfile.py +test/modules/aa/bb.odd/bfile.py +test/modules/aa/bb/cc/__init__.py +test/modules/aa/bb/cc/cfile.py +test/modules/pkg1/__init__.py +test/modules/pkg1/__main__.py +test/modules/pkg1/p1a.py +test/modules/pkg1/p1b.py +test/modules/pkg1/p1c.py +test/modules/pkg1/runmod2.py +test/modules/pkg1/sub/__init__.py +test/modules/pkg1/sub/__main__.py +test/modules/pkg1/sub/ps1a.py +test/modules/pkg1/sub/runmod3.py +test/modules/pkg2/__init__.py +test/modules/pkg2/p2a.py +test/modules/pkg2/p2b.py +test/moremodules/othermods/__init__.py +test/moremodules/othermods/othera.py +test/moremodules/othermods/otherb.py +test/moremodules/othermods/sub/__init__.py +test/moremodules/othermods/sub/osa.py +test/moremodules/othermods/sub/osb.py +test/qunit/jquery.tmpl.min.js +test/qunit/qunit.css +test/qunit/qunit.js \ No newline at end of file diff -Nru python-coverage-3.4/coverage.egg-info/entry_points.txt python-coverage-3.6/coverage.egg-info/entry_points.txt --- python-coverage-3.4/coverage.egg-info/entry_points.txt 2010-09-19 21:02:58.000000000 +0000 +++ python-coverage-3.6/coverage.egg-info/entry_points.txt 2013-01-06 00:59:49.000000000 +0000 @@ -1,3 +1,5 @@ [console_scripts] +coverage2 = coverage:main +coverage-2.7 = coverage:main coverage = coverage:main diff -Nru python-coverage-3.4/debian/changelog python-coverage-3.6/debian/changelog --- python-coverage-3.4/debian/changelog 2012-05-31 21:06:06.000000000 +0000 +++ python-coverage-3.6/debian/changelog 2013-04-23 00:55:07.000000000 +0000 @@ -1,3 +1,24 @@ +python-coverage (3.6-0ubuntu2~ubuntu12.10.1~ppa1) quantal; urgency=low + + * No-change backport to quantal + + -- Chris Johnston Mon, 22 Apr 2013 20:55:07 -0400 + +python-coverage (3.6-0ubuntu2) raring; urgency=low + + * Fix a typo in 02.use-system-js-libraries.patch that was causing + NameErrors (LP: #1109090). + + -- Dmitry Shachnev Tue, 19 Mar 2013 19:25:38 +0400 + +python-coverage (3.6-0ubuntu1) raring; urgency=low + + * New upstream release. + * Dropped: + - debian/patches/01.refactor-handling-static-files.patch: No longer needed. + + -- Chuck Short Fri, 25 Jan 2013 09:06:51 -0600 + python-coverage (3.4-3ubuntu1) quantal; urgency=low * Merge from Debian testing (LP: #1007137). Remaining changes: diff -Nru python-coverage-3.4/debian/patches/01.refactor-handling-static-files.patch python-coverage-3.6/debian/patches/01.refactor-handling-static-files.patch --- python-coverage-3.4/debian/patches/01.refactor-handling-static-files.patch 2012-05-10 00:21:36.000000000 +0000 +++ python-coverage-3.6/debian/patches/01.refactor-handling-static-files.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,24 +0,0 @@ -Description: Refactor handling of static report files to a separate function. -Origin: Created for ease of custom packaging changes for duplicate libraries. -Author: Ben Finney -Last-Update: 2011-07-27 - -diff -r 25f2c1579f1c -r 609f75d136a3 coverage/html.py ---- a/coverage/html.py Sun Sep 19 16:42:27 2010 -0400 -+++ b/coverage/html.py Wed Jul 27 13:28:51 2011 +1000 -@@ -50,10 +50,13 @@ - # Write the index file. - self.index_file() - -- # Create the once-per-directory files. -+ self.make_local_static_report_files() -+ -+ def make_local_static_report_files(self): -+ """ Make local instances of static files for HTML report. """ - for static in [ - "style.css", "coverage_html.js", -- "jquery-1.3.2.min.js", "jquery.tablesorter.min.js" -+ "jquery-1.3.2.min.js", "jquery-tablesorter.min.js", - ]: - shutil.copyfile( - data_filename("htmlfiles/" + static), diff -Nru python-coverage-3.4/debian/patches/02.use-system-js-libraries.patch python-coverage-3.6/debian/patches/02.use-system-js-libraries.patch --- python-coverage-3.4/debian/patches/02.use-system-js-libraries.patch 2012-05-10 00:21:36.000000000 +0000 +++ python-coverage-3.6/debian/patches/02.use-system-js-libraries.patch 2013-03-22 10:29:41.000000000 +0000 @@ -1,62 +1,62 @@ Description: Use Debian system ECMAScript libraries. Origin: Created to address Debian Bug#596212. Author: Ben Finney -Last-Update: 2011-07-27 - -diff -r 609f75d136a3 coverage/html.py ---- a/coverage/html.py Wed Jul 27 13:28:51 2011 +1000 -+++ b/coverage/html.py Wed Jul 27 14:39:37 2011 +1000 -@@ -55,13 +55,24 @@ - def make_local_static_report_files(self): - """ Make local instances of static files for HTML report. """ - for static in [ -- "style.css", "coverage_html.js", -- "jquery-1.3.2.min.js", "jquery-tablesorter.min.js", -- ]: -+ "style.css", "coverage_html.js", -+ ]: - shutil.copyfile( - data_filename("htmlfiles/" + static), -- os.path.join(self.directory, static) -- ) -+ os.path.join(self.directory, static)) -+ +Last-Update: 2013-03-19 +diff -Naurp python-coverage-3.6.orig/coverage/htmlfiles/index.html python-coverage-3.6/coverage/htmlfiles/index.html +--- python-coverage-3.6.orig/coverage/htmlfiles/index.html 2012-11-19 19:51:47.000000000 -0600 ++++ python-coverage-3.6/coverage/htmlfiles/index.html 2013-01-25 09:00:25.002246121 -0600 +@@ -7,7 +7,7 @@ + {% if extra_css %} + + {% endif %} +- ++ + + + +diff -Naurp python-coverage-3.6.orig/coverage/htmlfiles/pyfile.html python-coverage-3.6/coverage/htmlfiles/pyfile.html +--- python-coverage-3.6.orig/coverage/htmlfiles/pyfile.html 2012-11-19 19:51:55.000000000 -0600 ++++ python-coverage-3.6/coverage/htmlfiles/pyfile.html 2013-01-25 09:00:42.758246130 -0600 +@@ -10,7 +10,7 @@ + {% if extra_css %} + + {% endif %} +- ++ + + + +diff -Naurp python-coverage-3.6.orig/coverage/html.py python-coverage-3.6/coverage/html.py +--- python-coverage-3.6.orig/coverage/html.py 2012-11-19 17:44:27.000000000 -0600 ++++ python-coverage-3.6/coverage/html.py 2013-01-25 09:04:44.638246247 -0600 +@@ -33,10 +33,8 @@ class HtmlReporter(Reporter): + # These files will be copied from the htmlfiles dir to the output dir. + STATIC_FILES = [ + "style.css", +- "jquery-1.4.3.min.js", + "jquery.hotkeys.js", + "jquery.isonscreen.js", +- "jquery.tablesorter.min.js", + "coverage_html.js", + "keybd_closed.png", + "keybd_open.png", +@@ -115,6 +113,19 @@ class HtmlReporter(Reporter): + os.path.join(self.directory, self.extra_css) + ) + + system_javascript_path = os.path.join( -+ os.sep, "usr", "share", "javascript") ++ os.sep, "usr", "share", "javascript") + system_javascript_libraries = { -+ "jquery.min.js": -+ os.path.join(system_javascript_path, "jquery"), -+ "jquery.tablesorter.min.js": -+ os.path.join(system_javascript_path, "jquery-tablesorter"), -+ } ++ "jquery.min.js": ++ os.path.join(system_javascript_path, "jquery"), ++ "jquery.tablesorter.min.js": ++ os.path.join(system_javascript_path, "jquery-tablesorter"), ++ } + for static, source_dir in system_javascript_libraries.items(): + os.symlink( + os.path.join(source_dir, static), + os.path.join(self.directory, static)) - - def html_file(self, cu, analysis): - """Generate an HTML file for one source file.""" -diff -r 609f75d136a3 coverage/htmlfiles/index.html ---- a/coverage/htmlfiles/index.html Wed Jul 27 13:28:51 2011 +1000 -+++ b/coverage/htmlfiles/index.html Wed Jul 27 14:39:37 2011 +1000 -@@ -4,7 +4,7 @@ - - Coverage report - -- -+ - - - -+ - - + + + + + + + + + +
+ +

Hot-keys on this page

+
+

+ r + m + x + p   toggle line displays +

+

+ j + k   next/prev highlighted chunk +

+

+ 0   (zero) top of page +

+

+ 1   (one) first highlighted chunk +

+
+
+ +
+
+ + + + +
+

1

+

2

+

3

+

4

+

5

+

6

+

7

+ +
+

# A test file for HTML reporting by coverage. 

+

 

+

if 1 < 2: 

+

    # Needed a < to look at HTML entities. 

+

    a = 3 

+

else: 

+

    a = 4 

+ +
+
+ + + + + diff -Nru python-coverage-3.4/test/farm/html/gold_a/index.html python-coverage-3.6/test/farm/html/gold_a/index.html --- python-coverage-3.4/test/farm/html/gold_a/index.html 1970-01-01 00:00:00.000000000 +0000 +++ python-coverage-3.6/test/farm/html/gold_a/index.html 2011-06-27 03:28:31.000000000 +0000 @@ -0,0 +1,89 @@ + + + + + Coverage report + + + + + + + + + + + +
+ +

Hot-keys on this page

+
+

+ n + s + m + x + + c   change column sorting +

+
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Modulestatementsmissingexcludedcoverage
Total31067%
a31067%
+
+ + + + + diff -Nru python-coverage-3.4/test/farm/html/gold_b_branch/b.html python-coverage-3.6/test/farm/html/gold_b_branch/b.html --- python-coverage-3.4/test/farm/html/gold_b_branch/b.html 1970-01-01 00:00:00.000000000 +0000 +++ python-coverage-3.6/test/farm/html/gold_b_branch/b.html 2011-04-27 12:30:17.000000000 +0000 @@ -0,0 +1,139 @@ + + + + + + + + Coverage for b: 76% + + + + + + + + + + + +
+ +

Hot-keys on this page

+
+

+ r + m + x + p   toggle line displays +

+

+ j + k   next/prev highlighted chunk +

+

+ 0   (zero) top of page +

+

+ 1   (one) first highlighted chunk +

+
+
+ +
+ + + + + +
+

1

+

2

+

3

+

4

+

5

+

6

+

7

+

8

+

9

+

10

+

11

+

12

+

13

+

14

+

15

+

16

+

17

+

18

+

19

+

20

+

21

+

22

+

23

+

24

+

25

+

26

+

27

+

28

+ +
+

# A test file for HTML reporting by coverage. 

+

 

+

def one(x): 

+

    # This will be a branch that misses the else. 

+

8    if x < 2: 

+

        a = 3 

+

    else: 

+

        a = 4 

+

 

+

one(1) 

+

 

+

def two(x): 

+

    # A missed else that branches to "exit" 

+

exit    if x: 

+

        a = 5 

+

 

+

two(1) 

+

 

+

def three_way(): 

+

    # for-else can be a three-way branch. 

+

25   26    for i in range(10): 

+

        if i == 3: 

+

            break 

+

    else: 

+

        return 23 

+

    return 17 

+

 

+

three_way() 

+ +
+
+ + + + + diff -Nru python-coverage-3.4/test/farm/html/gold_b_branch/index.html python-coverage-3.6/test/farm/html/gold_b_branch/index.html --- python-coverage-3.4/test/farm/html/gold_b_branch/index.html 1970-01-01 00:00:00.000000000 +0000 +++ python-coverage-3.6/test/farm/html/gold_b_branch/index.html 2011-06-27 03:28:31.000000000 +0000 @@ -0,0 +1,101 @@ + + + + + Coverage report + + + + + + + + + + + +
+ +

Hot-keys on this page

+
+

+ n + s + m + x + + b + p + + c   change column sorting +

+
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Modulestatementsmissingexcludedbranchespartialcoverage
Total16209476%
b16209476%
+
+ + + + + diff -Nru python-coverage-3.4/test/farm/html/gold_bom/bom.html python-coverage-3.6/test/farm/html/gold_bom/bom.html --- python-coverage-3.4/test/farm/html/gold_bom/bom.html 1970-01-01 00:00:00.000000000 +0000 +++ python-coverage-3.6/test/farm/html/gold_bom/bom.html 2012-05-15 01:20:21.000000000 +0000 @@ -0,0 +1,104 @@ + + + + + + + + Coverage for bom: 71% + + + + + + + + + + + + +
+ +

Hot-keys on this page

+
+

+ r + m + x + p   toggle line displays +

+

+ j + k   next/prev highlighted chunk +

+

+ 0   (zero) top of page +

+

+ 1   (one) first highlighted chunk +

+
+
+ +
+ + + + + +
+

1

+

2

+

3

+

4

+

5

+

6

+

7

+

8

+

9

+

10

+

11

+ +
+

# 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 -Nru python-coverage-3.4/test/farm/html/gold_bom/index.html python-coverage-3.6/test/farm/html/gold_bom/index.html --- python-coverage-3.4/test/farm/html/gold_bom/index.html 1970-01-01 00:00:00.000000000 +0000 +++ python-coverage-3.6/test/farm/html/gold_bom/index.html 2012-05-15 01:20:21.000000000 +0000 @@ -0,0 +1,90 @@ + + + + + Coverage report + + + + + + + + + + + + +
+ +

Hot-keys on this page

+
+

+ n + s + m + x + + c   change column sorting +

+
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Modulestatementsmissingexcludedcoverage
Total72071%
bom72071%
+
+ + + + + diff -Nru python-coverage-3.4/test/farm/html/gold_isolatin1/index.html python-coverage-3.6/test/farm/html/gold_isolatin1/index.html --- python-coverage-3.4/test/farm/html/gold_isolatin1/index.html 1970-01-01 00:00:00.000000000 +0000 +++ python-coverage-3.6/test/farm/html/gold_isolatin1/index.html 2012-03-22 12:10:32.000000000 +0000 @@ -0,0 +1,89 @@ + + + + + Coverage report + + + + + + + + + + + +
+ +

Hot-keys on this page

+
+

+ n + s + m + x + + c   change column sorting +

+
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Modulestatementsmissingexcludedcoverage
Total200100%
isolatin1200100%
+
+ + + + + diff -Nru python-coverage-3.4/test/farm/html/gold_isolatin1/isolatin1.html python-coverage-3.6/test/farm/html/gold_isolatin1/isolatin1.html --- python-coverage-3.4/test/farm/html/gold_isolatin1/isolatin1.html 1970-01-01 00:00:00.000000000 +0000 +++ python-coverage-3.6/test/farm/html/gold_isolatin1/isolatin1.html 2012-03-22 12:10:32.000000000 +0000 @@ -0,0 +1,91 @@ + + + + + + + + Coverage for isolatin1: 100% + + + + + + + + + + + +
+ +

Hot-keys on this page

+
+

+ r + m + x + p   toggle line displays +

+

+ j + k   next/prev highlighted chunk +

+

+ 0   (zero) top of page +

+

+ 1   (one) first highlighted chunk +

+
+
+ +
+ + + + + +
+

1

+

2

+

3

+

4

+

5

+ +
+

# A python source file in another encoding. 

+

# -*- coding: iso8859-1 -*- 

+

 

+

math = "3×4 = 12, ÷2 = 6±0" 

+

assert len(math) == 18 

+ +
+
+ + + + + diff -Nru python-coverage-3.4/test/farm/html/gold_omit_1/index.html python-coverage-3.6/test/farm/html/gold_omit_1/index.html --- python-coverage-3.4/test/farm/html/gold_omit_1/index.html 1970-01-01 00:00:00.000000000 +0000 +++ python-coverage-3.6/test/farm/html/gold_omit_1/index.html 2011-06-27 03:28:31.000000000 +0000 @@ -0,0 +1,116 @@ + + + + + Coverage report + + + + + + + + + + + +
+ +

Hot-keys on this page

+
+

+ n + s + m + x + + c   change column sorting +

+
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Modulestatementsmissingexcludedcoverage
Total1400100%
m1200100%
m2200100%
m3200100%
main800100%
+
+ + + + + diff -Nru python-coverage-3.4/test/farm/html/gold_omit_1/m1.html python-coverage-3.6/test/farm/html/gold_omit_1/m1.html --- python-coverage-3.4/test/farm/html/gold_omit_1/m1.html 1970-01-01 00:00:00.000000000 +0000 +++ python-coverage-3.6/test/farm/html/gold_omit_1/m1.html 2011-04-27 12:30:17.000000000 +0000 @@ -0,0 +1,85 @@ + + + + + + + + Coverage for m1: 100% + + + + + + + + + + + +
+ +

Hot-keys on this page

+
+

+ r + m + x + p   toggle line displays +

+

+ j + k   next/prev highlighted chunk +

+

+ 0   (zero) top of page +

+

+ 1   (one) first highlighted chunk +

+
+
+ +
+ + + + + +
+

1

+

2

+ +
+

m1a = 1 

+

m1b = 2 

+ +
+
+ + + + + diff -Nru python-coverage-3.4/test/farm/html/gold_omit_1/m2.html python-coverage-3.6/test/farm/html/gold_omit_1/m2.html --- python-coverage-3.4/test/farm/html/gold_omit_1/m2.html 1970-01-01 00:00:00.000000000 +0000 +++ python-coverage-3.6/test/farm/html/gold_omit_1/m2.html 2011-04-27 12:30:17.000000000 +0000 @@ -0,0 +1,85 @@ + + + + + + + + Coverage for m2: 100% + + + + + + + + + + + +
+ +

Hot-keys on this page

+
+

+ r + m + x + p   toggle line displays +

+

+ j + k   next/prev highlighted chunk +

+

+ 0   (zero) top of page +

+

+ 1   (one) first highlighted chunk +

+
+
+ +
+ + + + + +
+

1

+

2

+ +
+

m2a = 1 

+

m2b = 2 

+ +
+
+ + + + + diff -Nru python-coverage-3.4/test/farm/html/gold_omit_1/m3.html python-coverage-3.6/test/farm/html/gold_omit_1/m3.html --- python-coverage-3.4/test/farm/html/gold_omit_1/m3.html 1970-01-01 00:00:00.000000000 +0000 +++ python-coverage-3.6/test/farm/html/gold_omit_1/m3.html 2011-04-27 12:30:17.000000000 +0000 @@ -0,0 +1,85 @@ + + + + + + + + Coverage for m3: 100% + + + + + + + + + + + +
+ +

Hot-keys on this page

+
+

+ r + m + x + p   toggle line displays +

+

+ j + k   next/prev highlighted chunk +

+

+ 0   (zero) top of page +

+

+ 1   (one) first highlighted chunk +

+
+
+ +
+ + + + + +
+

1

+

2

+ +
+

m3a = 1 

+

m3b = 2 

+ +
+
+ + + + + diff -Nru python-coverage-3.4/test/farm/html/gold_omit_1/main.html python-coverage-3.6/test/farm/html/gold_omit_1/main.html --- python-coverage-3.4/test/farm/html/gold_omit_1/main.html 1970-01-01 00:00:00.000000000 +0000 +++ python-coverage-3.6/test/farm/html/gold_omit_1/main.html 2011-04-27 12:30:17.000000000 +0000 @@ -0,0 +1,101 @@ + + + + + + + + Coverage for main: 100% + + + + + + + + + + + +
+ +

Hot-keys on this page

+
+

+ r + m + x + p   toggle line displays +

+

+ j + k   next/prev highlighted chunk +

+

+ 0   (zero) top of page +

+

+ 1   (one) first highlighted chunk +

+
+
+ +
+ + + + + +
+

1

+

2

+

3

+

4

+

5

+

6

+

7

+

8

+

9

+

10

+ +
+

import m1 

+

import m2 

+

import m3 

+

 

+

a = 5 

+

b = 6 

+

 

+

assert m1.m1a == 1 

+

assert m2.m2a == 1 

+

assert m3.m3a == 1 

+ +
+
+ + + + + diff -Nru python-coverage-3.4/test/farm/html/gold_omit_2/index.html python-coverage-3.6/test/farm/html/gold_omit_2/index.html --- python-coverage-3.4/test/farm/html/gold_omit_2/index.html 1970-01-01 00:00:00.000000000 +0000 +++ python-coverage-3.6/test/farm/html/gold_omit_2/index.html 2011-06-27 03:28:31.000000000 +0000 @@ -0,0 +1,107 @@ + + + + + Coverage report + + + + + + + + + + + +
+ +

Hot-keys on this page

+
+

+ n + s + m + x + + c   change column sorting +

+
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Modulestatementsmissingexcludedcoverage
Total1200100%
m2200100%
m3200100%
main800100%
+
+ + + + + diff -Nru python-coverage-3.4/test/farm/html/gold_omit_2/m2.html python-coverage-3.6/test/farm/html/gold_omit_2/m2.html --- python-coverage-3.4/test/farm/html/gold_omit_2/m2.html 1970-01-01 00:00:00.000000000 +0000 +++ python-coverage-3.6/test/farm/html/gold_omit_2/m2.html 2011-04-27 12:30:17.000000000 +0000 @@ -0,0 +1,85 @@ + + + + + + + + Coverage for m2: 100% + + + + + + + + + + + +
+ +

Hot-keys on this page

+
+

+ r + m + x + p   toggle line displays +

+

+ j + k   next/prev highlighted chunk +

+

+ 0   (zero) top of page +

+

+ 1   (one) first highlighted chunk +

+
+
+ +
+ + + + + +
+

1

+

2

+ +
+

m2a = 1 

+

m2b = 2 

+ +
+
+ + + + + diff -Nru python-coverage-3.4/test/farm/html/gold_omit_2/m3.html python-coverage-3.6/test/farm/html/gold_omit_2/m3.html --- python-coverage-3.4/test/farm/html/gold_omit_2/m3.html 1970-01-01 00:00:00.000000000 +0000 +++ python-coverage-3.6/test/farm/html/gold_omit_2/m3.html 2011-04-27 12:30:17.000000000 +0000 @@ -0,0 +1,85 @@ + + + + + + + + Coverage for m3: 100% + + + + + + + + + + + +
+ +

Hot-keys on this page

+
+

+ r + m + x + p   toggle line displays +

+

+ j + k   next/prev highlighted chunk +

+

+ 0   (zero) top of page +

+

+ 1   (one) first highlighted chunk +

+
+
+ +
+ + + + + +
+

1

+

2

+ +
+

m3a = 1 

+

m3b = 2 

+ +
+
+ + + + + diff -Nru python-coverage-3.4/test/farm/html/gold_omit_2/main.html python-coverage-3.6/test/farm/html/gold_omit_2/main.html --- python-coverage-3.4/test/farm/html/gold_omit_2/main.html 1970-01-01 00:00:00.000000000 +0000 +++ python-coverage-3.6/test/farm/html/gold_omit_2/main.html 2011-04-27 12:30:17.000000000 +0000 @@ -0,0 +1,101 @@ + + + + + + + + Coverage for main: 100% + + + + + + + + + + + +
+ +

Hot-keys on this page

+
+

+ r + m + x + p   toggle line displays +

+

+ j + k   next/prev highlighted chunk +

+

+ 0   (zero) top of page +

+

+ 1   (one) first highlighted chunk +

+
+
+ +
+ + + + + +
+

1

+

2

+

3

+

4

+

5

+

6

+

7

+

8

+

9

+

10

+ +
+

import m1 

+

import m2 

+

import m3 

+

 

+

a = 5 

+

b = 6 

+

 

+

assert m1.m1a == 1 

+

assert m2.m2a == 1 

+

assert m3.m3a == 1 

+ +
+
+ + + + + diff -Nru python-coverage-3.4/test/farm/html/gold_omit_3/index.html python-coverage-3.6/test/farm/html/gold_omit_3/index.html --- python-coverage-3.4/test/farm/html/gold_omit_3/index.html 1970-01-01 00:00:00.000000000 +0000 +++ python-coverage-3.6/test/farm/html/gold_omit_3/index.html 2011-06-27 03:28:31.000000000 +0000 @@ -0,0 +1,98 @@ + + + + + Coverage report + + + + + + + + + + + +
+ +

Hot-keys on this page

+
+

+ n + s + m + x + + c   change column sorting +

+
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Modulestatementsmissingexcludedcoverage
Total1000100%
m3200100%
main800100%
+
+ + + + + diff -Nru python-coverage-3.4/test/farm/html/gold_omit_3/m3.html python-coverage-3.6/test/farm/html/gold_omit_3/m3.html --- python-coverage-3.4/test/farm/html/gold_omit_3/m3.html 1970-01-01 00:00:00.000000000 +0000 +++ python-coverage-3.6/test/farm/html/gold_omit_3/m3.html 2011-04-27 12:30:17.000000000 +0000 @@ -0,0 +1,85 @@ + + + + + + + + Coverage for m3: 100% + + + + + + + + + + + +
+ +

Hot-keys on this page

+
+

+ r + m + x + p   toggle line displays +

+

+ j + k   next/prev highlighted chunk +

+

+ 0   (zero) top of page +

+

+ 1   (one) first highlighted chunk +

+
+
+ +
+ + + + + +
+

1

+

2

+ +
+

m3a = 1 

+

m3b = 2 

+ +
+
+ + + + + diff -Nru python-coverage-3.4/test/farm/html/gold_omit_3/main.html python-coverage-3.6/test/farm/html/gold_omit_3/main.html --- python-coverage-3.4/test/farm/html/gold_omit_3/main.html 1970-01-01 00:00:00.000000000 +0000 +++ python-coverage-3.6/test/farm/html/gold_omit_3/main.html 2011-04-27 12:30:17.000000000 +0000 @@ -0,0 +1,101 @@ + + + + + + + + Coverage for main: 100% + + + + + + + + + + + +
+ +

Hot-keys on this page

+
+

+ r + m + x + p   toggle line displays +

+

+ j + k   next/prev highlighted chunk +

+

+ 0   (zero) top of page +

+

+ 1   (one) first highlighted chunk +

+
+
+ +
+ + + + + +
+

1

+

2

+

3

+

4

+

5

+

6

+

7

+

8

+

9

+

10

+ +
+

import m1 

+

import m2 

+

import m3 

+

 

+

a = 5 

+

b = 6 

+

 

+

assert m1.m1a == 1 

+

assert m2.m2a == 1 

+

assert m3.m3a == 1 

+ +
+
+ + + + + diff -Nru python-coverage-3.4/test/farm/html/gold_omit_4/index.html python-coverage-3.6/test/farm/html/gold_omit_4/index.html --- python-coverage-3.4/test/farm/html/gold_omit_4/index.html 1970-01-01 00:00:00.000000000 +0000 +++ python-coverage-3.6/test/farm/html/gold_omit_4/index.html 2011-06-27 03:28:31.000000000 +0000 @@ -0,0 +1,107 @@ + + + + + Coverage report + + + + + + + + + + + +
+ +

Hot-keys on this page

+
+

+ n + s + m + x + + c   change column sorting +

+
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Modulestatementsmissingexcludedcoverage
Total1200100%
m1200100%
m3200100%
main800100%
+
+ + + + + diff -Nru python-coverage-3.4/test/farm/html/gold_omit_4/m1.html python-coverage-3.6/test/farm/html/gold_omit_4/m1.html --- python-coverage-3.4/test/farm/html/gold_omit_4/m1.html 1970-01-01 00:00:00.000000000 +0000 +++ python-coverage-3.6/test/farm/html/gold_omit_4/m1.html 2011-04-27 12:30:17.000000000 +0000 @@ -0,0 +1,85 @@ + + + + + + + + Coverage for m1: 100% + + + + + + + + + + + +
+ +

Hot-keys on this page

+
+

+ r + m + x + p   toggle line displays +

+

+ j + k   next/prev highlighted chunk +

+

+ 0   (zero) top of page +

+

+ 1   (one) first highlighted chunk +

+
+
+ +
+ + + + + +
+

1

+

2

+ +
+

m1a = 1 

+

m1b = 2 

+ +
+
+ + + + + diff -Nru python-coverage-3.4/test/farm/html/gold_omit_4/m3.html python-coverage-3.6/test/farm/html/gold_omit_4/m3.html --- python-coverage-3.4/test/farm/html/gold_omit_4/m3.html 1970-01-01 00:00:00.000000000 +0000 +++ python-coverage-3.6/test/farm/html/gold_omit_4/m3.html 2011-04-27 12:30:17.000000000 +0000 @@ -0,0 +1,85 @@ + + + + + + + + Coverage for m3: 100% + + + + + + + + + + + +
+ +

Hot-keys on this page

+
+

+ r + m + x + p   toggle line displays +

+

+ j + k   next/prev highlighted chunk +

+

+ 0   (zero) top of page +

+

+ 1   (one) first highlighted chunk +

+
+
+ +
+ + + + + +
+

1

+

2

+ +
+

m3a = 1 

+

m3b = 2 

+ +
+
+ + + + + diff -Nru python-coverage-3.4/test/farm/html/gold_omit_4/main.html python-coverage-3.6/test/farm/html/gold_omit_4/main.html --- python-coverage-3.4/test/farm/html/gold_omit_4/main.html 1970-01-01 00:00:00.000000000 +0000 +++ python-coverage-3.6/test/farm/html/gold_omit_4/main.html 2011-04-27 12:30:17.000000000 +0000 @@ -0,0 +1,101 @@ + + + + + + + + Coverage for main: 100% + + + + + + + + + + + +
+ +

Hot-keys on this page

+
+

+ r + m + x + p   toggle line displays +

+

+ j + k   next/prev highlighted chunk +

+

+ 0   (zero) top of page +

+

+ 1   (one) first highlighted chunk +

+
+
+ +
+ + + + + +
+

1

+

2

+

3

+

4

+

5

+

6

+

7

+

8

+

9

+

10

+ +
+

import m1 

+

import m2 

+

import m3 

+

 

+

a = 5 

+

b = 6 

+

 

+

assert m1.m1a == 1 

+

assert m2.m2a == 1 

+

assert m3.m3a == 1 

+ +
+
+ + + + + diff -Nru python-coverage-3.4/test/farm/html/gold_omit_5/index.html python-coverage-3.6/test/farm/html/gold_omit_5/index.html --- python-coverage-3.4/test/farm/html/gold_omit_5/index.html 1970-01-01 00:00:00.000000000 +0000 +++ python-coverage-3.6/test/farm/html/gold_omit_5/index.html 2011-06-27 03:28:31.000000000 +0000 @@ -0,0 +1,98 @@ + + + + + Coverage report + + + + + + + + + + + +
+ +

Hot-keys on this page

+
+

+ n + s + m + x + + c   change column sorting +

+
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Modulestatementsmissingexcludedcoverage
Total1000100%
m1200100%
main800100%
+
+ + + + + diff -Nru python-coverage-3.4/test/farm/html/gold_omit_5/m1.html python-coverage-3.6/test/farm/html/gold_omit_5/m1.html --- python-coverage-3.4/test/farm/html/gold_omit_5/m1.html 1970-01-01 00:00:00.000000000 +0000 +++ python-coverage-3.6/test/farm/html/gold_omit_5/m1.html 2011-04-27 12:30:17.000000000 +0000 @@ -0,0 +1,85 @@ + + + + + + + + Coverage for m1: 100% + + + + + + + + + + + +
+ +

Hot-keys on this page

+
+

+ r + m + x + p   toggle line displays +

+

+ j + k   next/prev highlighted chunk +

+

+ 0   (zero) top of page +

+

+ 1   (one) first highlighted chunk +

+
+
+ +
+ + + + + +
+

1

+

2

+ +
+

m1a = 1 

+

m1b = 2 

+ +
+
+ + + + + diff -Nru python-coverage-3.4/test/farm/html/gold_omit_5/main.html python-coverage-3.6/test/farm/html/gold_omit_5/main.html --- python-coverage-3.4/test/farm/html/gold_omit_5/main.html 1970-01-01 00:00:00.000000000 +0000 +++ python-coverage-3.6/test/farm/html/gold_omit_5/main.html 2011-04-27 12:30:17.000000000 +0000 @@ -0,0 +1,101 @@ + + + + + + + + Coverage for main: 100% + + + + + + + + + + + +
+ +

Hot-keys on this page

+
+

+ r + m + x + p   toggle line displays +

+

+ j + k   next/prev highlighted chunk +

+

+ 0   (zero) top of page +

+

+ 1   (one) first highlighted chunk +

+
+
+ +
+ + + + + +
+

1

+

2

+

3

+

4

+

5

+

6

+

7

+

8

+

9

+

10

+ +
+

import m1 

+

import m2 

+

import m3 

+

 

+

a = 5 

+

b = 6 

+

 

+

assert m1.m1a == 1 

+

assert m2.m2a == 1 

+

assert m3.m3a == 1 

+ +
+
+ + + + + diff -Nru python-coverage-3.4/test/farm/html/gold_other/blah_blah_other.html python-coverage-3.6/test/farm/html/gold_other/blah_blah_other.html --- python-coverage-3.4/test/farm/html/gold_other/blah_blah_other.html 1970-01-01 00:00:00.000000000 +0000 +++ python-coverage-3.6/test/farm/html/gold_other/blah_blah_other.html 2011-04-27 12:30:17.000000000 +0000 @@ -0,0 +1,89 @@ + + + + + + + + Coverage for /home/ned/coverage/trunk/test/farm/html/othersrc/other: 100% + + + + + + + + + + + +
+ +

Hot-keys on this page

+
+

+ r + m + x + p   toggle line displays +

+

+ j + k   next/prev highlighted chunk +

+

+ 0   (zero) top of page +

+

+ 1   (one) first highlighted chunk +

+
+
+ +
+ + + + + +
+

1

+

2

+

3

+

4

+ +
+

# A file in another directory.  We're checking that it ends up in the 

+

# HTML report. 

+

 

+

print("This is the other src!") 

+ +
+
+ + + + + diff -Nru python-coverage-3.4/test/farm/html/gold_other/here.html python-coverage-3.6/test/farm/html/gold_other/here.html --- python-coverage-3.4/test/farm/html/gold_other/here.html 1970-01-01 00:00:00.000000000 +0000 +++ python-coverage-3.6/test/farm/html/gold_other/here.html 2011-04-27 12:30:17.000000000 +0000 @@ -0,0 +1,97 @@ + + + + + + + + Coverage for here: 75% + + + + + + + + + + + +
+ +

Hot-keys on this page

+
+

+ r + m + x + p   toggle line displays +

+

+ j + k   next/prev highlighted chunk +

+

+ 0   (zero) top of page +

+

+ 1   (one) first highlighted chunk +

+
+
+ +
+ + + + + +
+

1

+

2

+

3

+

4

+

5

+

6

+

7

+

8

+ +
+

# A test file for HTML reporting by coverage. 

+

 

+

import other 

+

 

+

if 1 < 2: 

+

    h = 3 

+

else: 

+

    h = 4 

+ +
+
+ + + + + diff -Nru python-coverage-3.4/test/farm/html/gold_other/index.html python-coverage-3.6/test/farm/html/gold_other/index.html --- python-coverage-3.4/test/farm/html/gold_other/index.html 1970-01-01 00:00:00.000000000 +0000 +++ python-coverage-3.6/test/farm/html/gold_other/index.html 2011-06-27 03:28:31.000000000 +0000 @@ -0,0 +1,98 @@ + + + + + Coverage report + + + + + + + + + + + +
+ +

Hot-keys on this page

+
+

+ n + s + m + x + + c   change column sorting +

+
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Modulestatementsmissingexcludedcoverage
Total51080%
/home/ned/coverage/trunk/test/farm/html/othersrc/other100100%
here41075%
+
+ + + + + diff -Nru python-coverage-3.4/test/farm/html/gold_partial/index.html python-coverage-3.6/test/farm/html/gold_partial/index.html --- python-coverage-3.4/test/farm/html/gold_partial/index.html 1970-01-01 00:00:00.000000000 +0000 +++ python-coverage-3.6/test/farm/html/gold_partial/index.html 2011-06-27 03:28:31.000000000 +0000 @@ -0,0 +1,101 @@ + + + + + Coverage report + + + + + + + + + + + +
+ +

Hot-keys on this page

+
+

+ n + s + m + x + + b + p + + c   change column sorting +

+
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Modulestatementsmissingexcludedbranchespartialcoverage
Total80060100%
partial80060100%
+
+ + + + + diff -Nru python-coverage-3.4/test/farm/html/gold_partial/partial.html python-coverage-3.6/test/farm/html/gold_partial/partial.html --- python-coverage-3.4/test/farm/html/gold_partial/partial.html 1970-01-01 00:00:00.000000000 +0000 +++ python-coverage-3.6/test/farm/html/gold_partial/partial.html 2011-06-01 12:20:12.000000000 +0000 @@ -0,0 +1,121 @@ + + + + + + + + Coverage for partial: 100% + + + + + + + + + + + +
+ +

Hot-keys on this page

+
+

+ r + m + x + p   toggle line displays +

+

+ j + k   next/prev highlighted chunk +

+

+ 0   (zero) top of page +

+

+ 1   (one) first highlighted chunk +

+
+
+ +
+ + + + + +
+

1

+

2

+

3

+

4

+

5

+

6

+

7

+

8

+

9

+

10

+

11

+

12

+

13

+

14

+

15

+

16

+

17

+

18

+

19

+ +
+

# partial branches 

+

 

+

a = 3 

+

 

+

while True: 

+

    break 

+

 

+

while 1: 

+

    break 

+

 

+

while a:        # pragma: no branch 

+

    break 

+

 

+

if 0: 

+

    never_happen() 

+

 

+

if 1: 

+

    a = 13 

+

 

+ +
+
+ + + + + diff -Nru python-coverage-3.4/test/farm/html/gold_styled/a.html python-coverage-3.6/test/farm/html/gold_styled/a.html --- python-coverage-3.4/test/farm/html/gold_styled/a.html 1970-01-01 00:00:00.000000000 +0000 +++ python-coverage-3.6/test/farm/html/gold_styled/a.html 2012-04-28 14:05:39.000000000 +0000 @@ -0,0 +1,95 @@ + + + + + + + + Coverage for a: 67% + + + + + + + + + + + +
+ +

Hot-keys on this page

+
+

+ r + m + x + p   toggle line displays +

+

+ j + k   next/prev highlighted chunk +

+

+ 0   (zero) top of page +

+

+ 1   (one) first highlighted chunk +

+
+
+ +
+ + + + + +
+

1

+

2

+

3

+

4

+

5

+

6

+

7

+ +
+

# A test file for HTML reporting by coverage. 

+

 

+

if 1 < 2: 

+

    # Needed a < to look at HTML entities. 

+

    a = 3 

+

else: 

+

    a = 4 

+ +
+
+ + + + + diff -Nru python-coverage-3.4/test/farm/html/gold_styled/extra.css python-coverage-3.6/test/farm/html/gold_styled/extra.css --- python-coverage-3.4/test/farm/html/gold_styled/extra.css 1970-01-01 00:00:00.000000000 +0000 +++ python-coverage-3.6/test/farm/html/gold_styled/extra.css 2012-04-28 14:05:39.000000000 +0000 @@ -0,0 +1 @@ +/* Doesn't matter what goes in here, it gets copied. */ diff -Nru python-coverage-3.4/test/farm/html/gold_styled/index.html python-coverage-3.6/test/farm/html/gold_styled/index.html --- python-coverage-3.4/test/farm/html/gold_styled/index.html 1970-01-01 00:00:00.000000000 +0000 +++ python-coverage-3.6/test/farm/html/gold_styled/index.html 2012-04-28 14:05:39.000000000 +0000 @@ -0,0 +1,89 @@ + + + + + Coverage report + + + + + + + + + + + +
+ +

Hot-keys on this page

+
+

+ n + s + m + x + + c   change column sorting +

+
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Modulestatementsmissingexcludedcoverage
Total31067%
a31067%
+
+ + + + + diff -Nru python-coverage-3.4/test/farm/html/gold_styled/style.css python-coverage-3.6/test/farm/html/gold_styled/style.css --- python-coverage-3.4/test/farm/html/gold_styled/style.css 1970-01-01 00:00:00.000000000 +0000 +++ python-coverage-3.6/test/farm/html/gold_styled/style.css 2012-04-28 14:05:39.000000000 +0000 @@ -0,0 +1,275 @@ +/* CSS styles for Coverage. */ +/* Page-wide styles */ +html, body, h1, h2, h3, p, td, th { + margin: 0; + padding: 0; + border: 0; + outline: 0; + font-weight: inherit; + font-style: inherit; + font-size: 100%; + font-family: inherit; + vertical-align: baseline; + } + +/* Set baseline grid to 16 pt. */ +body { + font-family: georgia, serif; + font-size: 1em; + } + +html>body { + font-size: 16px; + } + +/* Set base font size to 12/16 */ +p { + font-size: .75em; /* 12/16 */ + line-height: 1.3333em; /* 16/12 */ + } + +table { + border-collapse: collapse; + } + +a.nav { + text-decoration: none; + color: inherit; + } +a.nav:hover { + text-decoration: underline; + color: inherit; + } + +/* Page structure */ +#header { + background: #f8f8f8; + width: 100%; + border-bottom: 1px solid #eee; + } + +#source { + padding: 1em; + font-family: "courier new", monospace; + } + +#indexfile #footer { + margin: 1em 3em; + } + +#pyfile #footer { + margin: 1em 1em; + } + +#footer .content { + padding: 0; + font-size: 85%; + font-family: verdana, sans-serif; + color: #666666; + font-style: italic; + } + +#index { + margin: 1em 0 0 3em; + } + +/* Header styles */ +#header .content { + padding: 1em 3em; + } + +h1 { + font-size: 1.25em; +} + +h2.stats { + margin-top: .5em; + font-size: 1em; +} +.stats span { + border: 1px solid; + padding: .1em .25em; + margin: 0 .1em; + cursor: pointer; + border-color: #999 #ccc #ccc #999; +} +.stats span.hide_run, .stats span.hide_exc, +.stats span.hide_mis, .stats span.hide_par, +.stats span.par.hide_run.hide_par { + border-color: #ccc #999 #999 #ccc; +} +.stats span.par.hide_run { + border-color: #999 #ccc #ccc #999; +} + +/* Help panel */ +#keyboard_icon { + float: right; + cursor: pointer; +} + +.help_panel { + position: absolute; + background: #ffc; + padding: .5em; + border: 1px solid #883; + display: none; +} + +#indexfile .help_panel { + width: 20em; height: 4em; +} + +#pyfile .help_panel { + width: 16em; height: 8em; +} + +.help_panel .legend { + font-style: italic; + margin-bottom: 1em; +} + +#panel_icon { + float: right; + cursor: pointer; +} + +.keyhelp { + margin: .75em; +} + +.keyhelp .key { + border: 1px solid black; + border-color: #888 #333 #333 #888; + padding: .1em .35em; + font-family: monospace; + font-weight: bold; + background: #eee; +} + +/* Source file styles */ +.linenos p { + text-align: right; + margin: 0; + padding: 0 .5em; + color: #999999; + font-family: verdana, sans-serif; + font-size: .625em; /* 10/16 */ + line-height: 1.6em; /* 16/10 */ + } +.linenos p.highlight { + background: #ffdd00; + } +.linenos p a { + text-decoration: none; + color: #999999; + } +.linenos p a:hover { + text-decoration: underline; + color: #999999; + } + +td.text { + width: 100%; + } +.text p { + margin: 0; + padding: 0 0 0 .5em; + border-left: 2px solid #ffffff; + white-space: nowrap; + } + +.text p.mis { + background: #ffdddd; + border-left: 2px solid #ff0000; + } +.text p.run, .text p.run.hide_par { + background: #ddffdd; + border-left: 2px solid #00ff00; + } +.text p.exc { + background: #eeeeee; + border-left: 2px solid #808080; + } +.text p.par, .text p.par.hide_run { + background: #ffffaa; + border-left: 2px solid #eeee99; + } +.text p.hide_run, .text p.hide_exc, .text p.hide_mis, .text p.hide_par, +.text p.hide_run.hide_par { + background: inherit; + } + +.text span.annotate { + font-family: georgia; + font-style: italic; + color: #666; + float: right; + padding-right: .5em; + } +.text p.hide_par span.annotate { + display: none; + } + +/* Syntax coloring */ +.text .com { + color: green; + font-style: italic; + line-height: 1px; + } +.text .key { + font-weight: bold; + line-height: 1px; + } +.text .str { + color: #000080; + } + +/* index styles */ +#index td, #index th { + text-align: right; + width: 5em; + padding: .25em .5em; + border-bottom: 1px solid #eee; + } +#index th { + font-style: italic; + color: #333; + border-bottom: 1px solid #ccc; + cursor: pointer; + } +#index th:hover { + background: #eee; + border-bottom: 1px solid #999; + } +#index td.left, #index th.left { + padding-left: 0; + } +#index td.right, #index th.right { + padding-right: 0; + } +#index th.headerSortDown, #index th.headerSortUp { + border-bottom: 1px solid #000; + } +#index td.name, #index th.name { + text-align: left; + width: auto; + } +#index td.name a { + text-decoration: none; + color: #000; + } +#index td.name a:hover { + text-decoration: underline; + color: #000; + } +#index tr.total { + } +#index tr.total td { + font-weight: bold; + border-top: 1px solid #ccc; + border-bottom: none; + } +#index tr.file:hover { + background: #eeeeee; + } diff -Nru python-coverage-3.4/test/farm/html/gold_unicode/index.html python-coverage-3.6/test/farm/html/gold_unicode/index.html --- python-coverage-3.4/test/farm/html/gold_unicode/index.html 1970-01-01 00:00:00.000000000 +0000 +++ python-coverage-3.6/test/farm/html/gold_unicode/index.html 2011-08-17 13:40:36.000000000 +0000 @@ -0,0 +1,89 @@ + + + + + Coverage report + + + + + + + + + + + +
+ +

Hot-keys on this page

+
+

+ n + s + m + x + + c   change column sorting +

+
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Modulestatementsmissingexcludedcoverage
Total100100%
unicode100100%
+
+ + + + + diff -Nru python-coverage-3.4/test/farm/html/gold_unicode/unicode.html python-coverage-3.6/test/farm/html/gold_unicode/unicode.html --- python-coverage-3.4/test/farm/html/gold_unicode/unicode.html 1970-01-01 00:00:00.000000000 +0000 +++ python-coverage-3.6/test/farm/html/gold_unicode/unicode.html 2011-08-17 13:40:36.000000000 +0000 @@ -0,0 +1,91 @@ + + + + + + + + Coverage for unicode: 100% + + + + + + + + + + + +
+ +

Hot-keys on this page

+
+

+ r + m + x + p   toggle line displays +

+

+ j + k   next/prev highlighted chunk +

+

+ 0   (zero) top of page +

+

+ 1   (one) first highlighted chunk +

+
+
+ +
+ + + + + +
+

1

+

2

+

3

+

4

+

5

+ +
+

# A python source file with exotic characters 

+

# -*- coding: utf-8 -*- 

+

 

+

upside_down = "ʎd˙ǝbɐɹǝʌoɔ" 

+

surrogate = "db40,dd00: x�� 󠄀" 

+ +
+
+ + + + + diff -Nru python-coverage-3.4/test/farm/html/gold_x_xml/coverage.xml python-coverage-3.6/test/farm/html/gold_x_xml/coverage.xml --- python-coverage-3.4/test/farm/html/gold_x_xml/coverage.xml 1970-01-01 00:00:00.000000000 +0000 +++ python-coverage-3.6/test/farm/html/gold_x_xml/coverage.xml 2012-11-22 02:01:55.000000000 +0000 @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + diff -Nru python-coverage-3.4/test/farm/html/gold_y_xml_branch/coverage.xml python-coverage-3.6/test/farm/html/gold_y_xml_branch/coverage.xml --- python-coverage-3.4/test/farm/html/gold_y_xml_branch/coverage.xml 1970-01-01 00:00:00.000000000 +0000 +++ python-coverage-3.6/test/farm/html/gold_y_xml_branch/coverage.xml 2012-11-22 02:01:55.000000000 +0000 @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + diff -Nru python-coverage-3.4/test/farm/html/othersrc/other.py python-coverage-3.6/test/farm/html/othersrc/other.py --- python-coverage-3.4/test/farm/html/othersrc/other.py 1970-01-01 00:00:00.000000000 +0000 +++ python-coverage-3.6/test/farm/html/othersrc/other.py 2010-05-15 14:40:26.000000000 +0000 @@ -0,0 +1,4 @@ +# A file in another directory. We're checking that it ends up in the +# HTML report. + +print("This is the other src!") diff -Nru python-coverage-3.4/test/farm/html/run_a.py python-coverage-3.6/test/farm/html/run_a.py --- python-coverage-3.4/test/farm/html/run_a.py 1970-01-01 00:00:00.000000000 +0000 +++ python-coverage-3.6/test/farm/html/run_a.py 2012-12-02 18:40:27.000000000 +0000 @@ -0,0 +1,25 @@ +def html_it(): + """Run coverage and make an HTML report for a.""" + import coverage + cov = coverage.coverage() + cov.start() + import a # pragma: nested + cov.stop() # pragma: nested + cov.html_report(a, directory="../html_a") + +runfunc(html_it, rundir="src") + +# HTML files will change often. Check that the sizes are reasonable, +# 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.html", + "if 1 < 2", + "    a = 3", + "67%" + ) +contains("html_a/index.html", + "a", + "67%" + ) + +clean("html_a") diff -Nru python-coverage-3.4/test/farm/html/run_a_xml_1.py python-coverage-3.6/test/farm/html/run_a_xml_1.py --- python-coverage-3.4/test/farm/html/run_a_xml_1.py 1970-01-01 00:00:00.000000000 +0000 +++ python-coverage-3.6/test/farm/html/run_a_xml_1.py 2012-12-02 18:40:27.000000000 +0000 @@ -0,0 +1,21 @@ +def html_it(): + """Run coverage and make an XML report for a.""" + import coverage + cov = coverage.coverage() + cov.start() + import a # pragma: nested + cov.stop() # pragma: nested + cov.xml_report(a, outfile="../xml_1/coverage.xml") + +import os +if not os.path.exists("xml_1"): + os.makedirs("xml_1") + +runfunc(html_it, rundir="src") + +compare("gold_x_xml", "xml_1", scrubs=[ + (r' timestamp="\d+"', ' timestamp="TIMESTAMP"'), + (r' version="[-.\w]+"', ' version="VERSION"'), + (r'/code/coverage/?[-.\w]*', '/code/coverage/VER'), + ]) +clean("xml_1") diff -Nru python-coverage-3.4/test/farm/html/run_a_xml_2.py python-coverage-3.6/test/farm/html/run_a_xml_2.py --- python-coverage-3.4/test/farm/html/run_a_xml_2.py 1970-01-01 00:00:00.000000000 +0000 +++ python-coverage-3.6/test/farm/html/run_a_xml_2.py 2012-12-02 18:40:27.000000000 +0000 @@ -0,0 +1,21 @@ +def html_it(): + """Run coverage and make an XML report for a.""" + import coverage + cov = coverage.coverage(config_file="run_a_xml_2.ini") + cov.start() + import a # pragma: nested + cov.stop() # pragma: nested + cov.xml_report(a) + +import os +if not os.path.exists("xml_2"): + os.makedirs("xml_2") + +runfunc(html_it, rundir="src") + +compare("gold_x_xml", "xml_2", scrubs=[ + (r' timestamp="\d+"', ' timestamp="TIMESTAMP"'), + (r' version="[-.\w]+"', ' version="VERSION"'), + (r'/code/coverage/?[-.\w]*', '/code/coverage/VER'), + ]) +clean("xml_2") diff -Nru python-coverage-3.4/test/farm/html/run_b_branch.py python-coverage-3.6/test/farm/html/run_b_branch.py --- python-coverage-3.4/test/farm/html/run_b_branch.py 1970-01-01 00:00:00.000000000 +0000 +++ python-coverage-3.6/test/farm/html/run_b_branch.py 2012-12-02 18:40:27.000000000 +0000 @@ -0,0 +1,28 @@ +def html_it(): + """Run coverage with branches and make an HTML report for b.""" + import coverage + cov = coverage.coverage(branch=True) + cov.start() + import b # pragma: nested + cov.stop() # pragma: nested + cov.html_report(b, directory="../html_b_branch") + +runfunc(html_it, rundir="src") + +# HTML files will change often. Check that the sizes are reasonable, +# 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.html", + "if x < 2", + "    a = 3", + "70%", + "8", + "exit", + "23   25", + ) +contains("html_b_branch/index.html", + "b", + "70%" + ) + +clean("html_b_branch") diff -Nru python-coverage-3.4/test/farm/html/run_bom.py python-coverage-3.6/test/farm/html/run_bom.py --- python-coverage-3.4/test/farm/html/run_bom.py 1970-01-01 00:00:00.000000000 +0000 +++ python-coverage-3.6/test/farm/html/run_bom.py 2012-12-02 18:40:27.000000000 +0000 @@ -0,0 +1,21 @@ +import sys + +def html_it(): + """Run coverage and make an HTML report for bom.py.""" + import coverage + cov = coverage.coverage() + cov.start() + import bom # pragma: nested + cov.stop() # pragma: nested + cov.html_report(bom, directory="../html_bom") + +runfunc(html_it, rundir="src") + +# HTML files will change often. Check that the sizes are reasonable, +# 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.html", + ""3×4 = 12, ÷2 = 6±0"", + ) + +clean("html_bom") diff -Nru python-coverage-3.4/test/farm/html/run_isolatin1.py python-coverage-3.6/test/farm/html/run_isolatin1.py --- python-coverage-3.4/test/farm/html/run_isolatin1.py 1970-01-01 00:00:00.000000000 +0000 +++ python-coverage-3.6/test/farm/html/run_isolatin1.py 2012-12-02 18:40:27.000000000 +0000 @@ -0,0 +1,21 @@ +import sys + +def html_it(): + """Run coverage and make an HTML report for isolatin1.py.""" + import coverage + cov = coverage.coverage() + cov.start() + import isolatin1 # pragma: nested + cov.stop() # pragma: nested + cov.html_report(isolatin1, directory="../html_isolatin1") + +runfunc(html_it, rundir="src") + +# HTML files will change often. Check that the sizes are reasonable, +# 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.html", + ""3×4 = 12, ÷2 = 6±0"", + ) + +clean("html_isolatin1") diff -Nru python-coverage-3.4/test/farm/html/run_omit_1.py python-coverage-3.6/test/farm/html/run_omit_1.py --- python-coverage-3.4/test/farm/html/run_omit_1.py 1970-01-01 00:00:00.000000000 +0000 +++ python-coverage-3.6/test/farm/html/run_omit_1.py 2012-12-02 18:40:27.000000000 +0000 @@ -0,0 +1,12 @@ +def html_it(): + """Run coverage and make an HTML report for main.""" + import coverage + cov = coverage.coverage() + cov.start() + import main # pragma: nested + cov.stop() # pragma: nested + cov.html_report(directory="../html_omit_1") + +runfunc(html_it, rundir="src") +compare("gold_omit_1", "html_omit_1", size_within=10, file_pattern="*.html") +clean("html_omit_1") diff -Nru python-coverage-3.4/test/farm/html/run_omit_2.py python-coverage-3.6/test/farm/html/run_omit_2.py --- python-coverage-3.4/test/farm/html/run_omit_2.py 1970-01-01 00:00:00.000000000 +0000 +++ python-coverage-3.6/test/farm/html/run_omit_2.py 2012-12-02 18:40:27.000000000 +0000 @@ -0,0 +1,12 @@ +def html_it(): + """Run coverage and make an HTML report for main.""" + import coverage + cov = coverage.coverage() + cov.start() + import main # pragma: nested + cov.stop() # pragma: nested + cov.html_report(directory="../html_omit_2", omit=["m1.py"]) + +runfunc(html_it, rundir="src") +compare("gold_omit_2", "html_omit_2", size_within=10, file_pattern="*.html") +clean("html_omit_2") diff -Nru python-coverage-3.4/test/farm/html/run_omit_3.py python-coverage-3.6/test/farm/html/run_omit_3.py --- python-coverage-3.4/test/farm/html/run_omit_3.py 1970-01-01 00:00:00.000000000 +0000 +++ python-coverage-3.6/test/farm/html/run_omit_3.py 2012-12-02 18:40:27.000000000 +0000 @@ -0,0 +1,12 @@ +def html_it(): + """Run coverage and make an HTML report for main.""" + import coverage + cov = coverage.coverage() + cov.start() + import main # pragma: nested + cov.stop() # pragma: nested + cov.html_report(directory="../html_omit_3", omit=["m1.py", "m2.py"]) + +runfunc(html_it, rundir="src") +compare("gold_omit_3", "html_omit_3", size_within=10, file_pattern="*.html") +clean("html_omit_3") diff -Nru python-coverage-3.4/test/farm/html/run_omit_4.py python-coverage-3.6/test/farm/html/run_omit_4.py --- python-coverage-3.4/test/farm/html/run_omit_4.py 1970-01-01 00:00:00.000000000 +0000 +++ python-coverage-3.6/test/farm/html/run_omit_4.py 2012-12-02 18:40:27.000000000 +0000 @@ -0,0 +1,12 @@ +def html_it(): + """Run coverage and make an HTML report for main.""" + import coverage + cov = coverage.coverage(config_file="omit4.ini") + cov.start() + import main # pragma: nested + cov.stop() # pragma: nested + cov.html_report(directory="../html_omit_4") + +runfunc(html_it, rundir="src") +compare("gold_omit_4", "html_omit_4", size_within=10, file_pattern="*.html") +clean("html_omit_4") diff -Nru python-coverage-3.4/test/farm/html/run_omit_5.py python-coverage-3.6/test/farm/html/run_omit_5.py --- python-coverage-3.4/test/farm/html/run_omit_5.py 1970-01-01 00:00:00.000000000 +0000 +++ python-coverage-3.6/test/farm/html/run_omit_5.py 2012-12-02 18:40:27.000000000 +0000 @@ -0,0 +1,12 @@ +def html_it(): + """Run coverage and make an HTML report for main.""" + import coverage + cov = coverage.coverage(config_file="omit5.ini") + cov.start() + import main # pragma: nested + cov.stop() # pragma: nested + cov.html_report() + +runfunc(html_it, rundir="src") +compare("gold_omit_5", "html_omit_5", size_within=10, file_pattern="*.html") +clean("html_omit_5") diff -Nru python-coverage-3.4/test/farm/html/run_other.py python-coverage-3.6/test/farm/html/run_other.py --- python-coverage-3.4/test/farm/html/run_other.py 1970-01-01 00:00:00.000000000 +0000 +++ python-coverage-3.6/test/farm/html/run_other.py 2012-12-02 18:40:27.000000000 +0000 @@ -0,0 +1,26 @@ +def html_it(): + """Run coverage and make an HTML report for everything.""" + import coverage + cov = coverage.coverage() + cov.start() + import here # pragma: nested + cov.stop() # pragma: nested + cov.html_report(directory="../html_other") + +runfunc(html_it, rundir="src", addtopath="../othersrc") + +# Different platforms will name the "other" file differently. Rename it +import os, glob + +for p in glob.glob("html_other/*_other.html"): + os.rename(p, "html_other/blah_blah_other.html") + +# HTML files will change often. Check that the sizes are reasonable, +# 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", + "here", + "other.html'>", "other", + ) + +clean("html_other") diff -Nru python-coverage-3.4/test/farm/html/run_partial.py python-coverage-3.6/test/farm/html/run_partial.py --- python-coverage-3.4/test/farm/html/run_partial.py 1970-01-01 00:00:00.000000000 +0000 +++ python-coverage-3.6/test/farm/html/run_partial.py 2012-12-02 18:40:27.000000000 +0000 @@ -0,0 +1,32 @@ +import sys + +def html_it(): + """Run coverage and make an HTML report for partial.""" + import coverage + cov = coverage.coverage(branch=True) + cov.start() + import partial # pragma: nested + cov.stop() # pragma: nested + cov.html_report(partial, directory="../html_partial") + +runfunc(html_it, rundir="src") + +# HTML files will change often. Check that the sizes are reasonable, +# 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.html", + "

", + "

", + "

", + # The "if 0" and "if 1" statements are optimized away. + "

", + ) +contains("html_partial/index.html", + "partial", + ) +if sys.version_info >= (2, 4): + contains("html_partial/index.html", + "100%" + ) + +clean("html_partial") diff -Nru python-coverage-3.4/test/farm/html/run_styled.py python-coverage-3.6/test/farm/html/run_styled.py --- python-coverage-3.4/test/farm/html/run_styled.py 1970-01-01 00:00:00.000000000 +0000 +++ python-coverage-3.6/test/farm/html/run_styled.py 2012-12-02 18:40:27.000000000 +0000 @@ -0,0 +1,28 @@ +def html_it(): + """Run coverage and make an HTML report for a.""" + import coverage + cov = coverage.coverage() + cov.start() + import a # pragma: nested + cov.stop() # pragma: nested + cov.html_report(a, directory="../html_styled", extra_css="extra.css") + +runfunc(html_it, rundir="src") + +# HTML files will change often. Check that the sizes are reasonable, +# and check that certain key strings are in the output. +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.html", + "", + "if 1 < 2", + "    a = 3", + "67%" + ) +contains("html_styled/index.html", + "", + "a", + "67%" + ) + +clean("html_styled") diff -Nru python-coverage-3.4/test/farm/html/run_tabbed.py python-coverage-3.6/test/farm/html/run_tabbed.py --- python-coverage-3.4/test/farm/html/run_tabbed.py 1970-01-01 00:00:00.000000000 +0000 +++ python-coverage-3.6/test/farm/html/run_tabbed.py 2012-12-02 18:40:27.000000000 +0000 @@ -0,0 +1,24 @@ +def html_it(): + """Run coverage and make an HTML report for tabbed.""" + import coverage + cov = coverage.coverage() + cov.start() + import tabbed # pragma: nested + cov.stop() # pragma: nested + cov.html_report(tabbed, directory="../html_tabbed") + +runfunc(html_it, rundir="src") + +# Editors like to change things, make sure our source file still has tabs. +contains("src/tabbed.py", "\tif x:\t\t\t\t\t# look nice") + +contains("html_tabbed/tabbed.html", + ">        if " + "x:" + "                    " + "               " + "# look nice" + ) + +doesnt_contain("html_tabbed/tabbed.html", "\t") +clean("html_tabbed") diff -Nru python-coverage-3.4/test/farm/html/run_unicode.py python-coverage-3.6/test/farm/html/run_unicode.py --- python-coverage-3.4/test/farm/html/run_unicode.py 1970-01-01 00:00:00.000000000 +0000 +++ python-coverage-3.6/test/farm/html/run_unicode.py 2012-12-02 18:40:27.000000000 +0000 @@ -0,0 +1,30 @@ +import sys + +def html_it(): + """Run coverage and make an HTML report for unicode.py.""" + import coverage + cov = coverage.coverage() + cov.start() + import unicode # pragma: nested + cov.stop() # pragma: nested + cov.html_report(unicode, directory="../html_unicode") + +runfunc(html_it, rundir="src") + +# HTML files will change often. Check that the sizes are reasonable, +# 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.html", + ""ʎd˙ǝbɐɹǝʌoɔ"", + ) + +if sys.maxunicode == 65535: + contains("html_unicode/unicode.html", + ""db40,dd00: x��"", + ) +else: + contains("html_unicode/unicode.html", + ""db40,dd00: x󠄀"", + ) + +clean("html_unicode") diff -Nru python-coverage-3.4/test/farm/html/run_y_xml_branch.py python-coverage-3.6/test/farm/html/run_y_xml_branch.py --- python-coverage-3.4/test/farm/html/run_y_xml_branch.py 1970-01-01 00:00:00.000000000 +0000 +++ python-coverage-3.6/test/farm/html/run_y_xml_branch.py 2012-12-02 18:40:27.000000000 +0000 @@ -0,0 +1,21 @@ +def xml_it(): + """Run coverage and make an XML report for y.""" + import coverage + cov = coverage.coverage(branch=True) + cov.start() + import y # pragma: nested + cov.stop() # pragma: nested + cov.xml_report(y, outfile="../xml_branch/coverage.xml") + +import os +if not os.path.exists("xml_branch"): + os.makedirs("xml_branch") + +runfunc(xml_it, rundir="src") + +compare("gold_y_xml_branch", "xml_branch", scrubs=[ + (r' timestamp="\d+"', ' timestamp="TIMESTAMP"'), + (r' version="[-.\w]+"', ' version="VERSION"'), + (r'/code/coverage/?[-.\w]*', '/code/coverage/VER'), + ]) +clean("xml_branch") diff -Nru python-coverage-3.4/test/farm/html/src/a.py python-coverage-3.6/test/farm/html/src/a.py --- python-coverage-3.4/test/farm/html/src/a.py 1970-01-01 00:00:00.000000000 +0000 +++ python-coverage-3.6/test/farm/html/src/a.py 2010-05-15 14:40:26.000000000 +0000 @@ -0,0 +1,7 @@ +# A test file for HTML reporting by coverage. + +if 1 < 2: + # Needed a < to look at HTML entities. + a = 3 +else: + a = 4 diff -Nru python-coverage-3.4/test/farm/html/src/b.py python-coverage-3.6/test/farm/html/src/b.py --- python-coverage-3.4/test/farm/html/src/b.py 1970-01-01 00:00:00.000000000 +0000 +++ python-coverage-3.6/test/farm/html/src/b.py 2011-08-03 02:35:59.000000000 +0000 @@ -0,0 +1,29 @@ +# A test file for HTML reporting by coverage. + +def one(x): + # This will be a branch that misses the else. + if x < 2: + a = 3 + else: + a = 4 + +one(1) + +def two(x): + # A missed else that branches to "exit" + if x: + a = 5 + +two(1) + +def three(): + try: + # This if has two branches, *neither* one taken. + if name_error_this_variable_doesnt_exist: + a = 1 + else: + a = 2 + except: + pass + +three() diff -Nru python-coverage-3.4/test/farm/html/src/bom.py python-coverage-3.6/test/farm/html/src/bom.py --- python-coverage-3.4/test/farm/html/src/bom.py 1970-01-01 00:00:00.000000000 +0000 +++ python-coverage-3.6/test/farm/html/src/bom.py 2012-05-12 02:51:57.000000000 +0000 @@ -0,0 +1,11 @@ +# 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 -Nru python-coverage-3.4/test/farm/html/src/coverage.xml python-coverage-3.6/test/farm/html/src/coverage.xml --- python-coverage-3.4/test/farm/html/src/coverage.xml 1970-01-01 00:00:00.000000000 +0000 +++ python-coverage-3.6/test/farm/html/src/coverage.xml 2012-11-22 02:01:55.000000000 +0000 @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + diff -Nru python-coverage-3.4/test/farm/html/src/extra.css python-coverage-3.6/test/farm/html/src/extra.css --- python-coverage-3.4/test/farm/html/src/extra.css 1970-01-01 00:00:00.000000000 +0000 +++ python-coverage-3.6/test/farm/html/src/extra.css 2012-04-28 14:05:39.000000000 +0000 @@ -0,0 +1 @@ +/* Doesn't matter what goes in here, it gets copied. */ diff -Nru python-coverage-3.4/test/farm/html/src/here.py python-coverage-3.6/test/farm/html/src/here.py --- python-coverage-3.4/test/farm/html/src/here.py 1970-01-01 00:00:00.000000000 +0000 +++ python-coverage-3.6/test/farm/html/src/here.py 2010-05-15 14:40:26.000000000 +0000 @@ -0,0 +1,8 @@ +# A test file for HTML reporting by coverage. + +import other + +if 1 < 2: + h = 3 +else: + h = 4 diff -Nru python-coverage-3.4/test/farm/html/src/isolatin1.py python-coverage-3.6/test/farm/html/src/isolatin1.py --- python-coverage-3.4/test/farm/html/src/isolatin1.py 1970-01-01 00:00:00.000000000 +0000 +++ python-coverage-3.6/test/farm/html/src/isolatin1.py 2012-03-22 12:10:32.000000000 +0000 @@ -0,0 +1,5 @@ +# A python source file in another encoding. +# -*- coding: iso8859-1 -*- + +math = "34 = 12, 2 = 60" +assert len(math) == 18 diff -Nru python-coverage-3.4/test/farm/html/src/m1.py python-coverage-3.6/test/farm/html/src/m1.py --- python-coverage-3.4/test/farm/html/src/m1.py 1970-01-01 00:00:00.000000000 +0000 +++ python-coverage-3.6/test/farm/html/src/m1.py 2010-05-30 04:26:51.000000000 +0000 @@ -0,0 +1,2 @@ +m1a = 1 +m1b = 2 diff -Nru python-coverage-3.4/test/farm/html/src/m2.py python-coverage-3.6/test/farm/html/src/m2.py --- python-coverage-3.4/test/farm/html/src/m2.py 1970-01-01 00:00:00.000000000 +0000 +++ python-coverage-3.6/test/farm/html/src/m2.py 2010-05-30 04:26:51.000000000 +0000 @@ -0,0 +1,2 @@ +m2a = 1 +m2b = 2 diff -Nru python-coverage-3.4/test/farm/html/src/m3.py python-coverage-3.6/test/farm/html/src/m3.py --- python-coverage-3.4/test/farm/html/src/m3.py 1970-01-01 00:00:00.000000000 +0000 +++ python-coverage-3.6/test/farm/html/src/m3.py 2010-05-30 04:26:51.000000000 +0000 @@ -0,0 +1,2 @@ +m3a = 1 +m3b = 2 diff -Nru python-coverage-3.4/test/farm/html/src/main.py python-coverage-3.6/test/farm/html/src/main.py --- python-coverage-3.4/test/farm/html/src/main.py 1970-01-01 00:00:00.000000000 +0000 +++ python-coverage-3.6/test/farm/html/src/main.py 2010-05-30 04:26:51.000000000 +0000 @@ -0,0 +1,10 @@ +import m1 +import m2 +import m3 + +a = 5 +b = 6 + +assert m1.m1a == 1 +assert m2.m2a == 1 +assert m3.m3a == 1 diff -Nru python-coverage-3.4/test/farm/html/src/omit4.ini python-coverage-3.6/test/farm/html/src/omit4.ini --- python-coverage-3.4/test/farm/html/src/omit4.ini 1970-01-01 00:00:00.000000000 +0000 +++ python-coverage-3.6/test/farm/html/src/omit4.ini 2010-08-08 12:43:15.000000000 +0000 @@ -0,0 +1,2 @@ +[report] +omit = m2.py diff -Nru python-coverage-3.4/test/farm/html/src/omit5.ini python-coverage-3.6/test/farm/html/src/omit5.ini --- python-coverage-3.4/test/farm/html/src/omit5.ini 1970-01-01 00:00:00.000000000 +0000 +++ python-coverage-3.6/test/farm/html/src/omit5.ini 2010-08-08 12:43:15.000000000 +0000 @@ -0,0 +1,8 @@ +[report] +omit = + fooey + gooey, m[23]*, kablooey + helloworld + +[html] +directory = ../html_omit_5 diff -Nru python-coverage-3.4/test/farm/html/src/partial.py python-coverage-3.6/test/farm/html/src/partial.py --- python-coverage-3.4/test/farm/html/src/partial.py 1970-01-01 00:00:00.000000000 +0000 +++ python-coverage-3.6/test/farm/html/src/partial.py 2012-10-30 11:31:42.000000000 +0000 @@ -0,0 +1,18 @@ +# partial branches + +a = 3 + +while True: + break + +while 1: + break + +while a: # pragma: no branch + break + +if 0: + never_happen() + +if 1: + a = 13 diff -Nru python-coverage-3.4/test/farm/html/src/run_a_xml_2.ini python-coverage-3.6/test/farm/html/src/run_a_xml_2.ini --- python-coverage-3.4/test/farm/html/src/run_a_xml_2.ini 1970-01-01 00:00:00.000000000 +0000 +++ python-coverage-3.6/test/farm/html/src/run_a_xml_2.ini 2010-05-30 04:26:51.000000000 +0000 @@ -0,0 +1,3 @@ +# Put all the XML output in xml_2 +[xml] +output = ../xml_2/coverage.xml diff -Nru python-coverage-3.4/test/farm/html/src/tabbed.py python-coverage-3.6/test/farm/html/src/tabbed.py --- python-coverage-3.4/test/farm/html/src/tabbed.py 1970-01-01 00:00:00.000000000 +0000 +++ python-coverage-3.6/test/farm/html/src/tabbed.py 2012-10-30 11:32:03.000000000 +0000 @@ -0,0 +1,7 @@ +# This file should have tabs. +x = 1 +if x: + a = "Tabbed" # Aligned comments + if x: # look nice + b = "No spaces" # when they + c = "Done" # line up. diff -Nru python-coverage-3.4/test/farm/html/src/unicode.py python-coverage-3.6/test/farm/html/src/unicode.py --- python-coverage-3.4/test/farm/html/src/unicode.py 1970-01-01 00:00:00.000000000 +0000 +++ python-coverage-3.6/test/farm/html/src/unicode.py 2011-08-17 13:40:36.000000000 +0000 @@ -0,0 +1,5 @@ +# A python source file with exotic characters +# -*- coding: utf-8 -*- + +upside_down = "ʎd˙ǝbɐɹǝʌoɔ" +surrogate = "db40,dd00: x󠄀" diff -Nru python-coverage-3.4/test/farm/html/src/y.py python-coverage-3.6/test/farm/html/src/y.py --- python-coverage-3.4/test/farm/html/src/y.py 1970-01-01 00:00:00.000000000 +0000 +++ python-coverage-3.6/test/farm/html/src/y.py 2010-05-15 14:40:26.000000000 +0000 @@ -0,0 +1,9 @@ +# A test file for XML reporting by coverage. + +def choice(x): + if x < 2: + return 3 + else: + return 4 + +assert choice(1) == 3 diff -Nru python-coverage-3.4/test/farm/run/run_chdir.py python-coverage-3.6/test/farm/run/run_chdir.py --- python-coverage-3.4/test/farm/run/run_chdir.py 1970-01-01 00:00:00.000000000 +0000 +++ python-coverage-3.6/test/farm/run/run_chdir.py 2010-05-15 14:40:26.000000000 +0000 @@ -0,0 +1,12 @@ +copy("src", "out") +run(""" + coverage run chdir.py + coverage -r + """, rundir="out", outfile="stdout.txt") +contains("out/stdout.txt", + "Line One", + "Line Two", + "chdir" + ) +doesnt_contain("out/stdout.txt", "No such file or directory") +clean("out") diff -Nru python-coverage-3.4/test/farm/run/run_timid.py python-coverage-3.6/test/farm/run/run_timid.py --- python-coverage-3.4/test/farm/run/run_timid.py 1970-01-01 00:00:00.000000000 +0000 +++ python-coverage-3.6/test/farm/run/run_timid.py 2012-12-02 18:40:27.000000000 +0000 @@ -0,0 +1,60 @@ +# Test that the --timid command line argument properly swaps the tracer +# function for a simpler one. +# +# This is complicated by the fact that the tests are run twice for each +# version: once with a compiled C-based trace function, and once without +# it, to also test the Python trace function. So this test has to examine +# an environment variable set in igor.py to know whether to expect to see +# the C trace function or not. + +import os + +# When meta-coverage testing, this test doesn't work, because it finds +# coverage.py's own trace function. +if os.environ.get('COVERAGE_COVERAGE', ''): + skip("Can't test timid during coverage measurement.") + +copy("src", "out") +run(""" + python showtrace.py none + coverage -e -x showtrace.py regular + coverage -e -x --timid showtrace.py timid + """, rundir="out", outfile="showtraceout.txt") + +# When running without coverage, no trace function +# When running timidly, the trace function is always Python. +contains("out/showtraceout.txt", + "none None", + "timid PyTracer", + ) + +if os.environ.get('COVERAGE_TEST_TRACER', 'c') == 'c': + # If the C trace function is being tested, then regular running should have + # the C function, which registers itself as f_trace. + contains("out/showtraceout.txt", "regular CTracer") +else: + # If the Python trace function is being tested, then regular running will + # also show the Python function. + contains("out/showtraceout.txt", "regular PyTracer") + +# Try the environment variable. +old_opts = os.environ.get('COVERAGE_OPTIONS') +os.environ['COVERAGE_OPTIONS'] = '--timid' + +run(""" + coverage -e -x showtrace.py regular + coverage -e -x --timid showtrace.py timid + """, rundir="out", outfile="showtraceout.txt") + +contains("out/showtraceout.txt", + "none None", + "timid PyTracer", + "regular PyTracer", + ) + +if old_opts: + os.environ['COVERAGE_OPTIONS'] = old_opts +else: + del os.environ['COVERAGE_OPTIONS'] + +clean("out") diff -Nru python-coverage-3.4/test/farm/run/run_xxx.py python-coverage-3.6/test/farm/run/run_xxx.py --- python-coverage-3.4/test/farm/run/run_xxx.py 1970-01-01 00:00:00.000000000 +0000 +++ python-coverage-3.6/test/farm/run/run_xxx.py 2010-08-23 03:09:14.000000000 +0000 @@ -0,0 +1,12 @@ +copy("src", "out") +run(""" + coverage -e -x xxx + coverage -r + """, rundir="out", outfile="stdout.txt") +contains("out/stdout.txt", + "xxx: 3 4 0 7", + "\nxxx ", # The reporting line for xxx + " 7 1 86%" # The reporting data for xxx + ) +doesnt_contain("out/stdout.txt", "No such file or directory") +clean("out") diff -Nru python-coverage-3.4/test/farm/run/src/chdir.py python-coverage-3.6/test/farm/run/src/chdir.py --- python-coverage-3.4/test/farm/run/src/chdir.py 1970-01-01 00:00:00.000000000 +0000 +++ python-coverage-3.6/test/farm/run/src/chdir.py 2012-10-30 11:32:56.000000000 +0000 @@ -0,0 +1,4 @@ +import os +print("Line One") +os.chdir("subdir") +print("Line Two") diff -Nru python-coverage-3.4/test/farm/run/src/showtrace.py python-coverage-3.6/test/farm/run/src/showtrace.py --- python-coverage-3.4/test/farm/run/src/showtrace.py 1970-01-01 00:00:00.000000000 +0000 +++ python-coverage-3.6/test/farm/run/src/showtrace.py 2011-04-27 14:20:35.000000000 +0000 @@ -0,0 +1,23 @@ +# Show the current frame's trace function, so that we can test what the +# command-line options do to the trace function used. + +import sys + +# Show what the trace function is. If a C-based function is used, then f_trace +# may be None. +trace_fn = sys._getframe(0).f_trace +if trace_fn is None: + trace_name = "None" +else: + # Get the name of the tracer class. Py3k has a different way to get it. + try: + trace_name = trace_fn.im_class.__name__ + except AttributeError: + try: + trace_name = trace_fn.__self__.__class__.__name__ + except AttributeError: + # A C-based function could also manifest as an f_trace value + # which doesn't have im_class or __self__. + trace_name = trace_fn.__class__.__name__ + +print("%s %s" % (sys.argv[1], trace_name)) diff -Nru python-coverage-3.4/test/farm/run/src/xxx python-coverage-3.6/test/farm/run/src/xxx --- python-coverage-3.4/test/farm/run/src/xxx 1970-01-01 00:00:00.000000000 +0000 +++ python-coverage-3.6/test/farm/run/src/xxx 2010-05-15 14:40:26.000000000 +0000 @@ -0,0 +1,8 @@ +# This is a python file though it doesn't look like it, like a main script. +a = b = c = d = 0 +a = 3 +b = 4 +if not b: + c = 6 +d = 7 +print("xxx: %r %r %r %r" % (a, b, c, d)) diff -Nru python-coverage-3.4/test/js/index.html python-coverage-3.6/test/js/index.html --- python-coverage-3.4/test/js/index.html 1970-01-01 00:00:00.000000000 +0000 +++ python-coverage-3.6/test/js/index.html 2011-06-23 11:58:58.000000000 +0000 @@ -0,0 +1,52 @@ + + + + Coverage.py Javascript Test Suite + + + + + + + + + + + + + + + + + + + + + +

Coverage.py Javascript Test Suite

+

+
+

+
    +
    + + diff -Nru python-coverage-3.4/test/js/tests.js python-coverage-3.6/test/js/tests.js --- python-coverage-3.4/test/js/tests.js 1970-01-01 00:00:00.000000000 +0000 +++ python-coverage-3.6/test/js/tests.js 2011-06-27 02:31:07.000000000 +0000 @@ -0,0 +1,204 @@ +// Tests of coverage.py HTML report chunk navigation. +/*global coverage, test, module, equals, jQuery, $ */ + +// Test helpers + +function selection_is(sel) { + raw_selection_is(sel, true); +} + +function raw_selection_is(sel, check_highlight) { + var beg = sel[0], end = sel[1]; + equals(coverage.sel_begin, beg); + equals(coverage.sel_end, end); + if (check_highlight) { + equals(coverage.code_container().find(".highlight").length, end-beg); + } +} + +function build_fixture(spec) { + var i, data; + $("#fixture-template").tmpl().appendTo("#qunit-fixture"); + for (i = 0; i < spec.length; i++) { + data = {number: i+1, klass: spec.substr(i, 1)}; + $("#lineno-template").tmpl(data).appendTo("#qunit-fixture .linenos"); + $("#text-template").tmpl(data).appendTo("#qunit-fixture .text"); + } + coverage.pyfile_ready(jQuery); +} + +// Tests + +// Zero-chunk tests + +module("Zero-chunk navigation", { + setup: function () { + build_fixture("wwww"); + } +}); + +test("set_sel defaults", function () { + coverage.set_sel(2); + equals(coverage.sel_begin, 2); + equals(coverage.sel_end, 3); +}); + +test("No first chunk to select", function () { + coverage.to_first_chunk(); +}); + +// One-chunk tests + +$.each([ + ['rrrrr', [1,6]], + ['r', [1,2]], + ['wwrrrr', [3,7]], + ['wwrrrrww', [3,7]], + ['rrrrww', [1,5]] +], function (i, params) { + + // Each of these tests uses a fixture with one highlighted chunks. + var id = params[0]; + var c1 = params[1]; + + module("One-chunk navigation - " + id, { + setup: function () { + build_fixture(id); + } + }); + + test("First chunk", function () { + coverage.to_first_chunk(); + selection_is(c1); + }); + + test("Next chunk is first chunk", function () { + coverage.to_next_chunk(); + selection_is(c1); + }); + + test("There is no next chunk", function () { + coverage.to_first_chunk(); + coverage.to_next_chunk(); + selection_is(c1); + }); + + test("There is no prev chunk", function () { + coverage.to_first_chunk(); + coverage.to_prev_chunk(); + selection_is(c1); + }); +}); + +// Two-chunk tests + +$.each([ + ['rrwwrrrr', [1,3], [5,9]], + ['rb', [1,2], [2,3]], + ['rbbbbbbbbbb', [1,2], [2,12]], + ['rrrrrrrrrrb', [1,11], [11,12]], + ['wrrwrrrrw', [2,4], [5,9]], + ['rrrbbb', [1,4], [4,7]] +], function (i, params) { + + // Each of these tests uses a fixture with two highlighted chunks. + var id = params[0]; + var c1 = params[1]; + var c2 = params[2]; + + module("Two-chunk navigation - " + id, { + setup: function () { + build_fixture(id); + } + }); + + test("First chunk", function () { + coverage.to_first_chunk(); + selection_is(c1); + }); + + test("Next chunk is first chunk", function () { + coverage.to_next_chunk(); + selection_is(c1); + }); + + test("Move to next chunk", function () { + coverage.to_first_chunk(); + coverage.to_next_chunk(); + selection_is(c2); + }); + + test("Move to first chunk", function () { + coverage.to_first_chunk(); + coverage.to_next_chunk(); + coverage.to_first_chunk(); + selection_is(c1); + }); + + test("Move to previous chunk", function () { + coverage.to_first_chunk(); + coverage.to_next_chunk(); + coverage.to_prev_chunk(); + selection_is(c1); + }); + + test("Next doesn't move after last chunk", function () { + coverage.to_first_chunk(); + coverage.to_next_chunk(); + coverage.to_next_chunk(); + selection_is(c2); + }); + + test("Prev doesn't move before first chunk", function () { + coverage.to_first_chunk(); + coverage.to_next_chunk(); + coverage.to_prev_chunk(); + coverage.to_prev_chunk(); + selection_is(c1); + }); + +}); + +module("Miscellaneous"); + +test("Jump from a line selected", function () { + build_fixture("rrwwrr"); + coverage.set_sel(3); + coverage.to_next_chunk(); + selection_is([5,7]); +}); + +// Tests of select_line_or_chunk. + +$.each([ + // The data for each test: a spec for the fixture to build, and an array + // of the selection that will be selected by select_line_or_chunk for + // each line in the fixture. + ['rrwwrr', [[1,3], [1,3], [3,4], [4,5], [5,7], [5,7]]], + ['rb', [[1,2], [2,3]]], + ['r', [[1,2]]], + ['w', [[1,2]]], + ['www', [[1,2], [2,3], [3,4]]], + ['wwwrrr', [[1,2], [2,3], [3,4], [4,7], [4,7], [4,7]]], + ['rrrwww', [[1,4], [1,4], [1,4], [4,5], [5,6], [6,7]]], + ['rrrbbb', [[1,4], [1,4], [1,4], [4,7], [4,7], [4,7]]] +], function (i, params) { + + // Each of these tests uses a fixture with two highlighted chunks. + var id = params[0]; + var sels = params[1]; + + module("Select line or chunk - " + id, { + setup: function () { + build_fixture(id); + } + }); + + $.each(sels, function (i, sel) { + i++; + test("Select line " + i, function () { + coverage.select_line_or_chunk(i); + raw_selection_is(sel); + }); + }); +}); diff -Nru python-coverage-3.4/test/modules/aa/__init__.py python-coverage-3.6/test/modules/aa/__init__.py --- python-coverage-3.4/test/modules/aa/__init__.py 1970-01-01 00:00:00.000000000 +0000 +++ python-coverage-3.6/test/modules/aa/__init__.py 2010-05-15 14:40:26.000000000 +0000 @@ -0,0 +1 @@ +# aa diff -Nru python-coverage-3.4/test/modules/aa/afile.odd.py python-coverage-3.6/test/modules/aa/afile.odd.py --- python-coverage-3.4/test/modules/aa/afile.odd.py 1970-01-01 00:00:00.000000000 +0000 +++ python-coverage-3.6/test/modules/aa/afile.odd.py 2010-08-08 12:26:31.000000000 +0000 @@ -0,0 +1 @@ +# afile.odd.py diff -Nru python-coverage-3.4/test/modules/aa/afile.py python-coverage-3.6/test/modules/aa/afile.py --- python-coverage-3.4/test/modules/aa/afile.py 1970-01-01 00:00:00.000000000 +0000 +++ python-coverage-3.6/test/modules/aa/afile.py 2010-05-15 14:40:26.000000000 +0000 @@ -0,0 +1 @@ +# afile.py diff -Nru python-coverage-3.4/test/modules/aa/bb/__init__.py python-coverage-3.6/test/modules/aa/bb/__init__.py --- python-coverage-3.4/test/modules/aa/bb/__init__.py 1970-01-01 00:00:00.000000000 +0000 +++ python-coverage-3.6/test/modules/aa/bb/__init__.py 2010-05-15 14:40:26.000000000 +0000 @@ -0,0 +1 @@ +# bb diff -Nru python-coverage-3.4/test/modules/aa/bb/bfile.odd.py python-coverage-3.6/test/modules/aa/bb/bfile.odd.py --- python-coverage-3.4/test/modules/aa/bb/bfile.odd.py 1970-01-01 00:00:00.000000000 +0000 +++ python-coverage-3.6/test/modules/aa/bb/bfile.odd.py 2010-08-08 12:26:31.000000000 +0000 @@ -0,0 +1 @@ +# bfile.odd.py diff -Nru python-coverage-3.4/test/modules/aa/bb/bfile.py python-coverage-3.6/test/modules/aa/bb/bfile.py --- python-coverage-3.4/test/modules/aa/bb/bfile.py 1970-01-01 00:00:00.000000000 +0000 +++ python-coverage-3.6/test/modules/aa/bb/bfile.py 2010-05-15 14:40:26.000000000 +0000 @@ -0,0 +1 @@ +# bfile.py diff -Nru python-coverage-3.4/test/modules/aa/bb/cc/cfile.py python-coverage-3.6/test/modules/aa/bb/cc/cfile.py --- python-coverage-3.4/test/modules/aa/bb/cc/cfile.py 1970-01-01 00:00:00.000000000 +0000 +++ python-coverage-3.6/test/modules/aa/bb/cc/cfile.py 2010-05-15 14:40:26.000000000 +0000 @@ -0,0 +1 @@ +# cfile.py diff -Nru python-coverage-3.4/test/modules/aa/bb.odd/bfile.py python-coverage-3.6/test/modules/aa/bb.odd/bfile.py --- python-coverage-3.4/test/modules/aa/bb.odd/bfile.py 1970-01-01 00:00:00.000000000 +0000 +++ python-coverage-3.6/test/modules/aa/bb.odd/bfile.py 2010-08-08 12:26:31.000000000 +0000 @@ -0,0 +1 @@ +# bfile.py diff -Nru python-coverage-3.4/test/modules/aa/zfile.py python-coverage-3.6/test/modules/aa/zfile.py --- python-coverage-3.4/test/modules/aa/zfile.py 1970-01-01 00:00:00.000000000 +0000 +++ python-coverage-3.6/test/modules/aa/zfile.py 2010-05-15 14:40:26.000000000 +0000 @@ -0,0 +1 @@ +# zfile.py diff -Nru python-coverage-3.4/test/modules/covmod1.py python-coverage-3.6/test/modules/covmod1.py --- python-coverage-3.4/test/modules/covmod1.py 1970-01-01 00:00:00.000000000 +0000 +++ python-coverage-3.6/test/modules/covmod1.py 2010-05-15 14:40:26.000000000 +0000 @@ -0,0 +1,3 @@ +# covmod1.py: Simplest module for testing. +i = 1 +i += 1 diff -Nru python-coverage-3.4/test/modules/pkg1/__init__.py python-coverage-3.6/test/modules/pkg1/__init__.py --- python-coverage-3.4/test/modules/pkg1/__init__.py 1970-01-01 00:00:00.000000000 +0000 +++ python-coverage-3.6/test/modules/pkg1/__init__.py 2010-08-08 13:36:52.000000000 +0000 @@ -0,0 +1,3 @@ +# This __init__.py has a module-level docstring, which is counted as a +# statement. +"""A simple package for testing with.""" diff -Nru python-coverage-3.4/test/modules/pkg1/__main__.py python-coverage-3.6/test/modules/pkg1/__main__.py --- python-coverage-3.4/test/modules/pkg1/__main__.py 1970-01-01 00:00:00.000000000 +0000 +++ python-coverage-3.6/test/modules/pkg1/__main__.py 2011-02-11 12:17:24.000000000 +0000 @@ -0,0 +1,3 @@ +# Used in the tests for run_python_module +import sys +print("pkg1.__main__: passed %s" % sys.argv[1]) diff -Nru python-coverage-3.4/test/modules/pkg1/p1a.py python-coverage-3.6/test/modules/pkg1/p1a.py --- python-coverage-3.4/test/modules/pkg1/p1a.py 1970-01-01 00:00:00.000000000 +0000 +++ python-coverage-3.6/test/modules/pkg1/p1a.py 2010-08-08 13:36:52.000000000 +0000 @@ -0,0 +1,5 @@ +import os, sys + +# Invoke functions in os and sys so we can see if we measure code there. +x = sys.getcheckinterval() +y = os.getcwd() diff -Nru python-coverage-3.4/test/modules/pkg1/p1b.py python-coverage-3.6/test/modules/pkg1/p1b.py --- python-coverage-3.4/test/modules/pkg1/p1b.py 1970-01-01 00:00:00.000000000 +0000 +++ python-coverage-3.6/test/modules/pkg1/p1b.py 2010-08-08 13:36:52.000000000 +0000 @@ -0,0 +1,3 @@ +x = 1 +y = 2 +z = 3 diff -Nru python-coverage-3.4/test/modules/pkg1/p1c.py python-coverage-3.6/test/modules/pkg1/p1c.py --- python-coverage-3.4/test/modules/pkg1/p1c.py 1970-01-01 00:00:00.000000000 +0000 +++ python-coverage-3.6/test/modules/pkg1/p1c.py 2010-08-30 00:28:10.000000000 +0000 @@ -0,0 +1,3 @@ +a = 1 +b = 2 +c = 3 diff -Nru python-coverage-3.4/test/modules/pkg1/runmod2.py python-coverage-3.6/test/modules/pkg1/runmod2.py --- python-coverage-3.4/test/modules/pkg1/runmod2.py 1970-01-01 00:00:00.000000000 +0000 +++ python-coverage-3.6/test/modules/pkg1/runmod2.py 2011-02-11 12:17:24.000000000 +0000 @@ -0,0 +1,3 @@ +# Used in the tests for run_python_module +import sys +print("runmod2: passed %s" % sys.argv[1]) diff -Nru python-coverage-3.4/test/modules/pkg1/sub/__main__.py python-coverage-3.6/test/modules/pkg1/sub/__main__.py --- python-coverage-3.4/test/modules/pkg1/sub/__main__.py 1970-01-01 00:00:00.000000000 +0000 +++ python-coverage-3.6/test/modules/pkg1/sub/__main__.py 2011-02-11 12:17:24.000000000 +0000 @@ -0,0 +1,3 @@ +# Used in the tests for run_python_module +import sys +print("pkg1.sub.__main__: passed %s" % sys.argv[1]) diff -Nru python-coverage-3.4/test/modules/pkg1/sub/ps1a.py python-coverage-3.6/test/modules/pkg1/sub/ps1a.py --- python-coverage-3.4/test/modules/pkg1/sub/ps1a.py 1970-01-01 00:00:00.000000000 +0000 +++ python-coverage-3.6/test/modules/pkg1/sub/ps1a.py 2010-08-30 00:29:09.000000000 +0000 @@ -0,0 +1,3 @@ +d = 1 +e = 2 +f = 3 diff -Nru python-coverage-3.4/test/modules/pkg1/sub/runmod3.py python-coverage-3.6/test/modules/pkg1/sub/runmod3.py --- python-coverage-3.4/test/modules/pkg1/sub/runmod3.py 1970-01-01 00:00:00.000000000 +0000 +++ python-coverage-3.6/test/modules/pkg1/sub/runmod3.py 2011-02-11 12:17:24.000000000 +0000 @@ -0,0 +1,3 @@ +# Used in the tests for run_python_module +import sys +print("runmod3: passed %s" % sys.argv[1]) diff -Nru python-coverage-3.4/test/modules/pkg2/__init__.py python-coverage-3.6/test/modules/pkg2/__init__.py --- python-coverage-3.4/test/modules/pkg2/__init__.py 1970-01-01 00:00:00.000000000 +0000 +++ python-coverage-3.6/test/modules/pkg2/__init__.py 2010-08-08 13:36:52.000000000 +0000 @@ -0,0 +1,2 @@ +# This is an __init__.py file, with no executable statements in it. +# This comment shouldn't confuse the parser. diff -Nru python-coverage-3.4/test/modules/pkg2/p2a.py python-coverage-3.6/test/modules/pkg2/p2a.py --- python-coverage-3.4/test/modules/pkg2/p2a.py 1970-01-01 00:00:00.000000000 +0000 +++ python-coverage-3.6/test/modules/pkg2/p2a.py 2010-08-08 13:36:52.000000000 +0000 @@ -0,0 +1,3 @@ +q = 1 +r = 1 +s = 1 diff -Nru python-coverage-3.4/test/modules/pkg2/p2b.py python-coverage-3.6/test/modules/pkg2/p2b.py --- python-coverage-3.4/test/modules/pkg2/p2b.py 1970-01-01 00:00:00.000000000 +0000 +++ python-coverage-3.6/test/modules/pkg2/p2b.py 2010-08-08 13:36:52.000000000 +0000 @@ -0,0 +1,3 @@ +t = 1 +u = 1 +v = 1 diff -Nru python-coverage-3.4/test/modules/runmod1.py python-coverage-3.6/test/modules/runmod1.py --- python-coverage-3.4/test/modules/runmod1.py 1970-01-01 00:00:00.000000000 +0000 +++ python-coverage-3.6/test/modules/runmod1.py 2011-02-11 12:17:24.000000000 +0000 @@ -0,0 +1,3 @@ +# Used in the tests for run_python_module +import sys +print("runmod1: passed %s" % sys.argv[1]) diff -Nru python-coverage-3.4/test/modules/usepkgs.py python-coverage-3.6/test/modules/usepkgs.py --- python-coverage-3.4/test/modules/usepkgs.py 1970-01-01 00:00:00.000000000 +0000 +++ python-coverage-3.6/test/modules/usepkgs.py 2011-03-22 11:07:47.000000000 +0000 @@ -0,0 +1,4 @@ +import pkg1.p1a, pkg1.p1b +import pkg2.p2a, pkg2.p2b +import othermods.othera, othermods.otherb +import othermods.sub.osa, othermods.sub.osb diff -Nru python-coverage-3.4/test/moremodules/othermods/othera.py python-coverage-3.6/test/moremodules/othermods/othera.py --- python-coverage-3.4/test/moremodules/othermods/othera.py 1970-01-01 00:00:00.000000000 +0000 +++ python-coverage-3.6/test/moremodules/othermods/othera.py 2011-06-01 12:20:12.000000000 +0000 @@ -0,0 +1,2 @@ +o = 1 +p = 2 diff -Nru python-coverage-3.4/test/moremodules/othermods/otherb.py python-coverage-3.6/test/moremodules/othermods/otherb.py --- python-coverage-3.4/test/moremodules/othermods/otherb.py 1970-01-01 00:00:00.000000000 +0000 +++ python-coverage-3.6/test/moremodules/othermods/otherb.py 2011-06-01 12:20:12.000000000 +0000 @@ -0,0 +1,2 @@ +q = 3 +r = 4 diff -Nru python-coverage-3.4/test/moremodules/othermods/sub/osa.py python-coverage-3.6/test/moremodules/othermods/sub/osa.py --- python-coverage-3.4/test/moremodules/othermods/sub/osa.py 1970-01-01 00:00:00.000000000 +0000 +++ python-coverage-3.6/test/moremodules/othermods/sub/osa.py 2011-06-01 12:20:12.000000000 +0000 @@ -0,0 +1,2 @@ +s = 5 +t = 6 diff -Nru python-coverage-3.4/test/moremodules/othermods/sub/osb.py python-coverage-3.6/test/moremodules/othermods/sub/osb.py --- python-coverage-3.4/test/moremodules/othermods/sub/osb.py 1970-01-01 00:00:00.000000000 +0000 +++ python-coverage-3.6/test/moremodules/othermods/sub/osb.py 2011-06-01 12:20:12.000000000 +0000 @@ -0,0 +1,2 @@ +u = 7 +v = 8 diff -Nru python-coverage-3.4/test/osinfo.py python-coverage-3.6/test/osinfo.py --- python-coverage-3.4/test/osinfo.py 1970-01-01 00:00:00.000000000 +0000 +++ python-coverage-3.6/test/osinfo.py 2011-02-05 04:20:13.000000000 +0000 @@ -0,0 +1,71 @@ +"""OS information for testing.""" + +import sys + +if sys.version_info >= (2, 5) and sys.platform == 'win32': + # Windows implementation + def process_ram(): + """How much RAM is this process using? (Windows)""" + import ctypes + # lifted from: + # lists.ubuntu.com/archives/bazaar-commits/2009-February/011990.html + class PROCESS_MEMORY_COUNTERS_EX(ctypes.Structure): + """Used by GetProcessMemoryInfo""" + _fields_ = [('cb', ctypes.c_ulong), + ('PageFaultCount', ctypes.c_ulong), + ('PeakWorkingSetSize', ctypes.c_size_t), + ('WorkingSetSize', ctypes.c_size_t), + ('QuotaPeakPagedPoolUsage', ctypes.c_size_t), + ('QuotaPagedPoolUsage', ctypes.c_size_t), + ('QuotaPeakNonPagedPoolUsage', ctypes.c_size_t), + ('QuotaNonPagedPoolUsage', ctypes.c_size_t), + ('PagefileUsage', ctypes.c_size_t), + ('PeakPagefileUsage', ctypes.c_size_t), + ('PrivateUsage', ctypes.c_size_t), + ] + + mem_struct = PROCESS_MEMORY_COUNTERS_EX() + ret = ctypes.windll.psapi.GetProcessMemoryInfo( + ctypes.windll.kernel32.GetCurrentProcess(), + ctypes.byref(mem_struct), + ctypes.sizeof(mem_struct) + ) + if not ret: + return 0 + return mem_struct.PrivateUsage + +elif sys.platform == 'linux2': + # Linux implementation + import os + + _scale = {'kb': 1024, 'mb': 1024*1024} + + def _VmB(key): + """Read the /proc/PID/status file to find memory use.""" + try: + # get pseudo file /proc//status + t = open('/proc/%d/status' % os.getpid()) + try: + v = t.read() + finally: + t.close() + except IOError: + return 0 # non-Linux? + # get VmKey line e.g. 'VmRSS: 9999 kB\n ...' + i = v.index(key) + v = v[i:].split(None, 3) + if len(v) < 3: + return 0 # invalid format? + # convert Vm value to bytes + return int(float(v[1]) * _scale[v[2].lower()]) + + def process_ram(): + """How much RAM is this process using? (Linux implementation)""" + return _VmB('VmRSS') + + +else: + # Don't have an implementation, at least satisfy the interface. + def process_ram(): + """How much RAM is this process using? (placebo implementation)""" + return 0 diff -Nru python-coverage-3.4/test/qunit/jquery.tmpl.min.js python-coverage-3.6/test/qunit/jquery.tmpl.min.js --- python-coverage-3.4/test/qunit/jquery.tmpl.min.js 1970-01-01 00:00:00.000000000 +0000 +++ python-coverage-3.6/test/qunit/jquery.tmpl.min.js 2011-04-17 20:36:09.000000000 +0000 @@ -0,0 +1,10 @@ +/* + * jQuery Templates Plugin 1.0.0pre + * http://github.com/jquery/jquery-tmpl + * Requires jQuery 1.4.2 + * + * Copyright Software Freedom Conservancy, Inc. + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + */ +(function(a){var r=a.fn.domManip,d="_tmplitem",q=/^[^<]*(<[\w\W]+>)[^>]*$|\{\{\! /,b={},f={},e,p={key:0,data:{}},i=0,c=0,l=[];function g(g,d,h,e){var c={data:e||(e===0||e===false)?e:d?d.data:{},_wrap:d?d._wrap:null,tmpl:null,parent:d||null,nodes:[],calls:u,nest:w,wrap:x,html:v,update:t};g&&a.extend(c,g,{nodes:[],parent:d});if(h){c.tmpl=h;c._ctnt=c._ctnt||c.tmpl(a,c);c.key=++i;(l.length?f:b)[i]=c}return c}a.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(f,d){a.fn[f]=function(n){var g=[],i=a(n),k,h,m,l,j=this.length===1&&this[0].parentNode;e=b||{};if(j&&j.nodeType===11&&j.childNodes.length===1&&i.length===1){i[d](this[0]);g=this}else{for(h=0,m=i.length;h0?this.clone(true):this).get();a(i[h])[d](k);g=g.concat(k)}c=0;g=this.pushStack(g,f,i.selector)}l=e;e=null;a.tmpl.complete(l);return g}});a.fn.extend({tmpl:function(d,c,b){return a.tmpl(this[0],d,c,b)},tmplItem:function(){return a.tmplItem(this[0])},template:function(b){return a.template(b,this[0])},domManip:function(d,m,k){if(d[0]&&a.isArray(d[0])){var g=a.makeArray(arguments),h=d[0],j=h.length,i=0,f;while(i").join(">").split('"').join(""").split("'").join("'")}});a.extend(a.tmpl,{tag:{tmpl:{_default:{$2:"null"},open:"if($notnull_1){__=__.concat($item.nest($1,$2));}"},wrap:{_default:{$2:"null"},open:"$item.calls(__,$1,$2);__=[];",close:"call=$item.calls();__=call._.concat($item.wrap(call,__));"},each:{_default:{$2:"$index, $value"},open:"if($notnull_1){$.each($1a,function($2){with(this){",close:"}});}"},"if":{open:"if(($notnull_1) && $1a){",close:"}"},"else":{_default:{$1:"true"},open:"}else if(($notnull_1) && $1a){"},html:{open:"if($notnull_1){__.push($1a);}"},"=":{_default:{$1:"$data"},open:"if($notnull_1){__.push($.encode($1a));}"},"!":{open:""}},complete:function(){b={}},afterManip:function(f,b,d){var e=b.nodeType===11?a.makeArray(b.childNodes):b.nodeType===1?[b]:[];d.call(f,b);m(e);c++}});function j(e,g,f){var b,c=f?a.map(f,function(a){return typeof a==="string"?e.key?a.replace(/(<\w+)(?=[\s>])(?![^>]*_tmplitem)([^>]*)/g,"$1 "+d+'="'+e.key+'" $2'):a:j(a,e,a._ctnt)}):e;if(g)return c;c=c.join("");c.replace(/^\s*([^<\s][^<]*)?(<[\w\W]+>)([^>]*[^>\s])?\s*$/,function(f,c,e,d){b=a(e).get();m(b);if(c)b=k(c).concat(b);if(d)b=b.concat(k(d))});return b?b:k(c)}function k(c){var b=document.createElement("div");b.innerHTML=c;return a.makeArray(b.childNodes)}function o(b){return new Function("jQuery","$item","var $=jQuery,call,__=[],$data=$item.data;with($data){__.push('"+a.trim(b).replace(/([\\'])/g,"\\$1").replace(/[\r\t\n]/g," ").replace(/\$\{([^\}]*)\}/g,"{{= $1}}").replace(/\{\{(\/?)(\w+|.)(?:\(((?:[^\}]|\}(?!\}))*?)?\))?(?:\s+(.*?)?)?(\(((?:[^\}]|\}(?!\}))*?)\))?\s*\}\}/g,function(m,l,k,g,b,c,d){var j=a.tmpl.tag[k],i,e,f;if(!j)throw"Unknown template tag: "+k;i=j._default||[];if(c&&!/\w$/.test(b)){b+=c;c=""}if(b){b=h(b);d=d?","+h(d)+")":c?")":"";e=c?b.indexOf(".")>-1?b+h(c):"("+b+").call($item"+d:b;f=c?e:"(typeof("+b+")==='function'?("+b+").call($item):("+b+"))"}else f=e=i.$1||"null";g=h(g);return"');"+j[l?"close":"open"].split("$notnull_1").join(b?"typeof("+b+")!=='undefined' && ("+b+")!=null":"true").split("$1a").join(f).split("$1").join(e).split("$2").join(g||i.$2||"")+"__.push('"})+"');}return __;")}function n(c,b){c._wrap=j(c,true,a.isArray(b)?b:[q.test(b)?b:a(b).html()]).join("")}function h(a){return a?a.replace(/\\'/g,"'").replace(/\\\\/g,"\\"):null}function s(b){var a=document.createElement("div");a.appendChild(b.cloneNode(true));return a.innerHTML}function m(o){var n="_"+c,k,j,l={},e,p,h;for(e=0,p=o.length;e=0;h--)m(j[h]);m(k)}function m(j){var p,h=j,k,e,m;if(m=j.getAttribute(d)){while(h.parentNode&&(h=h.parentNode).nodeType===1&&!(p=h.getAttribute(d)));if(p!==m){h=h.parentNode?h.nodeType===11?0:h.getAttribute(d)||0:0;if(!(e=b[m])){e=f[m];e=g(e,b[h]||f[h]);e.key=++i;b[i]=e}c&&o(m)}j.removeAttribute(d)}else if(c&&(e=a.data(j,"tmplItem"))){o(e.key);b[e.key]=e;h=a.data(j.parentNode,"tmplItem");h=h?h.key:0}if(e){k=e;while(k&&k.key!=h){k.nodes.push(j);k=k.parent}delete e._ctnt;delete e._wrap;a.data(j,"tmplItem",e)}function o(a){a=a+n;e=l[a]=l[a]||g(e,b[e.parent.key+n]||e.parent)}}}function u(a,d,c,b){if(!a)return l.pop();l.push({_:a,tmpl:d,item:this,data:c,options:b})}function w(d,c,b){return a.tmpl(a.template(d),c,b,this)}function x(b,d){var c=b.options||{};c.wrapped=d;return a.tmpl(a.template(b.tmpl),b.data,c,b.item)}function v(d,c){var b=this._wrap;return a.map(a(a.isArray(b)?b.join(""):b).filter(d||"*"),function(a){return c?a.innerText||a.textContent:a.outerHTML||s(a)})}function t(){var b=this.nodes;a.tmpl(null,null,null,this).insertBefore(b[0]);a(b).remove()}})(jQuery); \ No newline at end of file diff -Nru python-coverage-3.4/test/qunit/qunit.css python-coverage-3.6/test/qunit/qunit.css --- python-coverage-3.4/test/qunit/qunit.css 1970-01-01 00:00:00.000000000 +0000 +++ python-coverage-3.6/test/qunit/qunit.css 2011-06-23 11:07:35.000000000 +0000 @@ -0,0 +1,225 @@ +/** + * QUnit - A JavaScript Unit Testing Framework + * + * http://docs.jquery.com/QUnit + * + * Copyright (c) 2011 John Resig, Jörn Zaefferer + * Dual licensed under the MIT (MIT-LICENSE.txt) + * or GPL (GPL-LICENSE.txt) licenses. + */ + +/** Font Family and Sizes */ + +#qunit-tests, #qunit-header, #qunit-banner, #qunit-testrunner-toolbar, #qunit-userAgent, #qunit-testresult { + font-family: "Helvetica Neue Light", "HelveticaNeue-Light", "Helvetica Neue", Calibri, Helvetica, Arial, sans-serif; +} + +#qunit-testrunner-toolbar, #qunit-userAgent, #qunit-testresult, #qunit-tests li { font-size: small; } +#qunit-tests { font-size: smaller; } + + +/** Resets */ + +#qunit-tests, #qunit-tests ol, #qunit-header, #qunit-banner, #qunit-userAgent, #qunit-testresult { + margin: 0; + padding: 0; +} + + +/** Header */ + +#qunit-header { + padding: 0.5em 0 0.5em 1em; + + color: #8699a4; + background-color: #0d3349; + + font-size: 1.5em; + line-height: 1em; + font-weight: normal; + + border-radius: 15px 15px 0 0; + -moz-border-radius: 15px 15px 0 0; + -webkit-border-top-right-radius: 15px; + -webkit-border-top-left-radius: 15px; +} + +#qunit-header a { + text-decoration: none; + color: #c2ccd1; +} + +#qunit-header a:hover, +#qunit-header a:focus { + color: #fff; +} + +#qunit-banner { + height: 5px; +} + +#qunit-testrunner-toolbar { + padding: 0.5em 0 0.5em 2em; + color: #5E740B; + background-color: #eee; +} + +#qunit-userAgent { + padding: 0.5em 0 0.5em 2.5em; + background-color: #2b81af; + color: #fff; + text-shadow: rgba(0, 0, 0, 0.5) 2px 2px 1px; +} + + +/** Tests: Pass/Fail */ + +#qunit-tests { + list-style-position: inside; +} + +#qunit-tests li { + padding: 0.4em 0.5em 0.4em 2.5em; + border-bottom: 1px solid #fff; + list-style-position: inside; +} + +#qunit-tests.hidepass li.pass, #qunit-tests.hidepass li.running { + display: none; +} + +#qunit-tests li strong { + cursor: pointer; +} + +#qunit-tests li a { + padding: 0.5em; + color: #c2ccd1; + text-decoration: none; +} +#qunit-tests li a:hover, +#qunit-tests li a:focus { + color: #000; +} + +#qunit-tests ol { + margin-top: 0.5em; + padding: 0.5em; + + background-color: #fff; + + border-radius: 15px; + -moz-border-radius: 15px; + -webkit-border-radius: 15px; + + box-shadow: inset 0px 2px 13px #999; + -moz-box-shadow: inset 0px 2px 13px #999; + -webkit-box-shadow: inset 0px 2px 13px #999; +} + +#qunit-tests table { + border-collapse: collapse; + margin-top: .2em; +} + +#qunit-tests th { + text-align: right; + vertical-align: top; + padding: 0 .5em 0 0; +} + +#qunit-tests td { + vertical-align: top; +} + +#qunit-tests pre { + margin: 0; + white-space: pre-wrap; + word-wrap: break-word; +} + +#qunit-tests del { + background-color: #e0f2be; + color: #374e0c; + text-decoration: none; +} + +#qunit-tests ins { + background-color: #ffcaca; + color: #500; + text-decoration: none; +} + +/*** Test Counts */ + +#qunit-tests b.counts { color: black; } +#qunit-tests b.passed { color: #5E740B; } +#qunit-tests b.failed { color: #710909; } + +#qunit-tests li li { + margin: 0.5em; + padding: 0.4em 0.5em 0.4em 0.5em; + background-color: #fff; + border-bottom: none; + list-style-position: inside; +} + +/*** Passing Styles */ + +#qunit-tests li li.pass { + color: #5E740B; + background-color: #fff; + border-left: 26px solid #C6E746; +} + +#qunit-tests .pass { color: #528CE0; background-color: #D2E0E6; } +#qunit-tests .pass .test-name { color: #366097; } + +#qunit-tests .pass .test-actual, +#qunit-tests .pass .test-expected { color: #999999; } + +#qunit-banner.qunit-pass { background-color: #C6E746; } + +/*** Failing Styles */ + +#qunit-tests li li.fail { + color: #710909; + background-color: #fff; + border-left: 26px solid #EE5757; +} + +#qunit-tests > li:last-child { + border-radius: 0 0 15px 15px; + -moz-border-radius: 0 0 15px 15px; + -webkit-border-bottom-right-radius: 15px; + -webkit-border-bottom-left-radius: 15px; +} + +#qunit-tests .fail { color: #000000; background-color: #EE5757; } +#qunit-tests .fail .test-name, +#qunit-tests .fail .module-name { color: #000000; } + +#qunit-tests .fail .test-actual { color: #EE5757; } +#qunit-tests .fail .test-expected { color: green; } + +#qunit-banner.qunit-fail { background-color: #EE5757; } + + +/** Result */ + +#qunit-testresult { + padding: 0.5em 0.5em 0.5em 2.5em; + + color: #2b81af; + background-color: #D2E0E6; + + border-bottom: 1px solid white; +} + +/** Fixture */ + +#qunit-fixture { + position: absolute; + top: -10000px; + left: -10000px; +} diff -Nru python-coverage-3.4/test/qunit/qunit.js python-coverage-3.6/test/qunit/qunit.js --- python-coverage-3.4/test/qunit/qunit.js 1970-01-01 00:00:00.000000000 +0000 +++ python-coverage-3.6/test/qunit/qunit.js 2011-06-23 11:07:35.000000000 +0000 @@ -0,0 +1,1448 @@ +/** + * QUnit - A JavaScript Unit Testing Framework + * + * http://docs.jquery.com/QUnit + * + * Copyright (c) 2011 John Resig, Jörn Zaefferer + * Dual licensed under the MIT (MIT-LICENSE.txt) + * or GPL (GPL-LICENSE.txt) licenses. + */ + +(function(window) { + +var defined = { + setTimeout: typeof window.setTimeout !== "undefined", + sessionStorage: (function() { + try { + return !!sessionStorage.getItem; + } catch(e){ + return false; + } + })() +}; + +var testId = 0; + +var Test = function(name, testName, expected, testEnvironmentArg, async, callback) { + this.name = name; + this.testName = testName; + this.expected = expected; + this.testEnvironmentArg = testEnvironmentArg; + this.async = async; + this.callback = callback; + this.assertions = []; +}; +Test.prototype = { + init: function() { + var tests = id("qunit-tests"); + if (tests) { + var b = document.createElement("strong"); + b.innerHTML = "Running " + this.name; + var li = document.createElement("li"); + li.appendChild( b ); + li.className = "running"; + li.id = this.id = "test-output" + testId++; + tests.appendChild( li ); + } + }, + setup: function() { + if (this.module != config.previousModule) { + if ( config.previousModule ) { + QUnit.moduleDone( { + name: config.previousModule, + failed: config.moduleStats.bad, + passed: config.moduleStats.all - config.moduleStats.bad, + total: config.moduleStats.all + } ); + } + config.previousModule = this.module; + config.moduleStats = { all: 0, bad: 0 }; + QUnit.moduleStart( { + name: this.module + } ); + } + + config.current = this; + this.testEnvironment = extend({ + setup: function() {}, + teardown: function() {} + }, this.moduleTestEnvironment); + if (this.testEnvironmentArg) { + extend(this.testEnvironment, this.testEnvironmentArg); + } + + QUnit.testStart( { + name: this.testName + } ); + + // allow utility functions to access the current test environment + // TODO why?? + QUnit.current_testEnvironment = this.testEnvironment; + + try { + if ( !config.pollution ) { + saveGlobal(); + } + + this.testEnvironment.setup.call(this.testEnvironment); + } catch(e) { + QUnit.ok( false, "Setup failed on " + this.testName + ": " + e.message ); + } + }, + run: function() { + if ( this.async ) { + QUnit.stop(); + } + + if ( config.notrycatch ) { + this.callback.call(this.testEnvironment); + return; + } + try { + this.callback.call(this.testEnvironment); + } catch(e) { + fail("Test " + this.testName + " died, exception and test follows", e, this.callback); + QUnit.ok( false, "Died on test #" + (this.assertions.length + 1) + ": " + e.message + " - " + QUnit.jsDump.parse(e) ); + // else next test will carry the responsibility + saveGlobal(); + + // Restart the tests if they're blocking + if ( config.blocking ) { + start(); + } + } + }, + teardown: function() { + try { + this.testEnvironment.teardown.call(this.testEnvironment); + checkPollution(); + } catch(e) { + QUnit.ok( false, "Teardown failed on " + this.testName + ": " + e.message ); + } + }, + finish: function() { + if ( this.expected && this.expected != this.assertions.length ) { + QUnit.ok( false, "Expected " + this.expected + " assertions, but " + this.assertions.length + " were run" ); + } + + var good = 0, bad = 0, + tests = id("qunit-tests"); + + config.stats.all += this.assertions.length; + config.moduleStats.all += this.assertions.length; + + if ( tests ) { + var ol = document.createElement("ol"); + + for ( var i = 0; i < this.assertions.length; i++ ) { + var assertion = this.assertions[i]; + + var li = document.createElement("li"); + li.className = assertion.result ? "pass" : "fail"; + li.innerHTML = assertion.message || (assertion.result ? "okay" : "failed"); + ol.appendChild( li ); + + if ( assertion.result ) { + good++; + } else { + bad++; + config.stats.bad++; + config.moduleStats.bad++; + } + } + + // store result when possible + if ( QUnit.config.reorder && defined.sessionStorage ) { + if (bad) { + sessionStorage.setItem("qunit-" + this.module + "-" + this.testName, bad); + } else { + sessionStorage.removeItem("qunit-" + this.module + "-" + this.testName); + } + } + + if (bad == 0) { + ol.style.display = "none"; + } + + var b = document.createElement("strong"); + b.innerHTML = this.name + " (" + bad + ", " + good + ", " + this.assertions.length + ")"; + + var a = document.createElement("a"); + a.innerHTML = "Rerun"; + a.href = QUnit.url({ filter: getText([b]).replace(/\([^)]+\)$/, "").replace(/(^\s*|\s*$)/g, "") }); + + addEvent(b, "click", function() { + var next = b.nextSibling.nextSibling, + display = next.style.display; + next.style.display = display === "none" ? "block" : "none"; + }); + + addEvent(b, "dblclick", function(e) { + var target = e && e.target ? e.target : window.event.srcElement; + if ( target.nodeName.toLowerCase() == "span" || target.nodeName.toLowerCase() == "b" ) { + target = target.parentNode; + } + if ( window.location && target.nodeName.toLowerCase() === "strong" ) { + window.location = QUnit.url({ filter: getText([target]).replace(/\([^)]+\)$/, "").replace(/(^\s*|\s*$)/g, "") }); + } + }); + + var li = id(this.id); + li.className = bad ? "fail" : "pass"; + li.removeChild( li.firstChild ); + li.appendChild( b ); + li.appendChild( a ); + li.appendChild( ol ); + + } else { + for ( var i = 0; i < this.assertions.length; i++ ) { + if ( !this.assertions[i].result ) { + bad++; + config.stats.bad++; + config.moduleStats.bad++; + } + } + } + + try { + QUnit.reset(); + } catch(e) { + fail("reset() failed, following Test " + this.testName + ", exception and reset fn follows", e, QUnit.reset); + } + + QUnit.testDone( { + name: this.testName, + failed: bad, + passed: this.assertions.length - bad, + total: this.assertions.length + } ); + }, + + queue: function() { + var test = this; + synchronize(function() { + test.init(); + }); + function run() { + // each of these can by async + synchronize(function() { + test.setup(); + }); + synchronize(function() { + test.run(); + }); + synchronize(function() { + test.teardown(); + }); + synchronize(function() { + test.finish(); + }); + } + // defer when previous test run passed, if storage is available + var bad = QUnit.config.reorder && defined.sessionStorage && +sessionStorage.getItem("qunit-" + this.module + "-" + this.testName); + if (bad) { + run(); + } else { + synchronize(run); + }; + } + +}; + +var QUnit = { + + // call on start of module test to prepend name to all tests + module: function(name, testEnvironment) { + config.currentModule = name; + config.currentModuleTestEnviroment = testEnvironment; + }, + + asyncTest: function(testName, expected, callback) { + if ( arguments.length === 2 ) { + callback = expected; + expected = 0; + } + + QUnit.test(testName, expected, callback, true); + }, + + test: function(testName, expected, callback, async) { + var name = '' + testName + '', testEnvironmentArg; + + if ( arguments.length === 2 ) { + callback = expected; + expected = null; + } + // is 2nd argument a testEnvironment? + if ( expected && typeof expected === 'object') { + testEnvironmentArg = expected; + expected = null; + } + + if ( config.currentModule ) { + name = '' + config.currentModule + ": " + name; + } + + if ( !validTest(config.currentModule + ": " + testName) ) { + return; + } + + var test = new Test(name, testName, expected, testEnvironmentArg, async, callback); + test.module = config.currentModule; + test.moduleTestEnvironment = config.currentModuleTestEnviroment; + test.queue(); + }, + + /** + * Specify the number of expected assertions to gurantee that failed test (no assertions are run at all) don't slip through. + */ + expect: function(asserts) { + config.current.expected = asserts; + }, + + /** + * Asserts true. + * @example ok( "asdfasdf".length > 5, "There must be at least 5 chars" ); + */ + ok: function(a, msg) { + a = !!a; + var details = { + result: a, + message: msg + }; + msg = escapeHtml(msg); + QUnit.log(details); + config.current.assertions.push({ + result: a, + message: msg + }); + }, + + /** + * Checks that the first two arguments are equal, with an optional message. + * Prints out both actual and expected values. + * + * Prefered to ok( actual == expected, message ) + * + * @example equal( format("Received {0} bytes.", 2), "Received 2 bytes." ); + * + * @param Object actual + * @param Object expected + * @param String message (optional) + */ + equal: function(actual, expected, message) { + QUnit.push(expected == actual, actual, expected, message); + }, + + notEqual: function(actual, expected, message) { + QUnit.push(expected != actual, actual, expected, message); + }, + + deepEqual: function(actual, expected, message) { + QUnit.push(QUnit.equiv(actual, expected), actual, expected, message); + }, + + notDeepEqual: function(actual, expected, message) { + QUnit.push(!QUnit.equiv(actual, expected), actual, expected, message); + }, + + strictEqual: function(actual, expected, message) { + QUnit.push(expected === actual, actual, expected, message); + }, + + notStrictEqual: function(actual, expected, message) { + QUnit.push(expected !== actual, actual, expected, message); + }, + + raises: function(block, expected, message) { + var actual, ok = false; + + if (typeof expected === 'string') { + message = expected; + expected = null; + } + + try { + block(); + } catch (e) { + actual = e; + } + + if (actual) { + // we don't want to validate thrown error + if (!expected) { + ok = true; + // expected is a regexp + } else if (QUnit.objectType(expected) === "regexp") { + ok = expected.test(actual); + // expected is a constructor + } else if (actual instanceof expected) { + ok = true; + // expected is a validation function which returns true is validation passed + } else if (expected.call({}, actual) === true) { + ok = true; + } + } + + QUnit.ok(ok, message); + }, + + start: function() { + config.semaphore--; + if (config.semaphore > 0) { + // don't start until equal number of stop-calls + return; + } + if (config.semaphore < 0) { + // ignore if start is called more often then stop + config.semaphore = 0; + } + // A slight delay, to avoid any current callbacks + if ( defined.setTimeout ) { + window.setTimeout(function() { + if ( config.timeout ) { + clearTimeout(config.timeout); + } + + config.blocking = false; + process(); + }, 13); + } else { + config.blocking = false; + process(); + } + }, + + stop: function(timeout) { + config.semaphore++; + config.blocking = true; + + if ( timeout && defined.setTimeout ) { + clearTimeout(config.timeout); + config.timeout = window.setTimeout(function() { + QUnit.ok( false, "Test timed out" ); + QUnit.start(); + }, timeout); + } + } +}; + +// Backwards compatibility, deprecated +QUnit.equals = QUnit.equal; +QUnit.same = QUnit.deepEqual; + +// Maintain internal state +var config = { + // The queue of tests to run + queue: [], + + // block until document ready + blocking: true, + + // by default, run previously failed tests first + // very useful in combination with "Hide passed tests" checked + reorder: true, + + noglobals: false, + notrycatch: false +}; + +// Load paramaters +(function() { + var location = window.location || { search: "", protocol: "file:" }, + params = location.search.slice( 1 ).split( "&" ), + length = params.length, + urlParams = {}, + current; + + if ( params[ 0 ] ) { + for ( var i = 0; i < length; i++ ) { + current = params[ i ].split( "=" ); + current[ 0 ] = decodeURIComponent( current[ 0 ] ); + // allow just a key to turn on a flag, e.g., test.html?noglobals + current[ 1 ] = current[ 1 ] ? decodeURIComponent( current[ 1 ] ) : true; + urlParams[ current[ 0 ] ] = current[ 1 ]; + if ( current[ 0 ] in config ) { + config[ current[ 0 ] ] = current[ 1 ]; + } + } + } + + QUnit.urlParams = urlParams; + config.filter = urlParams.filter; + + // Figure out if we're running the tests from a server or not + QUnit.isLocal = !!(location.protocol === 'file:'); +})(); + +// Expose the API as global variables, unless an 'exports' +// object exists, in that case we assume we're in CommonJS +if ( typeof exports === "undefined" || typeof require === "undefined" ) { + extend(window, QUnit); + window.QUnit = QUnit; +} else { + extend(exports, QUnit); + exports.QUnit = QUnit; +} + +// define these after exposing globals to keep them in these QUnit namespace only +extend(QUnit, { + config: config, + + // Initialize the configuration options + init: function() { + extend(config, { + stats: { all: 0, bad: 0 }, + moduleStats: { all: 0, bad: 0 }, + started: +new Date, + updateRate: 1000, + blocking: false, + autostart: true, + autorun: false, + filter: "", + queue: [], + semaphore: 0 + }); + + var tests = id( "qunit-tests" ), + banner = id( "qunit-banner" ), + result = id( "qunit-testresult" ); + + if ( tests ) { + tests.innerHTML = ""; + } + + if ( banner ) { + banner.className = ""; + } + + if ( result ) { + result.parentNode.removeChild( result ); + } + + if ( tests ) { + result = document.createElement( "p" ); + result.id = "qunit-testresult"; + result.className = "result"; + tests.parentNode.insertBefore( result, tests ); + result.innerHTML = 'Running...
     '; + } + }, + + /** + * Resets the test setup. Useful for tests that modify the DOM. + * + * If jQuery is available, uses jQuery's html(), otherwise just innerHTML. + */ + reset: function() { + if ( window.jQuery ) { + jQuery( "#qunit-fixture" ).html( config.fixture ); + } else { + var main = id( 'qunit-fixture' ); + if ( main ) { + main.innerHTML = config.fixture; + } + } + }, + + /** + * Trigger an event on an element. + * + * @example triggerEvent( document.body, "click" ); + * + * @param DOMElement elem + * @param String type + */ + triggerEvent: function( elem, type, event ) { + if ( document.createEvent ) { + event = document.createEvent("MouseEvents"); + event.initMouseEvent(type, true, true, elem.ownerDocument.defaultView, + 0, 0, 0, 0, 0, false, false, false, false, 0, null); + elem.dispatchEvent( event ); + + } else if ( elem.fireEvent ) { + elem.fireEvent("on"+type); + } + }, + + // Safe object type checking + is: function( type, obj ) { + return QUnit.objectType( obj ) == type; + }, + + objectType: function( obj ) { + if (typeof obj === "undefined") { + return "undefined"; + + // consider: typeof null === object + } + if (obj === null) { + return "null"; + } + + var type = Object.prototype.toString.call( obj ) + .match(/^\[object\s(.*)\]$/)[1] || ''; + + switch (type) { + case 'Number': + if (isNaN(obj)) { + return "nan"; + } else { + return "number"; + } + case 'String': + case 'Boolean': + case 'Array': + case 'Date': + case 'RegExp': + case 'Function': + return type.toLowerCase(); + } + if (typeof obj === "object") { + return "object"; + } + return undefined; + }, + + push: function(result, actual, expected, message) { + var details = { + result: result, + message: message, + actual: actual, + expected: expected + }; + + message = escapeHtml(message) || (result ? "okay" : "failed"); + message = '' + message + ""; + expected = escapeHtml(QUnit.jsDump.parse(expected)); + actual = escapeHtml(QUnit.jsDump.parse(actual)); + var output = message + ''; + if (actual != expected) { + output += ''; + output += ''; + } + if (!result) { + var source = sourceFromStacktrace(); + if (source) { + details.source = source; + output += ''; + } + } + output += "
    Expected:
    ' + expected + '
    Result:
    ' + actual + '
    Diff:
    ' + QUnit.diff(expected, actual) +'
    Source:
    ' + escapeHtml(source) + '
    "; + + QUnit.log(details); + + config.current.assertions.push({ + result: !!result, + message: output + }); + }, + + url: function( params ) { + params = extend( extend( {}, QUnit.urlParams ), params ); + var querystring = "?", + key; + for ( key in params ) { + querystring += encodeURIComponent( key ) + "=" + + encodeURIComponent( params[ key ] ) + "&"; + } + return window.location.pathname + querystring.slice( 0, -1 ); + }, + + // Logging callbacks; all receive a single argument with the listed properties + // run test/logs.html for any related changes + begin: function() {}, + // done: { failed, passed, total, runtime } + done: function() {}, + // log: { result, actual, expected, message } + log: function() {}, + // testStart: { name } + testStart: function() {}, + // testDone: { name, failed, passed, total } + testDone: function() {}, + // moduleStart: { name } + moduleStart: function() {}, + // moduleDone: { name, failed, passed, total } + moduleDone: function() {} +}); + +if ( typeof document === "undefined" || document.readyState === "complete" ) { + config.autorun = true; +} + +addEvent(window, "load", function() { + QUnit.begin({}); + + // Initialize the config, saving the execution queue + var oldconfig = extend({}, config); + QUnit.init(); + extend(config, oldconfig); + + config.blocking = false; + + var userAgent = id("qunit-userAgent"); + if ( userAgent ) { + userAgent.innerHTML = navigator.userAgent; + } + var banner = id("qunit-header"); + if ( banner ) { + banner.innerHTML = ' ' + banner.innerHTML + ' ' + + '' + + ''; + addEvent( banner, "change", function( event ) { + var params = {}; + params[ event.target.name ] = event.target.checked ? true : undefined; + window.location = QUnit.url( params ); + }); + } + + var toolbar = id("qunit-testrunner-toolbar"); + if ( toolbar ) { + var filter = document.createElement("input"); + filter.type = "checkbox"; + filter.id = "qunit-filter-pass"; + addEvent( filter, "click", function() { + var ol = document.getElementById("qunit-tests"); + if ( filter.checked ) { + ol.className = ol.className + " hidepass"; + } else { + var tmp = " " + ol.className.replace( /[\n\t\r]/g, " " ) + " "; + ol.className = tmp.replace(/ hidepass /, " "); + } + if ( defined.sessionStorage ) { + if (filter.checked) { + sessionStorage.setItem("qunit-filter-passed-tests", "true"); + } else { + sessionStorage.removeItem("qunit-filter-passed-tests"); + } + } + }); + if ( defined.sessionStorage && sessionStorage.getItem("qunit-filter-passed-tests") ) { + filter.checked = true; + var ol = document.getElementById("qunit-tests"); + ol.className = ol.className + " hidepass"; + } + toolbar.appendChild( filter ); + + var label = document.createElement("label"); + label.setAttribute("for", "qunit-filter-pass"); + label.innerHTML = "Hide passed tests"; + toolbar.appendChild( label ); + } + + var main = id('qunit-fixture'); + if ( main ) { + config.fixture = main.innerHTML; + } + + if (config.autostart) { + QUnit.start(); + } +}); + +function done() { + config.autorun = true; + + // Log the last module results + if ( config.currentModule ) { + QUnit.moduleDone( { + name: config.currentModule, + failed: config.moduleStats.bad, + passed: config.moduleStats.all - config.moduleStats.bad, + total: config.moduleStats.all + } ); + } + + var banner = id("qunit-banner"), + tests = id("qunit-tests"), + runtime = +new Date - config.started, + passed = config.stats.all - config.stats.bad, + html = [ + 'Tests completed in ', + runtime, + ' milliseconds.
    ', + '', + passed, + ' tests of ', + config.stats.all, + ' passed, ', + config.stats.bad, + ' failed.' + ].join(''); + + if ( banner ) { + banner.className = (config.stats.bad ? "qunit-fail" : "qunit-pass"); + } + + if ( tests ) { + id( "qunit-testresult" ).innerHTML = html; + } + + if ( typeof document !== "undefined" && document.title ) { + // show ✖ for good, ✔ for bad suite result in title + // use escape sequences in case file gets loaded with non-utf-8-charset + document.title = (config.stats.bad ? "\u2716" : "\u2714") + " " + document.title; + } + + QUnit.done( { + failed: config.stats.bad, + passed: passed, + total: config.stats.all, + runtime: runtime + } ); +} + +function validTest( name ) { + var filter = config.filter, + run = false; + + if ( !filter ) { + return true; + } + + var not = filter.charAt( 0 ) === "!"; + if ( not ) { + filter = filter.slice( 1 ); + } + + if ( name.indexOf( filter ) !== -1 ) { + return !not; + } + + if ( not ) { + run = true; + } + + return run; +} + +// so far supports only Firefox, Chrome and Opera (buggy) +// could be extended in the future to use something like https://github.com/csnover/TraceKit +function sourceFromStacktrace() { + try { + throw new Error(); + } catch ( e ) { + if (e.stacktrace) { + // Opera + return e.stacktrace.split("\n")[6]; + } else if (e.stack) { + // Firefox, Chrome + return e.stack.split("\n")[4]; + } + } +} + +function escapeHtml(s) { + if (!s) { + return ""; + } + s = s + ""; + return s.replace(/[\&"<>\\]/g, function(s) { + switch(s) { + case "&": return "&"; + case "\\": return "\\\\"; + case '"': return '\"'; + case "<": return "<"; + case ">": return ">"; + default: return s; + } + }); +} + +function synchronize( callback ) { + config.queue.push( callback ); + + if ( config.autorun && !config.blocking ) { + process(); + } +} + +function process() { + var start = (new Date()).getTime(); + + while ( config.queue.length && !config.blocking ) { + if ( config.updateRate <= 0 || (((new Date()).getTime() - start) < config.updateRate) ) { + config.queue.shift()(); + } else { + window.setTimeout( process, 13 ); + break; + } + } + if (!config.blocking && !config.queue.length) { + done(); + } +} + +function saveGlobal() { + config.pollution = []; + + if ( config.noglobals ) { + for ( var key in window ) { + config.pollution.push( key ); + } + } +} + +function checkPollution( name ) { + var old = config.pollution; + saveGlobal(); + + var newGlobals = diff( config.pollution, old ); + if ( newGlobals.length > 0 ) { + ok( false, "Introduced global variable(s): " + newGlobals.join(", ") ); + } + + var deletedGlobals = diff( old, config.pollution ); + if ( deletedGlobals.length > 0 ) { + ok( false, "Deleted global variable(s): " + deletedGlobals.join(", ") ); + } +} + +// returns a new Array with the elements that are in a but not in b +function diff( a, b ) { + var result = a.slice(); + for ( var i = 0; i < result.length; i++ ) { + for ( var j = 0; j < b.length; j++ ) { + if ( result[i] === b[j] ) { + result.splice(i, 1); + i--; + break; + } + } + } + return result; +} + +function fail(message, exception, callback) { + if ( typeof console !== "undefined" && console.error && console.warn ) { + console.error(message); + console.error(exception); + console.warn(callback.toString()); + + } else if ( window.opera && opera.postError ) { + opera.postError(message, exception, callback.toString); + } +} + +function extend(a, b) { + for ( var prop in b ) { + if ( b[prop] === undefined ) { + delete a[prop]; + } else { + a[prop] = b[prop]; + } + } + + return a; +} + +function addEvent(elem, type, fn) { + if ( elem.addEventListener ) { + elem.addEventListener( type, fn, false ); + } else if ( elem.attachEvent ) { + elem.attachEvent( "on" + type, fn ); + } else { + fn(); + } +} + +function id(name) { + return !!(typeof document !== "undefined" && document && document.getElementById) && + document.getElementById( name ); +} + +// Test for equality any JavaScript type. +// Discussions and reference: http://philrathe.com/articles/equiv +// Test suites: http://philrathe.com/tests/equiv +// Author: Philippe Rathé +QUnit.equiv = function () { + + var innerEquiv; // the real equiv function + var callers = []; // stack to decide between skip/abort functions + var parents = []; // stack to avoiding loops from circular referencing + + // Call the o related callback with the given arguments. + function bindCallbacks(o, callbacks, args) { + var prop = QUnit.objectType(o); + if (prop) { + if (QUnit.objectType(callbacks[prop]) === "function") { + return callbacks[prop].apply(callbacks, args); + } else { + return callbacks[prop]; // or undefined + } + } + } + + var callbacks = function () { + + // for string, boolean, number and null + function useStrictEquality(b, a) { + if (b instanceof a.constructor || a instanceof b.constructor) { + // to catch short annotaion VS 'new' annotation of a declaration + // e.g. var i = 1; + // var j = new Number(1); + return a == b; + } else { + return a === b; + } + } + + return { + "string": useStrictEquality, + "boolean": useStrictEquality, + "number": useStrictEquality, + "null": useStrictEquality, + "undefined": useStrictEquality, + + "nan": function (b) { + return isNaN(b); + }, + + "date": function (b, a) { + return QUnit.objectType(b) === "date" && a.valueOf() === b.valueOf(); + }, + + "regexp": function (b, a) { + return QUnit.objectType(b) === "regexp" && + a.source === b.source && // the regex itself + a.global === b.global && // and its modifers (gmi) ... + a.ignoreCase === b.ignoreCase && + a.multiline === b.multiline; + }, + + // - skip when the property is a method of an instance (OOP) + // - abort otherwise, + // initial === would have catch identical references anyway + "function": function () { + var caller = callers[callers.length - 1]; + return caller !== Object && + typeof caller !== "undefined"; + }, + + "array": function (b, a) { + var i, j, loop; + var len; + + // b could be an object literal here + if ( ! (QUnit.objectType(b) === "array")) { + return false; + } + + len = a.length; + if (len !== b.length) { // safe and faster + return false; + } + + //track reference to avoid circular references + parents.push(a); + for (i = 0; i < len; i++) { + loop = false; + for(j=0;j= 0) { + type = "array"; + } else { + type = typeof obj; + } + return type; + }, + separator:function() { + return this.multiline ? this.HTML ? '
    ' : '\n' : this.HTML ? ' ' : ' '; + }, + indent:function( extra ) {// extra can be a number, shortcut for increasing-calling-decreasing + if ( !this.multiline ) + return ''; + var chr = this.indentChar; + if ( this.HTML ) + chr = chr.replace(/\t/g,' ').replace(/ /g,' '); + return Array( this._depth_ + (extra||0) ).join(chr); + }, + up:function( a ) { + this._depth_ += a || 1; + }, + down:function( a ) { + this._depth_ -= a || 1; + }, + setParser:function( name, parser ) { + this.parsers[name] = parser; + }, + // The next 3 are exposed so you can use them + quote:quote, + literal:literal, + join:join, + // + _depth_: 1, + // This is the list of parsers, to modify them, use jsDump.setParser + parsers:{ + window: '[Window]', + document: '[Document]', + error:'[ERROR]', //when no parser is found, shouldn't happen + unknown: '[Unknown]', + 'null':'null', + 'undefined':'undefined', + 'function':function( fn ) { + var ret = 'function', + name = 'name' in fn ? fn.name : (reName.exec(fn)||[])[1];//functions never have name in IE + if ( name ) + ret += ' ' + name; + ret += '('; + + ret = [ ret, QUnit.jsDump.parse( fn, 'functionArgs' ), '){'].join(''); + return join( ret, QUnit.jsDump.parse(fn,'functionCode'), '}' ); + }, + array: array, + nodelist: array, + arguments: array, + object:function( map ) { + var ret = [ ]; + QUnit.jsDump.up(); + for ( var key in map ) + ret.push( QUnit.jsDump.parse(key,'key') + ': ' + QUnit.jsDump.parse(map[key]) ); + QUnit.jsDump.down(); + return join( '{', ret, '}' ); + }, + node:function( node ) { + var open = QUnit.jsDump.HTML ? '<' : '<', + close = QUnit.jsDump.HTML ? '>' : '>'; + + var tag = node.nodeName.toLowerCase(), + ret = open + tag; + + for ( var a in QUnit.jsDump.DOMAttrs ) { + var val = node[QUnit.jsDump.DOMAttrs[a]]; + if ( val ) + ret += ' ' + a + '=' + QUnit.jsDump.parse( val, 'attribute' ); + } + return ret + close + open + '/' + tag + close; + }, + functionArgs:function( fn ) {//function calls it internally, it's the arguments part of the function + var l = fn.length; + if ( !l ) return ''; + + var args = Array(l); + while ( l-- ) + args[l] = String.fromCharCode(97+l);//97 is 'a' + return ' ' + args.join(', ') + ' '; + }, + key:quote, //object calls it internally, the key part of an item in a map + functionCode:'[code]', //function calls it internally, it's the content of the function + attribute:quote, //node calls it internally, it's an html attribute value + string:quote, + date:quote, + regexp:literal, //regex + number:literal, + 'boolean':literal + }, + DOMAttrs:{//attributes to dump from nodes, name=>realName + id:'id', + name:'name', + 'class':'className' + }, + HTML:false,//if true, entities are escaped ( <, >, \t, space and \n ) + indentChar:' ',//indentation unit + multiline:true //if true, items in a collection, are separated by a \n, else just a space. + }; + + return jsDump; +})(); + +// from Sizzle.js +function getText( elems ) { + var ret = "", elem; + + for ( var i = 0; elems[i]; i++ ) { + elem = elems[i]; + + // Get the text from text nodes and CDATA nodes + if ( elem.nodeType === 3 || elem.nodeType === 4 ) { + ret += elem.nodeValue; + + // Traverse everything else, except comment nodes + } else if ( elem.nodeType !== 8 ) { + ret += getText( elem.childNodes ); + } + } + + return ret; +}; + +/* + * Javascript Diff Algorithm + * By John Resig (http://ejohn.org/) + * Modified by Chu Alan "sprite" + * + * Released under the MIT license. + * + * More Info: + * http://ejohn.org/projects/javascript-diff-algorithm/ + * + * Usage: QUnit.diff(expected, actual) + * + * QUnit.diff("the quick brown fox jumped over", "the quick fox jumps over") == "the quick brown fox jumped jumps over" + */ +QUnit.diff = (function() { + function diff(o, n){ + var ns = new Object(); + var os = new Object(); + + for (var i = 0; i < n.length; i++) { + if (ns[n[i]] == null) + ns[n[i]] = { + rows: new Array(), + o: null + }; + ns[n[i]].rows.push(i); + } + + for (var i = 0; i < o.length; i++) { + if (os[o[i]] == null) + os[o[i]] = { + rows: new Array(), + n: null + }; + os[o[i]].rows.push(i); + } + + for (var i in ns) { + if (ns[i].rows.length == 1 && typeof(os[i]) != "undefined" && os[i].rows.length == 1) { + n[ns[i].rows[0]] = { + text: n[ns[i].rows[0]], + row: os[i].rows[0] + }; + o[os[i].rows[0]] = { + text: o[os[i].rows[0]], + row: ns[i].rows[0] + }; + } + } + + for (var i = 0; i < n.length - 1; i++) { + if (n[i].text != null && n[i + 1].text == null && n[i].row + 1 < o.length && o[n[i].row + 1].text == null && + n[i + 1] == o[n[i].row + 1]) { + n[i + 1] = { + text: n[i + 1], + row: n[i].row + 1 + }; + o[n[i].row + 1] = { + text: o[n[i].row + 1], + row: i + 1 + }; + } + } + + for (var i = n.length - 1; i > 0; i--) { + if (n[i].text != null && n[i - 1].text == null && n[i].row > 0 && o[n[i].row - 1].text == null && + n[i - 1] == o[n[i].row - 1]) { + n[i - 1] = { + text: n[i - 1], + row: n[i].row - 1 + }; + o[n[i].row - 1] = { + text: o[n[i].row - 1], + row: i - 1 + }; + } + } + + return { + o: o, + n: n + }; + } + + return function(o, n){ + o = o.replace(/\s+$/, ''); + n = n.replace(/\s+$/, ''); + var out = diff(o == "" ? [] : o.split(/\s+/), n == "" ? [] : n.split(/\s+/)); + + var str = ""; + + var oSpace = o.match(/\s+/g); + if (oSpace == null) { + oSpace = [" "]; + } + else { + oSpace.push(" "); + } + var nSpace = n.match(/\s+/g); + if (nSpace == null) { + nSpace = [" "]; + } + else { + nSpace.push(" "); + } + + if (out.n.length == 0) { + for (var i = 0; i < out.o.length; i++) { + str += '' + out.o[i] + oSpace[i] + ""; + } + } + else { + if (out.n[0].text == null) { + for (n = 0; n < out.o.length && out.o[n].text == null; n++) { + str += '' + out.o[n] + oSpace[n] + ""; + } + } + + for (var i = 0; i < out.n.length; i++) { + if (out.n[i].text == null) { + str += '' + out.n[i] + nSpace[i] + ""; + } + else { + var pre = ""; + + for (n = out.n[i].row + 1; n < out.o.length && out.o[n].text == null; n++) { + pre += '' + out.o[n] + oSpace[n] + ""; + } + str += " " + out.n[i].text + nSpace[i] + pre; + } + } + } + + return str; + }; +})(); + +})(this); diff -Nru python-coverage-3.4/test/stress_phystoken.tok python-coverage-3.6/test/stress_phystoken.tok --- python-coverage-3.4/test/stress_phystoken.tok 1970-01-01 00:00:00.000000000 +0000 +++ python-coverage-3.6/test/stress_phystoken.tok 2010-08-08 12:26:31.000000000 +0000 @@ -0,0 +1,52 @@ +# 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. + +first_back = """\ +hey there! +""" + +other_back = """ +hey \ +there +""" + +lots_of_back = """\ +hey \ +there +""" +# This next line is supposed to have trailing whitespace: +fake_back = """\ +ouch +""" + +# Lots of difficulty happens with code like: +# +# fake_back = """\ +# ouch +# """ +# +# Ugh, the edge cases... + +# What about a comment like this\ +"what's this string doing here?" + +class C(object): + def there(): + this = 5 + \ + 7 + that = \ + "a continued line" + +cont1 = "one line of text" + \ + "another line of text" + +a_long_string = \ + "part 1" \ + "2" \ + "3 is longer" + +def hello(): + print("Hello world!") + +hello() diff -Nru python-coverage-3.4/test/stress_phystoken_dos.tok python-coverage-3.6/test/stress_phystoken_dos.tok --- python-coverage-3.4/test/stress_phystoken_dos.tok 1970-01-01 00:00:00.000000000 +0000 +++ python-coverage-3.6/test/stress_phystoken_dos.tok 2010-08-08 12:26:31.000000000 +0000 @@ -0,0 +1,52 @@ +# 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. + +first_back = """\ +hey there! +""" + +other_back = """ +hey \ +there +""" + +lots_of_back = """\ +hey \ +there +""" +# This next line is supposed to have trailing whitespace: +fake_back = """\ +ouch +""" + +# Lots of difficulty happens with code like: +# +# fake_back = """\ +# ouch +# """ +# +# Ugh, the edge cases... + +# What about a comment like this\ +"what's this string doing here?" + +class C(object): + def there(): + this = 5 + \ + 7 + that = \ + "a continued line" + +cont1 = "one line of text" + \ + "another line of text" + +a_long_string = \ + "part 1" \ + "2" \ + "3 is longer" + +def hello(): + print("Hello world!") + +hello() diff -Nru python-coverage-3.4/test/test_api.py python-coverage-3.6/test/test_api.py --- python-coverage-3.4/test/test_api.py 1970-01-01 00:00:00.000000000 +0000 +++ python-coverage-3.6/test/test_api.py 2013-01-02 01:44:20.000000000 +0000 @@ -0,0 +1,571 @@ +"""Tests for Coverage's api.""" + +import fnmatch, os, re, sys, textwrap + +import coverage +from coverage.backward import StringIO + +from test.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 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 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 7 3 57% 4-6", rpt) + + +class ApiTest(CoverageTest): + """Api-oriented tests for Coverage.""" + + def clean_files(self, files, pats): + """Remove names matching `pats` from `files`, a list of filenames.""" + good = [] + for f in files: + for pat in pats: + if fnmatch.fnmatch(f, pat): + break + else: + good.append(f) + return good + + def assertFiles(self, files): + """Assert that the files here are `files`, ignoring the usual junk.""" + here = os.listdir(".") + here = self.clean_files(here, ["*.pyc", "__pycache__"]) + self.assertSameElements(here, files) + + def test_unexecuted_file(self): + cov = coverage.coverage() + + self.make_file("mycode.py", """\ + a = 1 + b = 2 + if b == 3: + c = 4 + d = 5 + """) + + self.make_file("not_run.py", """\ + fooey = 17 + """) + + # Import the python file, executing it. + self.start_import_stop(cov, "mycode") + + _, statements, missing, _ = cov.analysis("not_run.py") + self.assertEqual(statements, [1]) + self.assertEqual(missing, [1]) + + def test_filenames(self): + + self.make_file("mymain.py", """\ + import mymod + a = 1 + """) + + self.make_file("mymod.py", """\ + fooey = 17 + """) + + # Import the python file, executing it. + cov = coverage.coverage() + self.start_import_stop(cov, "mymain") + + filename, _, _, _ = cov.analysis("mymain.py") + self.assertEqual(os.path.basename(filename), "mymain.py") + filename, _, _, _ = cov.analysis("mymod.py") + self.assertEqual(os.path.basename(filename), "mymod.py") + + filename, _, _, _ = cov.analysis(sys.modules["mymain"]) + self.assertEqual(os.path.basename(filename), "mymain.py") + filename, _, _, _ = cov.analysis(sys.modules["mymod"]) + self.assertEqual(os.path.basename(filename), "mymod.py") + + # Import the python file, executing it again, once it's been compiled + # already. + cov = coverage.coverage() + self.start_import_stop(cov, "mymain") + + filename, _, _, _ = cov.analysis("mymain.py") + self.assertEqual(os.path.basename(filename), "mymain.py") + filename, _, _, _ = cov.analysis("mymod.py") + self.assertEqual(os.path.basename(filename), "mymod.py") + + filename, _, _, _ = cov.analysis(sys.modules["mymain"]) + self.assertEqual(os.path.basename(filename), "mymain.py") + filename, _, _, _ = cov.analysis(sys.modules["mymod"]) + self.assertEqual(os.path.basename(filename), "mymod.py") + + def test_ignore_stdlib(self): + self.make_file("mymain.py", """\ + import colorsys + a = 1 + hls = colorsys.rgb_to_hls(1.0, 0.5, 0.0) + """) + + # Measure without the stdlib. + cov1 = coverage.coverage() + self.assertEqual(cov1.config.cover_pylib, False) + self.start_import_stop(cov1, "mymain") + + # some statements were marked executed in mymain.py + _, statements, missing, _ = cov1.analysis("mymain.py") + self.assertNotEqual(statements, missing) + # but none were in colorsys.py + _, statements, missing, _ = cov1.analysis("colorsys.py") + self.assertEqual(statements, missing) + + # Measure with the stdlib. + cov2 = coverage.coverage(cover_pylib=True) + self.start_import_stop(cov2, "mymain") + + # some statements were marked executed in mymain.py + _, statements, missing, _ = cov2.analysis("mymain.py") + self.assertNotEqual(statements, missing) + # and some were marked executed in colorsys.py + _, statements, missing, _ = cov2.analysis("colorsys.py") + self.assertNotEqual(statements, missing) + + def test_include_can_measure_stdlib(self): + self.make_file("mymain.py", """\ + import colorsys, random + a = 1 + r, g, b = [random.random() for _ in range(3)] + hls = colorsys.rgb_to_hls(r, g, b) + """) + + # Measure without the stdlib, but include colorsys. + cov1 = coverage.coverage(cover_pylib=False, include=["*/colorsys.py"]) + self.start_import_stop(cov1, "mymain") + + # some statements were marked executed in colorsys.py + _, statements, missing, _ = cov1.analysis("colorsys.py") + self.assertNotEqual(statements, missing) + # but none were in random.py + _, statements, missing, _ = cov1.analysis("random.py") + self.assertEqual(statements, missing) + + def test_exclude_list(self): + cov = coverage.coverage() + cov.clear_exclude() + self.assertEqual(cov.get_exclude_list(), []) + cov.exclude("foo") + self.assertEqual(cov.get_exclude_list(), ["foo"]) + cov.exclude("bar") + self.assertEqual(cov.get_exclude_list(), ["foo", "bar"]) + self.assertEqual(cov._exclude_regex('exclude'), "(foo)|(bar)") + cov.clear_exclude() + self.assertEqual(cov.get_exclude_list(), []) + + def test_exclude_partial_list(self): + cov = coverage.coverage() + cov.clear_exclude(which='partial') + self.assertEqual(cov.get_exclude_list(which='partial'), []) + cov.exclude("foo", which='partial') + self.assertEqual(cov.get_exclude_list(which='partial'), ["foo"]) + cov.exclude("bar", which='partial') + self.assertEqual(cov.get_exclude_list(which='partial'), ["foo", "bar"]) + self.assertEqual(cov._exclude_regex(which='partial'), "(foo)|(bar)") + cov.clear_exclude(which='partial') + self.assertEqual(cov.get_exclude_list(which='partial'), []) + + def test_exclude_and_partial_are_separate_lists(self): + cov = coverage.coverage() + cov.clear_exclude(which='partial') + cov.clear_exclude(which='exclude') + cov.exclude("foo", which='partial') + self.assertEqual(cov.get_exclude_list(which='partial'), ['foo']) + self.assertEqual(cov.get_exclude_list(which='exclude'), []) + cov.exclude("bar", which='exclude') + self.assertEqual(cov.get_exclude_list(which='partial'), ['foo']) + self.assertEqual(cov.get_exclude_list(which='exclude'), ['bar']) + cov.exclude("p2", which='partial') + cov.exclude("e2", which='exclude') + self.assertEqual(cov.get_exclude_list(which='partial'), ['foo', 'p2']) + self.assertEqual(cov.get_exclude_list(which='exclude'), ['bar', 'e2']) + cov.clear_exclude(which='partial') + self.assertEqual(cov.get_exclude_list(which='partial'), []) + self.assertEqual(cov.get_exclude_list(which='exclude'), ['bar', 'e2']) + cov.clear_exclude(which='exclude') + self.assertEqual(cov.get_exclude_list(which='partial'), []) + self.assertEqual(cov.get_exclude_list(which='exclude'), []) + + def test_datafile_default(self): + # Default data file behavior: it's .coverage + self.make_file("datatest1.py", """\ + fooey = 17 + """) + + self.assertFiles(["datatest1.py"]) + cov = coverage.coverage() + self.start_import_stop(cov, "datatest1") + cov.save() + self.assertFiles(["datatest1.py", ".coverage"]) + + def test_datafile_specified(self): + # You can specify the data file name. + self.make_file("datatest2.py", """\ + fooey = 17 + """) + + self.assertFiles(["datatest2.py"]) + cov = coverage.coverage(data_file="cov.data") + self.start_import_stop(cov, "datatest2") + cov.save() + self.assertFiles(["datatest2.py", "cov.data"]) + + def test_datafile_and_suffix_specified(self): + # You can specify the data file name and suffix. + self.make_file("datatest3.py", """\ + fooey = 17 + """) + + self.assertFiles(["datatest3.py"]) + 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"]) + + def test_datafile_from_rcfile(self): + # You can specify the data file name in the .coveragerc file + self.make_file("datatest4.py", """\ + fooey = 17 + """) + self.make_file(".coveragerc", """\ + [run] + data_file = mydata.dat + """) + + self.assertFiles(["datatest4.py", ".coveragerc"]) + 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() + cov.erase() + cov.report() + + def test_start_stop_start_stop(self): + self.make_file("code1.py", """\ + code1 = 1 + """) + self.make_file("code2.py", """\ + code2 = 1 + code2 = 2 + """) + cov = coverage.coverage() + self.start_import_stop(cov, "code1") + cov.save() + self.start_import_stop(cov, "code2") + _, statements, missing, _ = cov.analysis("code1.py") + self.assertEqual(statements, [1]) + self.assertEqual(missing, []) + _, statements, missing, _ = cov.analysis("code2.py") + self.assertEqual(statements, [1, 2]) + self.assertEqual(missing, []) + + if 0: # expected failure + # for https://bitbucket.org/ned/coveragepy/issue/79 + def test_start_save_stop(self): + self.make_file("code1.py", """\ + code1 = 1 + """) + self.make_file("code2.py", """\ + code2 = 1 + code2 = 2 + """) + cov = coverage.coverage() + cov.start() + self.import_local_file("code1") + cov.save() + self.import_local_file("code2") + cov.stop() + + _, statements, missing, _ = cov.analysis("code1.py") + self.assertEqual(statements, [1]) + self.assertEqual(missing, []) + _, statements, missing, _ = cov.analysis("code2.py") + self.assertEqual(statements, [1, 2]) + self.assertEqual(missing, []) + + + +class UsingModulesMixin(object): + """A mixin for importing modules from test/modules and test/moremodules.""" + + run_in_temp_dir = False + + def setUp(self): + super(UsingModulesMixin, self).setUp() + # Parent class saves and restores sys.path, we can just modify it. + self.old_dir = os.getcwd() + os.chdir(self.nice_file(os.path.dirname(__file__), 'modules')) + sys.path.append(".") + sys.path.append("../moremodules") + + def tearDown(self): + os.chdir(self.old_dir) + super(UsingModulesMixin, self).tearDown() + + +class OmitIncludeTestsMixin(UsingModulesMixin): + """Test methods for coverage methods taking include and omit.""" + + def filenames_in(self, summary, filenames): + """Assert the `filenames` are in the keys of `summary`.""" + for filename in filenames.split(): + self.assertIn(filename, summary) + + def filenames_not_in(self, summary, filenames): + """Assert the `filenames` are not in the keys of `summary`.""" + for filename in filenames.split(): + self.assertNotIn(filename, summary) + + def test_nothing_specified(self): + result = self.coverage_usepkgs() + self.filenames_in(result, "p1a p1b p2a p2b othera otherb osa osb") + self.filenames_not_in(result, "p1c") + # Because there was no source= specified, we don't search for + # unexecuted files. + + def test_include(self): + result = self.coverage_usepkgs(include=["*/p1a.py"]) + self.filenames_in(result, "p1a") + self.filenames_not_in(result, "p1b p1c p2a p2b othera otherb osa osb") + + def test_include_2(self): + result = self.coverage_usepkgs(include=["*a.py"]) + self.filenames_in(result, "p1a p2a othera osa") + self.filenames_not_in(result, "p1b p1c p2b otherb osb") + + def test_include_as_string(self): + result = self.coverage_usepkgs(include="*a.py") + self.filenames_in(result, "p1a p2a othera osa") + self.filenames_not_in(result, "p1b p1c p2b otherb osb") + + def test_omit(self): + result = self.coverage_usepkgs(omit=["*/p1a.py"]) + self.filenames_in(result, "p1b p2a p2b") + self.filenames_not_in(result, "p1a p1c") + + def test_omit_2(self): + result = self.coverage_usepkgs(omit=["*a.py"]) + self.filenames_in(result, "p1b p2b otherb osb") + self.filenames_not_in(result, "p1a p1c p2a othera osa") + + def test_omit_as_string(self): + result = self.coverage_usepkgs(omit="*a.py") + self.filenames_in(result, "p1b p2b otherb osb") + self.filenames_not_in(result, "p1a p1c p2a othera osa") + + def test_omit_and_include(self): + result = self.coverage_usepkgs( include=["*/p1*"], omit=["*/p1a.py"]) + self.filenames_in(result, "p1b") + self.filenames_not_in(result, "p1a p1c p2a p2b") + + +class SourceOmitIncludeTest(OmitIncludeTestsMixin, CoverageTest): + """Test using `source`, `omit` and `include` when measuring code.""" + + def coverage_usepkgs(self, **kwargs): + """Run coverage on usepkgs and return the line summary. + + Arguments are passed to the `coverage.coverage` constructor. + + """ + cov = coverage.coverage(**kwargs) + cov.start() + import usepkgs # pragma: nested # pylint: disable=F0401,W0612 + cov.stop() # pragma: nested + cov._harvest_data() # private! sshhh... + summary = cov.data.summary() + for k, v in list(summary.items()): + assert k.endswith(".py") + summary[k[:-3]] = v + return summary + + def test_source_package(self): + lines = self.coverage_usepkgs(source=["pkg1"]) + self.filenames_in(lines, "p1a p1b") + self.filenames_not_in(lines, "p2a p2b othera otherb osa osb") + # Because source= was specified, we do search for unexecuted files. + self.assertEqual(lines['p1c'], 0) + + def test_source_package_dotted(self): + lines = self.coverage_usepkgs(source=["pkg1.p1b"]) + self.filenames_in(lines, "p1b") + self.filenames_not_in(lines, "p1a p1c p2a p2b othera otherb osa osb") + + +class ReportIncludeOmitTest(OmitIncludeTestsMixin, CoverageTest): + """Tests of the report include/omit functionality.""" + + def coverage_usepkgs(self, **kwargs): + """Try coverage.report().""" + cov = coverage.coverage() + cov.start() + import usepkgs # pragma: nested # pylint: disable=F0401,W0612 + cov.stop() # pragma: nested + report = StringIO() + cov.report(file=report, **kwargs) + return report.getvalue() + + +class XmlIncludeOmitTest(OmitIncludeTestsMixin, CoverageTest): + """Tests of the xml include/omit functionality. + + This also takes care of the HTML and annotate include/omit, by virtue + of the structure of the code. + + """ + + def coverage_usepkgs(self, **kwargs): + """Try coverage.xml_report().""" + cov = coverage.coverage() + cov.start() + import usepkgs # pragma: nested # pylint: disable=F0401,W0612 + cov.stop() # pragma: nested + cov.xml_report(outfile="-", **kwargs) + return self.stdout() + + +class AnalysisTest(CoverageTest): + """Test the numerical analysis of results.""" + def test_many_missing_branches(self): + cov = coverage.coverage(branch=True) + + self.make_file("missing.py", """\ + def fun1(x): + if x == 1: + print("one") + else: + print("not one") + print("done") # pragma: nocover + + def fun2(x): + print("x") + + fun2(3) + """) + + # Import the python file, executing it. + self.start_import_stop(cov, "missing") + + nums = cov._analyze("missing.py").numbers + self.assertEqual(nums.n_files, 1) + self.assertEqual(nums.n_statements, 7) + self.assertEqual(nums.n_excluded, 1) + self.assertEqual(nums.n_missing, 3) + self.assertEqual(nums.n_branches, 2) + self.assertEqual(nums.n_partial_branches, 0) + self.assertEqual(nums.n_missing_branches, 2) + + +class PluginTest(CoverageTest): + """Test that the API works properly the way the plugins call it. + + We don't actually use the plugins, but these tests call the API the same + way they do. + + """ + def pretend_to_be_nose_with_cover(self, erase): + """This is what the nose --with-cover plugin does.""" + cov = coverage.coverage() + + self.make_file("no_biggie.py", """\ + a = 1 + b = 2 + if b == 1: + c = 4 + """) + + if erase: + cov.combine() + cov.erase() + cov.load() + self.start_import_stop(cov, "no_biggie") + cov.combine() + cov.save() + cov.report(["no_biggie.py"]) + self.assertEqual(self.stdout(), textwrap.dedent("""\ + Name Stmts Miss Cover Missing + ----------------------------------------- + no_biggie 4 1 75% 4 + """)) + + def test_nose_plugin(self): + self.pretend_to_be_nose_with_cover(erase=False) + + def test_nose_plugin_with_erase(self): + self.pretend_to_be_nose_with_cover(erase=True) diff -Nru python-coverage-3.4/test/test_arcs.py python-coverage-3.6/test/test_arcs.py --- python-coverage-3.4/test/test_arcs.py 1970-01-01 00:00:00.000000000 +0000 +++ python-coverage-3.6/test/test_arcs.py 2013-01-02 01:44:20.000000000 +0000 @@ -0,0 +1,571 @@ +"""Tests for Coverage.py's arc measurement.""" + +import sys +from test.coveragetest import CoverageTest + + +class SimpleArcTest(CoverageTest): + """Tests for Coverage.py's arc measurement.""" + + def test_simple_sequence(self): + self.check_coverage("""\ + a = 1 + b = 2 + """, + arcz=".1 12 2.") + self.check_coverage("""\ + a = 1 + + b = 3 + """, + arcz=".1 13 3.") + self.check_coverage("""\ + + a = 2 + b = 3 + + c = 5 + """, + arcz=".2 23 35 5-2") + + def test_function_def(self): + self.check_coverage("""\ + def foo(): + a = 2 + + foo() + """, + arcz=".1 .2 14 2. 4.") + + def test_if(self): + self.check_coverage("""\ + a = 1 + if len([]) == 0: + a = 3 + assert a == 3 + """, + arcz=".1 12 23 24 34 4.", arcz_missing="24") + self.check_coverage("""\ + a = 1 + if len([]) == 1: + a = 3 + assert a == 1 + """, + arcz=".1 12 23 24 34 4.", arcz_missing="23 34") + + def test_if_else(self): + self.check_coverage("""\ + if len([]) == 0: + a = 2 + else: + a = 4 + assert a == 2 + """, + arcz=".1 12 25 14 45 5.", arcz_missing="14 45") + self.check_coverage("""\ + if len([]) == 1: + a = 2 + else: + a = 4 + assert a == 4 + """, + arcz=".1 12 25 14 45 5.", arcz_missing="12 25") + + def test_compact_if(self): + self.check_coverage("""\ + a = 1 + if len([]) == 0: a = 2 + assert a == 2 + """, + arcz=".1 12 23 3.", arcz_missing="") + self.check_coverage("""\ + def fn(x): + if x % 2: return True + return False + a = fn(1) + assert a == True + """, + arcz=".1 14 45 5. .2 2. 23 3.", arcz_missing="23 3.") + + def test_multiline(self): + # The firstlineno of the a assignment below differs among Python + # versions. + if sys.version_info >= (2, 5): + arcz = ".1 15 5-2" + else: + arcz = ".1 15 5-1" + self.check_coverage("""\ + a = ( + 2 + + 3 + ) + b = \\ + 6 + """, + arcz=arcz, arcz_missing="") + + def test_if_return(self): + self.check_coverage("""\ + def if_ret(a): + if a: + return 3 + b = 4 + return 5 + x = if_ret(0) + if_ret(1) + assert x == 8 + """, + arcz=".1 16 67 7. .2 23 24 3. 45 5.", arcz_missing="" + ) + + def test_dont_confuse_exit_and_else(self): + self.check_coverage("""\ + def foo(): + if foo: + a = 3 + else: + a = 5 + return a + assert foo() == 3 # 7 + """, + arcz=".1 17 7. .2 23 36 25 56 6.", arcz_missing="25 56" + ) + self.check_coverage("""\ + def foo(): + if foo: + a = 3 + else: + a = 5 + foo() # 6 + """, + arcz=".1 16 6. .2 23 3. 25 5.", arcz_missing="25 5." + ) + + if 0: # expected failure + def test_lambdas_are_confusing_bug_90(self): + self.check_coverage("""\ + fn = lambda x: x + a = 1 + """, + arcz=".1 12 2." + ) + + +if sys.version_info >= (2, 6): + class WithTest(CoverageTest): + """Arc-measuring tests involving context managers.""" + + def test_with(self): + self.check_coverage("""\ + def example(): + with open("test", "w") as f: # exit + f.write("") + return 1 + + example() + """, + arcz=".1 .2 23 34 4. 16 6." + ) + + +class LoopArcTest(CoverageTest): + """Arc-measuring tests involving loops.""" + + def test_loop(self): + self.check_coverage("""\ + for i in range(10): + a = i + assert a == 9 + """, + arcz=".1 12 21 13 3.", arcz_missing="") + self.check_coverage("""\ + a = -1 + for i in range(0): + a = i + assert a == -1 + """, + arcz=".1 12 23 32 24 4.", arcz_missing="23 32") + + def test_nested_loop(self): + self.check_coverage("""\ + for i in range(3): + for j in range(3): + a = i + j + assert a == 4 + """, + arcz=".1 12 23 32 21 14 4.", arcz_missing="") + + def test_break(self): + self.check_coverage("""\ + for i in range(10): + a = i + break # 3 + a = 99 + assert a == 0 # 5 + """, + arcz=".1 12 23 35 15 41 5.", arcz_missing="15 41") + + def test_continue(self): + self.check_coverage("""\ + for i in range(10): + a = i + continue # 3 + a = 99 + assert a == 9 # 5 + """, + arcz=".1 12 23 31 15 41 5.", arcz_missing="41") + + def test_nested_breaks(self): + self.check_coverage("""\ + for i in range(3): + for j in range(3): + a = i + j + break # 4 + if i == 2: + break + assert a == 2 and i == 2 # 7 + """, + arcz=".1 12 23 34 45 25 56 51 67 17 7.", arcz_missing="17 25") + + def test_while_true(self): + # With "while 1", the loop knows it's constant. + self.check_coverage("""\ + a, i = 1, 0 + while 1: + if i >= 3: + a = 4 + break + i += 1 + assert a == 4 and i == 3 + """, + arcz=".1 12 23 34 45 36 63 57 7.", + ) + # With "while True", 2.x thinks it's computation, 3.x thinks it's + # constant. + if sys.version_info >= (3, 0): + arcz = ".1 12 23 34 45 36 63 57 7." + else: + arcz = ".1 12 23 27 34 45 36 62 57 7." + self.check_coverage("""\ + a, i = 1, 0 + while True: + if i >= 3: + a = 4 + break + i += 1 + assert a == 4 and i == 3 + """, + arcz=arcz, + ) + + def test_for_if_else_for(self): + self.check_coverage("""\ + def branches_2(l): + if l: + for e in l: + a = 4 + else: + a = 6 + + def branches_3(l): + for x in l: + if x: + for e in l: + a = 12 + else: + a = 14 + + branches_2([0,1]) + branches_3([0,1]) + """, + arcz= + ".1 18 8G GH H. " + ".2 23 34 43 26 3. 6. " + ".9 9A 9-8 AB BC CB B9 AE E9", + arcz_missing="26 6." + ) + + def test_for_else(self): + self.check_coverage("""\ + def forelse(seq): + for n in seq: + if n > 5: + break + else: + print('None of the values were greater than 5') + print('Done') + forelse([1,2]) + forelse([1,6]) + """, + arcz=".1 .2 23 32 34 47 26 67 7. 18 89 9." + ) + + if 0: # expected failure + def test_confusing_for_loop_bug_175(self): + self.check_coverage("""\ + o = [(1,2), (3,4)] + o = [a for a in o if a[0] > 1] + for tup in o: + x = tup[0] + y = tup[1] + """, + arcz=".1 12 23 34 45 53 3.", + arcz_missing="", arcz_unpredicted="") + self.check_coverage("""\ + o = [(1,2), (3,4)] + for tup in [a for a in o if a[0] > 1]: + x = tup[0] + y = tup[1] + """, + arcz=".1 12 23 34 42 2.", + arcz_missing="", arcz_unpredicted="") + + +class ExceptionArcTest(CoverageTest): + """Arc-measuring tests involving exception handling.""" + + def test_try_except(self): + self.check_coverage("""\ + a, b = 1, 1 + try: + a = 3 + except: + b = 5 + assert a == 3 and b == 1 + """, + arcz=".1 12 23 36 45 56 6.", arcz_missing="45 56") + self.check_coverage("""\ + a, b = 1, 1 + try: + a = 3 + raise Exception("Yikes!") + a = 5 + except: + b = 7 + assert a == 3 and b == 7 + """, + arcz=".1 12 23 34 58 67 78 8.", + arcz_missing="58", arcz_unpredicted="46") + + def test_hidden_raise(self): + self.check_coverage("""\ + a, b = 1, 1 + def oops(x): + if x % 2: raise Exception("odd") + try: + a = 5 + oops(1) + a = 7 + except: + b = 9 + assert a == 5 and b == 9 + """, + arcz=".1 12 .3 3-2 24 45 56 67 7A 89 9A A.", + arcz_missing="67 7A", arcz_unpredicted="68") + + def test_except_with_type(self): + self.check_coverage("""\ + a, b = 1, 1 + def oops(x): + if x % 2: raise ValueError("odd") + def try_it(x): + try: + a = 6 + oops(x) + a = 8 + except ValueError: + b = 10 + return a + assert try_it(0) == 8 # C + assert try_it(1) == 6 # D + """, + arcz=".1 12 .3 3-2 24 4C CD D. .5 56 67 78 8B 9A AB B-4", + arcz_missing="", + arcz_unpredicted="79") + + def test_try_finally(self): + self.check_coverage("""\ + a, c = 1, 1 + try: + a = 3 + finally: + c = 5 + assert a == 3 and c == 5 + """, + arcz=".1 12 23 35 56 6.", arcz_missing="") + self.check_coverage("""\ + a, c, d = 1, 1, 1 + try: + try: + a = 4 + finally: + c = 6 + except: + d = 8 + assert a == 4 and c == 6 and d == 1 # 9 + """, + arcz=".1 12 23 34 46 67 78 89 69 9.", + arcz_missing="67 78 89", arcz_unpredicted="") + self.check_coverage("""\ + a, c, d = 1, 1, 1 + try: + try: + a = 4 + raise Exception("Yikes!") + a = 6 + finally: + c = 8 + except: + d = 10 # A + assert a == 4 and c == 8 and d == 10 # B + """, + arcz=".1 12 23 34 45 68 89 8B 9A AB B.", + arcz_missing="68 8B", arcz_unpredicted="58") + + def test_finally_in_loop(self): + self.check_coverage("""\ + a, c, d, i = 1, 1, 1, 99 + try: + for i in range(5): + try: + a = 5 + if i > 0: + raise Exception("Yikes!") + a = 8 + finally: + c = 10 + except: + d = 12 # C + assert a == 5 and c == 10 and d == 12 # D + """, + arcz=".1 12 23 34 3D 45 56 67 68 8A A3 AB AD BC CD D.", + arcz_missing="3D AD", arcz_unpredicted="7A") + self.check_coverage("""\ + a, c, d, i = 1, 1, 1, 99 + try: + for i in range(5): + try: + a = 5 + if i > 10: + raise Exception("Yikes!") + a = 8 + finally: + c = 10 + except: + d = 12 # C + assert a == 8 and c == 10 and d == 1 # D + """, + arcz=".1 12 23 34 3D 45 56 67 68 8A A3 AB AD BC CD D.", + arcz_missing="67 AB AD BC CD", arcz_unpredicted="") + + + def test_break_in_finally(self): + self.check_coverage("""\ + a, c, d, i = 1, 1, 1, 99 + try: + for i in range(5): + try: + a = 5 + if i > 0: + break + a = 8 + finally: + c = 10 + except: + d = 12 # C + assert a == 5 and c == 10 and d == 1 # D + """, + arcz=".1 12 23 34 3D 45 56 67 68 7A 8A A3 AB AD BC CD D.", + arcz_missing="3D AB BC CD", arcz_unpredicted="") + + if 0: # expected failure + def test_finally_in_loop_2(self): + self.check_coverage("""\ + for i in range(5): + try: + j = 3 + finally: + f = 5 + g = 6 + h = 7 + """, + arcz=".1 12 23 35 56 61 17 7.", + arcz_missing="", arcz_unpredicted="") + + if sys.version_info >= (2, 5): + # Try-except-finally was new in 2.5 + def test_except_finally(self): + self.check_coverage("""\ + a, b, c = 1, 1, 1 + try: + a = 3 + except: + b = 5 + finally: + c = 7 + assert a == 3 and b == 1 and c == 7 + """, + arcz=".1 12 23 45 37 57 78 8.", arcz_missing="45 57") + self.check_coverage("""\ + a, b, c = 1, 1, 1 + def oops(x): + if x % 2: raise Exception("odd") + try: + a = 5 + oops(1) + a = 7 + except: + b = 9 + finally: + c = 11 + assert a == 5 and b == 9 and c == 11 + """, + arcz=".1 12 .3 3-2 24 45 56 67 7B 89 9B BC C.", + arcz_missing="67 7B", arcz_unpredicted="68") + + +class MiscArcTest(CoverageTest): + """Miscellaneous arc-measuring tests.""" + + def test_dict_literal(self): + self.check_coverage("""\ + d = { + 'a': 2, + 'b': 3, + 'c': { + 'd': 5, + 'e': 6, + } + } + assert d + """, + arcz=".1 19 9.") + + +class ExcludeTest(CoverageTest): + """Tests of exclusions to indicate known partial branches.""" + + def test_default(self): + # A number of forms of pragma comment are accepted. + self.check_coverage("""\ + a = 1 + if a: #pragma: no branch + b = 3 + c = 4 + if c: # pragma NOBRANCH + d = 6 + e = 7 + """, + [1,2,3,4,5,6,7], + arcz=".1 12 23 24 34 45 56 57 67 7.", arcz_missing="") + + def test_custom_pragmas(self): + self.check_coverage("""\ + a = 1 + while a: # [only some] + c = 3 + break + assert c == 5-2 + """, + [1,2,3,4,5], + partials=["only some"], + arcz=".1 12 23 34 45 25 5.", arcz_missing="") diff -Nru python-coverage-3.4/test/test_cmdline.py python-coverage-3.6/test/test_cmdline.py --- python-coverage-3.4/test/test_cmdline.py 1970-01-01 00:00:00.000000000 +0000 +++ python-coverage-3.6/test/test_cmdline.py 2012-12-30 15:17:29.000000000 +0000 @@ -0,0 +1,702 @@ +"""Test cmdline.py for coverage.""" + +import pprint, re, shlex, sys, textwrap +import mock +import coverage +import coverage.cmdline +from coverage.misc import ExceptionDuringRun + +from test.coveragetest import CoverageTest, OK, ERR + + +class CmdLineTest(CoverageTest): + """Tests of execution paths through the command line interpreter.""" + + run_in_temp_dir = False + + INIT_LOAD = """\ + .coverage(cover_pylib=None, data_suffix=None, timid=None, branch=None, config_file=True, source=None, include=None, omit=None) + .load()\n""" + + def model_object(self): + """Return a Mock suitable for use in CoverageScript.""" + mk = mock.Mock() + mk.coverage.return_value = mk + return mk + + def mock_command_line(self, args): + """Run `args` through the command line, with a Mock. + + Returns the Mock it used and the status code returned. + + """ + m = self.model_object() + ret = coverage.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)) + return m, ret + + def cmd_executes(self, args, code, ret=OK): + """Assert that the `args` end up executing the sequence in `code`.""" + m1, r1 = self.mock_command_line(args) + self.assertEqual(r1, ret, + "Wrong status: got %s, wanted %s" % (r1, ret) + ) + + # Remove all indentation, and change ".foo()" to "m2.foo()". + code = re.sub(r"(?m)^\s+", "", code) + code = re.sub(r"(?m)^\.", "m2.", code) + m2 = self.model_object() + code_obj = compile(code, "", "exec") + eval(code_obj, globals(), { 'm2': m2 }) + self.assert_same_method_calls(m1, m2) + + def cmd_executes_same(self, args1, args2): + """Assert that the `args1` executes the same as `args2`.""" + m1, r1 = self.mock_command_line(args1) + m2, r2 = self.mock_command_line(args2) + self.assertEqual(r1, r2) + self.assert_same_method_calls(m1, m2) + + def assert_same_method_calls(self, m1, m2): + """Assert that `m1.method_calls` and `m2.method_calls` are the same.""" + # Use a real equality comparison, but if it fails, use a nicer assert + # so we can tell what's going on. We have to use the real == first due + # to CmdOptionParser.__eq__ + if m1.method_calls != m2.method_calls: + pp1 = pprint.pformat(m1.method_calls) + pp2 = pprint.pformat(m2.method_calls) + self.assertMultiLineEqual(pp1+'\n', pp2+'\n') + + def cmd_help(self, args, help_msg=None, topic=None, ret=ERR): + """Run a command line, and check that it prints the right help. + + Only the last function call in the mock is checked, which should be the + help message that we want to see. + + """ + m, r = self.mock_command_line(args) + self.assertEqual(r, ret, + "Wrong status: got %s, wanted %s" % (r, ret) + ) + if help_msg: + self.assertEqual(m.method_calls[-1], + ('help_fn', (help_msg,), {}) + ) + else: + self.assertEqual(m.method_calls[-1], + ('help_fn', (), {'topic':topic}) + ) + + +class CmdLineTestTest(CmdLineTest): + """Tests that our CmdLineTest helpers work.""" + def test_assert_same_method_calls(self): + # All the other tests here use self.cmd_executes_same in successful + # ways, so here we just check that it fails. + self.assertRaises(AssertionError, self.cmd_executes_same, "-e", "-c") + + +class ClassicCmdLineTest(CmdLineTest): + """Tests of the classic coverage.py command line.""" + + def test_erase(self): + # coverage -e + self.cmd_executes("-e", """\ + .coverage(cover_pylib=None, data_suffix=None, timid=None, branch=None, config_file=True, source=None, include=None, omit=None) + .erase() + """) + self.cmd_executes_same("-e", "--erase") + + def test_execute(self): + # coverage -x [-p] [-L] [--timid] MODULE.py [ARG1 ARG2 ...] + + # -x calls coverage.load first. + self.cmd_executes("-x foo.py", """\ + .coverage(cover_pylib=None, data_suffix=None, timid=None, branch=None, config_file=True, source=None, include=None, omit=None) + .load() + .start() + .run_python_file('foo.py', ['foo.py']) + .stop() + .save() + """) + # -e -x calls coverage.erase first. + self.cmd_executes("-e -x foo.py", """\ + .coverage(cover_pylib=None, data_suffix=None, timid=None, branch=None, config_file=True, source=None, include=None, omit=None) + .erase() + .start() + .run_python_file('foo.py', ['foo.py']) + .stop() + .save() + """) + # --timid sets a flag, and program arguments get passed through. + self.cmd_executes("-x --timid foo.py abc 123", """\ + .coverage(cover_pylib=None, data_suffix=None, timid=True, branch=None, config_file=True, source=None, include=None, omit=None) + .load() + .start() + .run_python_file('foo.py', ['foo.py', 'abc', '123']) + .stop() + .save() + """) + # -L sets a flag, and flags for the program don't confuse us. + self.cmd_executes("-x -p -L foo.py -a -b", """\ + .coverage(cover_pylib=True, data_suffix=True, timid=None, branch=None, config_file=True, source=None, include=None, omit=None) + .load() + .start() + .run_python_file('foo.py', ['foo.py', '-a', '-b']) + .stop() + .save() + """) + + # Check that long forms of flags do the same thing as short forms. + self.cmd_executes_same("-x f.py", "--execute f.py") + self.cmd_executes_same("-e -x f.py", "--erase --execute f.py") + self.cmd_executes_same("-x -p f.py", "-x --parallel-mode f.py") + self.cmd_executes_same("-x -L f.py", "-x --pylib f.py") + + def test_combine(self): + # coverage -c + self.cmd_executes("-c", """\ + .coverage(cover_pylib=None, data_suffix=None, timid=None, branch=None, config_file=True, source=None, include=None, omit=None) + .load() + .combine() + .save() + """) + self.cmd_executes_same("-c", "--combine") + + def test_report(self): + # coverage -r [-m] [-i] [-o DIR,...] [FILE1 FILE2 ...] + self.cmd_executes("-r", self.INIT_LOAD + """\ + .report(ignore_errors=None, omit=None, include=None, morfs=[], + show_missing=None) + """) + self.cmd_executes("-r -i", self.INIT_LOAD + """\ + .report(ignore_errors=True, omit=None, include=None, morfs=[], + show_missing=None) + """) + self.cmd_executes("-r -m", self.INIT_LOAD + """\ + .report(ignore_errors=None, omit=None, include=None, morfs=[], + show_missing=True) + """) + self.cmd_executes("-r -o fooey", """\ + .coverage(cover_pylib=None, data_suffix=None, timid=None, branch=None, config_file=True, source=None, include=None, omit=["fooey"]) + .load() + .report(ignore_errors=None, omit=["fooey"], include=None, + morfs=[], show_missing=None) + """) + self.cmd_executes("-r -o fooey,booey", """\ + .coverage(cover_pylib=None, data_suffix=None, timid=None, branch=None, config_file=True, source=None, include=None, omit=["fooey", "booey"]) + .load() + .report(ignore_errors=None, omit=["fooey", "booey"], include=None, + morfs=[], show_missing=None) + """) + self.cmd_executes("-r mod1", self.INIT_LOAD + """\ + .report(ignore_errors=None, omit=None, include=None, + morfs=["mod1"], show_missing=None) + """) + self.cmd_executes("-r mod1 mod2 mod3", self.INIT_LOAD + """\ + .report(ignore_errors=None, omit=None, include=None, + morfs=["mod1", "mod2", "mod3"], show_missing=None) + """) + + self.cmd_executes_same("-r", "--report") + self.cmd_executes_same("-r -i", "-r --ignore-errors") + self.cmd_executes_same("-r -m", "-r --show-missing") + self.cmd_executes_same("-r -o f", "-r --omit=f") + self.cmd_executes_same("-r -o f", "-r --omit f") + self.cmd_executes_same("-r -o f,b", "-r --omit=f,b") + self.cmd_executes_same("-r -o f,b", "-r --omit f,b") + self.cmd_executes_same("-r -of", "-r --omit=f") + self.cmd_executes_same("-r -of,b", "-r --omit=f,b") + + def test_annotate(self): + # coverage -a [-d DIR] [-i] [-o DIR,...] [FILE1 FILE2 ...] + self.cmd_executes("-a", self.INIT_LOAD + """\ + .annotate(directory=None, ignore_errors=None, + omit=None, include=None, morfs=[]) + """) + self.cmd_executes("-a -d dir1", self.INIT_LOAD + """\ + .annotate(directory="dir1", ignore_errors=None, + omit=None, include=None, morfs=[]) + """) + self.cmd_executes("-a -i", self.INIT_LOAD + """\ + .annotate(directory=None, ignore_errors=True, + omit=None, include=None, morfs=[]) + """) + self.cmd_executes("-a -o fooey", """\ + .coverage(cover_pylib=None, data_suffix=None, timid=None, branch=None, config_file=True, source=None, include=None, omit=["fooey"]) + .load() + .annotate(directory=None, ignore_errors=None, + omit=["fooey"], include=None, morfs=[]) + """) + self.cmd_executes("-a -o fooey,booey", """\ + .coverage(cover_pylib=None, data_suffix=None, timid=None, branch=None, config_file=True, source=None, include=None, omit=["fooey", "booey"]) + .load() + .annotate(directory=None, ignore_errors=None, + omit=["fooey", "booey"], include=None, morfs=[]) + """) + self.cmd_executes("-a mod1", self.INIT_LOAD + """\ + .annotate(directory=None, ignore_errors=None, + omit=None, include=None, morfs=["mod1"]) + """) + self.cmd_executes("-a mod1 mod2 mod3", self.INIT_LOAD + """\ + .annotate(directory=None, ignore_errors=None, + omit=None, include=None, morfs=["mod1", "mod2", "mod3"]) + """) + + self.cmd_executes_same("-a", "--annotate") + self.cmd_executes_same("-a -d d1", "-a --directory=d1") + self.cmd_executes_same("-a -i", "-a --ignore-errors") + self.cmd_executes_same("-a -o f", "-a --omit=f") + self.cmd_executes_same("-a -o f", "-a --omit f") + self.cmd_executes_same("-a -o f,b", "-a --omit=f,b") + self.cmd_executes_same("-a -o f,b", "-a --omit f,b") + self.cmd_executes_same("-a -of", "-a --omit=f") + self.cmd_executes_same("-a -of,b", "-a --omit=f,b") + + def test_html_report(self): + # coverage -b -d DIR [-i] [-o DIR,...] [FILE1 FILE2 ...] + self.cmd_executes("-b", self.INIT_LOAD + """\ + .html_report(directory=None, ignore_errors=None, title=None, + omit=None, include=None, morfs=[]) + """) + self.cmd_executes("-b -d dir1", self.INIT_LOAD + """\ + .html_report(directory="dir1", ignore_errors=None, title=None, + omit=None, include=None, morfs=[]) + """) + self.cmd_executes("-b -i", self.INIT_LOAD + """\ + .html_report(directory=None, ignore_errors=True, title=None, + omit=None, include=None, morfs=[]) + """) + self.cmd_executes("-b -o fooey", """\ + .coverage(cover_pylib=None, data_suffix=None, timid=None, branch=None, config_file=True, source=None, include=None, omit=["fooey"]) + .load() + .html_report(directory=None, ignore_errors=None, title=None, + omit=["fooey"], include=None, morfs=[]) + """) + self.cmd_executes("-b -o fooey,booey", """\ + .coverage(cover_pylib=None, data_suffix=None, timid=None, branch=None, config_file=True, source=None, include=None, omit=["fooey", "booey"]) + .load() + .html_report(directory=None, ignore_errors=None, title=None, + omit=["fooey", "booey"], include=None, morfs=[]) + """) + self.cmd_executes("-b mod1", self.INIT_LOAD + """\ + .html_report(directory=None, ignore_errors=None, title=None, + omit=None, include=None, morfs=["mod1"]) + """) + self.cmd_executes("-b mod1 mod2 mod3", self.INIT_LOAD + """\ + .html_report(directory=None, ignore_errors=None, title=None, + omit=None, include=None, morfs=["mod1", "mod2", "mod3"]) + """) + + self.cmd_executes_same("-b", "--html") + self.cmd_executes_same("-b -d d1", "-b --directory=d1") + self.cmd_executes_same("-b -i", "-b --ignore-errors") + self.cmd_executes_same("-b -o f", "-b --omit=f") + self.cmd_executes_same("-b -o f,b", "-b --omit=f,b") + self.cmd_executes_same("-b -of", "-b --omit=f") + self.cmd_executes_same("-b -of,b", "-b --omit=f,b") + + def test_help(self): + # coverage -h + self.cmd_help("-h", topic="help", ret=OK) + self.cmd_help("--help", topic="help", ret=OK) + + def test_version(self): + # coverage --version + self.cmd_help("--version", topic="version", ret=OK) + + ## Error cases + + def test_argless_actions(self): + self.cmd_help("-e foo bar", "Unexpected arguments: foo bar") + self.cmd_help("-c baz quux", "Unexpected arguments: baz quux") + + def test_need_action(self): + self.cmd_help("-p", "You must specify at least one of " + "-e, -x, -c, -r, -a, or -b.") + + def test_bad_action_combinations(self): + self.cmd_help('-e -a', + "You can't specify the 'erase' and 'annotate' " + "options at the same time." + ) + self.cmd_help('-e -r', + "You can't specify the 'erase' and 'report' " + "options at the same time." + ) + self.cmd_help('-e -b', + "You can't specify the 'erase' and 'html' " + "options at the same time." + ) + self.cmd_help('-e -c', + "You can't specify the 'erase' and 'combine' " + "options at the same time." + ) + self.cmd_help('-x -a', + "You can't specify the 'execute' and 'annotate' " + "options at the same time." + ) + self.cmd_help('-x -r', + "You can't specify the 'execute' and 'report' " + "options at the same time." + ) + self.cmd_help('-x -b', + "You can't specify the 'execute' and 'html' " + "options at the same time." + ) + self.cmd_help('-x -c', + "You can't specify the 'execute' and 'combine' " + "options at the same time." + ) + + def test_nothing_to_do(self): + self.cmd_help("-x", "Nothing to do.") + + def test_unknown_option(self): + self.cmd_help("-z", "no such option: -z") + + +class FakeCoverageForDebugData(object): + """Just enough of a fake coverage package for the 'debug data' tests.""" + def __init__(self, summary): + self._summary = summary + 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=W0613 + """Fake coverage().data.summary()""" + return self._summary + + +class NewCmdLineTest(CmdLineTest): + """Tests of the coverage.py command line.""" + + def test_annotate(self): + self.cmd_executes_same("annotate", "-a") + self.cmd_executes_same("annotate -i", "-a -i") + self.cmd_executes_same("annotate -d d1", "-a -d d1") + self.cmd_executes_same("annotate --omit f", "-a --omit f") + self.cmd_executes_same("annotate --omit f,b", "-a --omit f,b") + self.cmd_executes_same("annotate m1", "-a m1") + self.cmd_executes_same("annotate m1 m2 m3", "-a m1 m2 m3") + + def test_combine(self): + self.cmd_executes_same("combine", "-c") + + def test_debug(self): + 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({ + 'file1.py': 17, 'file2.py': 23, + }) + 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 + file2.py: 23 lines + """)) + + def test_debug_data_with_no_data(self): + fake = FakeCoverageForDebugData({}) + 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() + assert "version:" in out + assert "data_path:" in out + + def test_erase(self): + self.cmd_executes_same("erase", "-e") + + def test_help(self): + self.cmd_executes("help", ".help_fn(topic='help')") + + def test_cmd_help(self): + self.cmd_executes("run --help", + ".help_fn(parser='')") + self.cmd_executes_same("help run", "run --help") + + def test_html(self): + self.cmd_executes_same("html", "-b") + self.cmd_executes_same("html -i", "-b -i") + self.cmd_executes_same("html -d d1", "-b -d d1") + self.cmd_executes_same("html --omit f", "-b --omit f") + self.cmd_executes_same("html --omit f,b", "-b --omit f,b") + self.cmd_executes_same("html m1", "-b m1") + self.cmd_executes_same("html m1 m2 m3", "-b m1 m2 m3") + self.cmd_executes("html", self.INIT_LOAD + """\ + .html_report(ignore_errors=None, omit=None, include=None, morfs=[], + directory=None, title=None) + """) + self.cmd_executes("html --title=Hello_there", self.INIT_LOAD + """\ + .html_report(ignore_errors=None, omit=None, include=None, morfs=[], + directory=None, title='Hello_there') + """) + + def test_report(self): + self.cmd_executes_same("report", "-r") + self.cmd_executes_same("report -i", "-r -i") + self.cmd_executes_same("report -m", "-r -m") + self.cmd_executes_same("report --omit f", "-r --omit f") + self.cmd_executes_same("report --omit f,b", "-r --omit f,b") + self.cmd_executes_same("report m1", "-r m1") + self.cmd_executes_same("report m1 m2 m3", "-r m1 m2 m3") + + def test_run(self): + self.cmd_executes_same("run f.py", "-e -x f.py") + self.cmd_executes_same("run f.py -a arg -z", "-e -x f.py -a arg -z") + self.cmd_executes_same("run -a f.py", "-x f.py") + self.cmd_executes_same("run -p f.py", "-e -x -p f.py") + self.cmd_executes_same("run -L f.py", "-e -x -L f.py") + self.cmd_executes_same("run --timid f.py", "-e -x --timid f.py") + self.cmd_executes_same("run", "-x") + self.cmd_executes("run --branch foo.py", """\ + .coverage(cover_pylib=None, data_suffix=None, timid=None, branch=True, config_file=True, source=None, include=None, omit=None) + .erase() + .start() + .run_python_file('foo.py', ['foo.py']) + .stop() + .save() + """) + self.cmd_executes("run --rcfile=myrc.rc foo.py", """\ + .coverage(cover_pylib=None, data_suffix=None, timid=None, branch=None, config_file="myrc.rc", source=None, include=None, omit=None) + .erase() + .start() + .run_python_file('foo.py', ['foo.py']) + .stop() + .save() + """) + self.cmd_executes("run --include=pre1,pre2 foo.py", """\ + .coverage(cover_pylib=None, data_suffix=None, timid=None, branch=None, config_file=True, source=None, include=["pre1", "pre2"], omit=None) + .erase() + .start() + .run_python_file('foo.py', ['foo.py']) + .stop() + .save() + """) + self.cmd_executes("run --omit=opre1,opre2 foo.py", """\ + .coverage(cover_pylib=None, data_suffix=None, timid=None, branch=None, config_file=True, source=None, include=None, omit=["opre1", "opre2"]) + .erase() + .start() + .run_python_file('foo.py', ['foo.py']) + .stop() + .save() + """) + self.cmd_executes("run --include=pre1,pre2 --omit=opre1,opre2 foo.py", + """\ + .coverage(cover_pylib=None, data_suffix=None, timid=None, + branch=None, config_file=True, source=None, + include=["pre1", "pre2"], + omit=["opre1", "opre2"]) + .erase() + .start() + .run_python_file('foo.py', ['foo.py']) + .stop() + .save() + """) + self.cmd_executes("run --source=quux,hi.there,/home/bar foo.py", + """\ + .coverage(cover_pylib=None, data_suffix=None, timid=None, + branch=None, config_file=True, + source=["quux", "hi.there", "/home/bar"], include=None, + omit=None) + .erase() + .start() + .run_python_file('foo.py', ['foo.py']) + .stop() + .save() + """) + + def test_run_module(self): + self.cmd_executes("run -m mymodule", """\ + .coverage(cover_pylib=None, data_suffix=None, timid=None, branch=None, config_file=True, source=None, include=None, omit=None) + .erase() + .start() + .run_python_module('mymodule', ['mymodule']) + .stop() + .save() + """) + self.cmd_executes("run -m mymodule -qq arg1 arg2", """\ + .coverage(cover_pylib=None, data_suffix=None, timid=None, branch=None, config_file=True, source=None, include=None, omit=None) + .erase() + .start() + .run_python_module('mymodule', ['mymodule', '-qq', 'arg1', 'arg2']) + .stop() + .save() + """) + self.cmd_executes("run --branch -m mymodule", """\ + .coverage(cover_pylib=None, data_suffix=None, timid=None, branch=True, config_file=True, source=None, include=None, omit=None) + .erase() + .start() + .run_python_module('mymodule', ['mymodule']) + .stop() + .save() + """) + self.cmd_executes_same("run -m mymodule", "run --module mymodule") + + def test_xml(self): + # coverage xml [-i] [--omit DIR,...] [FILE1 FILE2 ...] + self.cmd_executes("xml", self.INIT_LOAD + """\ + .xml_report(ignore_errors=None, omit=None, include=None, morfs=[], + outfile=None) + """) + self.cmd_executes("xml -i", self.INIT_LOAD + """\ + .xml_report(ignore_errors=True, omit=None, include=None, morfs=[], + outfile=None) + """) + self.cmd_executes("xml -o myxml.foo", self.INIT_LOAD + """\ + .xml_report(ignore_errors=None, omit=None, include=None, morfs=[], + outfile="myxml.foo") + """) + self.cmd_executes("xml -o -", self.INIT_LOAD + """\ + .xml_report(ignore_errors=None, omit=None, include=None, morfs=[], + outfile="-") + """) + self.cmd_executes("xml --omit fooey", """\ + .coverage(cover_pylib=None, data_suffix=None, timid=None, branch=None, config_file=True, source=None, include=None, omit=["fooey"]) + .load() + .xml_report(ignore_errors=None, omit=["fooey"], include=None, morfs=[], + outfile=None) + """) + self.cmd_executes("xml --omit fooey,booey", """\ + .coverage(cover_pylib=None, data_suffix=None, timid=None, branch=None, config_file=True, source=None, include=None, omit=["fooey", "booey"]) + .load() + .xml_report(ignore_errors=None, omit=["fooey", "booey"], include=None, + morfs=[], outfile=None) + """) + self.cmd_executes("xml mod1", self.INIT_LOAD + """\ + .xml_report(ignore_errors=None, omit=None, include=None, morfs=["mod1"], + outfile=None) + """) + self.cmd_executes("xml mod1 mod2 mod3", self.INIT_LOAD + """\ + .xml_report(ignore_errors=None, omit=None, include=None, + morfs=["mod1", "mod2", "mod3"], outfile=None) + """) + + def test_no_arguments_at_all(self): + self.cmd_help("", topic="minimum_help", ret=OK) + + def test_bad_command(self): + self.cmd_help("xyzzy", "Unknown command: 'xyzzy'") + + +class CmdLineStdoutTest(CmdLineTest): + """Test the command line with real stdout output.""" + + def test_minimum_help(self): + self.command_line("") + out = self.stdout() + assert "Code coverage for Python." in out + assert out.count("\n") < 4 + + def test_version(self): + self.command_line("--version") + out = self.stdout() + assert "ersion " in out + assert out.count("\n") < 4 + + def test_help(self): + self.command_line("help") + out = self.stdout() + assert "nedbatchelder.com" in out + assert out.count("\n") > 10 + + def test_cmd_help(self): + self.command_line("help run") + out = self.stdout() + assert "" in out + assert "--timid" in out + assert out.count("\n") > 10 + + def test_error(self): + self.command_line("fooey kablooey", ret=ERR) + out = self.stdout() + assert "fooey" in out + assert "help" in out + + +class CmdMainTest(CoverageTest): + """Tests of coverage.cmdline.main(), using mocking for isolation.""" + + class CoverageScriptStub(object): + """A stub for coverage.cmdline.CoverageScript, used by CmdMainTest.""" + + def command_line(self, argv): + """Stub for command_line, the arg determines what it will do.""" + if argv[0] == 'hello': + print("Hello, world!") + elif argv[0] == 'raise': + try: + raise Exception("oh noes!") + except: + raise ExceptionDuringRun(*sys.exc_info()) + elif argv[0] == 'internalraise': + raise ValueError("coverage is broken") + elif argv[0] == 'exit': + sys.exit(23) + else: + raise AssertionError("Bad CoverageScriptStub: %r"% (argv,)) + return 0 + + def setUp(self): + super(CmdMainTest, self).setUp() + self.old_CoverageScript = coverage.cmdline.CoverageScript + coverage.cmdline.CoverageScript = self.CoverageScriptStub + + def tearDown(self): + coverage.cmdline.CoverageScript = self.old_CoverageScript + super(CmdMainTest, self).tearDown() + + def test_normal(self): + ret = coverage.cmdline.main(['hello']) + self.assertEqual(ret, 0) + self.assertEqual(self.stdout(), "Hello, world!\n") + + def test_raise(self): + ret = coverage.cmdline.main(['raise']) + self.assertEqual(ret, 1) + self.assertEqual(self.stdout(), "") + err = self.stderr().split('\n') + self.assertEqual(err[0], 'Traceback (most recent call last):') + self.assertEqual(err[-3], ' raise Exception("oh noes!")') + self.assertEqual(err[-2], 'Exception: oh noes!') + + def test_internalraise(self): + self.assertRaisesRegexp(ValueError, + "coverage is broken", + coverage.cmdline.main, ['internalraise'] + ) + + def test_exit(self): + ret = coverage.cmdline.main(['exit']) + self.assertEqual(ret, 23) diff -Nru python-coverage-3.4/test/test_codeunit.py python-coverage-3.6/test/test_codeunit.py --- python-coverage-3.4/test/test_codeunit.py 1970-01-01 00:00:00.000000000 +0000 +++ python-coverage-3.6/test/test_codeunit.py 2012-12-30 15:17:29.000000000 +0000 @@ -0,0 +1,103 @@ +"""Tests for coverage.codeunit""" + +import os, sys + +from coverage.codeunit import code_unit_factory +from coverage.files import FileLocator + +from test.coveragetest import CoverageTest + +# pylint: disable=F0401 +# Unable to import 'aa' (No module named aa) + +class CodeUnitTest(CoverageTest): + """Tests for coverage.codeunit""" + + run_in_temp_dir = False + + def setUp(self): + super(CodeUnitTest, self).setUp() + # Parent class saves and restores sys.path, we can just modify it. + testmods = self.nice_file(os.path.dirname(__file__), 'modules') + sys.path.append(testmods) + + def test_filenames(self): + acu = code_unit_factory("aa/afile.py", FileLocator()) + bcu = code_unit_factory("aa/bb/bfile.py", FileLocator()) + ccu = code_unit_factory("aa/bb/cc/cfile.py", FileLocator()) + self.assertEqual(acu[0].name, "aa/afile") + self.assertEqual(bcu[0].name, "aa/bb/bfile") + self.assertEqual(ccu[0].name, "aa/bb/cc/cfile") + self.assertEqual(acu[0].flat_rootname(), "aa_afile") + self.assertEqual(bcu[0].flat_rootname(), "aa_bb_bfile") + self.assertEqual(ccu[0].flat_rootname(), "aa_bb_cc_cfile") + self.assertEqual(acu[0].source_file().read(), "# afile.py\n") + self.assertEqual(bcu[0].source_file().read(), "# bfile.py\n") + self.assertEqual(ccu[0].source_file().read(), "# cfile.py\n") + + def test_odd_filenames(self): + acu = code_unit_factory("aa/afile.odd.py", FileLocator()) + bcu = code_unit_factory("aa/bb/bfile.odd.py", FileLocator()) + b2cu = code_unit_factory("aa/bb.odd/bfile.py", FileLocator()) + self.assertEqual(acu[0].name, "aa/afile.odd") + self.assertEqual(bcu[0].name, "aa/bb/bfile.odd") + self.assertEqual(b2cu[0].name, "aa/bb.odd/bfile") + self.assertEqual(acu[0].flat_rootname(), "aa_afile_odd") + self.assertEqual(bcu[0].flat_rootname(), "aa_bb_bfile_odd") + self.assertEqual(b2cu[0].flat_rootname(), "aa_bb_odd_bfile") + self.assertEqual(acu[0].source_file().read(), "# afile.odd.py\n") + self.assertEqual(bcu[0].source_file().read(), "# bfile.odd.py\n") + self.assertEqual(b2cu[0].source_file().read(), "# bfile.py\n") + + def test_modules(self): + import aa, aa.bb, aa.bb.cc + cu = code_unit_factory([aa, aa.bb, aa.bb.cc], FileLocator()) + self.assertEqual(cu[0].name, "aa") + self.assertEqual(cu[1].name, "aa.bb") + self.assertEqual(cu[2].name, "aa.bb.cc") + self.assertEqual(cu[0].flat_rootname(), "aa") + self.assertEqual(cu[1].flat_rootname(), "aa_bb") + self.assertEqual(cu[2].flat_rootname(), "aa_bb_cc") + self.assertEqual(cu[0].source_file().read(), "# aa\n") + self.assertEqual(cu[1].source_file().read(), "# bb\n") + self.assertEqual(cu[2].source_file().read(), "") # yes, empty + + def test_module_files(self): + import aa.afile, aa.bb.bfile, aa.bb.cc.cfile + cu = code_unit_factory([aa.afile, aa.bb.bfile, aa.bb.cc.cfile], + FileLocator()) + self.assertEqual(cu[0].name, "aa.afile") + self.assertEqual(cu[1].name, "aa.bb.bfile") + self.assertEqual(cu[2].name, "aa.bb.cc.cfile") + self.assertEqual(cu[0].flat_rootname(), "aa_afile") + self.assertEqual(cu[1].flat_rootname(), "aa_bb_bfile") + self.assertEqual(cu[2].flat_rootname(), "aa_bb_cc_cfile") + self.assertEqual(cu[0].source_file().read(), "# afile.py\n") + self.assertEqual(cu[1].source_file().read(), "# bfile.py\n") + self.assertEqual(cu[2].source_file().read(), "# cfile.py\n") + + def test_comparison(self): + acu = code_unit_factory("aa/afile.py", FileLocator())[0] + acu2 = code_unit_factory("aa/afile.py", FileLocator())[0] + zcu = code_unit_factory("aa/zfile.py", FileLocator())[0] + bcu = code_unit_factory("aa/bb/bfile.py", FileLocator())[0] + assert acu == acu2 and acu <= acu2 and acu >= acu2 + assert acu < zcu and acu <= zcu and acu != zcu + assert zcu > acu and zcu >= acu and zcu != acu + assert acu < bcu and acu <= bcu and acu != bcu + assert bcu > acu and bcu >= acu and bcu != acu + + def test_egg(self): + # Test that we can get files out of eggs, and read their source files. + # The egg1 module is installed by an action in igor.py. + import egg1, egg1.egg1 + # Verify that we really imported from an egg. If we did, then the + # __file__ won't be an actual file, because one of the "directories" + # in the path is actually the .egg zip file. + self.assert_doesnt_exist(egg1.__file__) + + cu = code_unit_factory([egg1, egg1.egg1], FileLocator()) + self.assertEqual(cu[0].source_file().read(), "") + self.assertEqual(cu[1].source_file().read().split("\n")[0], + "# My egg file!" + ) diff -Nru python-coverage-3.4/test/test_config.py python-coverage-3.6/test/test_config.py --- python-coverage-3.4/test/test_config.py 1970-01-01 00:00:00.000000000 +0000 +++ python-coverage-3.6/test/test_config.py 2012-12-30 15:17:29.000000000 +0000 @@ -0,0 +1,225 @@ +# -*- coding: utf-8 -*- +"""Test the config file handling for coverage.py""" + +import sys +import coverage +from coverage.misc import CoverageException + +from test.coveragetest import CoverageTest + + +class ConfigTest(CoverageTest): + """Tests of the different sources of configuration settings.""" + + def test_default_config(self): + # Just constructing a coverage() object gets the right defaults. + 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 configuation. + 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") + + def test_config_file(self): + # A .coveragerc file will be read into the configuration. + self.make_file(".coveragerc", """\ + # This is just a bogus .rc file for testing. + [run] + timid = True + data_file = .hello_kitty.data + """) + cov = coverage.coverage() + self.assertTrue(cov.config.timid) + self.assertFalse(cov.config.branch) + self.assertEqual(cov.config.data_file, ".hello_kitty.data") + + def test_named_config_file(self): + # You can name the config file what you like. + self.make_file("my_cov.ini", """\ + [run] + timid = True + ; I wouldn't really use this as a data file... + data_file = delete.me + """) + 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") + + def test_ignored_config_file(self): + # You can disable reading the .coveragerc file. + self.make_file(".coveragerc", """\ + [run] + timid = True + data_file = delete.me + """) + cov = coverage.coverage(config_file=False) + self.assertFalse(cov.config.timid) + self.assertFalse(cov.config.branch) + self.assertEqual(cov.config.data_file, ".coverage") + + def test_config_file_then_args(self): + # The arguments override the .coveragerc file. + self.make_file(".coveragerc", """\ + [run] + timid = True + data_file = weirdo.file + """) + 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") + + def test_data_file_from_environment(self): + # There's an environment variable for the data_file. + self.make_file(".coveragerc", """\ + [run] + timid = True + data_file = weirdo.file + """) + self.set_environ("COVERAGE_FILE", "fromenv.dat") + cov = coverage.coverage() + self.assertEqual(cov.config.data_file, "fromenv.dat") + # But the constructor args override the env var. + cov = coverage.coverage(data_file="fromarg.dat") + self.assertEqual(cov.config.data_file, "fromarg.dat") + + def test_parse_errors(self): + # Im-parseable values raise CoverageException + self.make_file(".coveragerc", """\ + [run] + timid = maybe? + """) + self.assertRaises(CoverageException, coverage.coverage) + + def test_environment_vars_in_config(self): + # Config files can have $envvars in them. + self.make_file(".coveragerc", """\ + [run] + data_file = $DATA_FILE.fooey + branch = $OKAY + [report] + exclude_lines = + the_$$one + another${THING} + x${THING}y + x${NOTHING}y + huh$${X}what + """) + self.set_environ("DATA_FILE", "hello-world") + self.set_environ("THING", "ZZZ") + self.set_environ("OKAY", "yes") + cov = coverage.coverage() + self.assertEqual(cov.config.data_file, "hello-world.fooey") + self.assertEqual(cov.config.branch, True) + self.assertEqual(cov.config.exclude_list, + ["the_$one", "anotherZZZ", "xZZZy", "xy", "huh${X}what"] + ) + + +class ConfigFileTest(CoverageTest): + """Tests of the config file settings in particular.""" + + def test_config_file_settings(self): + # This sample file tries to use lots of variation of syntax... + self.make_file(".coveragerc", """\ + # This is a settings file for coverage.py + [run] + timid = yes + data_file = something_or_other.dat + branch = 1 + cover_pylib = TRUE + parallel = on + include = a/ , b/ + + [report] + ; these settings affect reporting. + exclude_lines = + if 0: + + pragma:?\\s+no cover + another_tab + + ignore_errors = TRUE + omit = + one, another, some_more, + yet_more + precision = 3 + + partial_branches = + pragma:?\\s+no branch + partial_branches_always = + if 0: + while True: + + show_missing= TruE + + [html] + + directory = c:\\tricky\\dir.somewhere + extra_css=something/extra.css + title = Title & nums # nums! + [xml] + output=mycov.xml + + [paths] + source = + . + /home/ned/src/ + + other = other, /home/ned/other, c:\\Ned\\etc + + """) + cov = coverage.coverage() + + self.assertTrue(cov.config.timid) + self.assertEqual(cov.config.data_file, "something_or_other.dat") + self.assertTrue(cov.config.branch) + self.assertTrue(cov.config.cover_pylib) + self.assertTrue(cov.config.parallel) + + 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.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.assertTrue(cov.config.show_missing) + self.assertEqual(cov.config.html_dir, r"c:\tricky\dir.somewhere") + self.assertEqual(cov.config.extra_css, "something/extra.css") + self.assertEqual(cov.config.html_title, "Title & nums # nums!") + + self.assertEqual(cov.config.xml_output, "mycov.xml") + + self.assertEqual(cov.config.paths, { + 'source': ['.', '/home/ned/src/'], + 'other': ['other', '/home/ned/other', 'c:\\Ned\\etc'] + }) + + if sys.version_info[:2] != (3,1): + def test_one(self): + # This sample file tries to use lots of variation of syntax... + self.make_file(".coveragerc", """\ + [html] + title = tabblo & «ταБЬℓσ» # numbers + """) + cov = coverage.coverage() + + self.assertEqual(cov.config.html_title, + "tabblo & «ταБЬℓσ» # numbers" + ) diff -Nru python-coverage-3.4/test/test_coverage.py python-coverage-3.6/test/test_coverage.py --- python-coverage-3.4/test/test_coverage.py 1970-01-01 00:00:00.000000000 +0000 +++ python-coverage-3.6/test/test_coverage.py 2013-01-02 01:44:20.000000000 +0000 @@ -0,0 +1,1730 @@ +"""Tests for Coverage.""" +# http://nedbatchelder.com/code/coverage + +import sys +import coverage +from coverage.misc import CoverageException +from test.coveragetest import CoverageTest + + +class TestCoverageTest(CoverageTest): + """Make sure our complex self.check_coverage method works.""" + + def test_successful_coverage(self): + # The simplest run possible. + self.check_coverage("""\ + a = 1 + b = 2 + """, + [1,2] + ) + # You can provide a list of possible statement matches. + self.check_coverage("""\ + a = 1 + b = 2 + """, + ([100], [1,2], [1723,47]), + ) + # You can specify missing lines. + self.check_coverage("""\ + a = 1 + if a == 2: + a = 3 + """, + [1,2,3], + missing="3", + ) + # You can specify a list of possible missing lines. + self.check_coverage("""\ + a = 1 + if a == 2: + a = 3 + """, + [1,2,3], + missing=("47-49", "3", "100,102") + ) + + def test_failed_coverage(self): + # If the lines are wrong, the message shows right and wrong. + self.assertRaisesRegexp(AssertionError, + r"\[1, 2] != \[1]", + self.check_coverage, """\ + a = 1 + b = 2 + """, + [1] + ) + # If the list of lines possibilities is wrong, the msg shows right. + self.assertRaisesRegexp(AssertionError, + r"None of the lines choices matched \[1, 2]", + self.check_coverage, """\ + a = 1 + b = 2 + """, + ([1], [2]) + ) + # If the missing lines are wrong, the message shows right and wrong. + self.assertRaisesRegexp(AssertionError, + r"'3' != '37'", + self.check_coverage, """\ + a = 1 + if a == 2: + a = 3 + """, + [1,2,3], + missing="37", + ) + # If the missing lines possibilities are wrong, the msg shows right. + self.assertRaisesRegexp(AssertionError, + r"None of the missing choices matched '3'", + self.check_coverage, """\ + a = 1 + if a == 2: + a = 3 + """, + [1,2,3], + missing=("37", "4-10"), + ) + + +class BasicCoverageTest(CoverageTest): + """The simplest tests, for quick smoke testing of fundamental changes.""" + + def test_simple(self): + self.check_coverage("""\ + a = 1 + b = 2 + + c = 4 + # Nothing here + d = 6 + """, + [1,2,4,6], report="4 0 100%") + + def test_indentation_wackiness(self): + # Partial final lines are OK. + self.check_coverage("""\ + import sys + if not sys.path: + a = 1 + """, + [1,2,3], "3") + + def test_multiline_initializer(self): + self.check_coverage("""\ + d = { + 'foo': 1+2, + 'bar': (lambda x: x+1)(1), + 'baz': str(1), + } + + e = { 'foo': 1, 'bar': 2 } + """, + [1,7], "") + + def test_list_comprehension(self): + self.check_coverage("""\ + l = [ + 2*i for i in range(10) + if i > 5 + ] + assert l == [12, 14, 16, 18] + """, + [1,5], "") + + +class SimpleStatementTest(CoverageTest): + """Testing simple single-line statements.""" + + def test_expression(self): + # Bare expressions as statements are tricky: some implementations + # optimize some of them away. All implementations seem to count + # the implicit return at the end as executable. + self.check_coverage("""\ + 12 + 23 + """, + ([1,2],[2]), "") + self.check_coverage("""\ + 12 + 23 + a = 3 + """, + ([1,2,3],[3]), "") + self.check_coverage("""\ + 1 + 2 + 1 + \\ + 2 + """, + ([1,2], [2]), "") + self.check_coverage("""\ + 1 + 2 + 1 + \\ + 2 + a = 4 + """, + ([1,2,4], [4]), "") + + def test_assert(self): + self.check_coverage("""\ + assert (1 + 2) + assert (1 + + 2) + assert (1 + 2), 'the universe is broken' + assert (1 + + 2), \\ + 'something is amiss' + """, + [1,2,4,5], "") + + def test_assignment(self): + # Simple variable assignment + self.check_coverage("""\ + a = (1 + 2) + b = (1 + + 2) + c = \\ + 1 + """, + [1,2,4], "") + + def test_assign_tuple(self): + self.check_coverage("""\ + a = 1 + a,b,c = 7,8,9 + assert a == 7 and b == 8 and c == 9 + """, + [1,2,3], "") + + def test_attribute_assignment(self): + # Attribute assignment + self.check_coverage("""\ + class obj: pass + o = obj() + o.foo = (1 + 2) + o.foo = (1 + + 2) + o.foo = \\ + 1 + """, + [1,2,3,4,6], "") + + def test_list_of_attribute_assignment(self): + self.check_coverage("""\ + class obj: pass + o = obj() + o.a, o.b = (1 + 2), 3 + o.a, o.b = (1 + + 2), (3 + + 4) + o.a, o.b = \\ + 1, \\ + 2 + """, + [1,2,3,4,7], "") + + def test_augmented_assignment(self): + self.check_coverage("""\ + a = 1 + a += 1 + a += (1 + + 2) + a += \\ + 1 + """, + [1,2,3,5], "") + + def test_triple_string_stuff(self): + self.check_coverage("""\ + a = ''' + a multiline + string. + ''' + b = ''' + long expression + ''' + ''' + on many + lines. + ''' + c = len(''' + long expression + ''' + + ''' + on many + lines. + ''') + """, + [1,5,11], "") + + def test_pass(self): + # pass is tricky: if it's the only statement in a block, then it is + # "executed". But if it is not the only statement, then it is not. + self.check_coverage("""\ + if 1==1: + pass + """, + [1,2], "") + self.check_coverage("""\ + def foo(): + pass + foo() + """, + [1,2,3], "") + self.check_coverage("""\ + def foo(): + "doc" + pass + foo() + """, + ([1,3,4], [1,4]), "") + self.check_coverage("""\ + class Foo: + def foo(self): + pass + Foo().foo() + """, + [1,2,3,4], "") + self.check_coverage("""\ + class Foo: + def foo(self): + "Huh?" + pass + Foo().foo() + """, + ([1,2,4,5], [1,2,5]), "") + + def test_del(self): + self.check_coverage("""\ + d = { 'a': 1, 'b': 1, 'c': 1, 'd': 1, 'e': 1 } + del d['a'] + del d[ + 'b' + ] + del d['c'], \\ + d['d'], \\ + d['e'] + assert(len(d.keys()) == 0) + """, + [1,2,3,6,9], "") + + if sys.version_info < (3, 0): # Print statement is gone in Py3k. + def test_print(self): + self.check_coverage("""\ + print "hello, world!" + print ("hey: %d" % + 17) + print "goodbye" + print "hello, world!", + print ("hey: %d" % + 17), + print "goodbye", + """, + [1,2,4,5,6,8], "") + + def test_raise(self): + self.check_coverage("""\ + try: + raise Exception( + "hello %d" % + 17) + except: + pass + """, + [1,2,5,6], "") + + def test_return(self): + self.check_coverage("""\ + def fn(): + a = 1 + return a + + x = fn() + assert(x == 1) + """, + [1,2,3,5,6], "") + self.check_coverage("""\ + def fn(): + a = 1 + return ( + a + + 1) + + x = fn() + assert(x == 2) + """, + [1,2,3,7,8], "") + self.check_coverage("""\ + def fn(): + a = 1 + return (a, + a + 1, + a + 2) + + x,y,z = fn() + assert x == 1 and y == 2 and z == 3 + """, + [1,2,3,7,8], "") + + def test_yield(self): + self.check_coverage("""\ + from __future__ import generators + def gen(): + yield 1 + yield (2+ + 3+ + 4) + yield 1, \\ + 2 + a,b,c = gen() + assert a == 1 and b == 9 and c == (1,2) + """, + [1,2,3,4,7,9,10], "") + + def test_break(self): + self.check_coverage("""\ + for x in range(10): + a = 2 + x + break + a = 4 + assert a == 2 + """, + [1,2,3,4,5], "4") + + def test_continue(self): + self.check_coverage("""\ + for x in range(10): + a = 2 + x + continue + a = 4 + assert a == 11 + """, + [1,2,3,4,5], "4") + + if 0: # expected failure + # Peephole optimization of jumps to jumps can mean that some statements + # never hit the line tracer. The behavior is different in different + # versions of Python, so don't run this test: + def test_strange_unexecuted_continue(self): + self.check_coverage("""\ + a = b = c = 0 + for n in range(100): + if n % 2: + if n % 4: + a += 1 + continue # <-- This line may not be hit. + else: + b += 1 + c += 1 + assert a == 50 and b == 50 and c == 50 + + a = b = c = 0 + for n in range(100): + if n % 2: + if n % 3: + a += 1 + continue # <-- This line is always hit. + else: + b += 1 + c += 1 + assert a == 33 and b == 50 and c == 50 + """, + [1,2,3,4,5,6,8,9,10, 12,13,14,15,16,17,19,20,21], "") + + def test_import(self): + self.check_coverage("""\ + import string + from sys import path + a = 1 + """, + [1,2,3], "") + self.check_coverage("""\ + import string + if 1 == 2: + from sys import path + a = 1 + """, + [1,2,3,4], "3") + self.check_coverage("""\ + import string, \\ + os, \\ + re + from sys import path, \\ + stdout + a = 1 + """, + [1,4,6], "") + self.check_coverage("""\ + import sys, sys as s + assert s.path == sys.path + """, + [1,2], "") + self.check_coverage("""\ + import sys, \\ + sys as s + assert s.path == sys.path + """, + [1,3], "") + self.check_coverage("""\ + from sys import path, \\ + path as p + assert p == path + """, + [1,3], "") + self.check_coverage("""\ + from sys import \\ + * + assert len(path) > 0 + """, + [1,3], "") + + def test_global(self): + self.check_coverage("""\ + g = h = i = 1 + def fn(): + global g + global h, \\ + i + g = h = i = 2 + fn() + assert g == 2 and h == 2 and i == 2 + """, + [1,2,6,7,8], "") + self.check_coverage("""\ + g = h = i = 1 + def fn(): + global g; g = 2 + fn() + assert g == 2 and h == 1 and i == 1 + """, + [1,2,3,4,5], "") + + if sys.version_info < (3, 0): + # In Python 2.x, exec is a statement. + def test_exec(self): + self.check_coverage("""\ + a = b = c = 1 + exec "a = 2" + exec ("b = " + + "c = " + + "2") + assert a == 2 and b == 2 and c == 2 + """, + [1,2,3,6], "") + self.check_coverage("""\ + vars = {'a': 1, 'b': 1, 'c': 1} + exec "a = 2" in vars + exec ("b = " + + "c = " + + "2") in vars + assert vars['a'] == 2 and vars['b'] == 2 and vars['c'] == 2 + """, + [1,2,3,6], "") + self.check_coverage("""\ + globs = {} + locs = {'a': 1, 'b': 1, 'c': 1} + exec "a = 2" in globs, locs + exec ("b = " + + "c = " + + "2") in globs, locs + assert locs['a'] == 2 and locs['b'] == 2 and locs['c'] == 2 + """, + [1,2,3,4,7], "") + else: + # In Python 3.x, exec is a function. + def test_exec(self): + self.check_coverage("""\ + a = b = c = 1 + exec("a = 2") + exec("b = " + + "c = " + + "2") + assert a == 2 and b == 2 and c == 2 + """, + [1,2,3,6], "") + self.check_coverage("""\ + vars = {'a': 1, 'b': 1, 'c': 1} + exec("a = 2", vars) + exec("b = " + + "c = " + + "2", vars) + assert vars['a'] == 2 and vars['b'] == 2 and vars['c'] == 2 + """, + [1,2,3,6], "") + self.check_coverage("""\ + globs = {} + locs = {'a': 1, 'b': 1, 'c': 1} + exec("a = 2", globs, locs) + exec("b = " + + "c = " + + "2", globs, locs) + assert locs['a'] == 2 and locs['b'] == 2 and locs['c'] == 2 + """, + [1,2,3,4,7], "") + + def test_extra_doc_string(self): + self.check_coverage("""\ + a = 1 + "An extra docstring, should be a comment." + b = 3 + assert (a,b) == (1,3) + """, + [1,3,4], "") + self.check_coverage("""\ + a = 1 + "An extra docstring, should be a comment." + b = 3 + 123 # A number for some reason: ignored + 1+1 # An expression: executed. + c = 6 + assert (a,b,c) == (1,3,6) + """, + ([1,3,6,7], [1,3,5,6,7], [1,3,4,5,6,7]), "") + + +class CompoundStatementTest(CoverageTest): + """Testing coverage of multi-line compound statements.""" + + def test_statement_list(self): + self.check_coverage("""\ + a = 1; + b = 2; c = 3 + d = 4; e = 5; + + assert (a,b,c,d,e) == (1,2,3,4,5) + """, + [1,2,3,5], "") + + def test_if(self): + self.check_coverage("""\ + a = 1 + if a == 1: + x = 3 + assert x == 3 + if (a == + 1): + x = 7 + assert x == 7 + """, + [1,2,3,4,5,7,8], "") + self.check_coverage("""\ + a = 1 + if a == 1: + x = 3 + else: + y = 5 + assert x == 3 + """, + [1,2,3,5,6], "5") + self.check_coverage("""\ + a = 1 + if a != 1: + x = 3 + else: + y = 5 + assert y == 5 + """, + [1,2,3,5,6], "3") + self.check_coverage("""\ + a = 1; b = 2 + if a == 1: + if b == 2: + x = 4 + else: + y = 6 + else: + z = 8 + assert x == 4 + """, + [1,2,3,4,6,8,9], "6-8") + + def test_elif(self): + self.check_coverage("""\ + a = 1; b = 2; c = 3; + if a == 1: + x = 3 + elif b == 2: + y = 5 + else: + z = 7 + assert x == 3 + """, + [1,2,3,4,5,7,8], "4-7", report="7 3 57% 4-7") + self.check_coverage("""\ + a = 1; b = 2; c = 3; + if a != 1: + x = 3 + elif b == 2: + y = 5 + else: + z = 7 + assert y == 5 + """, + [1,2,3,4,5,7,8], "3, 7", report="7 2 71% 3, 7") + self.check_coverage("""\ + a = 1; b = 2; c = 3; + if a != 1: + x = 3 + elif b != 2: + y = 5 + else: + z = 7 + assert z == 7 + """, + [1,2,3,4,5,7,8], "3, 5", report="7 2 71% 3, 5") + + def test_elif_no_else(self): + self.check_coverage("""\ + a = 1; b = 2; c = 3; + if a == 1: + x = 3 + elif b == 2: + y = 5 + assert x == 3 + """, + [1,2,3,4,5,6], "4-5", report="6 2 67% 4-5") + self.check_coverage("""\ + a = 1; b = 2; c = 3; + if a != 1: + x = 3 + elif b == 2: + y = 5 + assert y == 5 + """, + [1,2,3,4,5,6], "3", report="6 1 83% 3") + + def test_elif_bizarre(self): + self.check_coverage("""\ + def f(self): + if self==1: + x = 3 + elif self.m('fred'): + x = 5 + elif (g==1) and (b==2): + x = 7 + elif self.m('fred')==True: + x = 9 + elif ((g==1) and (b==2))==True: + x = 11 + else: + x = 13 + """, + [1,2,3,4,5,6,7,8,9,10,11,13], "2-13") + + def test_split_if(self): + self.check_coverage("""\ + a = 1; b = 2; c = 3; + if \\ + a == 1: + x = 3 + elif \\ + b == 2: + y = 5 + else: + z = 7 + assert x == 3 + """, + [1,2,4,5,7,9,10], "5-9") + self.check_coverage("""\ + a = 1; b = 2; c = 3; + if \\ + a != 1: + x = 3 + elif \\ + b == 2: + y = 5 + else: + z = 7 + assert y == 5 + """, + [1,2,4,5,7,9,10], "4, 9") + self.check_coverage("""\ + a = 1; b = 2; c = 3; + if \\ + a != 1: + x = 3 + elif \\ + b != 2: + y = 5 + else: + z = 7 + assert z == 7 + """, + [1,2,4,5,7,9,10], "4, 7") + + def test_pathological_split_if(self): + self.check_coverage("""\ + a = 1; b = 2; c = 3; + if ( + a == 1 + ): + x = 3 + elif ( + b == 2 + ): + y = 5 + else: + z = 7 + assert x == 3 + """, + [1,2,5,6,9,11,12], "6-11") + self.check_coverage("""\ + a = 1; b = 2; c = 3; + if ( + a != 1 + ): + x = 3 + elif ( + b == 2 + ): + y = 5 + else: + z = 7 + assert y == 5 + """, + [1,2,5,6,9,11,12], "5, 11") + self.check_coverage("""\ + a = 1; b = 2; c = 3; + if ( + a != 1 + ): + x = 3 + elif ( + b != 2 + ): + y = 5 + else: + z = 7 + assert z == 7 + """, + [1,2,5,6,9,11,12], "5, 9") + + def test_absurd_split_if(self): + self.check_coverage("""\ + a = 1; b = 2; c = 3; + if a == 1 \\ + : + x = 3 + elif b == 2 \\ + : + y = 5 + else: + z = 7 + assert x == 3 + """, + [1,2,4,5,7,9,10], "5-9") + self.check_coverage("""\ + a = 1; b = 2; c = 3; + if a != 1 \\ + : + x = 3 + elif b == 2 \\ + : + y = 5 + else: + z = 7 + assert y == 5 + """, + [1,2,4,5,7,9,10], "4, 9") + self.check_coverage("""\ + a = 1; b = 2; c = 3; + if a != 1 \\ + : + x = 3 + elif b != 2 \\ + : + y = 5 + else: + z = 7 + assert z == 7 + """, + [1,2,4,5,7,9,10], "4, 7") + + if sys.version_info >= (2, 4): + # In 2.4 and up, constant if's were compiled away. + def test_constant_if(self): + self.check_coverage("""\ + if 1: + a = 2 + assert a == 2 + """, + [2,3], "") + + def test_while(self): + self.check_coverage("""\ + a = 3; b = 0 + while a: + b += 1 + a -= 1 + assert a == 0 and b == 3 + """, + [1,2,3,4,5], "") + self.check_coverage("""\ + a = 3; b = 0 + while a: + b += 1 + break + b = 99 + assert a == 3 and b == 1 + """, + [1,2,3,4,5,6], "5") + + def test_while_else(self): + # Take the else branch. + self.check_coverage("""\ + a = 3; b = 0 + while a: + b += 1 + a -= 1 + else: + b = 99 + assert a == 0 and b == 99 + """, + [1,2,3,4,6,7], "") + # Don't take the else branch. + self.check_coverage("""\ + a = 3; b = 0 + while a: + b += 1 + a -= 1 + break + b = 123 + else: + b = 99 + assert a == 2 and b == 1 + """, + [1,2,3,4,5,6,8,9], "6-8") + + def test_split_while(self): + self.check_coverage("""\ + a = 3; b = 0 + while \\ + a: + b += 1 + a -= 1 + assert a == 0 and b == 3 + """, + [1,2,4,5,6], "") + self.check_coverage("""\ + a = 3; b = 0 + while ( + a + ): + b += 1 + a -= 1 + assert a == 0 and b == 3 + """, + [1,2,5,6,7], "") + + def test_for(self): + self.check_coverage("""\ + a = 0 + for i in [1,2,3,4,5]: + a += i + assert a == 15 + """, + [1,2,3,4], "") + self.check_coverage("""\ + a = 0 + for i in [1, + 2,3,4, + 5]: + a += i + assert a == 15 + """, + [1,2,5,6], "") + self.check_coverage("""\ + a = 0 + for i in [1,2,3,4,5]: + a += i + break + a = 99 + assert a == 1 + """, + [1,2,3,4,5,6], "5") + + def test_for_else(self): + self.check_coverage("""\ + a = 0 + for i in range(5): + a += i+1 + else: + a = 99 + assert a == 99 + """, + [1,2,3,5,6], "") + self.check_coverage("""\ + a = 0 + for i in range(5): + a += i+1 + break + a = 99 + else: + a = 123 + assert a == 1 + """, + [1,2,3,4,5,7,8], "5-7") + + def test_split_for(self): + self.check_coverage("""\ + a = 0 + for \\ + i in [1,2,3,4,5]: + a += i + assert a == 15 + """, + [1,2,4,5], "") + self.check_coverage("""\ + a = 0 + for \\ + i in [1, + 2,3,4, + 5]: + a += i + assert a == 15 + """, + [1,2,6,7], "") + + def test_try_except(self): + self.check_coverage("""\ + a = 0 + try: + a = 1 + except: + a = 99 + assert a == 1 + """, + [1,2,3,4,5,6], "4-5") + self.check_coverage("""\ + a = 0 + try: + a = 1 + raise Exception("foo") + except: + a = 99 + assert a == 99 + """, + [1,2,3,4,5,6,7], "") + self.check_coverage("""\ + a = 0 + try: + a = 1 + raise Exception("foo") + except ImportError: + a = 99 + except: + a = 123 + assert a == 123 + """, + [1,2,3,4,5,6,7,8,9], "6") + self.check_coverage("""\ + a = 0 + try: + a = 1 + raise IOError("foo") + except ImportError: + a = 99 + except IOError: + a = 17 + except: + a = 123 + assert a == 17 + """, + [1,2,3,4,5,6,7,8,9,10,11], "6, 9-10") + self.check_coverage("""\ + a = 0 + try: + a = 1 + except: + a = 99 + else: + a = 123 + assert a == 123 + """, + [1,2,3,4,5,7,8], "4-5") + self.check_coverage("""\ + a = 0 + try: + a = 1 + raise Exception("foo") + except: + a = 99 + else: + a = 123 + assert a == 99 + """, + [1,2,3,4,5,6,8,9], "8") + + def test_try_finally(self): + self.check_coverage("""\ + a = 0 + try: + a = 1 + finally: + a = 99 + assert a == 99 + """, + [1,2,3,5,6], "") + self.check_coverage("""\ + a = 0; b = 0 + try: + a = 1 + try: + raise Exception("foo") + finally: + b = 123 + except: + a = 99 + assert a == 99 and b == 123 + """, + [1,2,3,4,5,7,8,9,10], "") + + def test_function_def(self): + self.check_coverage("""\ + a = 99 + def foo(): + ''' docstring + ''' + return 1 + + a = foo() + assert a == 1 + """, + [1,2,5,7,8], "") + self.check_coverage("""\ + def foo( + a, + b + ): + ''' docstring + ''' + return a+b + + x = foo(17, 23) + assert x == 40 + """, + [1,7,9,10], "") + self.check_coverage("""\ + def foo( + a = (lambda x: x*2)(10), + b = ( + lambda x: + x+1 + )(1) + ): + ''' docstring + ''' + return a+b + + x = foo() + assert x == 22 + """, + [1,10,12,13], "") + + def test_class_def(self): + self.check_coverage("""\ + # A comment. + class theClass: + ''' the docstring. + Don't be fooled. + ''' + def __init__(self): + ''' Another docstring. ''' + self.a = 1 + + def foo(self): + return self.a + + x = theClass().foo() + assert x == 1 + """, + [2,6,8,10,11,13,14], "") + + +class ExcludeTest(CoverageTest): + """Tests of the exclusion feature to mark lines as not covered.""" + + def test_default(self): + # A number of forms of pragma comment are accepted. + self.check_coverage("""\ + a = 1 + b = 2 # pragma: no cover + c = 3 + d = 4 #pragma NOCOVER + e = 5 + """, + [1,3,5] + ) + + def test_simple(self): + self.check_coverage("""\ + a = 1; b = 2 + + if 0: + a = 4 # -cc + """, + [1,3], "", excludes=['-cc']) + + def test_two_excludes(self): + self.check_coverage("""\ + a = 1; b = 2 + + if a == 99: + a = 4 # -cc + b = 5 + c = 6 # -xx + assert a == 1 and b == 2 + """, + [1,3,5,7], "5", excludes=['-cc', '-xx']) + + def test_excluding_if_suite(self): + self.check_coverage("""\ + a = 1; b = 2 + + if 0: + a = 4 + b = 5 + c = 6 + assert a == 1 and b == 2 + """, + [1,7], "", excludes=['if 0:']) + + def test_excluding_if_but_not_else_suite(self): + self.check_coverage("""\ + a = 1; b = 2 + + if 0: + a = 4 + b = 5 + c = 6 + else: + a = 8 + b = 9 + assert a == 8 and b == 9 + """, + [1,8,9,10], "", excludes=['if 0:']) + + def test_excluding_else_suite(self): + self.check_coverage("""\ + a = 1; b = 2 + + if 1==1: + a = 4 + b = 5 + c = 6 + else: #pragma: NO COVER + a = 8 + b = 9 + assert a == 4 and b == 5 and c == 6 + """, + [1,3,4,5,6,10], "", excludes=['#pragma: NO COVER']) + self.check_coverage("""\ + a = 1; b = 2 + + if 1==1: + a = 4 + b = 5 + c = 6 + + # Lots of comments to confuse the else handler. + # more. + + else: #pragma: NO COVER + + # Comments here too. + + a = 8 + b = 9 + assert a == 4 and b == 5 and c == 6 + """, + [1,3,4,5,6,17], "", excludes=['#pragma: NO COVER']) + + def test_excluding_elif_suites(self): + self.check_coverage("""\ + a = 1; b = 2 + + if 1==1: + a = 4 + b = 5 + c = 6 + elif 1==0: #pragma: NO COVER + a = 8 + b = 9 + else: + a = 11 + b = 12 + assert a == 4 and b == 5 and c == 6 + """, + [1,3,4,5,6,11,12,13], "11-12", excludes=['#pragma: NO COVER']) + + def test_excluding_oneline_if(self): + self.check_coverage("""\ + def foo(): + a = 2 + if 0: x = 3 # no cover + b = 4 + + foo() + """, + [1,2,4,6], "", excludes=["no cover"]) + + def test_excluding_a_colon_not_a_suite(self): + self.check_coverage("""\ + def foo(): + l = list(range(10)) + a = l[:3] # no cover + b = 4 + + foo() + """, + [1,2,4,6], "", excludes=["no cover"]) + + def test_excluding_for_suite(self): + self.check_coverage("""\ + a = 0 + for i in [1,2,3,4,5]: #pragma: NO COVER + a += i + assert a == 15 + """, + [1,4], "", excludes=['#pragma: NO COVER']) + self.check_coverage("""\ + a = 0 + for i in [1, + 2,3,4, + 5]: #pragma: NO COVER + a += i + assert a == 15 + """, + [1,6], "", excludes=['#pragma: NO COVER']) + self.check_coverage("""\ + a = 0 + for i in [1,2,3,4,5 + ]: #pragma: NO COVER + a += i + break + a = 99 + assert a == 1 + """, + [1,7], "", excludes=['#pragma: NO COVER']) + + def test_excluding_for_else(self): + self.check_coverage("""\ + a = 0 + for i in range(5): + a += i+1 + break + a = 99 + else: #pragma: NO COVER + a = 123 + assert a == 1 + """, + [1,2,3,4,5,8], "5", excludes=['#pragma: NO COVER']) + + def test_excluding_while(self): + self.check_coverage("""\ + a = 3; b = 0 + while a*b: #pragma: NO COVER + b += 1 + break + b = 99 + assert a == 3 and b == 0 + """, + [1,6], "", excludes=['#pragma: NO COVER']) + self.check_coverage("""\ + a = 3; b = 0 + while ( + a*b + ): #pragma: NO COVER + b += 1 + break + b = 99 + assert a == 3 and b == 0 + """, + [1,8], "", excludes=['#pragma: NO COVER']) + + def test_excluding_while_else(self): + self.check_coverage("""\ + a = 3; b = 0 + while a: + b += 1 + break + b = 99 + else: #pragma: NO COVER + b = 123 + assert a == 3 and b == 1 + """, + [1,2,3,4,5,8], "5", excludes=['#pragma: NO COVER']) + + def test_excluding_try_except(self): + self.check_coverage("""\ + a = 0 + try: + a = 1 + except: #pragma: NO COVER + a = 99 + assert a == 1 + """, + [1,2,3,6], "", excludes=['#pragma: NO COVER']) + self.check_coverage("""\ + a = 0 + try: + a = 1 + raise Exception("foo") + except: + a = 99 + assert a == 99 + """, + [1,2,3,4,5,6,7], "", excludes=['#pragma: NO COVER']) + self.check_coverage("""\ + a = 0 + try: + a = 1 + raise Exception("foo") + except ImportError: #pragma: NO COVER + a = 99 + except: + a = 123 + assert a == 123 + """, + [1,2,3,4,7,8,9], "", excludes=['#pragma: NO COVER']) + self.check_coverage("""\ + a = 0 + try: + a = 1 + except: #pragma: NO COVER + a = 99 + else: + a = 123 + assert a == 123 + """, + [1,2,3,7,8], "", excludes=['#pragma: NO COVER']) + self.check_coverage("""\ + a = 0 + try: + a = 1 + raise Exception("foo") + except: + a = 99 + else: #pragma: NO COVER + a = 123 + assert a == 99 + """, + [1,2,3,4,5,6,9], "", excludes=['#pragma: NO COVER']) + + def test_excluding_try_except_pass(self): + self.check_coverage("""\ + a = 0 + try: + a = 1 + except: #pragma: NO COVER + x = 2 + assert a == 1 + """, + [1,2,3,6], "", excludes=['#pragma: NO COVER']) + self.check_coverage("""\ + a = 0 + try: + a = 1 + raise Exception("foo") + except ImportError: #pragma: NO COVER + x = 2 + except: + a = 123 + assert a == 123 + """, + [1,2,3,4,7,8,9], "", excludes=['#pragma: NO COVER']) + self.check_coverage("""\ + a = 0 + try: + a = 1 + except: #pragma: NO COVER + x = 2 + else: + a = 123 + assert a == 123 + """, + [1,2,3,7,8], "", excludes=['#pragma: NO COVER']) + self.check_coverage("""\ + a = 0 + try: + a = 1 + raise Exception("foo") + except: + a = 99 + else: #pragma: NO COVER + x = 2 + assert a == 99 + """, + [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: + self.check_coverage("""\ + def f(): + if False: # pragma: no cover + pass # This line still reported as missing + if False: # pragma: no cover + x = 1 # Now it is skipped. + + f() + """, + [1,7], "", excludes=["no cover"]) + + def test_excluding_function(self): + self.check_coverage("""\ + def fn(foo): #pragma: NO COVER + a = 1 + b = 2 + c = 3 + + x = 1 + assert x == 1 + """, + [6,7], "", excludes=['#pragma: NO COVER']) + + def test_excluding_method(self): + self.check_coverage("""\ + class Fooey: + def __init__(self): + self.a = 1 + + def foo(self): #pragma: NO COVER + return self.a + + x = Fooey() + assert x.a == 1 + """, + [1,2,3,8,9], "", excludes=['#pragma: NO COVER']) + + def test_excluding_class(self): + self.check_coverage("""\ + class Fooey: #pragma: NO COVER + def __init__(self): + self.a = 1 + + def foo(self): + return self.a + + x = 1 + assert x == 1 + """, + [8,9], "", excludes=['#pragma: NO COVER']) + + +if sys.version_info >= (2, 4): + class Py24Test(CoverageTest): + """Tests of new syntax in Python 2.4.""" + + def test_function_decorators(self): + self.check_coverage("""\ + def require_int(func): + def wrapper(arg): + assert isinstance(arg, int) + return func(arg) + + return wrapper + + @require_int + def p1(arg): + return arg*2 + + assert p1(10) == 20 + """, + [1,2,3,4,6,8,10,12], "") + + def test_function_decorators_with_args(self): + self.check_coverage("""\ + def boost_by(extra): + def decorator(func): + def wrapper(arg): + return extra*func(arg) + return wrapper + return decorator + + @boost_by(10) + def boosted(arg): + return arg*2 + + assert boosted(10) == 200 + """, + [1,2,3,4,5,6,8,10,12], "") + + def test_double_function_decorators(self): + self.check_coverage("""\ + def require_int(func): + def wrapper(arg): + assert isinstance(arg, int) + return func(arg) + return wrapper + + def boost_by(extra): + def decorator(func): + def wrapper(arg): + return extra*func(arg) + return wrapper + return decorator + + @require_int + @boost_by(10) + def boosted1(arg): + return arg*2 + + assert boosted1(10) == 200 + + @boost_by(10) + @require_int + def boosted2(arg): + return arg*2 + + assert boosted2(10) == 200 + """, + ([1,2,3,4,5,7,8,9,10,11,12,14,15,17,19,21,22,24,26], + [1,2,3,4,5,7,8,9,10,11,12,14, 17,19,21, 24,26]), "") + + +if sys.version_info >= (2, 5): + class Py25Test(CoverageTest): + """Tests of new syntax in Python 2.5.""" + + def test_with_statement(self): + self.check_coverage("""\ + from __future__ import with_statement + + class Managed: + def __enter__(self): + desc = "enter" + + def __exit__(self, type, value, tb): + desc = "exit" + + m = Managed() + with m: + desc = "block1a" + desc = "block1b" + + try: + with m: + desc = "block2" + raise Exception("Boo!") + except: + desc = "caught" + """, + [1,3,4,5,7,8,10,11,12,13,15,16,17,18,19,20], "") + + def test_try_except_finally(self): + self.check_coverage("""\ + a = 0; b = 0 + try: + a = 1 + except: + a = 99 + finally: + b = 2 + assert a == 1 and b == 2 + """, + [1,2,3,4,5,7,8], "4-5") + self.check_coverage("""\ + a = 0; b = 0 + try: + a = 1 + raise Exception("foo") + except: + a = 99 + finally: + b = 2 + assert a == 99 and b == 2 + """, + [1,2,3,4,5,6,8,9], "") + self.check_coverage("""\ + a = 0; b = 0 + try: + a = 1 + raise Exception("foo") + except ImportError: + a = 99 + except: + a = 123 + finally: + b = 2 + assert a == 123 and b == 2 + """, + [1,2,3,4,5,6,7,8,10,11], "6") + self.check_coverage("""\ + a = 0; b = 0 + try: + a = 1 + raise IOError("foo") + except ImportError: + a = 99 + except IOError: + a = 17 + except: + a = 123 + finally: + b = 2 + assert a == 17 and b == 2 + """, + [1,2,3,4,5,6,7,8,9,10,12,13], "6, 9-10") + self.check_coverage("""\ + a = 0; b = 0 + try: + a = 1 + except: + a = 99 + else: + a = 123 + finally: + b = 2 + assert a == 123 and b == 2 + """, + [1,2,3,4,5,7,9,10], "4-5") + self.check_coverage("""\ + a = 0; b = 0 + try: + a = 1 + raise Exception("foo") + except: + a = 99 + else: + a = 123 + finally: + b = 2 + assert a == 99 and b == 2 + """, + [1,2,3,4,5,6,8,10,11], "8") + + +class ModuleTest(CoverageTest): + """Tests for the module-level behavior of the `coverage` module.""" + + def test_not_singleton(self): + # You *can* create another coverage object. + coverage.coverage() + coverage.coverage() + + +class ReportingTest(CoverageTest): + """Tests of some reporting behavior.""" + + def test_no_data_to_report_on_annotate(self): + # Reporting with no data produces a nice message and no output dir. + self.assertRaisesRegexp( + CoverageException, "No data to report.", + self.command_line, "annotate -d ann" + ) + self.assert_doesnt_exist("ann") + + def test_no_data_to_report_on_html(self): + # Reporting with no data produces a nice message and no output dir. + self.assertRaisesRegexp( + CoverageException, "No data to report.", + self.command_line, "html -d htmlcov" + ) + self.assert_doesnt_exist("htmlcov") + + def test_no_data_to_report_on_xml(self): + # Reporting with no data produces a nice message. + self.assertRaisesRegexp( + CoverageException, "No data to report.", + self.command_line, "xml" + ) + # Currently, this leaves an empty coverage.xml file... :( diff -Nru python-coverage-3.4/test/test_data.py python-coverage-3.6/test/test_data.py --- python-coverage-3.4/test/test_data.py 1970-01-01 00:00:00.000000000 +0000 +++ python-coverage-3.6/test/test_data.py 2012-12-30 15:17:29.000000000 +0000 @@ -0,0 +1,146 @@ +"""Tests for coverage.data""" + +from coverage.backward import pickle +from coverage.data import CoverageData +from coverage.files import PathAliases + +from test.coveragetest import CoverageTest + + +DATA_1 = { 'a.py': {1:None, 2:None}, 'b.py': {3:None} } +SUMMARY_1 = { 'a.py':2, 'b.py':1 } +MEASURED_FILES_1 = [ 'a.py', 'b.py' ] +A_PY_LINES_1 = [1,2] +B_PY_LINES_1 = [3] + +DATA_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 = { 'x.py': {(1,2):None, (2,3):None}, 'y.py': {(17,23):None} } +X_PY_ARCS_3 = [(1,2), (2,3)] +Y_PY_ARCS_3 = [(17,23)] + + +class DataTest(CoverageTest): + """Test cases for coverage.data.""" + + def assert_summary(self, covdata, summary, fullpath=False): + """Check that the summary of `covdata` is `summary`.""" + self.assertEqual(covdata.summary(fullpath), summary) + + def assert_measured_files(self, covdata, measured): + """Check that `covdata`'s measured files are `measured`.""" + self.assertSameElements(covdata.measured_files(), measured) + + def test_reading_empty(self): + covdata = CoverageData() + covdata.read() + self.assert_summary(covdata, {}) + + def test_adding_data(self): + covdata = CoverageData() + covdata.add_line_data(DATA_1) + self.assert_summary(covdata, SUMMARY_1) + self.assert_measured_files(covdata, MEASURED_FILES_1) + + def test_touch_file(self): + covdata = CoverageData() + covdata.add_line_data(DATA_1) + covdata.touch_file('x.py') + self.assert_measured_files(covdata, MEASURED_FILES_1 + ['x.py']) + + def test_writing_and_reading(self): + covdata1 = CoverageData() + covdata1.add_line_data(DATA_1) + covdata1.write() + + covdata2 = CoverageData() + covdata2.read() + self.assert_summary(covdata2, SUMMARY_1) + + def test_combining(self): + covdata1 = CoverageData() + covdata1.add_line_data(DATA_1) + covdata1.write(suffix='1') + + covdata2 = CoverageData() + covdata2.add_line_data(DATA_2) + covdata2.write(suffix='2') + + covdata3 = CoverageData() + covdata3.combine_parallel_data() + self.assert_summary(covdata3, SUMMARY_1_2) + self.assert_measured_files(covdata3, MEASURED_FILES_1_2) + + def test_erasing(self): + covdata1 = CoverageData() + covdata1.add_line_data(DATA_1) + covdata1.write() + covdata1.erase() + self.assert_summary(covdata1, {}) + + covdata2 = CoverageData() + covdata2.read() + self.assert_summary(covdata2, {}) + + def test_file_format(self): + # Write with CoverageData, then read the pickle explicitly. + covdata = CoverageData() + covdata.add_line_data(DATA_1) + covdata.write() + + fdata = open(".coverage", 'rb') + try: + data = pickle.load(fdata) + finally: + fdata.close() + + lines = data['lines'] + self.assertSameElements(lines.keys(), MEASURED_FILES_1) + self.assertSameElements(lines['a.py'], A_PY_LINES_1) + self.assertSameElements(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') + + def test_file_format_with_arcs(self): + # Write with CoverageData, then read the pickle explicitly. + covdata = CoverageData() + covdata.add_arc_data(ARC_DATA_3) + covdata.write() + + fdata = open(".coverage", 'rb') + try: + data = pickle.load(fdata) + finally: + fdata.close() + + self.assertSameElements(data['lines'].keys(), []) + arcs = data['arcs'] + self.assertSameElements(arcs['x.py'], X_PY_ARCS_3) + self.assertSameElements(arcs['y.py'], Y_PY_ARCS_3) + + def test_combining_with_aliases(self): + covdata1 = CoverageData() + covdata1.add_line_data({ + '/home/ned/proj/src/a.py': {1:None, 2:None}, + '/home/ned/proj/src/sub/b.py': {3:None}, + }) + covdata1.write(suffix='1') + + covdata2 = CoverageData() + covdata2.add_line_data({ + r'c:\ned\test\a.py': {4:None, 5:None}, + r'c:\ned\test\sub\b.py': {6:None}, + }) + covdata2.write(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' ]) diff -Nru python-coverage-3.4/test/test_execfile.py python-coverage-3.6/test/test_execfile.py --- python-coverage-3.4/test/test_execfile.py 1970-01-01 00:00:00.000000000 +0000 +++ python-coverage-3.6/test/test_execfile.py 2012-12-30 15:17:29.000000000 +0000 @@ -0,0 +1,116 @@ +"""Tests for coverage.execfile""" + +import os, sys + +from coverage.execfile import run_python_file, run_python_module +from coverage.misc import NoSource + +from test.coveragetest import CoverageTest + +here = os.path.dirname(__file__) + +class RunFileTest(CoverageTest): + """Test cases for `run_python_file`.""" + + def test_run_python_file(self): + tryfile = os.path.join(here, "try_execfile.py") + run_python_file(tryfile, [tryfile, "arg1", "arg2"]) + mod_globs = eval(self.stdout()) + + # The file should think it is __main__ + self.assertEqual(mod_globs['__name__'], "__main__") + + # It should seem to come from a file named try_execfile.py + dunder_file = os.path.basename(mod_globs['__file__']) + self.assertEqual(dunder_file, "try_execfile.py") + + # It should have its correct module data. + self.assertEqual(mod_globs['__doc__'], + "Test file for run_python_file.") + self.assertEqual(mod_globs['DATA'], "xyzzy") + self.assertEqual(mod_globs['FN_VAL'], "my_fn('fooey')") + + # It must be self-importable as __main__. + self.assertEqual(mod_globs['__main__.DATA'], "xyzzy") + + # Argv should have the proper values. + self.assertEqual(mod_globs['argv'], [tryfile, "arg1", "arg2"]) + + # __builtins__ should have the right values, like open(). + self.assertEqual(mod_globs['__builtins__.has_open'], True) + + def test_no_extra_file(self): + # Make sure that running a file doesn't create an extra compiled file. + self.make_file("xxx", """\ + desc = "a non-.py file!" + """) + + self.assertEqual(os.listdir("."), ["xxx"]) + run_python_file("xxx", ["xxx"]) + self.assertEqual(os.listdir("."), ["xxx"]) + + def test_universal_newlines(self): + # Make sure we can read any sort of line ending. + pylines = """# try newlines|print('Hello, world!')|""".split('|') + for nl in ('\n', '\r\n', '\r'): + fpy = open('nl.py', 'wb') + try: + fpy.write(nl.join(pylines).encode('utf-8')) + finally: + fpy.close() + run_python_file('nl.py', ['nl.py']) + self.assertEqual(self.stdout(), "Hello, world!\n"*3) + + def test_missing_final_newline(self): + # Make sure we can deal with a Python file with no final newline. + self.make_file("abrupt.py", """\ + if 1: + a = 1 + print("a is %r" % a) + #""") + abrupt = open("abrupt.py").read() + self.assertEqual(abrupt[-1], '#') + run_python_file("abrupt.py", ["abrupt.py"]) + self.assertEqual(self.stdout(), "a is 1\n") + + def test_no_such_file(self): + self.assertRaises(NoSource, run_python_file, "xyzzy.py", []) + + +class RunModuleTest(CoverageTest): + """Test run_python_module.""" + + run_in_temp_dir = False + + def setUp(self): + super(RunModuleTest, self).setUp() + # Parent class saves and restores sys.path, we can just modify it. + sys.path.append(self.nice_file(os.path.dirname(__file__), 'modules')) + + def test_runmod1(self): + run_python_module("runmod1", ["runmod1", "hello"]) + self.assertEqual(self.stdout(), "runmod1: passed hello\n") + + def test_runmod2(self): + run_python_module("pkg1.runmod2", ["runmod2", "hello"]) + self.assertEqual(self.stdout(), "runmod2: passed hello\n") + + def test_runmod3(self): + run_python_module("pkg1.sub.runmod3", ["runmod3", "hello"]) + self.assertEqual(self.stdout(), "runmod3: passed hello\n") + + def test_pkg1_main(self): + run_python_module("pkg1", ["pkg1", "hello"]) + self.assertEqual(self.stdout(), "pkg1.__main__: passed hello\n") + + def test_pkg1_sub_main(self): + run_python_module("pkg1.sub", ["pkg1.sub", "hello"]) + self.assertEqual(self.stdout(), "pkg1.sub.__main__: passed hello\n") + + def test_no_such_module(self): + self.assertRaises(NoSource, run_python_module, "i_dont_exist", []) + self.assertRaises(NoSource, run_python_module, "i.dont_exist", []) + self.assertRaises(NoSource, run_python_module, "i.dont.exist", []) + + def test_no_main(self): + self.assertRaises(NoSource, run_python_module, "pkg2", ["pkg2", "hi"]) diff -Nru python-coverage-3.4/test/test_farm.py python-coverage-3.6/test/test_farm.py --- python-coverage-3.4/test/test_farm.py 1970-01-01 00:00:00.000000000 +0000 +++ python-coverage-3.6/test/test_farm.py 2012-12-30 15:17:29.000000000 +0000 @@ -0,0 +1,366 @@ +"""Run tests in the farm subdirectory. Designed for nose.""" + +import difflib, filecmp, fnmatch, glob, os, re, shutil, sys +from nose.plugins.skip import SkipTest + +from test.backtest import run_command, execfile # pylint: disable=W0622 + +from coverage.control import _TEST_NAME_FILE + + +def test_farm(clean_only=False): + """A test-generating function for nose to find and run.""" + for fname in glob.glob("test/farm/*/*.py"): + case = FarmTestCase(fname, clean_only) + yield (case,) + + +class FarmTestCase(object): + """A test case from the farm tree. + + Tests are short Python script files, often called run.py: + + copy("src", "out") + run(''' + coverage -x white.py + coverage -a white.py + ''', rundir="out") + compare("out", "gold", "*,cover") + clean("out") + + Verbs (copy, run, compare, clean) are methods in this class. FarmTestCase + has options to allow various uses of the test cases (normal execution, + cleaning-only, or run and leave the results for debugging). + + """ + def __init__(self, runpy, clean_only=False, dont_clean=False): + """Create a test case from a run.py file. + + `clean_only` means that only the clean() action is executed. + `dont_clean` means that the clean() action is not executed. + + """ + self.description = runpy + self.dir, self.runpy = os.path.split(runpy) + self.clean_only = clean_only + self.dont_clean = dont_clean + + def cd(self, newdir): + """Change the current directory, and return the old one.""" + cwd = os.getcwd() + os.chdir(newdir) + return cwd + + def addtopath(self, directory): + """Add `directory` to the path, and return the old path.""" + oldpath = sys.path[:] + if directory is not None: + sys.path.insert(0, directory) + return oldpath + + def restorepath(self, path): + """Restore the system path to `path`.""" + sys.path = path + + def __call__(self): + """Execute the test from the run.py file. + + """ + if _TEST_NAME_FILE: + f = open(_TEST_NAME_FILE, "w") + f.write(self.description.replace("/", "_")) + f.close() + + cwd = self.cd(self.dir) + + # Prepare a dictionary of globals for the run.py files to use. + fns = """ + copy run runfunc compare contains doesnt_contain clean skip + """.split() + if self.clean_only: + glo = dict([(fn, self.noop) for fn in fns]) + glo['clean'] = self.clean + else: + glo = dict([(fn, getattr(self, fn)) for fn in fns]) + if self.dont_clean: # pragma: not covered + glo['clean'] = self.noop + + old_mods = dict(sys.modules) + try: + execfile(self.runpy, glo) + finally: + self.cd(cwd) + # Remove any new modules imported during the test run. This lets us + # import the same source files for more than one test. + to_del = [m for m in sys.modules if m not in old_mods] + for m in to_del: + del sys.modules[m] + + def run_fully(self): # pragma: not covered + """Run as a full test case, with setUp and tearDown.""" + self.setUp() + try: + self() + finally: + self.tearDown() + + def fnmatch_list(self, files, file_pattern): + """Filter the list of `files` to only those that match `file_pattern`. + + If `file_pattern` is None, then return the entire list of files. + + Returns a list of the filtered files. + + """ + if file_pattern: + files = [f for f in files if fnmatch.fnmatch(f, file_pattern)] + return files + + def setUp(self): + """Test set up, run by nose before __call__.""" + + # Modules should be importable from the current directory. + self.old_syspath = sys.path[:] + sys.path.insert(0, '') + + def tearDown(self): + """Test tear down, run by nose after __call__.""" + # Make sure no matter what, the test is cleaned up. + if not self.dont_clean: # pragma: part covered + self.clean_only = True + self() + + # Restore the original sys.path + sys.path = self.old_syspath + + # Functions usable inside farm run.py files + + def noop(self, *args, **kwargs): + """A no-op function to stub out run, copy, etc, when only cleaning.""" + pass + + def copy(self, src, dst): + """Copy a directory.""" + + if os.path.exists(dst): + shutil.rmtree(dst) + shutil.copytree(src, dst) + + def run(self, cmds, rundir="src", outfile=None): + """Run a list of commands. + + `cmds` is a string, commands separated by newlines. + `rundir` is the directory in which to run the commands. + `outfile` is a filename to redirect stdout to. + + """ + cwd = self.cd(rundir) + if outfile: + fout = open(outfile, "a+") + try: + for cmd in cmds.split("\n"): + cmd = cmd.strip() + if not cmd: + continue + retcode, output = run_command(cmd) + print(output.rstrip()) + if outfile: + fout.write(output) + if retcode: + raise Exception("command exited abnormally") + finally: + if outfile: + fout.close() + self.cd(cwd) + + def runfunc(self, fn, rundir="src", addtopath=None): + """Run a function. + + `fn` is a callable. + `rundir` is the directory in which to run the function. + + """ + + cwd = self.cd(rundir) + oldpath = self.addtopath(addtopath) + try: + fn() + finally: + self.cd(cwd) + self.restorepath(oldpath) + + def compare(self, dir1, dir2, file_pattern=None, size_within=0, + left_extra=False, right_extra=False, scrubs=None + ): + """Compare files matching `file_pattern` in `dir1` and `dir2`. + + `dir2` is interpreted as a prefix, with Python version numbers appended + to find the actual directory to compare with. "foo" will compare against + "foo_v241", "foo_v24", "foo_v2", or "foo", depending on which directory + is found first. + + `size_within` is a percentage delta for the file sizes. If non-zero, + then the file contents are not compared (since they are expected to + often be different), but the file sizes must be within this amount. + For example, size_within=10 means that the two files' sizes must be + within 10 percent of each other to compare equal. + + `left_extra` true means the left directory can have extra files in it + 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. + + An assertion will be raised if the directories fail one of their + matches. + + """ + # Search for a dir2 with a version suffix. + version_suff = ''.join(map(str, sys.version_info[:3])) + while version_suff: + trydir = dir2 + '_v' + version_suff + if os.path.exists(trydir): + dir2 = trydir + break + version_suff = version_suff[:-1] + + assert os.path.exists(dir1), "Left directory missing: %s" % dir1 + assert os.path.exists(dir2), "Right directory missing: %s" % dir2 + + dc = filecmp.dircmp(dir1, dir2) + diff_files = self.fnmatch_list(dc.diff_files, file_pattern) + left_only = self.fnmatch_list(dc.left_only, file_pattern) + right_only = self.fnmatch_list(dc.right_only, file_pattern) + + if size_within: + # The files were already compared, use the diff_files list as a + # guide for size comparison. + wrong_size = [] + for f in diff_files: + left = open(os.path.join(dir1, f), "rb").read() + right = open(os.path.join(dir2, f), "rb").read() + size_l, size_r = len(left), len(right) + big, little = max(size_l, size_r), min(size_l, size_r) + if (big - little) / float(little) > size_within/100.0: + # print "%d %d" % (big, little) + # print "Left: ---\n%s\n-----\n%s" % (left, right) + wrong_size.append(f) + assert not wrong_size, ( + "File sizes differ between %s and %s: %s" % ( + dir1, dir2, wrong_size + )) + else: + # filecmp only compares in binary mode, but we want text mode. So + # look through the list of different files, and compare them + # ourselves. + text_diff = [] + for f in diff_files: + left = open(os.path.join(dir1, f), "rU").readlines() + right = open(os.path.join(dir2, f), "rU").readlines() + if scrubs: + left = self._scrub(left, scrubs) + right = self._scrub(right, scrubs) + if left != right: + text_diff.append(f) + print("".join(list(difflib.Differ().compare(left, right)))) + assert not text_diff, "Files differ: %s" % text_diff + + if not left_extra: + assert not left_only, "Files in %s only: %s" % (dir1, left_only) + if not right_extra: + assert not right_only, "Files in %s only: %s" % (dir2, right_only) + + def _scrub(self, strlist, scrubs): + """Scrub uninteresting data from the strings in `strlist`. + + `scrubs is a list of (find, replace) pairs of regexes that are used on + each string in `strlist`. A list of scrubbed strings is returned. + + """ + scrubbed = [] + for s in strlist: + for rgx_find, rgx_replace in scrubs: + s = re.sub(rgx_find, rgx_replace, s) + scrubbed.append(s) + return scrubbed + + def contains(self, filename, *strlist): + """Check that the file contains all of a list of strings. + + An assert will be raised if one of the arguments in `strlist` is + missing in `filename`. + + """ + text = open(filename, "r").read() + for s in strlist: + assert s in text, "Missing content in %s: %r" % (filename, s) + + def doesnt_contain(self, filename, *strlist): + """Check that the file contains none of a list of strings. + + An assert will be raised if any of the strings in strlist appears in + `filename`. + + """ + text = open(filename, "r").read() + for s in strlist: + assert s not in text, "Forbidden content in %s: %r" % (filename, s) + + def clean(self, cleandir): + """Clean `cleandir` by removing it and all its children completely.""" + # rmtree gives mysterious failures on Win7, so retry a "few" times. + # I've seen it take over 100 tries, so, 1000! This is probably the + # most unpleasant hack I've written in a long time... + tries = 1000 + while tries: # pragma: part covered + if os.path.exists(cleandir): + try: + shutil.rmtree(cleandir) + except OSError: # pragma: not covered + if tries == 1: + raise + else: + tries -= 1 + continue + break + + def skip(self, msg=None): + """Skip the current test.""" + raise SkipTest(msg) + + +def main(): # pragma: not covered + """Command-line access to test_farm. + + Commands: + + run testcase - Run a single test case. + out testcase - Run a test case, but don't clean up, to see the output. + clean - Clean all the output for all tests. + + """ + op = 'help' + try: + op = sys.argv[1] + except IndexError: + pass + + if op == 'run': + # Run the test for real. + case = FarmTestCase(sys.argv[2]) + case.run_fully() + elif op == 'out': + # Run the test, but don't clean up, so we can examine the output. + case = FarmTestCase(sys.argv[2], dont_clean=True) + case.run_fully() + elif op == 'clean': + # Run all the tests, but just clean. + for test in test_farm(clean_only=True): + test[0].run_fully() + else: + print(main.__doc__) + +# So that we can run just one farm run.py at a time. +if __name__ == '__main__': + main() diff -Nru python-coverage-3.4/test/test_files.py python-coverage-3.6/test/test_files.py --- python-coverage-3.4/test/test_files.py 1970-01-01 00:00:00.000000000 +0000 +++ python-coverage-3.6/test/test_files.py 2012-12-30 15:17:29.000000000 +0000 @@ -0,0 +1,169 @@ +"""Tests for files.py""" + +import os + +from coverage.files import FileLocator, TreeMatcher, FnmatchMatcher +from coverage.files import PathAliases, find_python_files, abs_file +from coverage.backward import set # pylint: disable=W0622 +from coverage.misc import CoverageException + +from test.coveragetest import CoverageTest + + +class FileLocatorTest(CoverageTest): + """Tests of `FileLocator`.""" + + def abs_path(self, p): + """Return the absolute path for `p`.""" + return os.path.join(os.getcwd(), os.path.normpath(p)) + + def test_simple(self): + self.make_file("hello.py") + fl = FileLocator() + self.assertEqual(fl.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") + + def test_peer_directories(self): + self.make_file("sub/proj1/file1.py") + self.make_file("sub/proj2/file2.py") + a1 = self.abs_path("sub/proj1/file1.py") + 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) + + 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 acheive. + fl = FileLocator() + 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) + + +class MatcherTest(CoverageTest): + """Tests of file matchers.""" + + def test_tree_matcher(self): + file1 = self.make_file("sub/file1.py") + file2 = self.make_file("sub/file2.c") + file3 = self.make_file("sub2/file3.h") + file4 = self.make_file("sub3/file4.py") + file5 = self.make_file("sub3/file5.c") + fl = FileLocator() + tm = TreeMatcher([ + fl.canonical_filename("sub"), + fl.canonical_filename(file4), + ]) + self.assertTrue(tm.match(fl.canonical_filename(file1))) + self.assertTrue(tm.match(fl.canonical_filename(file2))) + self.assertFalse(tm.match(fl.canonical_filename(file3))) + self.assertTrue(tm.match(fl.canonical_filename(file4))) + self.assertFalse(tm.match(fl.canonical_filename(file5))) + + def test_fnmatch_matcher(self): + file1 = self.make_file("sub/file1.py") + file2 = self.make_file("sub/file2.c") + file3 = self.make_file("sub2/file3.h") + file4 = self.make_file("sub3/file4.py") + file5 = self.make_file("sub3/file5.c") + fl = FileLocator() + fnm = FnmatchMatcher(["*.py", "*/sub2/*"]) + self.assertTrue(fnm.match(fl.canonical_filename(file1))) + self.assertFalse(fnm.match(fl.canonical_filename(file2))) + self.assertTrue(fnm.match(fl.canonical_filename(file3))) + self.assertTrue(fnm.match(fl.canonical_filename(file4))) + self.assertFalse(fnm.match(fl.canonical_filename(file5))) + + +class PathAliasesTest(CoverageTest): + """Tests for coverage/files.py:PathAliases""" + + def test_noop(self): + aliases = PathAliases() + self.assertEqual(aliases.map('/ned/home/a.py'), '/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') + + 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') + aliases = PathAliases() + aliases.add('/ned/home/*/src/', './mysrc') + self.assertEqual(aliases.map('/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') + + 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') + + def test_cant_have_wildcard_at_end(self): + aliases = PathAliases() + self.assertRaisesRegexp( + CoverageException, "Pattern must not end with wildcards.", + aliases.add, "/ned/home/*", "fooey" + ) + self.assertRaisesRegexp( + CoverageException, "Pattern must not end with wildcards.", + aliases.add, "/ned/home/*/", "fooey" + ) + self.assertRaisesRegexp( + CoverageException, "Pattern must not end with wildcards.", + aliases.add, "/ned/home/*/*/", "fooey" + ) + + def test_no_accidental_munging(self): + 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') + + 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') + + 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') + + +class FindPythonFilesTest(CoverageTest): + """Tests of `find_python_files`.""" + + def test_find_python_files(self): + self.make_file("sub/a.py") + self.make_file("sub/b.py") + self.make_file("sub/x.c") # nope: not .py + self.make_file("sub/ssub/__init__.py") + self.make_file("sub/ssub/s.py") + self.make_file("sub/ssub/~s.py") # nope: editor effluvia + self.make_file("sub/lab/exp.py") # nope: no __init__.py + py_files = set(find_python_files("sub")) + self.assert_same_files(py_files, [ + "sub/a.py", "sub/b.py", + "sub/ssub/__init__.py", "sub/ssub/s.py", + ]) diff -Nru python-coverage-3.4/test/test_html.py python-coverage-3.6/test/test_html.py --- python-coverage-3.4/test/test_html.py 1970-01-01 00:00:00.000000000 +0000 +++ python-coverage-3.6/test/test_html.py 2012-12-30 15:17:29.000000000 +0000 @@ -0,0 +1,297 @@ +# -*- coding: utf-8 -*- +"""Tests that HTML generation is awesome.""" + +import os.path, re, sys +import coverage +from coverage.misc import NotPython, NoSource + +from test.coveragetest import CoverageTest + +class HtmlTestHelpers(CoverageTest): + """Methods that help with HTML tests.""" + + def create_initial_files(self): + """Create the source files we need to run these tests.""" + self.make_file("main_file.py", """\ + import helper1, helper2 + helper1.func1(12) + helper2.func2(12) + """) + self.make_file("helper1.py", """\ + def func1(x): + if x % 2: + print("odd") + """) + self.make_file("helper2.py", """\ + def func2(x): + print("x is %d" % x) + """) + + def run_coverage(self, covargs=None, htmlargs=None): + """Run coverage on main_file.py, and create an HTML report.""" + self.clean_local_file_imports() + cov = coverage.coverage(**(covargs or {})) + self.start_import_stop(cov, "main_file") + cov.html_report(**(htmlargs or {})) + + def remove_html_files(self): + """Remove the HTML files created as part of the HTML report.""" + os.remove("htmlcov/index.html") + os.remove("htmlcov/main_file.html") + os.remove("htmlcov/helper1.html") + os.remove("htmlcov/helper2.html") + + +class HtmlDeltaTest(HtmlTestHelpers, CoverageTest): + """Tests of the HTML delta speed-ups.""" + + def setUp(self): + super(HtmlDeltaTest, self).setUp() + + # At least one of our tests monkey-patches the version of coverage, + # so grab it here to restore it later. + self.real_coverage_version = coverage.__version__ + + self.maxDiff = None + + def tearDown(self): + coverage.__version__ = self.real_coverage_version + super(HtmlDeltaTest, self).tearDown() + + def test_html_created(self): + # Test basic HTML generation: files should be created. + self.create_initial_files() + self.run_coverage() + + self.assert_exists("htmlcov/index.html") + self.assert_exists("htmlcov/main_file.html") + self.assert_exists("htmlcov/helper1.html") + self.assert_exists("htmlcov/helper2.html") + self.assert_exists("htmlcov/style.css") + self.assert_exists("htmlcov/coverage_html.js") + + def test_html_delta_from_source_change(self): + # HTML generation can create only the files that have changed. + # In this case, helper1 changes because its source is different. + self.create_initial_files() + self.run_coverage() + index1 = open("htmlcov/index.html").read() + self.remove_html_files() + + # Now change a file and do it again + self.make_file("helper1.py", """\ + def func1(x): # A nice function + if x % 2: + print("odd") + """) + + self.run_coverage() + + # Only the changed files should have been created. + self.assert_exists("htmlcov/index.html") + self.assert_exists("htmlcov/helper1.html") + self.assert_doesnt_exist("htmlcov/main_file.html") + self.assert_doesnt_exist("htmlcov/helper2.html") + index2 = open("htmlcov/index.html").read() + self.assertMultiLineEqual(index1, index2) + + def test_html_delta_from_coverage_change(self): + # HTML generation can create only the files that have changed. + # In this case, helper1 changes because its coverage is different. + self.create_initial_files() + self.run_coverage() + self.remove_html_files() + + # Now change a file and do it again + self.make_file("main_file.py", """\ + import helper1, helper2 + helper1.func1(23) + helper2.func2(23) + """) + + self.run_coverage() + + # Only the changed files should have been created. + self.assert_exists("htmlcov/index.html") + self.assert_exists("htmlcov/helper1.html") + self.assert_exists("htmlcov/main_file.html") + self.assert_doesnt_exist("htmlcov/helper2.html") + + 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. + self.create_initial_files() + self.run_coverage(covargs=dict(omit=[])) + index1 = open("htmlcov/index.html").read() + self.remove_html_files() + + self.run_coverage(covargs=dict(omit=['xyzzy*'])) + + # All the files have been reported again. + self.assert_exists("htmlcov/index.html") + self.assert_exists("htmlcov/helper1.html") + self.assert_exists("htmlcov/main_file.html") + self.assert_exists("htmlcov/helper2.html") + index2 = open("htmlcov/index.html").read() + 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 + # changed. + self.create_initial_files() + self.run_coverage() + index1 = open("htmlcov/index.html").read() + self.remove_html_files() + + # "Upgrade" coverage.py! + coverage.__version__ = "XYZZY" + + self.run_coverage() + + # All the files have been reported again. + self.assert_exists("htmlcov/index.html") + self.assert_exists("htmlcov/helper1.html") + self.assert_exists("htmlcov/main_file.html") + self.assert_exists("htmlcov/helper2.html") + index2 = open("htmlcov/index.html").read() + fixed_index2 = index2.replace("XYZZY", self.real_coverage_version) + self.assertMultiLineEqual(index1, fixed_index2) + + +class HtmlTitleTests(HtmlTestHelpers, CoverageTest): + """Tests of the HTML title support.""" + + def test_default_title(self): + self.create_initial_files() + self.run_coverage() + index = open("htmlcov/index.html").read() + self.assertIn("Coverage report", index) + self.assertIn("

    Coverage report:", index) + + def test_title_set_in_config_file(self): + self.create_initial_files() + self.make_file(".coveragerc", "[html]\ntitle = Metrics & stuff!\n") + self.run_coverage() + index = open("htmlcov/index.html").read() + self.assertIn("Metrics & stuff!", index) + self.assertIn("

    Metrics & stuff!:", index) + + if sys.version_info[:2] != (3,1): + def test_non_ascii_title_set_in_config_file(self): + self.create_initial_files() + self.make_file(".coveragerc", + "[html]\ntitle = «ταБЬℓσ» numbers" + ) + self.run_coverage() + index = open("htmlcov/index.html").read() + self.assertIn( + "«ταБЬℓσ»" + " numbers", index + ) + self.assertIn( + "<h1>«ταБЬℓσ»" + " numbers", index + ) + + def test_title_set_in_args(self): + self.create_initial_files() + self.make_file(".coveragerc", "[html]\ntitle = Good title\n") + self.run_coverage(htmlargs=dict(title="«ταБЬℓσ» & stüff!")) + index = open("htmlcov/index.html").read() + self.assertIn( + "<title>«ταБЬℓσ»" + " & stüff!", index + ) + self.assertIn( + "

    «ταБЬℓσ»" + " & stüff!:", index + ) + + +class HtmlWithUnparsableFilesTest(CoverageTest): + """Test the behavior when measuring unparsable files.""" + + def test_dotpy_not_python(self): + self.make_file("innocuous.py", "a = 1") + cov = coverage.coverage() + self.start_import_stop(cov, "innocuous") + self.make_file("innocuous.py", "

    This isn't python!

    ") + self.assertRaisesRegexp( + NotPython, + "Couldn't parse '.*innocuous.py' as Python source: '.*' at line 1", + cov.html_report + ) + + def test_dotpy_not_python_ignored(self): + self.make_file("innocuous.py", "a = 2") + cov = coverage.coverage() + self.start_import_stop(cov, "innocuous") + self.make_file("innocuous.py", "

    This isn't python!

    ") + cov.html_report(ignore_errors=True) + self.assert_exists("htmlcov/index.html") + # this would be better as a glob, if the html layout changes: + self.assert_doesnt_exist("htmlcov/innocuous.html") + + def test_dothtml_not_python(self): + # We run a .html file, and when reporting, we can't parse it as + # Python. Since it wasn't .py, no error is reported. + + # Run an "html" file + self.make_file("innocuous.html", "a = 3") + self.run_command("coverage run innocuous.html") + # Before reporting, change it to be an HTML file. + self.make_file("innocuous.html", "

    This isn't python at all!

    ") + output = self.run_command("coverage html") + self.assertEqual(output.strip(), "No data to report.") + + def test_execed_liar_ignored(self): + # Jinja2 sets __file__ to be a non-Python file, and then execs code. + # If that file contains non-Python code, a TokenError shouldn't + # have been raised when writing the HTML report. + if sys.version_info < (3, 0): + source = "exec compile('','','exec') in {'__file__': 'liar.html'}" + else: + 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() + self.start_import_stop(cov, "liar") + cov.html_report() + self.assert_exists("htmlcov/index.html") + + def test_execed_liar_ignored_indentation_error(self): + # Jinja2 sets __file__ to be a non-Python file, and then execs code. + # If that file contains untokenizable code, we shouldn't get an + # exception. + if sys.version_info < (3, 0): + source = "exec compile('','','exec') in {'__file__': 'liar.html'}" + else: + source = "exec(compile('','','exec'), {'__file__': 'liar.html'})" + 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() + self.start_import_stop(cov, "liar") + cov.html_report() + self.assert_exists("htmlcov/index.html") + + +class HtmlTest(CoverageTest): + """Moar HTML tests.""" + + def test_missing_source_file_incorrect_message(self): + # https://bitbucket.org/ned/coveragepy/issue/60 + 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() + self.start_import_stop(cov, 'thefile') + os.remove("sub/another.py") + + missing_file = os.path.join(self.temp_dir, "sub", "another.py") + self.assertRaisesRegexp(NoSource, + "(?i)No source for code: '%s'" % re.escape(missing_file), + cov.html_report + ) diff -Nru python-coverage-3.4/test/test_misc.py python-coverage-3.6/test/test_misc.py --- python-coverage-3.4/test/test_misc.py 1970-01-01 00:00:00.000000000 +0000 +++ python-coverage-3.6/test/test_misc.py 2012-12-30 15:17:29.000000000 +0000 @@ -0,0 +1,73 @@ +"""Tests of miscellaneous stuff.""" + +from coverage.misc import Hasher, file_be_gone +from coverage import __version__, __url__ +from test.coveragetest import CoverageTest + +class HasherTest(CoverageTest): + """Test our wrapper of md5 hashing.""" + + def test_string_hashing(self): + h1 = Hasher() + h1.update("Hello, world!") + h2 = Hasher() + h2.update("Goodbye!") + h3 = Hasher() + h3.update("Hello, world!") + self.assertNotEqual(h1.digest(), h2.digest()) + self.assertEqual(h1.digest(), h3.digest()) + + def test_dict_hashing(self): + h1 = Hasher() + h1.update({'a': 17, 'b': 23}) + h2 = Hasher() + h2.update({'b': 23, 'a': 17}) + self.assertEqual(h1.digest(), h2.digest()) + + +class RemoveFileTest(CoverageTest): + """Tests of misc.file_be_gone.""" + + def test_remove_nonexistent_file(self): + # it's ok to try to remove a file that doesn't exist. + file_be_gone("not_here.txt") + + def test_remove_actual_file(self): + # it really does remove a file that does exist. + self.make_file("here.txt", "We are here, we are here, we are here!") + file_be_gone("here.txt") + self.assert_doesnt_exist("here.txt") + + def test_actual_errors(self): + # Errors can still happen. + # ". is a directory" on Unix, or "Access denied" on Windows + self.assertRaises(OSError, file_be_gone, ".") + + +class SetupPyTest(CoverageTest): + """Tests of setup.py""" + + run_in_temp_dir = False + + def test_metadata(self): + status, output = self.run_command_status( + "python setup.py --description --version --url --author" + ) + self.assertEqual(status, 0) + out = output.splitlines() + self.assertIn("measurement", out[0]) + self.assertEqual(out[1], __version__) + self.assertEqual(out[2], __url__) + self.assertIn("Ned Batchelder", out[3]) + + def test_more_metadata(self): + from setup import setup_args + + classifiers = setup_args['classifiers'] + self.assertGreater(len(classifiers), 7) + self.assertTrue(classifiers[-1].startswith("Development Status ::")) + + long_description = setup_args['long_description'].splitlines() + self.assertGreater(len(long_description), 7) + self.assertNotEqual(long_description[0].strip(), "") + self.assertNotEqual(long_description[-1].strip(), "") diff -Nru python-coverage-3.4/test/test_oddball.py python-coverage-3.6/test/test_oddball.py --- python-coverage-3.4/test/test_oddball.py 1970-01-01 00:00:00.000000000 +0000 +++ python-coverage-3.6/test/test_oddball.py 2012-12-30 15:17:29.000000000 +0000 @@ -0,0 +1,386 @@ +"""Oddball cases for testing coverage.py""" + +import os, sys +import coverage + +from test.coveragetest import CoverageTest +from test import osinfo + +class ThreadingTest(CoverageTest): + """Tests of the threading support.""" + + def test_threading(self): + self.check_coverage("""\ + import threading + + def fromMainThread(): + return "called from main thread" + + def fromOtherThread(): + return "called from other thread" + + def neverCalled(): + return "no one calls me" + + other = threading.Thread(target=fromOtherThread) + other.start() + fromMainThread() + other.join() + """, + [1,3,4,6,7,9,10,12,13,14,15], "10") + + def test_thread_run(self): + self.check_coverage("""\ + import threading + + class TestThread(threading.Thread): + def run(self): + self.a = 5 + self.do_work() + self.a = 7 + + def do_work(self): + self.a = 10 + + thd = TestThread() + thd.start() + thd.join() + """, + [1,3,4,5,6,7,9,10,12,13,14], "") + + +class RecursionTest(CoverageTest): + """Check what happens when recursive code gets near limits.""" + + def test_short_recursion(self): + # We can definitely get close to 500 stack frames. + self.check_coverage("""\ + def recur(n): + if n == 0: + return 0 + else: + return recur(n-1)+1 + + recur(495) # We can get at least this many stack frames. + i = 8 # and this line will be traced + """, + [1,2,3,5,7,8], "") + + def test_long_recursion(self): + # We can't finish a very deep recursion, but we don't crash. + self.assertRaises(RuntimeError, self.check_coverage, + """\ + def recur(n): + if n == 0: + return 0 + else: + return recur(n-1)+1 + + recur(100000) # This is definitely too many frames. + """, + [1,2,3,5,7], "") + + def test_long_recursion_recovery(self): + # Test the core of bug 93: http://bitbucket.org/ned/coveragepy/issue/93 + # When recovering from a stack overflow, the Python trace function is + # disabled, but the C trace function is not. So if we're using a + # Python trace function, we won't trace anything after the stack + # overflow, and there should be a warning about it. If we're using + # the C trace function, only line 3 will be missing, and all else + # will be traced. + + self.make_file("recur.py", """\ + def recur(n): + if n == 0: + return 0 # never hit + else: + return recur(n-1)+1 + + try: + recur(100000) # This is definitely too many frames. + except RuntimeError: + i = 10 + i = 11 + """) + + cov = coverage.coverage() + self.start_import_stop(cov, "recur") + + pytrace = (cov.collector.tracer_name() == "PyTracer") + expected_missing = [3] + if pytrace: + expected_missing += [9,10,11] + + _, statements, missing, _ = cov.analysis("recur.py") + self.assertEqual(statements, [1,2,3,5,7,8,9,10,11]) + self.assertEqual(missing, expected_missing) + + # We can get a warning about the stackoverflow effect on the tracing + # function only if we have sys.gettrace + if pytrace and hasattr(sys, "gettrace"): + self.assertEqual(cov._warnings, + ["Trace function changed, measurement is likely wrong: None"] + ) + else: + self.assertEqual(cov._warnings, []) + + +class MemoryLeakTest(CoverageTest): + """Attempt the impossible: test that memory doesn't leak. + + Note: this test is truly unusual, and may fail unexpectedly. + In particular, it is known to fail on PyPy if test_oddball.py is run in + isolation: https://bitbucket.org/ned/coveragepy/issue/186 + + """ + + def test_for_leaks(self): + lines = list(range(301, 315)) + lines.remove(306) + # Ugly string mumbo jumbo to get 300 blank lines at the beginning.. + code = """\ + # blank line\n""" * 300 + """\ + def once(x): + if x % 100 == 0: + raise Exception("100!") + elif x % 2: + return 10 + else: + return 11 + i = 0 # Portable loop without alloc'ing memory. + while i < ITERS: + try: + once(i) + except: + pass + i += 1 + """ + ram_0 = osinfo.process_ram() + self.check_coverage(code.replace("ITERS", "10"), lines, "") + ram_1 = osinfo.process_ram() + self.check_coverage(code.replace("ITERS", "10000"), lines, "") + ram_2 = osinfo.process_ram() + ram_growth = (ram_2 - ram_1) - (ram_1 - ram_0) + self.assertTrue(ram_growth < 100000, "RAM grew by %d" % (ram_growth)) + + +class PyexpatTest(CoverageTest): + """Pyexpat screws up tracing. Make sure we've counter-defended properly.""" + + def test_pyexpat(self): + # pyexpat calls the trace function explicitly (inexplicably), and does + # it wrong for exceptions. Parsing a DOCTYPE for some reason throws + # an exception internally, and triggers its wrong behavior. This test + # checks that our fake PyTrace_RETURN hack in tracer.c works. It will + # also detect if the pyexpat bug is fixed unbeknownst to us, meaning + # we'd see two RETURNs where there should only be one. + + self.make_file("trydom.py", """\ + import xml.dom.minidom + + XML = '''\\ + + + ''' + + def foo(): + dom = xml.dom.minidom.parseString(XML) + assert len(dom.getElementsByTagName('child')) == 2 + a = 11 + + foo() + """) + + self.make_file("outer.py", "\n"*100 + "import trydom\na = 102\n") + + cov = coverage.coverage() + cov.erase() + + # Import the python file, executing it. + self.start_import_stop(cov, "outer") + + _, statements, missing, _ = cov.analysis("trydom.py") + self.assertEqual(statements, [1,3,8,9,10,11,13]) + self.assertEqual(missing, []) + + _, statements, missing, _ = cov.analysis("outer.py") + self.assertEqual(statements, [101,102]) + self.assertEqual(missing, []) + + +class ExceptionTest(CoverageTest): + """I suspect different versions of Python deal with exceptions differently + in the trace function. + """ + + def test_exception(self): + # Python 2.3's trace function doesn't get called with "return" if the + # scope is exiting due to an exception. This confounds our trace + # function which relies on scope announcements to track which files to + # trace. + # + # This test is designed to sniff this out. Each function in the call + # stack is in a different file, to try to trip up the tracer. Each + # file has active lines in a different range so we'll see if the lines + # get attributed to the wrong file. + + self.make_file("oops.py", """\ + def oops(args): + a = 2 + raise Exception("oops") + a = 4 + """) + + self.make_file("fly.py", "\n"*100 + """\ + def fly(calls): + a = 2 + calls[0](calls[1:]) + a = 4 + """) + + self.make_file("catch.py", "\n"*200 + """\ + def catch(calls): + try: + a = 3 + calls[0](calls[1:]) + a = 5 + except: + a = 7 + """) + + self.make_file("doit.py", "\n"*300 + """\ + def doit(calls): + try: + calls[0](calls[1:]) + except: + a = 5 + """) + + # Import all the modules before starting coverage, so the def lines + # won't be in all the results. + for mod in "oops fly catch doit".split(): + self.import_local_file(mod) + + # Each run nests the functions differently to get different + # combinations of catching exceptions and letting them fly. + runs = [ + ("doit fly oops", { + 'doit.py': [302,303,304,305], + 'fly.py': [102,103], + 'oops.py': [2,3], + }), + ("doit catch oops", { + 'doit.py': [302,303], + 'catch.py': [202,203,204,206,207], + 'oops.py': [2,3], + }), + ("doit fly catch oops", { + 'doit.py': [302,303], + 'fly.py': [102,103,104], + 'catch.py': [202,203,204,206,207], + 'oops.py': [2,3], + }), + ("doit catch fly oops", { + 'doit.py': [302,303], + 'catch.py': [202,203,204,206,207], + 'fly.py': [102,103], + 'oops.py': [2,3], + }), + ] + + 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()] + + cov = coverage.coverage() + cov.start() + # Call our list of functions: invoke the first, with the rest as + # an argument. + calls[0](calls[1:]) # pragma: nested + cov.stop() # pragma: nested + + # 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(): + if f == __file__: + # ignore this file. + continue + clean_lines[os.path.basename(f)] = llist + self.assertEqual(clean_lines, lines_expected) + + +if sys.version_info >= (2, 5): + class DoctestTest(CoverageTest): + """Tests invoked with doctest should measure properly.""" + + def setUp(self): + super(DoctestTest, self).setUp() + + # Oh, the irony! This test case exists because Python 2.4's + # doctest module doesn't play well with coverage. But nose fixes + # the problem by monkeypatching doctest. I want to undo the + # monkeypatch to be sure I'm getting the doctest module that users + # of coverage will get. Deleting the imported module here is + # enough: when the test imports doctest again, it will get a fresh + # copy without the monkeypatch. + del sys.modules['doctest'] + + def test_doctest(self): + self.check_coverage('''\ + def return_arg_or_void(arg): + """If is None, return "Void"; otherwise return + + >>> return_arg_or_void(None) + 'Void' + >>> return_arg_or_void("arg") + 'arg' + >>> return_arg_or_void("None") + 'None' + """ + if arg is None: + return "Void" + else: + return arg + + import doctest, sys + doctest.testmod(sys.modules[__name__]) # we're not __main__ :( + ''', + [1,11,12,14,16,17], "") + + +if hasattr(sys, 'gettrace'): + class GettraceTest(CoverageTest): + """Tests that we work properly with `sys.gettrace()`.""" + def test_round_trip(self): + self.check_coverage('''\ + import sys + def foo(n): + return 3*n + def bar(n): + return 5*n + a = foo(6) + sys.settrace(sys.gettrace()) + a = bar(8) + ''', + [1,2,3,4,5,6,7,8], "") + + def test_multi_layers(self): + self.check_coverage('''\ + import sys + def level1(): + a = 3 + level2() + b = 5 + def level2(): + c = 7 + sys.settrace(sys.gettrace()) + d = 9 + e = 10 + level1() + f = 12 + ''', + [1,2,3,4,5,6,7,8,9,10,11,12], "") diff -Nru python-coverage-3.4/test/test_parser.py python-coverage-3.6/test/test_parser.py --- python-coverage-3.4/test/test_parser.py 1970-01-01 00:00:00.000000000 +0000 +++ python-coverage-3.6/test/test_parser.py 2012-12-30 15:17:29.000000000 +0000 @@ -0,0 +1,131 @@ +"""Tests for Coverage.py's code parsing.""" + +import textwrap +from test.coveragetest import CoverageTest +from coverage.parser import CodeParser + + +class ParserTest(CoverageTest): + """Tests for Coverage.py's code parsing.""" + + run_in_temp_dir = False + + def parse_source(self, text): + """Parse `text` as source, and return the `CodeParser` used.""" + text = textwrap.dedent(text) + cp = CodeParser(text=text, exclude="nocover") + cp.parse_source() + return cp + + def test_exit_counts(self): + cp = self.parse_source("""\ + # check some basic branch counting + class Foo: + def foo(self, a): + if a: + return 5 + else: + return 7 + + class Bar: + pass + """) + self.assertEqual(cp.exit_counts(), { + 2:1, 3:1, 4:2, 5:1, 7:1, 9:1, 10:1 + }) + + def test_try_except(self): + cp = self.parse_source("""\ + try: + a = 2 + except ValueError: + a = 4 + except ZeroDivideError: + a = 6 + except: + a = 8 + b = 9 + """) + self.assertEqual(cp.exit_counts(), { + 1: 1, 2:1, 3:1, 4:1, 5:1, 6:1, 7:1, 8:1, 9:1 + }) + + def test_excluded_classes(self): + cp = self.parse_source("""\ + class Foo: + def __init__(self): + pass + + if 0: # nocover + class Bar: + pass + """) + self.assertEqual(cp.exit_counts(), { + 1:0, 2:1, 3:1 + }) + + def test_missing_branch_to_excluded_code(self): + cp = self.parse_source("""\ + if fooey: + a = 2 + else: # nocover + a = 4 + b = 5 + """) + self.assertEqual(cp.exit_counts(), { 1:1, 2:1, 5:1 }) + cp = self.parse_source("""\ + def foo(): + if fooey: + a = 3 + else: + a = 5 + b = 6 + """) + self.assertEqual(cp.exit_counts(), { 1:1, 2:2, 3:1, 5:1, 6:1 }) + cp = self.parse_source("""\ + def foo(): + if fooey: + a = 3 + else: # nocover + a = 5 + b = 6 + """) + self.assertEqual(cp.exit_counts(), { 1:1, 2:1, 3:1, 6:1 }) + + +class ParserFileTest(CoverageTest): + """Tests for Coverage.py's code parsing from files.""" + + def parse_file(self, filename): + """Parse `text` as source, and return the `CodeParser` used.""" + cp = CodeParser(filename=filename, exclude="nocover") + cp.parse_source() + return cp + + def test_line_endings(self): + text = """\ + # check some basic branch counting + class Foo: + def foo(self, a): + if a: + return 5 + else: + return 7 + + class Bar: + pass + """ + counts = { 2:1, 3:1, 4:2, 5:1, 7:1, 9:1, 10:1 } + name_endings = (("unix", "\n"), ("dos", "\r\n"), ("mac", "\r")) + for fname, newline in name_endings: + fname = fname + ".py" + self.make_file(fname, text, newline=newline) + cp = self.parse_file(fname) + self.assertEqual(cp.exit_counts(), counts) + + def test_encoding(self): + self.make_file("encoded.py", """\ + coverage = "\xe7\xf6v\xear\xe3g\xe9" + """) + cp = self.parse_file("encoded.py") + cp.exit_counts() diff -Nru python-coverage-3.4/test/test_phystokens.py python-coverage-3.6/test/test_phystokens.py --- python-coverage-3.4/test/test_phystokens.py 1970-01-01 00:00:00.000000000 +0000 +++ python-coverage-3.6/test/test_phystokens.py 2012-12-30 15:17:29.000000000 +0000 @@ -0,0 +1,79 @@ +"""Tests for Coverage.py's improved tokenizer.""" + +import os, re +from test.coveragetest import CoverageTest +from coverage.phystokens import source_token_lines + + +SIMPLE = """\ +# yay! +def foo(): + say('two = %d' % 2) +""" + +MIXED_WS = """\ +def hello(): + a="Hello world!" +\tb="indented" +""" + +HERE = os.path.split(__file__)[0] + + +class PhysTokensTest(CoverageTest): + """Tests for Coverage.py's improver tokenizer.""" + + run_in_temp_dir = False + + def check_tokenization(self, source): + """Tokenize `source`, then put it back together, should be the same.""" + tokenized = "" + for line in source_token_lines(source): + text = "".join([t for _,t in line]) + tokenized += text + "\n" + # source_token_lines doesn't preserve trailing spaces, so trim all that + # before comparing. + source = source.replace('\r\n', '\n') + source = re.sub(r"(?m)[ \t]+$", "", source) + tokenized = re.sub(r"(?m)[ \t]+$", "", tokenized) + self.assertMultiLineEqual(source, tokenized) + + def check_file_tokenization(self, fname): + """Use the contents of `fname` for `check_tokenization`.""" + self.check_tokenization(open(fname).read()) + + def test_simple(self): + self.assertEqual(list(source_token_lines(SIMPLE)), + [ + [('com', "# yay!")], + [('key', 'def'), ('ws', ' '), ('nam', 'foo'), ('op', '('), + ('op', ')'), ('op', ':')], + [('ws', ' '), ('nam', 'say'), ('op', '('), + ('str', "'two = %d'"), ('ws', ' '), ('op', '%'), + ('ws', ' '), ('num', '2'), ('op', ')')] + ]) + self.check_tokenization(SIMPLE) + + def test_tab_indentation(self): + # Mixed tabs and spaces... + self.assertEqual(list(source_token_lines(MIXED_WS)), + [ + [('key', 'def'), ('ws', ' '), ('nam', 'hello'), ('op', '('), + ('op', ')'), ('op', ':')], + [('ws', ' '), ('nam', 'a'), ('op', '='), + ('str', '"Hello world!"')], + [('ws', ' '), ('nam', 'b'), ('op', '='), + ('str', '"indented"')], + ]) + + def test_tokenize_real_file(self): + # Check the tokenization of a real file (large, btw). + real_file = os.path.join(HERE, "test_coverage.py") + self.check_file_tokenization(real_file) + + def test_stress(self): + # Check the tokenization of a stress-test file. + stress = os.path.join(HERE, "stress_phystoken.tok") + self.check_file_tokenization(stress) + stress = os.path.join(HERE, "stress_phystoken_dos.tok") + self.check_file_tokenization(stress) diff -Nru python-coverage-3.4/test/test_process.py python-coverage-3.6/test/test_process.py --- python-coverage-3.4/test/test_process.py 1970-01-01 00:00:00.000000000 +0000 +++ python-coverage-3.6/test/test_process.py 2013-01-02 01:44:20.000000000 +0000 @@ -0,0 +1,575 @@ +"""Tests for process behavior of coverage.py.""" + +import glob, os, sys, textwrap +from nose.plugins.skip import SkipTest +import coverage + +from test.coveragetest import CoverageTest + +here = os.path.dirname(__file__) + +class ProcessTest(CoverageTest): + """Tests of the per-process behavior of coverage.py.""" + + def number_of_data_files(self): + """Return the number of coverage data files in this directory.""" + num = 0 + for f in os.listdir('.'): + if f.startswith('.coverage.') or f == '.coverage': + num += 1 + return num + + def test_save_on_exit(self): + self.make_file("mycode.py", """\ + h = "Hello" + w = "world" + """) + + self.assert_doesnt_exist(".coverage") + self.run_command("coverage -x mycode.py") + self.assert_exists(".coverage") + + def test_environment(self): + # Checks that we can import modules from the test directory at all! + self.make_file("mycode.py", """\ + import covmod1 + import covmodzip1 + a = 1 + print ('done') + """) + + self.assert_doesnt_exist(".coverage") + out = self.run_command("coverage -x mycode.py") + self.assert_exists(".coverage") + self.assertEqual(out, 'done\n') + + def test_combine_parallel_data(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') + """) + + out = self.run_command("coverage -x -p b_or_c.py b") + self.assertEqual(out, 'done\n') + self.assert_doesnt_exist(".coverage") + + out = self.run_command("coverage -x -p b_or_c.py c") + self.assertEqual(out, 'done\n') + self.assert_doesnt_exist(".coverage") + + # After two -p runs, there should be two .coverage.machine.123 files. + self.assertEqual(self.number_of_data_files(), 2) + + # Combine the parallel coverage data files into .coverage . + self.run_command("coverage -c") + self.assert_exists(".coverage") + + # After combining, there should be only the .coverage file. + 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.summary()['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') + """) + + out = self.run_command("coverage -x -p b_or_c.py b") + self.assertEqual(out, 'done\n') + self.assert_doesnt_exist(".coverage") + self.assertEqual(self.number_of_data_files(), 1) + + # Combine the (one) parallel coverage data file into .coverage . + self.run_command("coverage -c") + self.assert_exists(".coverage") + self.assertEqual(self.number_of_data_files(), 1) + + out = self.run_command("coverage -x -p b_or_c.py c") + self.assertEqual(out, 'done\n') + self.assert_exists(".coverage") + self.assertEqual(self.number_of_data_files(), 2) + + # Combine the parallel coverage data files into .coverage . + self.run_command("coverage -c") + self.assert_exists(".coverage") + + # After combining, there should be only the .coverage file. + 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.summary()['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') + """) + + self.make_file(".coveragerc", """\ + [run] + parallel = true + """) + + out = self.run_command("coverage run b_or_c.py b") + self.assertEqual(out, 'done\n') + self.assert_doesnt_exist(".coverage") + + out = self.run_command("coverage run b_or_c.py c") + self.assertEqual(out, 'done\n') + self.assert_doesnt_exist(".coverage") + + # After two runs, there should be two .coverage.machine.123 files. + self.assertEqual(self.number_of_data_files(), 2) + + # Combine the parallel coverage data files into .coverage . + self.run_command("coverage combine") + self.assert_exists(".coverage") + self.assert_exists(".coveragerc") + + # After combining, there should be only the .coverage file. + 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.summary()['b_or_c.py'], 7) + + # Reporting should still work even with the .rc file + out = self.run_command("coverage report") + self.assertMultiLineEqual(out, textwrap.dedent("""\ + Name Stmts Miss Cover + ---------------------------- + b_or_c 7 0 100% + """)) + + def test_combine_with_aliases(self): + self.make_file("d1/x.py", """\ + a = 1 + b = 2 + print("%s %s" % (a, b)) + """) + + self.make_file("d2/x.py", """\ + # 1 + # 2 + # 3 + c = 4 + d = 5 + print("%s %s" % (c, d)) + """) + + self.make_file(".coveragerc", """\ + [run] + parallel = True + + [paths] + source = + src + */d1 + */d2 + """) + + out = self.run_command("coverage run " + os.path.normpath("d1/x.py")) + self.assertEqual(out, '1 2\n') + out = self.run_command("coverage run " + os.path.normpath("d2/x.py")) + self.assertEqual(out, '4 5\n') + + self.assertEqual(self.number_of_data_files(), 2) + + self.run_command("coverage combine") + self.assert_exists(".coverage") + + # After combining, there should be only the .coverage file. + self.assertEqual(self.number_of_data_files(), 1) + + # Read the coverage data file and see that the two different x.py + # files have been combined together. + data = coverage.CoverageData() + data.read_file(".coverage") + summary = data.summary(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_missing_source_file(self): + # Check what happens if the source is missing when reporting happens. + self.make_file("fleeting.py", """\ + s = 'goodbye, cruel world!' + """) + + self.run_command("coverage run fleeting.py") + os.remove("fleeting.py") + out = self.run_command("coverage html -d htmlcov") + self.assertRegexpMatches(out, "No source for code: '.*fleeting.py'") + self.assertNotIn("Traceback", out) + + # It happens that the code paths are different for *.py and other + # files, so try again with no extension. + self.make_file("fleeting", """\ + s = 'goodbye, cruel world!' + """) + + self.run_command("coverage run fleeting") + os.remove("fleeting") + status, out = self.run_command_status("coverage html -d htmlcov", 1) + self.assertRegexpMatches(out, "No source for code: '.*fleeting'") + self.assertNotIn("Traceback", out) + self.assertEqual(status, 1) + + def test_running_missing_file(self): + status, out = self.run_command_status("coverage run xyzzy.py", 1) + self.assertRegexpMatches(out, "No file to run: .*xyzzy.py") + self.assertNotIn("raceback", out) + self.assertNotIn("rror", out) + self.assertEqual(status, 1) + + def test_code_throws(self): + self.make_file("throw.py", """\ + def f1(): + raise Exception("hey!") + + def f2(): + f1() + + f2() + """) + + # The important thing is for "coverage run" and "python" to report the + # same traceback. + status, out = self.run_command_status("coverage run throw.py", 1) + out2 = self.run_command("python throw.py") + if '__pypy__' in sys.builtin_module_names: + # Pypy has an extra frame in the traceback for some reason + lines2 = out2.splitlines() + out2 = "".join([l+"\n" for l in lines2 if "toplevel" not in l]) + self.assertMultiLineEqual(out, out2) + + # But also make sure that the output is what we expect. + self.assertIn('File "throw.py", line 5, in f2', out) + self.assertIn('raise Exception("hey!")', out) + self.assertNotIn('coverage', out) + self.assertEqual(status, 1) + + def test_code_exits(self): + self.make_file("exit.py", """\ + import sys + def f1(): + print("about to exit..") + sys.exit(17) + + def f2(): + f1() + + f2() + """) + + # The important thing is for "coverage run" and "python" to have the + # same output. No traceback. + status, out = self.run_command_status("coverage run exit.py", 17) + status2, out2 = self.run_command_status("python exit.py", 17) + self.assertMultiLineEqual(out, out2) + self.assertMultiLineEqual(out, "about to exit..\n") + self.assertEqual(status, status2) + self.assertEqual(status, 17) + + def test_code_exits_no_arg(self): + self.make_file("exit_none.py", """\ + import sys + def f1(): + print("about to exit quietly..") + sys.exit() + + f1() + """) + status, out = self.run_command_status("coverage run exit_none.py", 0) + status2, out2 = self.run_command_status("python exit_none.py", 0) + self.assertMultiLineEqual(out, out2) + self.assertMultiLineEqual(out, "about to exit quietly..\n") + self.assertEqual(status, status2) + self.assertEqual(status, 0) + + def test_coverage_run_is_like_python(self): + tryfile = os.path.join(here, "try_execfile.py") + self.make_file("run_me.py", open(tryfile).read()) + out = self.run_command("coverage run run_me.py") + out2 = self.run_command("python run_me.py") + self.assertMultiLineEqual(out, out2) + + if sys.version_info >= (2, 6): # Doesn't work in 2.5, and I don't care! + def test_coverage_run_dashm_is_like_python_dashm(self): + # These -m commands assume the coverage tree is on the path. + out = self.run_command("coverage run -m test.try_execfile") + out2 = self.run_command("python -m test.try_execfile") + self.assertMultiLineEqual(out, out2) + + if 0: # Expected failure + # For https://bitbucket.org/ned/coveragepy/issue/207 + def test_coverage_run_dashm_is_like_python_dashm_with__main__207(self): + self.make_file("package/__init__.py") # empty + self.make_file("package/__main__.py", "#\n") # empty + out = self.run_command("coverage run -m package") + out2 = self.run_command("python -m package") + self.assertMultiLineEqual(out, out2) + + if hasattr(os, 'fork'): + def test_fork(self): + self.make_file("fork.py", """\ + import os + + def child(): + print('Child!') + + def main(): + ret = os.fork() + + if ret == 0: + child() + else: + os.waitpid(ret, 0) + + main() + """) + + out = self.run_command("coverage run -p fork.py") + self.assertEqual(out, 'Child!\n') + self.assert_doesnt_exist(".coverage") + + # After running the forking program, there should be two + # .coverage.machine.123 files. + self.assertEqual(self.number_of_data_files(), 2) + + # Combine the parallel coverage data files into .coverage . + self.run_command("coverage -c") + self.assert_exists(".coverage") + + # After combining, there should be only the .coverage file. + 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.summary()['fork.py'], 9) + + def test_warnings(self): + self.make_file("hello.py", """\ + import sys, os + print("Hello") + """) + out = self.run_command("coverage run --source=sys,xyzzy,quux hello.py") + + self.assertIn("Hello\n", out) + self.assertIn(textwrap.dedent("""\ + Coverage.py warning: Module sys has no Python source. + Coverage.py warning: Module xyzzy was never imported. + Coverage.py warning: Module quux was never imported. + Coverage.py warning: No data was collected. + """), out) + + def test_warnings_during_reporting(self): + # While fixing issue #224, the warnings were being printed far too + # often. Make sure they're not any more. + self.make_file("hello.py", """\ + import sys, os, the_other + print("Hello") + """) + self.make_file("the_other.py", """\ + print("What?") + """) + self.make_file(".coveragerc", """\ + [run] + source = + . + xyzzy + """) + + self.run_command("coverage run hello.py") + out = self.run_command("coverage html") + self.assertEqual(out.count("Module xyzzy was never imported."), 0) + + def test_warnings_if_never_run(self): + out = self.run_command("coverage run i_dont_exist.py") + self.assertIn("No file to run: 'i_dont_exist.py'", out) + self.assertNotIn("warning", out) + self.assertNotIn("Exception", out) + + out = self.run_command("coverage run -m no_such_module") + self.assertTrue( + ("No module named no_such_module" in out) or + ("No module named 'no_such_module'" in out) + ) + self.assertNotIn("warning", out) + self.assertNotIn("Exception", out) + + if sys.version_info >= (3, 0): # This only works on 3.x for now. + # It only works with the C tracer, + c_tracer = os.getenv('COVERAGE_TEST_TRACER', 'c') == 'c' + # and if we aren't measuring ourselves. + metacov = os.getenv('COVERAGE_COVERAGE', '') != '' + if c_tracer and not metacov: # pragma: not covered + def test_fullcoverage(self): + # fullcoverage is a trick to get stdlib modules measured from + # the very beginning of the process. Here we import os and + # then check how many lines are measured. + self.make_file("getenv.py", """\ + import os + print("FOOEY == %s" % os.getenv("FOOEY")) + """) + + fullcov = os.path.join( + os.path.dirname(coverage.__file__), "fullcoverage" + ) + self.set_environ("FOOEY", "BOO") + self.set_environ("PYTHONPATH", fullcov) + out = self.run_command("python -m coverage run -L getenv.py") + self.assertEqual(out, "FOOEY == BOO\n") + data = coverage.CoverageData() + data.read_file(".coverage") + # 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) + + +class AliasedCommandTests(CoverageTest): + """Tests of the version-specific command aliases.""" + + def test_major_version_works(self): + # "coverage2" works on py2 + cmd = "coverage%d" % sys.version_info[0] + out = self.run_command(cmd) + self.assertIn("Code coverage for Python", out) + + def test_wrong_alias_doesnt_work(self): + # "coverage3" doesn't work on py2 + badcmd = "coverage%d" % (5 - sys.version_info[0]) + out = self.run_command(badcmd) + self.assertNotIn("Code coverage for Python", out) + + def test_specific_alias_works(self): + # "coverage-2.7" works on py2.7 + cmd = "coverage-%d.%d" % sys.version_info[:2] + out = self.run_command(cmd) + self.assertIn("Code coverage for Python", out) + + +class FailUnderTest(CoverageTest): + """Tests of the --fail-under switch.""" + + def setUp(self): + super(FailUnderTest, self).setUp() + self.make_file("fifty.py", """\ + # I have 50% coverage! + a = 1 + if a > 2: + b = 3 + c = 4 + """) + st, _ = self.run_command_status("coverage run fifty.py", 0) + self.assertEqual(st, 0) + + def test_report(self): + st, _ = self.run_command_status("coverage report --fail-under=50", 0) + self.assertEqual(st, 0) + st, _ = self.run_command_status("coverage report --fail-under=51", 2) + self.assertEqual(st, 2) + + def test_html_report(self): + st, _ = self.run_command_status("coverage html --fail-under=50", 0) + self.assertEqual(st, 0) + st, _ = self.run_command_status("coverage html --fail-under=51", 2) + self.assertEqual(st, 2) + + def test_xml_report(self): + st, _ = self.run_command_status("coverage xml --fail-under=50", 0) + self.assertEqual(st, 0) + st, _ = self.run_command_status("coverage xml --fail-under=51", 2) + self.assertEqual(st, 2) + + +class ProcessStartupTest(CoverageTest): + """Test that we can measure coverage in subprocesses.""" + + def setUp(self): + super(ProcessStartupTest, self).setUp() + # Find a place to put a .pth file. + pth_contents = "import coverage; coverage.process_startup()\n" + for d in sys.path: # pragma: part covered + g = glob.glob(os.path.join(d, "*.pth")) + if g: + pth_path = os.path.join(d, "subcover.pth") + pth = open(pth_path, "w") + try: + try: + pth.write(pth_contents) + self.pth_path = pth_path + break + except (IOError, OSError): # pragma: not covered + pass + finally: + pth.close() + else: # pragma: not covered + raise Exception("Couldn't find a place for the .pth file") + + def tearDown(self): + super(ProcessStartupTest, self).tearDown() + # Clean up the .pth file we made. + os.remove(self.pth_path) + + def test_subprocess_with_pth_files(self): # pragma: not covered + if os.environ.get('COVERAGE_COVERAGE', ''): + raise SkipTest( + "Can't test subprocess pth file suppport during metacoverage" + ) + # Main will run sub.py + self.make_file("main.py", """\ + import os + os.system("python sub.py") + """) + # sub.py will write a few lines. + self.make_file("sub.py", """\ + f = open("out.txt", "w") + f.write("Hello, world!\\n") + f.close() + """) + self.make_file("coverage.ini", """\ + [run] + data_file = .mycovdata + """) + self.set_environ("COVERAGE_PROCESS_START", "coverage.ini") + import main # pylint: disable=F0401,W0612 + + self.assertEqual(open("out.txt").read(), "Hello, world!\n") + # Read the data from .coverage + data = coverage.CoverageData() + data.read_file(".mycovdata") + self.assertEqual(data.summary()['sub.py'], 3) diff -Nru python-coverage-3.4/test/test_results.py python-coverage-3.6/test/test_results.py --- python-coverage-3.4/test/test_results.py 1970-01-01 00:00:00.000000000 +0000 +++ python-coverage-3.6/test/test_results.py 2012-12-30 15:17:29.000000000 +0000 @@ -0,0 +1,60 @@ +"""Tests for Coverage.py's results analysis.""" + +from coverage.results import Numbers +from test.coveragetest import CoverageTest + + +class NumbersTest(CoverageTest): + """Tests for Coverage.py's numeric measurement summaries.""" + + run_in_temp_dir = False + + def test_basic(self): + n1 = Numbers(n_files=1, n_statements=200, n_missing=20) + self.assertEqual(n1.n_statements, 200) + self.assertEqual(n1.n_executed, 180) + self.assertEqual(n1.n_missing, 20) + self.assertEqual(n1.pc_covered, 90) + + def test_addition(self): + n1 = Numbers(n_files=1, n_statements=200, n_missing=20) + n2 = Numbers(n_files=1, n_statements=10, n_missing=8) + n3 = n1 + n2 + self.assertEqual(n3.n_files, 2) + self.assertEqual(n3.n_statements, 210) + self.assertEqual(n3.n_executed, 182) + self.assertEqual(n3.n_missing, 28) + self.assertAlmostEqual(n3.pc_covered, 86.666666666) + + def test_sum(self): + n1 = Numbers(n_files=1, n_statements=200, n_missing=20) + n2 = Numbers(n_files=1, n_statements=10, n_missing=8) + n3 = sum([n1, n2]) + self.assertEqual(n3.n_files, 2) + self.assertEqual(n3.n_statements, 210) + self.assertEqual(n3.n_executed, 182) + self.assertEqual(n3.n_missing, 28) + self.assertAlmostEqual(n3.pc_covered, 86.666666666) + + def test_pc_covered_str(self): + n0 = Numbers(n_files=1, n_statements=1000, n_missing=0) + n1 = Numbers(n_files=1, n_statements=1000, n_missing=1) + n999 = Numbers(n_files=1, n_statements=1000, n_missing=999) + n1000 = Numbers(n_files=1, n_statements=1000, n_missing=1000) + self.assertEqual(n0.pc_covered_str, "100") + self.assertEqual(n1.pc_covered_str, "99") + self.assertEqual(n999.pc_covered_str, "1") + self.assertEqual(n1000.pc_covered_str, "0") + + def test_pc_covered_str_precision(self): + assert Numbers._precision == 0 + Numbers.set_precision(1) + n0 = Numbers(n_files=1, n_statements=10000, n_missing=0) + n1 = Numbers(n_files=1, n_statements=10000, n_missing=1) + n9999 = Numbers(n_files=1, n_statements=10000, n_missing=9999) + n10000 = Numbers(n_files=1, n_statements=10000, n_missing=10000) + self.assertEqual(n0.pc_covered_str, "100.0") + self.assertEqual(n1.pc_covered_str, "99.9") + self.assertEqual(n9999.pc_covered_str, "0.1") + self.assertEqual(n10000.pc_covered_str, "0.0") + Numbers.set_precision(0) diff -Nru python-coverage-3.4/test/test_summary.py python-coverage-3.6/test/test_summary.py --- python-coverage-3.4/test/test_summary.py 1970-01-01 00:00:00.000000000 +0000 +++ python-coverage-3.6/test/test_summary.py 2012-12-30 15:17:29.000000000 +0000 @@ -0,0 +1,298 @@ +"""Test text-based summary reporting for coverage.py""" + +import os, re, sys + +import coverage +from coverage.backward import StringIO + +from test.coveragetest import CoverageTest + +class SummaryTest(CoverageTest): + """Tests of the text summary reporting for coverage.py.""" + + def setUp(self): + super(SummaryTest, self).setUp() + 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(os.path.dirname(__file__), 'modules')) + + def report_from_command(self, cmd): + """Return the report from the `cmd`, with some convenience added.""" + report = self.run_command(cmd).replace('\\', '/') + self.assertNotIn("error", report.lower()) + return report + + def line_count(self, report): + """How many lines are in `report`?""" + self.assertEqual(report.split('\n')[-1], "") + return len(report.split('\n')) - 1 + + def last_line_squeezed(self, report): + """Return the last line of `report` with the spaces squeezed down.""" + last_line = report.split('\n')[-2] + return re.sub(r"\s+", " ", last_line) + + def test_report(self): + out = self.run_command("coverage -x mycode.py") + self.assertEqual(out, 'done\n') + report = self.report_from_command("coverage -r") + + # Name Stmts Miss Cover + # --------------------------------------------------------------------- + # c:/ned/coverage/trunk/test/modules/covmod1 2 0 100% + # c:/ned/coverage/trunk/test/zipmods.zip/covmodzip1 2 0 100% + # mycode 4 0 100% + # --------------------------------------------------------------------- + # TOTAL 8 0 100% + + self.assertNotIn("/coverage/__init__/", report) + self.assertIn("/test/modules/covmod1 ", report) + self.assertIn("/test/zipmods.zip/covmodzip1 ", report) + self.assertIn("mycode ", report) + self.assertEqual(self.last_line_squeezed(report), "TOTAL 8 0 100%") + + def test_report_just_one(self): + # Try reporting just one module + self.run_command("coverage -x mycode.py") + report = self.report_from_command("coverage -r mycode.py") + + # Name Stmts Miss Cover + # ---------------------------- + # mycode 4 0 100% + + self.assertEqual(self.line_count(report), 3) + self.assertNotIn("/coverage/", report) + self.assertNotIn("/test/modules/covmod1 ", report) + self.assertNotIn("/test/zipmods.zip/covmodzip1 ", report) + self.assertIn("mycode ", report) + self.assertEqual(self.last_line_squeezed(report), "mycode 4 0 100%") + + def test_report_omitting(self): + # Try reporting while omitting some modules + prefix = os.path.split(__file__)[0] + self.run_command("coverage -x mycode.py") + report = self.report_from_command("coverage -r -o '%s/*'" % prefix) + + # Name Stmts Miss Cover + # ---------------------------- + # mycode 4 0 100% + + self.assertEqual(self.line_count(report), 3) + self.assertNotIn("/coverage/", report) + self.assertNotIn("/test/modules/covmod1 ", report) + self.assertNotIn("/test/zipmods.zip/covmodzip1 ", report) + self.assertIn("mycode ", report) + self.assertEqual(self.last_line_squeezed(report), "mycode 4 0 100%") + + def test_report_including(self): + # Try reporting while including some modules + self.run_command("coverage run mycode.py") + report = self.report_from_command("coverage report --include=mycode*") + + # Name Stmts Miss Cover + # ---------------------------- + # mycode 4 0 100% + + self.assertEqual(self.line_count(report), 3) + self.assertNotIn("/coverage/", report) + self.assertNotIn("/test/modules/covmod1 ", report) + self.assertNotIn("/test/zipmods.zip/covmodzip1 ", report) + self.assertIn("mycode ", report) + self.assertEqual(self.last_line_squeezed(report), "mycode 4 0 100%") + + def test_report_branches(self): + self.make_file("mybranch.py", """\ + def branch(x): + if x: + print("x") + return x + branch(1) + """) + out = self.run_command("coverage run --branch mybranch.py") + self.assertEqual(out, 'x\n') + report = self.report_from_command("coverage report") + + # Name Stmts Miss Branch BrMiss Cover + # -------------------------------------------- + # mybranch 5 0 2 1 85% + + self.assertEqual(self.line_count(report), 3) + self.assertIn("mybranch ", report) + self.assertEqual(self.last_line_squeezed(report), + "mybranch 5 0 2 1 86%") + + def test_dotpy_not_python(self): + # 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.run_command("coverage run mycode.py") + self.make_file("mycode.py", "This isn't python at all!") + report = self.report_from_command("coverage -r mycode.py") + + # pylint: disable=C0301 + # Name Stmts Miss Cover + # ---------------------------- + # mycode NotPython: Couldn't parse '/tmp/test_cover/63354509363/mycode.py' as Python source: 'invalid syntax' at line 1 + + last = self.last_line_squeezed(report) + # 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 + last = re.sub(r": '.*' at", ": 'error' at", last) + self.assertEqual(last, + "mycode NotPython: " + "Couldn't parse 'mycode.py' as Python source: " + "'error' at line 1" + ) + + 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.run_command("coverage run mycode.py") + self.make_file("mycode.py", "This isn't python at all!") + report = self.report_from_command("coverage -r -i mycode.py") + + # Name Stmts Miss Cover + # ---------------------------- + + self.assertEqual(self.line_count(report), 2) + + def test_dothtml_not_python(self): + # We run a .html file, and when reporting, we can't parse it as + # Python. Since it wasn't .py, no error is reported. + + # Run an "html" file + self.make_file("mycode.html", "a = 1") + self.run_command("coverage run mycode.html") + # Before reporting, change it to be an HTML file. + self.make_file("mycode.html", "

    This isn't python at all!

    ") + report = self.report_from_command("coverage -r mycode.html") + + # Name Stmts Miss Cover + # ---------------------------- + + self.assertEqual(self.line_count(report), 2) + + def get_report(self, cov): + """Get the report from `cov`, and canonicalize it.""" + repout = StringIO() + cov.report(file=repout, show_missing=False) + report = repout.getvalue().replace('\\', '/') + report = re.sub(r" +", " ", report) + return report + + def test_bug_156_file_not_run_should_be_zero(self): + # https://bitbucket.org/ned/coveragepy/issue/156 + self.make_file("mybranch.py", """\ + def branch(x): + if x: + print("x") + return x + branch(1) + """) + self.make_file("main.py", """\ + print("y") + """) + cov = coverage.coverage(branch=True, source=["."]) + cov.start() + import main # pragma: nested # pylint: disable=F0401,W0612 + cov.stop() # pragma: nested + report = self.get_report(cov).splitlines() + self.assertIn("mybranch 5 5 2 2 0%", report) + + def run_TheCode_and_report_it(self): + """A helper for the next few tests.""" + cov = coverage.coverage() + cov.start() + import TheCode # pragma: nested # pylint: disable=F0401,W0612 + cov.stop() # pragma: nested + return self.get_report(cov) + + def test_bug_203_mixed_case_listed_twice_with_rc(self): + self.make_file("TheCode.py", "a = 1\n") + self.make_file(".coveragerc", "[run]\nsource = .\n") + + report = self.run_TheCode_and_report_it() + + self.assertIn("TheCode", report) + self.assertNotIn("thecode", report) + + def test_bug_203_mixed_case_listed_twice(self): + self.make_file("TheCode.py", "a = 1\n") + + report = self.run_TheCode_and_report_it() + + self.assertIn("TheCode", report) + self.assertNotIn("thecode", report) + + +class SummaryTest2(CoverageTest): + """Another bunch of summary tests.""" + # This class exists because tests naturally clump into classes based on the + # needs of their setUp and tearDown, rather than the product features they + # are testing. There's probably a better way to organize these. + + run_in_temp_dir = False + + def setUp(self): + super(SummaryTest2, self).setUp() + # Parent class saves and restores sys.path, we can just modify it. + this_dir = os.path.dirname(__file__) + sys.path.append(self.nice_file(this_dir, 'modules')) + sys.path.append(self.nice_file(this_dir, 'moremodules')) + + 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.start() + import usepkgs # pragma: nested # pylint: disable=F0401,W0612 + cov.stop() # pragma: nested + + repout = StringIO() + cov.report(file=repout, show_missing=False) + + report = repout.getvalue().replace('\\', '/') + report = re.sub(r"\s+", " ", report) + self.assertIn("test/modules/pkg1/__init__ 1 0 100%", report) + self.assertIn("test/modules/pkg2/__init__ 0 0 100%", report) + + +class ReportingReturnValue(CoverageTest): + """Tests of reporting functions returning values.""" + + def run_coverage(self): + """Run coverage on doit.py and return the coverage object.""" + self.make_file("doit.py", """\ + a = 1 + b = 2 + c = 3 + d = 4 + if a > 10: + f = 6 + g = 7 + """) + + cov = coverage.coverage() + self.start_import_stop(cov, "doit") + return cov + + def test_report(self): + cov = self.run_coverage() + val = cov.report(include="*/doit.py") + self.assertAlmostEqual(val, 85.7, 1) + + def test_html(self): + cov = self.run_coverage() + val = cov.html_report(include="*/doit.py") + self.assertAlmostEqual(val, 85.7, 1) + + def test_xml(self): + cov = self.run_coverage() + val = cov.xml_report(include="*/doit.py") + self.assertAlmostEqual(val, 85.7, 1) diff -Nru python-coverage-3.4/test/test_templite.py python-coverage-3.6/test/test_templite.py --- python-coverage-3.4/test/test_templite.py 1970-01-01 00:00:00.000000000 +0000 +++ python-coverage-3.6/test/test_templite.py 2011-02-05 04:20:13.000000000 +0000 @@ -0,0 +1,204 @@ +"""Tests for coverage.templite.""" + +from coverage.templite import Templite +import unittest + +# pylint: disable=W0612,E1101 +# Disable W0612 (Unused variable) and +# E1101 (Instance of 'foo' has no 'bar' member) + +class AnyOldObject(object): + """Simple testing object. + + Use keyword arguments in the constructor to set attributes on the object. + + """ + def __init__(self, **attrs): + for n, v in attrs.items(): + setattr(self, n, v) + + +class TempliteTest(unittest.TestCase): + """Tests for Templite.""" + + def try_render(self, text, ctx, result): + """Render `text` through `ctx`, and it had better be `result`.""" + self.assertEqual(Templite(text).render(ctx), result) + + def test_passthrough(self): + # Strings without variables are passed through unchanged. + self.assertEqual(Templite("Hello").render(), "Hello") + self.assertEqual( + Templite("Hello, 20% fun time!").render(), + "Hello, 20% fun time!" + ) + + def test_variables(self): + # Variables use {{var}} syntax. + self.try_render("Hello, {{name}}!", {'name':'Ned'}, "Hello, Ned!") + + def test_pipes(self): + # Variables can be filtered with pipes. + data = { + 'name': 'Ned', + 'upper': lambda x: x.upper(), + 'second': lambda x: x[1], + } + self.try_render("Hello, {{name|upper}}!", data, "Hello, NED!") + + # Pipes can be concatenated. + self.try_render("Hello, {{name|upper|second}}!", data, "Hello, E!") + + def test_reusability(self): + # A single Templite can be used more than once with different data. + globs = { + 'upper': lambda x: x.upper(), + 'punct': '!', + } + + template = Templite("This is {{name|upper}}{{punct}}", globs) + self.assertEqual(template.render({'name':'Ned'}), "This is NED!") + self.assertEqual(template.render({'name':'Ben'}), "This is BEN!") + + def test_attribute(self): + # Variables' attributes can be accessed with dots. + obj = AnyOldObject(a="Ay") + self.try_render("{{obj.a}}", locals(), "Ay") + + obj2 = AnyOldObject(obj=obj, b="Bee") + self.try_render("{{obj2.obj.a}} {{obj2.b}}", locals(), "Ay Bee") + + def test_member_function(self): + # Variables' member functions can be used, as long as they are nullary. + class WithMemberFns(AnyOldObject): + """A class to try out member function access.""" + def ditto(self): + """Return twice the .txt attribute.""" + return self.txt + self.txt + obj = WithMemberFns(txt="Once") + self.try_render("{{obj.ditto}}", locals(), "OnceOnce") + + def test_item_access(self): + # Variables' items can be used. + d = {'a':17, 'b':23} + self.try_render("{{d.a}} < {{d.b}}", locals(), "17 < 23") + + def test_loops(self): + # Loops work like in Django. + nums = [1,2,3,4] + self.try_render( + "Look: {% for n in nums %}{{n}}, {% endfor %}done.", + locals(), + "Look: 1, 2, 3, 4, done." + ) + # Loop iterables can be filtered. + def rev(l): + """Return the reverse of `l`.""" + l = l[:] + l.reverse() + return l + + self.try_render( + "Look: {% for n in nums|rev %}{{n}}, {% endfor %}done.", + locals(), + "Look: 4, 3, 2, 1, done." + ) + + def test_empty_loops(self): + self.try_render( + "Empty: {% for n in nums %}{{n}}, {% endfor %}done.", + {'nums':[]}, + "Empty: done." + ) + + def test_multiline_loops(self): + self.try_render( + "Look: \n{% for n in nums %}\n{{n}}, \n{% endfor %}done.", + {'nums':[1,2,3]}, + "Look: \n\n1, \n\n2, \n\n3, \ndone." + ) + + def test_multiple_loops(self): + self.try_render( + "{% for n in nums %}{{n}}{% endfor %} and " + "{% for n in nums %}{{n}}{% endfor %}", + {'nums': [1,2,3]}, + "123 and 123" + ) + + def test_comments(self): + # Single-line comments work: + self.try_render( + "Hello, {# Name goes here: #}{{name}}!", + {'name':'Ned'}, "Hello, Ned!" + ) + # and so do multi-line comments: + self.try_render( + "Hello, {# Name\ngoes\nhere: #}{{name}}!", + {'name':'Ned'}, "Hello, Ned!" + ) + + def test_if(self): + self.try_render( + "Hi, {% if ned %}NED{% endif %}{% if ben %}BEN{% endif %}!", + {'ned': 1, 'ben': 0}, + "Hi, NED!" + ) + self.try_render( + "Hi, {% if ned %}NED{% endif %}{% if ben %}BEN{% endif %}!", + {'ned': 0, 'ben': 1}, + "Hi, BEN!" + ) + self.try_render( + "Hi, {% if ned %}NED{% if ben %}BEN{% endif %}{% endif %}!", + {'ned': 0, 'ben': 0}, + "Hi, !" + ) + self.try_render( + "Hi, {% if ned %}NED{% if ben %}BEN{% endif %}{% endif %}!", + {'ned': 1, 'ben': 0}, + "Hi, NED!" + ) + self.try_render( + "Hi, {% if ned %}NED{% if ben %}BEN{% endif %}{% endif %}!", + {'ned': 1, 'ben': 1}, + "Hi, NEDBEN!" + ) + + def test_loop_if(self): + self.try_render( + "@{% for n in nums %}{% if n %}Z{% endif %}{{n}}{% endfor %}!", + {'nums': [0,1,2]}, + "@0Z1Z2!" + ) + self.try_render( + "X{%if nums%}@{% for n in nums %}{{n}}{% endfor %}{%endif%}!", + {'nums': [0,1,2]}, + "X@012!" + ) + self.try_render( + "X{%if nums%}@{% for n in nums %}{{n}}{% endfor %}{%endif%}!", + {'nums': []}, + "X!" + ) + + def test_nested_loops(self): + self.try_render( + "@{% for n in nums %}" + "{% for a in abc %}{{a}}{{n}}{% endfor %}" + "{% endfor %}!", + {'nums': [0,1,2], 'abc': ['a', 'b', 'c']}, + "@a0b0c0a1b1c1a2b2c2!" + ) + + def test_exception_during_evaluation(self): + # TypeError: Couldn't evaluate {{ foo.bar.baz }}: + # 'NoneType' object is unsubscriptable + self.assertRaises(TypeError, self.try_render, + "Hey {{foo.bar.baz}} there", {'foo': None}, "Hey ??? there" + ) + + def test_bogus_tag_syntax(self): + self.assertRaises(SyntaxError, self.try_render, + "Huh: {% bogus %}!!{% endbogus %}??", {}, "" + ) diff -Nru python-coverage-3.4/test/test_testing.py python-coverage-3.6/test/test_testing.py --- python-coverage-3.4/test/test_testing.py 1970-01-01 00:00:00.000000000 +0000 +++ python-coverage-3.6/test/test_testing.py 2012-12-30 15:17:29.000000000 +0000 @@ -0,0 +1,192 @@ +# -*- coding: utf-8 -*- +"""Tests that our test infrastructure is really working!""" + +import os, sys +from coverage.backward import to_bytes, rpartition +from test.backunittest import TestCase +from test.coveragetest import CoverageTest + +from coverage.backward import set # pylint: disable=W0622 + +class TestingTest(TestCase): + """Tests of helper methods on `backunittest.TestCase`.""" + + run_in_temp_dir = False + + def please_raise(self, exc, msg): + """Raise an exception for testing assertRaisesRegexp.""" + raise exc(msg) + + def please_succeed(self): + """A simple successful method for testing assertRaisesRegexp.""" + return "All is well" + + def test_assert_same_elements(self): + self.assertSameElements(set(), set()) + self.assertSameElements(set([1,2,3]), set([3,1,2])) + self.assertRaises(AssertionError, self.assertSameElements, + set([1,2,3]), set() + ) + self.assertRaises(AssertionError, self.assertSameElements, + set([1,2,3]), set([4,5,6]) + ) + + def test_assert_regexp_matches(self): + self.assertRegexpMatches("hello", "hel*o") + self.assertRegexpMatches("Oh, hello there!", "hel*o") + self.assertRaises(AssertionError, self.assertRegexpMatches, + "hello there", "^hello$" + ) + + def test_assert_multiline_equal(self): + self.assertMultiLineEqual("hello", "hello") + self.assertRaises(AssertionError, self.assertMultiLineEqual, + "hello there", "Hello there" + ) + self.assertRaises(AssertionError, self.assertMultiLineEqual, + "hello\nthere", "hello\nThere" + ) + # With messages also. + self.assertMultiLineEqual("hi", "hi", "it's ok") + self.assertRaisesRegexp( + AssertionError, "my message", + self.assertMultiLineEqual, "xyz", "abc", "my message" + ) + + def test_assert_raises_regexp(self): + # Raising the right error with the right message passes. + self.assertRaisesRegexp( + ZeroDivisionError, "Wow! Zero!", + self.please_raise, ZeroDivisionError, "Wow! Zero!" + ) + # Raising the right error with a match passes. + self.assertRaisesRegexp( + ZeroDivisionError, "Zero", + self.please_raise, ZeroDivisionError, "Wow! Zero!" + ) + # Raising the right error with a mismatch fails. + self.assertRaises(AssertionError, + self.assertRaisesRegexp, ZeroDivisionError, "XYZ", + self.please_raise, ZeroDivisionError, "Wow! Zero!" + ) + # Raising the right error with a mismatch fails. + self.assertRaises(AssertionError, + self.assertRaisesRegexp, ZeroDivisionError, "XYZ", + self.please_raise, ZeroDivisionError, "Wow! Zero!" + ) + # Raising the wrong error raises the error itself. + self.assertRaises(ZeroDivisionError, + self.assertRaisesRegexp, IOError, "Wow! Zero!", + self.please_raise, ZeroDivisionError, "Wow! Zero!" + ) + # Raising no error fails. + self.assertRaises(AssertionError, + self.assertRaisesRegexp, ZeroDivisionError, "XYZ", + self.please_succeed + ) + + def test_assert_true(self): + self.assertTrue(True) + self.assertRaises(AssertionError, self.assertTrue, False) + + def test_assert_false(self): + self.assertFalse(False) + self.assertRaises(AssertionError, self.assertFalse, True) + + def test_assert_in(self): + self.assertIn("abc", "hello abc") + self.assertIn("abc", ["xyz", "abc", "foo"]) + self.assertIn("abc", {'abc': 1, 'xyz': 2}) + self.assertRaises(AssertionError, self.assertIn, "abc", "xyz") + self.assertRaises(AssertionError, self.assertIn, "abc", ["x", "xabc"]) + self.assertRaises(AssertionError, self.assertIn, "abc", {'x':'abc'}) + + def test_assert_not_in(self): + self.assertRaises(AssertionError, self.assertNotIn, "abc", "hello abc") + self.assertRaises(AssertionError, + self.assertNotIn, "abc", ["xyz", "abc", "foo"] + ) + self.assertRaises(AssertionError, + self.assertNotIn, "abc", {'abc': 1, 'xyz': 2} + ) + self.assertNotIn("abc", "xyz") + self.assertNotIn("abc", ["x", "xabc"]) + self.assertNotIn("abc", {'x':'abc'}) + + def test_assert_greater(self): + self.assertGreater(10, 9) + self.assertGreater("xyz", "abc") + self.assertRaises(AssertionError, self.assertGreater, 9, 10) + self.assertRaises(AssertionError, self.assertGreater, 10, 10) + self.assertRaises(AssertionError, self.assertGreater, "abc", "xyz") + self.assertRaises(AssertionError, self.assertGreater, "xyz", "xyz") + + +class CoverageTestTest(CoverageTest): + """Test the methods in `CoverageTest`.""" + + def file_text(self, fname): + """Return the text read from a file.""" + return open(fname, "rb").read().decode('ascii') + + def test_make_file(self): + # A simple file. + self.make_file("fooey.boo", "Hello there") + self.assertEqual(open("fooey.boo").read(), "Hello there") + # A file in a sub-directory + self.make_file("sub/another.txt", "Another") + self.assertEqual(open("sub/another.txt").read(), "Another") + # A second file in that sub-directory + self.make_file("sub/second.txt", "Second") + self.assertEqual(open("sub/second.txt").read(), "Second") + # A deeper directory + self.make_file("sub/deeper/evenmore/third.txt") + self.assertEqual(open("sub/deeper/evenmore/third.txt").read(), "") + + def test_make_file_newline(self): + self.make_file("unix.txt", "Hello\n") + self.assertEqual(self.file_text("unix.txt"), "Hello\n") + self.make_file("dos.txt", "Hello\n", newline="\r\n") + self.assertEqual(self.file_text("dos.txt"), "Hello\r\n") + self.make_file("mac.txt", "Hello\n", newline="\r") + self.assertEqual(self.file_text("mac.txt"), "Hello\r") + + def test_make_file_non_ascii(self): + self.make_file("unicode.txt", "tabblo: «ταБЬℓσ»") + self.assertEqual( + open("unicode.txt", "rb").read(), + to_bytes("tabblo: «ταБЬℓσ»") + ) + + def test_file_exists(self): + self.make_file("whoville.txt", "We are here!") + self.assert_exists("whoville.txt") + self.assert_doesnt_exist("shadow.txt") + self.assertRaises( + AssertionError, self.assert_doesnt_exist, "whoville.txt" + ) + self.assertRaises(AssertionError, self.assert_exists, "shadow.txt") + + def test_sub_python_is_this_python(self): + # Try it with a python command. + os.environ['COV_FOOBAR'] = 'XYZZY' + self.make_file("showme.py", """\ + import os, sys + print(sys.executable) + print(os.__file__) + print(os.environ['COV_FOOBAR']) + """) + out = self.run_command("python showme.py").splitlines() + self.assertEqual(out[0], sys.executable) + self.assertEqual(out[1], os.__file__) + self.assertEqual(out[2], 'XYZZY') + + # 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 = [l for l in out if "executable:" in l][0] + executable = executable.split(":", 1)[1].strip() + self.assertEqual(executable, sys.executable) + environ = [l for l in out if "COV_FOOBAR" in l][0] + _, _, environ = rpartition(environ, ":") + self.assertEqual(environ.strip(), "COV_FOOBAR = XYZZY") diff -Nru python-coverage-3.4/test/test_xml.py python-coverage-3.6/test/test_xml.py --- python-coverage-3.4/test/test_xml.py 1970-01-01 00:00:00.000000000 +0000 +++ python-coverage-3.6/test/test_xml.py 2012-12-30 15:17:29.000000000 +0000 @@ -0,0 +1,84 @@ +"""Tests for XML reports from coverage.py.""" + +import os, re +import coverage + +from test.coveragetest import CoverageTest + +class XmlReportTest(CoverageTest): + """Tests of the XML reports from coverage.py.""" + + def run_mycode(self): + """Run mycode.py, so we can report on it.""" + self.make_file("mycode.py", "print('hello')\n") + self.run_command("coverage run mycode.py") + + def test_default_file_placement(self): + self.run_mycode() + self.run_command("coverage xml") + self.assert_exists("coverage.xml") + + def test_argument_affects_xml_placement(self): + self.run_mycode() + self.run_command("coverage xml -o put_it_there.xml") + self.assert_doesnt_exist("coverage.xml") + self.assert_exists("put_it_there.xml") + + def test_config_affects_xml_placement(self): + self.run_mycode() + self.make_file(".coveragerc", "[xml]\noutput = xml.out\n") + self.run_command("coverage xml") + self.assert_doesnt_exist("coverage.xml") + self.assert_exists("xml.out") + + def test_no_data(self): + # https://bitbucket.org/ned/coveragepy/issue/210 + self.run_command("coverage xml") + self.assert_doesnt_exist("coverage.xml") + + def test_no_source(self): + # 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() + self.start_import_stop(cov, "innocuous") + os.remove("innocuous.py") + cov.xml_report(ignore_errors=True) + self.assert_exists("coverage.xml") + + def run_doit(self): + """Construct a simple sub-package.""" + 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() + self.start_import_stop(cov, "main") + return cov + + def test_filename_format_showing_everything(self): + cov = self.run_doit() + cov.xml_report(outfile="-") + xml = self.stdout() + doit_line = re_line(xml, "class.*doit") + self.assertIn('filename="sub/doit.py"', doit_line) + + def test_filename_format_including_filename(self): + cov = self.run_doit() + cov.xml_report(["sub/doit.py"], outfile="-") + xml = self.stdout() + doit_line = re_line(xml, "class.*doit") + self.assertIn('filename="sub/doit.py"', doit_line) + + def test_filename_format_including_module(self): + cov = self.run_doit() + import sub.doit # pylint: disable=F0401 + cov.xml_report([sub.doit], outfile="-") + xml = self.stdout() + doit_line = re_line(xml, "class.*doit") + self.assertIn('filename="sub/doit.py"', doit_line) + + +def re_line(text, pat): + """Return the one line in `text` that matches regex `pat`.""" + lines = [l for l in text.splitlines() if re.search(pat, l)] + return lines[0] diff -Nru python-coverage-3.4/test/try_execfile.py python-coverage-3.6/test/try_execfile.py --- python-coverage-3.4/test/try_execfile.py 1970-01-01 00:00:00.000000000 +0000 +++ python-coverage-3.6/test/try_execfile.py 2011-10-31 03:09:58.000000000 +0000 @@ -0,0 +1,34 @@ +"""Test file for run_python_file.""" + +import os, pprint, sys + +DATA = "xyzzy" + +import __main__ + +def my_function(a): + """A function to force execution of module-level values.""" + return "my_fn(%r)" % a + +FN_VAL = my_function("fooey") + +try: + pkg = __package__ +except NameError: + pkg = "*No __package__*" + +globals_to_check = { + '__name__': __name__, + '__file__': __file__, + '__doc__': __doc__, + '__builtins__.has_open': hasattr(__builtins__, 'open'), + '__builtins__.dir': dir(__builtins__), + '__package__': pkg, + 'DATA': DATA, + 'FN_VAL': FN_VAL, + '__main__.DATA': getattr(__main__, "DATA", "nothing"), + 'argv': sys.argv, + 'path': [os.path.normcase(p) for p in sys.path], +} + +pprint.pprint(globals_to_check) diff -Nru python-coverage-3.4/test_old.sh python-coverage-3.6/test_old.sh --- python-coverage-3.4/test_old.sh 1970-01-01 00:00:00.000000000 +0000 +++ python-coverage-3.6/test_old.sh 2012-12-23 03:04:06.000000000 +0000 @@ -0,0 +1,8 @@ +# 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 +python setup.py --quiet clean develop +python igor.py zip_mods install_egg remove_extension +python igor.py test_with_tracer py +python setup.py --quiet build_ext --inplace +python igor.py test_with_tracer c diff -Nru python-coverage-3.4/tox.ini python-coverage-3.6/tox.ini --- python-coverage-3.4/tox.ini 1970-01-01 00:00:00.000000000 +0000 +++ python-coverage-3.6/tox.ini 2012-12-21 14:40:42.000000000 +0000 @@ -0,0 +1,32 @@ +# 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. + +[tox] +envlist = py25, py26, py27, py31, py32, py33, pypy + +[testenv] +commands = + {envpython} setup.py --quiet clean develop + + # Create test/zipmods.zip + # Install the egg1 egg + # Remove the C extension so that we can test the PyTracer + {envpython} igor.py zip_mods install_egg remove_extension + + # Test with the PyTracer + {envpython} igor.py test_with_tracer py {posargs} + + # Build the C extension and test with the CTracer + {envpython} setup.py --quiet build_ext --inplace + {envpython} igor.py test_with_tracer c {posargs} + +deps = + nose + mock + +[testenv:pypy] +# PyPy has no C extensions +setenv = + COVERAGE_NO_EXTENSION=1