Skip to content

Commit

Permalink
Improve py.test covered paths reporting.
Browse files Browse the repository at this point in the history
Use a coverage plugin instead of `coverage combine` to open the door to
using a single PEX test source chroot even when testing against multiple
repo source roots at once.

Groundwork for pantsbuild#5426.
  • Loading branch information
jsirois committed Mar 1, 2018
1 parent 7e82cb1 commit 876c52b
Show file tree
Hide file tree
Showing 8 changed files with 357 additions and 112 deletions.
6 changes: 3 additions & 3 deletions 3rdparty/python/requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ ansicolors==1.0.2
beautifulsoup4>=4.3.2,<4.4
cffi==1.11.1
contextlib2==0.5.5
coverage>=4.3.4,<4.4
coverage>=4.5,<4.6
docutils>=0.12,<0.13
fasteners==0.14.1
faulthandler==2.6
Expand All @@ -20,8 +20,8 @@ pyflakes==1.1.0
Pygments==1.4
pyopenssl==17.3.0
pystache==0.5.3
pytest-cov>=2.4,<2.5
pytest>=3.0.7,<4.0
pytest-cov>=2.5,<2.6
pytest>=3.4,<4.0
pywatchman==1.4.1
requests[security]>=2.5.0,<2.19
scandir==1.2
Expand Down
1 change: 1 addition & 0 deletions src/python/pants/backend/python/tasks/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ python_library(
'src/python/pants/backend/python:interpreter_cache',
'src/python/pants/backend/python/subsystems',
'src/python/pants/backend/python/targets',
'src/python/pants/backend/python/tasks/coverage:plugin',
'src/python/pants/base:build_environment',
'src/python/pants/base:exceptions',
'src/python/pants/base:fingerprint_strategy',
Expand Down
7 changes: 7 additions & 0 deletions src/python/pants/backend/python/tasks/coverage/BUILD
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# Copyright 2018 Pants project contributors (see CONTRIBUTORS.md).
# Licensed under the Apache License, Version 2.0 (see LICENSE).

resources(
name='plugin',
sources=['plugin.py']
)
Empty file.
94 changes: 94 additions & 0 deletions src/python/pants/backend/python/tasks/coverage/plugin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
# coding=utf-8
# Copyright 2018 Pants project contributors (see CONTRIBUTORS.md).
# Licensed under the Apache License, Version 2.0 (see LICENSE).

from __future__ import (absolute_import, division, generators, nested_scopes, print_function,
unicode_literals, with_statement)

import json
import os

from coverage import CoveragePlugin, FileTracer
from coverage.config import DEFAULT_PARTIAL, DEFAULT_PARTIAL_ALWAYS
from coverage.misc import join_regex
from coverage.parser import PythonParser
from coverage.python import PythonFileReporter


class MyFileTracer(FileTracer):
def __init__(self, filename):
super(MyFileTracer, self).__init__()
self._filename = filename

def source_filename(self):
return self._filename


class MyFileReporter(PythonFileReporter):
"""A python file reporter that knows how to map Pants PEX chroots back to repo source code."""

def __init__(self, morf, relpath):
super(MyFileReporter, self).__init__(morf, coverage=None)
self._relpath = relpath

def relative_filename(self):
return self._relpath

# TODO(John Sirois): Kill the workaround overrides below if there is a useable upstream
# resolution to:
# https://bitbucket.org/ned/coveragepy/issues/646/modifying-coverage-reporting-for-python

@property
def parser(self):
if self._parser is None:
self._parser = PythonParser(filename=self.filename)
self._parser.parse_source()
return self._parser

def no_branch_lines(self):
return self.parser.lines_matching(join_regex(DEFAULT_PARTIAL[:]),
join_regex(DEFAULT_PARTIAL_ALWAYS[:]))


class MyPlugin(CoveragePlugin):
"""A plugin that knows how to map Pants PEX chroots back to repo source code when reporting."""

def __init__(self, buildroot, src_to_chroot):
super(MyPlugin, self).__init__()
self._buildroot = buildroot
self._src_to_chroot = src_to_chroot

def find_executable_files(self, top):
for chroot_path in self._src_to_chroot.values():
if top.startswith(chroot_path):
for root, _, files in os.walk(top):
for f in files:
if f.endswith('.py'):
yield os.path.join(root, f)
break

def file_tracer(self, filename):
for chroot_path in self._src_to_chroot.values():
if filename.startswith(chroot_path):
return MyFileTracer(filename)

def file_reporter(self, filename):
src_file = self._map_to_src(filename)
mapped_relpath = os.path.relpath(src_file, self._buildroot)
return MyFileReporter(filename, mapped_relpath)

def _map_to_src(self, chroot):
for src_dir, chroot_dir in self._src_to_chroot.items():
if chroot.startswith(chroot_dir):
return src_dir + chroot[len(chroot_dir):]
raise AssertionError('Failed to map traced file {} to any source root via '
'source root -> chroot mappings:\n\t{}'
.format(chroot, '\n\t'.join(sorted('{} -> {}'.format(src_dir, chroot_dir)
for src_dir, chroot_dir
in self._src_to_chroot.items()))))


def coverage_init(reg, options):
buildroot = options['buildroot']
src_to_chroot = json.loads(options['src_to_chroot'])
reg.add_file_tracer(MyPlugin(buildroot, src_to_chroot))
10 changes: 10 additions & 0 deletions src/python/pants/backend/python/tasks/pytest_prep.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

import os

import pkg_resources
from pex.pex_info import PexInfo

from pants.backend.python.subsystems.pytest import PyTest
Expand Down Expand Up @@ -38,6 +39,10 @@ def config_path(self):
"""
return os.path.join(self._pex.path(), 'pytest.ini')

@classmethod
def implementation_version(cls):
return super(PytestPrep, cls).implementation_version() + [('PytestPrep', 1)]

@classmethod
def product_types(cls):
return [cls.PytestBinary]
Expand All @@ -52,6 +57,11 @@ def extra_requirements(self):
def extra_files(self):
yield self.ExtraFile.empty('pytest.ini')

enclosing_dir = os.path.dirname(__name__.replace('.', os.sep))
plugin_path = os.path.join(enclosing_dir, 'coverage/plugin.py')
yield self.ExtraFile(path=plugin_path,
content=pkg_resources.resource_string(__name__, 'coverage/plugin.py'))

def execute(self):
pex_info = PexInfo.default()
pex_info.entry_point = 'pytest'
Expand Down
Loading

0 comments on commit 876c52b

Please sign in to comment.