Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
00441a9
Drop support for pytest<=2.8 and upgrade some test deps.
ionelmc Oct 28, 2017
6b1e878
New coverage instance to combine
ryanhiebert Jan 2, 2017
bdfd9c3
Load data again before combine.
ionelmc Jan 3, 2017
58c3ef1
Make sure config path is absulutized early. Fixes failures in finish …
ionelmc Jan 3, 2017
d6ef668
Add a test for aliasing (the combine in finish changes).
ionelmc Jan 3, 2017
c463fd7
Seem this becomes absolute path, just let it be.
ionelmc Jan 3, 2017
d833b1e
Add tests for xdist and suprocess and fix handling in xdist mode (mis…
ionelmc Jan 3, 2017
5735d9b
Check if it's a path first (don't absolutize None).
ionelmc Jan 4, 2017
3845245
Create a duplicated (ish) cov object for combining as @ryanhiebert su…
ionelmc Jan 4, 2017
194266c
Override cov with combining_cov
ryanhiebert Jan 4, 2017
7438666
Correct tests (missing arguments).
ionelmc Oct 28, 2017
ea895d2
Use aliasing for non-colocation test.
ionelmc Oct 28, 2017
2cfbaa8
Add missing branch option.
ionelmc Oct 28, 2017
1da96c2
Undo these changes.
ionelmc Oct 28, 2017
8eae224
Add a coveragerc with path configuration.
ionelmc Oct 28, 2017
0d75fd2
Some debugging.
ionelmc Oct 28, 2017
3f2ea3f
Drop support for py2.6, pytest<=2.8 and coverage<4.0.
ionelmc Oct 28, 2017
6dee02a
Drop support for old coverage internals.
ionelmc Nov 24, 2017
c065327
Drop support for EOL Python 2.6
hugovk Nov 21, 2017
da66172
Drop support for EOL Python 3.3
hugovk Nov 21, 2017
a7328c7
Add python version constraint like in https://github.com/pytest-dev/p…
ionelmc Nov 24, 2017
73e9d66
Drop old coverage. Close #182.
ionelmc Nov 24, 2017
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
392 changes: 10 additions & 382 deletions .travis.yml

Large diffs are not rendered by default.

9 changes: 4 additions & 5 deletions appveyor.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,10 @@ cache:
environment:
matrix:
- TOXENV: check
- TOXENV: 'py26-28-40,py26-28-41,py26-28-42,py26-28-43,py26-29-40,py26-29-41,py26-29-42,py26-29-43,py26-30-40,py26-30-41,py26-30-42,py26-30-43,py26-31-40,py26-31-41,py26-31-42,py26-31-43'
- TOXENV: 'py27-28-40,py27-28-41,py27-28-42,py27-28-43,py27-29-40,py27-29-41,py27-29-42,py27-29-43,py27-30-40,py27-30-41,py27-30-42,py27-30-43,py27-31-40,py27-31-41,py27-31-42,py27-31-43,py27-28-43,py27-28-44,py27-29-43,py27-29-44,py27-30-43,py27-30-44,py27-31-43,py27-31-44'
- TOXENV: 'py34-28-40,py34-28-41,py34-28-42,py34-28-43,py34-29-40,py34-29-41,py34-29-42,py34-29-43,py34-30-40,py34-30-41,py34-30-42,py34-30-43,py34-31-40,py34-31-41,py34-31-42,py34-31-43,py34-28-43,py34-28-44,py34-29-43,py34-29-44,py34-30-43,py34-30-44,py34-31-43,py34-31-44'
- TOXENV: 'py35-28-40,py35-28-41,py35-28-42,py35-28-43,py35-29-40,py35-29-41,py35-29-42,py35-29-43,py35-30-40,py35-30-41,py35-30-42,py35-30-43,py35-31-40,py35-31-41,py35-31-42,py35-31-43,py35-28-43,py35-28-44,py35-29-43,py35-29-44,py35-30-43,py35-30-44,py35-31-43,py35-31-44'
- TOXENV: 'pypy-28-40,pypy-28-41,pypy-28-42,pypy-28-43,pypy-29-40,pypy-29-41,pypy-29-42,pypy-29-43,pypy-30-40,pypy-30-41,pypy-30-42,pypy-30-43,pypy-31-40,pypy-31-41,pypy-31-42,pypy-31-43,pypy-28-43,pypy-28-44,pypy-29-43,pypy-29-44,pypy-30-43,pypy-30-44,pypy-31-43,pypy-31-44'
- TOXENV: 'py27-29-44,py27-30-44,py27-31-44,py27-32-44'
- TOXENV: 'py34-29-44,py34-30-44,py34-31-44,py34-32-44'
- TOXENV: 'py35-29-44,py35-30-44,py35-31-44,py35-32-44'
- TOXENV: 'pypy-29-44,pypy-30-44,pypy-31-44,pypy-32-44'

init:
- ps: echo $env:TOXENV
Expand Down
6 changes: 3 additions & 3 deletions ci/bootstrap.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,15 @@

if __name__ == "__main__":
base_path = dirname(dirname(abspath(__file__)))
print("Project path: {0}".format(base_path))
print("Project path: {}".format(base_path))
env_path = join(base_path, ".tox", "bootstrap")
if sys.platform == "win32":
bin_path = join(env_path, "Scripts")
else:
bin_path = join(env_path, "bin")
if not exists(env_path):
import subprocess
print("Making bootstrap env in: {0} ...".format(env_path))
print("Making bootstrap env in: {} ...".format(env_path))
try:
subprocess.check_call(["virtualenv", env_path])
except Exception:
Expand All @@ -46,7 +46,7 @@
tox_environments = [line for line in tox_environments if line not in ['clean', 'report', 'docs', 'check']]

template_vars = {'tox_environments': tox_environments}
for py_ver in '26 27 33 34 35 py'.split():
for py_ver in '27 34 35 py'.split():
template_vars['py%s_environments' % py_ver] = [x for x in tox_environments if x.startswith('py' + py_ver)]

for name in os.listdir(join("ci", "templates")):
Expand Down
1 change: 0 additions & 1 deletion ci/templates/appveyor.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ cache:
environment:
matrix:
- TOXENV: check
- TOXENV: '{{ py26_environments|join(",") }}'
- TOXENV: '{{ py27_environments|join(",") }}'
- TOXENV: '{{ py34_environments|join(",") }}'
- TOXENV: '{{ py35_environments|join(",") }}'
Expand Down
2 changes: 1 addition & 1 deletion docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
project = 'pytest-cov'
year = '2016'
author = 'pytest-cov contributors'
copyright = '{0}, {1}'.format(year, author)
copyright = '{}, {}'.format(year, author)
version = release = '2.5.1'

pygments_style = 'trac'
Expand Down
8 changes: 4 additions & 4 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -105,10 +105,9 @@ def run(self):
'Operating System :: POSIX',
'Operating System :: Unix',
'Programming Language :: Python',
'Programming Language :: Python :: 2.6',
'Programming Language :: Python :: 2',
'Programming Language :: Python :: 2.7',
'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3.3',
'Programming Language :: Python :: 3.4',
'Programming Language :: Python :: 3.5',
'Programming Language :: Python :: 3.6',
Expand All @@ -121,9 +120,10 @@ def run(self):
'cover', 'coverage', 'pytest', 'py.test', 'distributed', 'parallel',
],
install_requires=[
'pytest>=2.6.0',
'coverage>=3.7.1'
'pytest>=2.9',
'coverage>=4.4'
],
python_requires='>=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*',
extras_require={
},
entry_points={
Expand Down
37 changes: 24 additions & 13 deletions src/pytest_cov/engine.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ def __init__(self, cov_source, cov_report, cov_config, cov_append, cov_branch, c
self.nodeid = nodeid

self.cov = None
self.combining_cov = None
self.data_file = None
self.node_descs = set()
self.failed_slaves = []
self.topdir = os.getcwd()
Expand Down Expand Up @@ -136,10 +138,13 @@ class Central(CovController):

def start(self):
"""Erase any previous coverage data and start coverage."""

self.cov = coverage.coverage(source=self.cov_source,
branch=self.cov_branch,
config_file=self.cov_config)
self.combining_cov = coverage.coverage(source=self.cov_source,
branch=self.cov_branch,
data_file=os.path.abspath(self.cov.config.data_file),
config_file=self.cov_config)
if self.cov_append:
self.cov.load()
else:
Expand All @@ -152,8 +157,13 @@ def finish(self):

self.unset_env()
self.cov.stop()
self.cov.save()

self.cov = self.combining_cov
self.cov.load()
self.cov.combine()
self.cov.save()

node_desc = self.get_node_desc(sys.platform, sys.version_info)
self.node_descs.add(node_desc)

Expand All @@ -170,6 +180,10 @@ def start(self):
self.cov = coverage.coverage(source=self.cov_source,
branch=self.cov_branch,
config_file=self.cov_config)
self.combining_cov = coverage.coverage(source=self.cov_source,
branch=self.cov_branch,
data_file=os.path.abspath(self.cov.config.data_file),
config_file=self.cov_config)
if self.cov_append:
self.cov.load()
else:
Expand Down Expand Up @@ -207,12 +221,9 @@ def testnodedown(self, node, error):
data_suffix=data_suffix,
config_file=self.cov_config)
cov.start()
if hasattr(self.cov.data, 'read_fileobj'): # for coverage 4.0
data = CoverageData()
data.read_fileobj(StringIO(node.slaveoutput['cov_slave_data']))
cov.data.update(data)
else:
cov.data.lines, cov.data.arcs = node.slaveoutput['cov_slave_data']
data = CoverageData()
data.read_fileobj(StringIO(node.slaveoutput['cov_slave_data']))
cov.data.update(data)
cov.stop()
cov.save()
path = node.slaveoutput['cov_slave_path']
Expand All @@ -228,6 +239,9 @@ def finish(self):

# Combine all the suffix files into the data file.
self.cov.stop()
self.cov.save()
self.cov = self.combining_cov
self.cov.load()
self.cov.combine()
self.cov.save()

Expand Down Expand Up @@ -286,12 +300,9 @@ def finish(self):
# Send all the data to the master over the channel.
self.config.slaveoutput['cov_slave_path'] = self.topdir
self.config.slaveoutput['cov_slave_node_id'] = self.nodeid
if hasattr(self.cov.data, 'write_fileobj'): # for coverage 4.0
buff = StringIO()
self.cov.data.write_fileobj(buff)
self.config.slaveoutput['cov_slave_data'] = buff.getvalue()
else:
self.config.slaveoutput['cov_slave_data'] = self.cov.data.lines, self.cov.data.arcs
buff = StringIO()
self.cov.data.write_fileobj(buff)
self.config.slaveoutput['cov_slave_data'] = buff.getvalue()

def summary(self, stream):
"""Only the master reports so do nothing."""
Expand Down
8 changes: 0 additions & 8 deletions src/pytest_cov/plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,14 +98,6 @@ def pytest_load_initial_conftests(early_config, parser, args):
early_config.pluginmanager.register(plugin, '_cov')


def pytest_configure(config):
"""Activate coverage plugin if appropriate."""
if config.getvalue('cov_source'):
if not config.pluginmanager.hasplugin('_cov'):
plugin = CovPlugin(config.option, config.pluginmanager, start=False)
config.pluginmanager.register(plugin, '_cov')


class CovPlugin(object):
"""Use coverage package to produce code coverage reports.

Expand Down
110 changes: 96 additions & 14 deletions tests/test_pytest_cov.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ def test_bar():
'''

SCRIPT_PARENT = '''
import os
import subprocess
import sys

Expand All @@ -74,7 +75,7 @@ def pytest_generate_tests(metafunc):

def test_foo(idx):
out, err = subprocess.Popen(
[sys.executable, 'child_script.py', str(idx)],
[sys.executable, os.path.join(os.path.dirname(__file__), 'child_script.py'), str(idx)],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE).communicate()

Expand Down Expand Up @@ -149,7 +150,7 @@ def test_fail():
'''

CHILD_SCRIPT_RESULT = '[56] * 100%'
PARENT_SCRIPT_RESULT = '8 * 100%'
PARENT_SCRIPT_RESULT = '9 * 100%'
DEST_DIR = 'cov_dest'
REPORT_NAME = 'cov.xml'

Expand All @@ -168,6 +169,7 @@ def prop(request):
code2=SCRIPT2,
conf=request.param[0],
fullconf='[run]\n%s\n' % request.param[0],
prefixedfullconf='[coverage:run]\n%s\n' % request.param[0],
args=request.param[1].split(),
result=request.param[2],
result2=request.param[3],
Expand Down Expand Up @@ -271,10 +273,7 @@ def test_term_output_dir(testdir):
'--cov-report=term:' + DEST_DIR,
script)

# backport of argparse to py26 doesn't display ArgumentTypeError message
result.stderr.fnmatch_lines([
'*argument --cov-report: *',
] if tuple(sys.version_info[:2]) == (2, 6) else [
'*argument --cov-report: output specifier not supported for: "term:%s"*' % DEST_DIR,
])
assert result.ret != 0
Expand All @@ -288,10 +287,7 @@ def test_term_missing_output_dir(testdir):
'--cov-report=term-missing:' + DEST_DIR,
script)

# backport of argparse to py26 doesn't display ArgumentTypeError message
result.stderr.fnmatch_lines([
'*argument --cov-report: *',
] if tuple(sys.version_info[:2]) == (2, 6) else [
'*argument --cov-report: output specifier not supported for: '
'"term-missing:%s"*' % DEST_DIR,
])
Expand Down Expand Up @@ -400,6 +396,79 @@ def test_central_coveragerc(testdir, prop):
assert result.ret == 0


@xdist
def test_central_with_path_aliasing(testdir, monkeypatch, opts, prop):
mod1 = testdir.mkdir('src').join('mod.py')
mod1.write(SCRIPT)
mod2 = testdir.mkdir('aliased').join('mod.py')
mod2.write(SCRIPT)
script = testdir.makepyfile('''
from mod import *
''')
testdir.tmpdir.join('setup.cfg').write("""
[coverage:paths]
source =
src
aliased
[coverage:run]
source = mod
parallel = true
%s
""" % prop.conf)

monkeypatch.setitem(os.environ, 'PYTHONPATH', os.pathsep.join([os.environ.get('PYTHONPATH', ''), 'aliased']))
result = testdir.runpytest('-v', '-s',
'--cov',
'--cov-report=term-missing',
script, *opts.split()+prop.args)

result.stdout.fnmatch_lines([
'*- coverage: platform *, python * -*',
'src[\\/]mod* %s *' % prop.result,
'*10 passed*',
])

# single-module coverage report
assert all(not line.startswith('TOTAL ') for line in result.stdout.lines[-4:])

assert result.ret == 0


def test_subprocess_with_path_aliasing(testdir, monkeypatch):
src = testdir.mkdir('src')
src.join('parent_script.py').write(SCRIPT_PARENT)
src.join('child_script.py').write(SCRIPT_CHILD)
aliased = testdir.mkdir('aliased')
parent_script = aliased.join('parent_script.py')
parent_script.write(SCRIPT_PARENT)
aliased.join('child_script.py').write(SCRIPT_CHILD)

testdir.tmpdir.join('.coveragerc').write("""
[paths]
source =
src
aliased
[run]
source =
parent_script
child_script
parallel = true
""")

monkeypatch.setitem(os.environ, 'PYTHONPATH', os.pathsep.join([os.environ.get('PYTHONPATH',''), 'aliased']))
result = testdir.runpytest('-v',
'--cov',
'--cov-report=term-missing',
parent_script)

result.stdout.fnmatch_lines([
'*- coverage: platform *, python * -*',
'src[\\/]child_script* %s*' % CHILD_SCRIPT_RESULT,
'src[\\/]parent_script* %s*' % PARENT_SCRIPT_RESULT,
])
assert result.ret == 0


def test_show_missing_coveragerc(testdir, prop):
script = testdir.makepyfile(prop.code)
testdir.tmpdir.join('.coveragerc').write("""
Expand Down Expand Up @@ -524,7 +593,14 @@ def test_dist_not_collocated(testdir, prop):
script = testdir.makepyfile(prop.code)
dir1 = testdir.mkdir('dir1')
dir2 = testdir.mkdir('dir2')
testdir.tmpdir.join('.coveragerc').write(prop.fullconf)
testdir.tmpdir.join('.coveragerc').write('''
[run]
%s
[paths]
source =
.
dir1
dir2''' % prop.conf)

result = testdir.runpytest('-v',
'--cov=%s' % script.dirpath(),
Expand Down Expand Up @@ -581,8 +657,8 @@ def test_central_subprocess_change_cwd(testdir):

result.stdout.fnmatch_lines([
'*- coverage: platform *, python * -*',
'child_script* %s*' % CHILD_SCRIPT_RESULT,
'parent_script* 100%*',
'*child_script* %s*' % CHILD_SCRIPT_RESULT,
'*parent_script* 100%*',
])
assert result.ret == 0

Expand Down Expand Up @@ -666,15 +742,21 @@ def test_dist_subprocess_not_collocated(testdir, tmpdir):

dir1 = tmpdir.mkdir('dir1')
dir2 = tmpdir.mkdir('dir2')

testdir.tmpdir.join('.coveragerc').write('''
[paths]
source =
%s
*/dir1
*/dir2
''' % scripts.dirpath())
result = testdir.runpytest('-v',
'--cov=%s' % scripts.dirpath(),
'--cov-report=term-missing',
'--dist=load',
'--tx=popen//chdir=%s' % dir1,
'--tx=popen//chdir=%s' % dir2,
'--rsyncdir=%s' % child_script,
'--rsyncdir=%s' % parent_script,
'--rsyncdir=.coveragerc',
'--max-slave-restart=0',
parent_script)

Expand Down Expand Up @@ -719,7 +801,7 @@ def test_dist_missing_data(testdir):
exe = os.path.join(venv_path, 'bin', 'python')
subprocess.check_call([
exe,
'-mpip' if sys.version_info >= (2, 7) else '-mpip.__main__',
'-mpip',
'install',
'py==%s' % py.__version__,
'pytest==%s' % pytest.__version__
Expand Down
Loading