diff options
-rw-r--r-- | CHANGES.rst | 7 | ||||
-rw-r--r-- | coverage/control.py | 10 | ||||
-rw-r--r-- | tests/test_concurrency.py | 28 |
3 files changed, 36 insertions, 9 deletions
diff --git a/CHANGES.rst b/CHANGES.rst index 61c28d19..0c426023 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -24,7 +24,12 @@ want to know what's different in 5.0 since 4.5.x, see :ref:`whatsnew5x`. Unreleased ---------- -Nothing yet. +- When using ``coverage run --concurrency=multiprocessing``, all data files + should be named with parallel-ready suffixes. 5.0 mistakenly named the main + process' file with no suffix when using ``--append``. This is now fixed, + closing `issue 880`_. + +.. _issue 880: https://github.com/nedbat/coveragepy/issues/880 .. _changes_50: diff --git a/coverage/control.py b/coverage/control.py index 1de0e28f..5194b5d6 100644 --- a/coverage/control.py +++ b/coverage/control.py @@ -252,6 +252,11 @@ class Coverage(object): # to. self._debug = DebugControl(self.config.debug, self._debug_file) + if "multiprocessing" in (self.config.concurrency or ()): + # Multi-processing uses parallel for the subprocesses, so also use + # it for the main process. + self.config.parallel = True + # _exclude_re is a dict that maps exclusion list names to compiled regexes. self._exclude_re = {} @@ -393,16 +398,13 @@ class Coverage(object): def _init_for_start(self): """Initialization for start()""" # Construct the collector. - concurrency = self.config.concurrency or [] + concurrency = self.config.concurrency or () if "multiprocessing" in concurrency: if not patch_multiprocessing: raise CoverageException( # pragma: only jython "multiprocessing is not supported on this Python" ) patch_multiprocessing(rcfile=self.config.config_file) - # Multi-processing uses parallel for the subprocesses, so also use - # it for the main process. - self.config.parallel = True dycon = self.config.dynamic_context if not dycon or dycon == "none": diff --git a/tests/test_concurrency.py b/tests/test_concurrency.py index a397ea99..5d492a72 100644 --- a/tests/test_concurrency.py +++ b/tests/test_concurrency.py @@ -3,6 +3,7 @@ """Tests for concurrency libraries.""" +import glob import os import random import sys @@ -360,6 +361,12 @@ MULTI_CODE = """ """ +def remove_files(*patterns): + for pattern in patterns: + for fname in glob.glob(pattern): + os.remove(fname) + + @flaky(max_runs=30) # Sometimes a test fails due to inherent randomness. Try more times. class MultiprocessingTest(CoverageTest): """Test support of the multiprocessing module.""" @@ -370,7 +377,7 @@ class MultiprocessingTest(CoverageTest): super(MultiprocessingTest, self).setUp() def try_multiprocessing_code( - self, code, expected_out, the_module, concurrency="multiprocessing" + self, code, expected_out, the_module, nprocs, concurrency="multiprocessing", args="" ): """Run code using multiprocessing, it should produce `expected_out`.""" self.make_file("multi.py", code) @@ -389,13 +396,18 @@ class MultiprocessingTest(CoverageTest): if start_method and start_method not in multiprocessing.get_all_start_methods(): continue - out = self.run_command("coverage run multi.py %s" % (start_method,)) + remove_files(".coverage", ".coverage.*") + cmd = "coverage run {args} multi.py {start_method}".format( + args=args, start_method=start_method, + ) + out = self.run_command(cmd) expected_cant_trace = cant_trace_msg(concurrency, the_module) if expected_cant_trace is not None: self.assertEqual(out, expected_cant_trace) else: self.assertEqual(out.rstrip(), expected_out) + self.assertEqual(len(glob.glob(".coverage.*")), nprocs + 1) out = self.run_command("coverage combine") self.assertEqual(out, "") @@ -410,7 +422,15 @@ class MultiprocessingTest(CoverageTest): code = (SQUARE_OR_CUBE_WORK + MULTI_CODE).format(NPROCS=nprocs, UPTO=upto) total = sum(x*x if x%2 else x*x*x for x in range(upto)) expected_out = "{nprocs} pids, total = {total}".format(nprocs=nprocs, total=total) - self.try_multiprocessing_code(code, expected_out, threading) + self.try_multiprocessing_code(code, expected_out, threading, nprocs) + + def test_multiprocessing_append(self): + nprocs = 3 + upto = 30 + code = (SQUARE_OR_CUBE_WORK + MULTI_CODE).format(NPROCS=nprocs, UPTO=upto) + total = sum(x*x if x%2 else x*x*x for x in range(upto)) + expected_out = "{nprocs} pids, total = {total}".format(nprocs=nprocs, total=total) + self.try_multiprocessing_code(code, expected_out, threading, nprocs, args="--append") def test_multiprocessing_and_gevent(self): nprocs = 3 @@ -421,7 +441,7 @@ class MultiprocessingTest(CoverageTest): total = sum(sum(range((x + 1) * 100)) for x in range(upto)) expected_out = "{nprocs} pids, total = {total}".format(nprocs=nprocs, total=total) self.try_multiprocessing_code( - code, expected_out, eventlet, concurrency="multiprocessing,eventlet" + code, expected_out, eventlet, nprocs, concurrency="multiprocessing,eventlet" ) def try_multiprocessing_code_with_branching(self, code, expected_out): |