diff options
-rw-r--r-- | coverage/control.py | 23 | ||||
-rw-r--r-- | doc/contributing.rst | 14 | ||||
-rw-r--r-- | howto.txt | 7 | ||||
-rw-r--r-- | igor.py | 76 | ||||
-rw-r--r-- | metacov.ini | 7 |
5 files changed, 112 insertions, 15 deletions
diff --git a/coverage/control.py b/coverage/control.py index 81017b91..1f6dbd7e 100644 --- a/coverage/control.py +++ b/coverage/control.py @@ -104,7 +104,6 @@ class coverage(object): ) self.auto_data = auto_data - self.atexit_registered = False # _exclude_re is a dict mapping exclusion list names to compiled # regexes. @@ -180,6 +179,12 @@ class coverage(object): # Set the reporting precision. Numbers.set_precision(self.config.precision) + # Is it ok for no data to be collected? + self._warn_no_data = True + self._started = 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] @@ -338,10 +343,6 @@ class coverage(object): 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: @@ -358,11 +359,20 @@ class coverage(object): self._harvested = False self.collector.start() + self._started = True def stop(self): """Stop measuring code coverage.""" + self._started = False self.collector.stop() + 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. @@ -468,7 +478,7 @@ class coverage(object): # 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. @@ -688,3 +698,4 @@ def process_startup(): # Measuring coverage within coverage.py takes yet more trickery. cov.cover_dir = "Please measure coverage.py!" cov.start() + cov._warn_no_data = False diff --git a/doc/contributing.rst b/doc/contributing.rst index 23eb1adb..90b9ba1b 100644 --- a/doc/contributing.rst +++ b/doc/contributing.rst @@ -138,6 +138,20 @@ some warnings. Please try to keep it that way, but don't let pylint warnings keep you from sending patches. I can clean them up. +Coverage testing coverage.py +---------------------------- + +Coverage.py can measure itself, but it's complicated. The process has been +packaged up to make it easier:: + + $ COVERAGE_COVERAGE=yes tox + $ python igor.py combine_html + +Then look at htmlcov/index.html. Note that due to the recursive nature of +coverage.py measuring itself, there are some parts of the code that will never +appear as covered, even though they are executed. + + Contributing ------------ @@ -72,11 +72,10 @@ - activate the virtualenv - $ tox +- For complete coverage testing: -- For complete coverage testing, in each Python installation, create a - "zzz_coverage_process_start.pth" containing:: - - import coverage; coverage.process_startup() + $ COVERAGE_COVERAGE=yes tox + $ python igor.py combine_html - To run the Javascript tests: @@ -30,8 +30,8 @@ def do_remove_extension(args): except OSError: pass -def do_test_with_tracer(args): - """Run nosetests with a particular tracer.""" +def run_tests(args): + """The actual running of tests.""" import nose.core tracer = args[0] if tracer == "py": @@ -46,6 +46,78 @@ def do_test_with_tracer(args): nose_args = ["nosetests"] + args[1:] nose.core.main(argv=nose_args) +def run_tests_with_coverage(args): + """Run tests, but with coverage.""" + import coverage + + os.environ['COVERAGE_COVERAGE'] = 'yes please' + os.environ['COVERAGE_PROCESS_START'] = os.path.abspath('metacov.ini') + + # Create the .pth file that will let us measure coverage in sub-processes. + import nose + pth_path = os.path.join(os.path.dirname(os.path.dirname(nose.__file__)), "covcov.pth") + pth_file = open(pth_path, "w") + try: + pth_file.write("import coverage; coverage.process_startup()\n") + finally: + pth_file.close() + + tracer = os.environ.get('COVERAGE_TEST_TRACER', 'c') + version = "%s%s" % sys.version_info[:2] + suffix = "%s_%s" % (version, tracer) + + cov = coverage.coverage(config_file="metacov.ini", data_suffix=suffix) + # Cheap trick: the coverage code itself is excluded from measurement, but + # if we clobber the cover_prefix in the coverage object, we can defeat the + # self-detection. + cov.cover_prefix = "Please measure coverage.py!" + cov.erase() + cov.start() + + try: + # Re-import coverage to get it coverage tested! I don't understand all the + # mechanics here, but if I don't carry over the imported modules (in + # covmods), then things go haywire (os == None, eventually). + covmods = {} + covdir = os.path.split(coverage.__file__)[0] + # We have to make a list since we'll be deleting in the loop. + modules = list(sys.modules.items()) + for name, mod in modules: + if name.startswith('coverage'): + if hasattr(mod, '__file__') and mod.__file__.startswith(covdir): + covmods[name] = mod + del sys.modules[name] + import coverage # don't warn about re-import: pylint: disable=W0404 + sys.modules.update(covmods) + + # Run nosetests, with the arguments from our command line. + print(":: Running nosetests") + try: + run_tests(args) + except SystemExit: + # nose3 seems to raise SystemExit, not sure why? + pass + finally: + cov.stop() + os.remove(pth_path) + + print(":: Saving data %s" % suffix) + cov.save() + +def do_combine_html(args): + import coverage + cov = coverage.coverage(config_file="metacov.ini") + cov.combine() + cov.save() + cov.html_report() + +def do_test_with_tracer(args): + """Run nosetests with a particular tracer.""" + if os.environ.get("COVERAGE_COVERAGE", ""): + return run_tests_with_coverage(args) + else: + return run_tests(args) + def do_zip_mods(args): """Build the zipmods.zip file.""" zf = zipfile.ZipFile("test/zipmods.zip", "w") diff --git a/metacov.ini b/metacov.ini index 9d8b4d97..3a7b0238 100644 --- a/metacov.ini +++ b/metacov.ini @@ -1,11 +1,11 @@ # Settings to use when using coverage.py to measure itself.
[run]
branch = true
-data_file = c:\ned\coverage\trunk\.coverage
+data_file = /home/ned/coverage/trunk/.coverage.meta
parallel = true
source =
- c:\ned\coverage\trunk\coverage
- c:\ned\coverage\trunk\test
+ /home/ned/coverage/trunk/coverage
+ /home/ned/coverage/trunk/test
[report]
exclude_lines =
@@ -17,3 +17,4 @@ exclude_lines = omit = mock.py
ignore_errors = true
+precision = 3
|