Skip to content

Commit

Permalink
Merge pull request #13 from LeastAuthority/11.correct-coverage-collected
Browse files Browse the repository at this point in the history
Collect the correct coverage information
  • Loading branch information
exarkun authored Feb 10, 2020
2 parents 048a7b0 + 01fb40b commit e1885cf
Show file tree
Hide file tree
Showing 6 changed files with 105 additions and 31 deletions.
9 changes: 4 additions & 5 deletions .coveragerc
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
# -*- mode: conf -*-

[run]
# only record trace data for allmydata.*
# only record trace data for this package
source =
allmydata
# and don't trace the test files themselves, or generated files
magic_folder
# and don't trace the test files themselves
omit =
*/allmydata/test/*
*/allmydata/_version.py
*/magic_folder/test/*
parallel = True
branch = True
29 changes: 25 additions & 4 deletions integration/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
)

from eliot import (
Message,
to_file,
log_call,
start_action,
Expand Down Expand Up @@ -46,7 +47,7 @@
_ProcessExitedProtocol,
_create_node,
_cleanup_tahoe_process,
_tahoe_runner_optional_coverage,
_tahoe_runner,
await_client_ready,
TahoeProcess,
)
Expand Down Expand Up @@ -198,7 +199,7 @@ def introducer(reactor, temp_dir, flog_gatherer, request):
if not exists(intro_dir):
mkdir(intro_dir)
done_proto = _ProcessExitedProtocol()
_tahoe_runner_optional_coverage(
_tahoe_runner(
done_proto,
reactor,
request,
Expand All @@ -219,7 +220,7 @@ def introducer(reactor, temp_dir, flog_gatherer, request):
# but on linux it means daemonize. "tahoe run" is consistent
# between platforms.
protocol = _MagicTextProtocol('introducer running')
transport = _tahoe_runner_optional_coverage(
transport = _tahoe_runner(
protocol,
reactor,
request,
Expand Down Expand Up @@ -387,15 +388,35 @@ def _run_magic_folder(reactor, request, temp_dir, name, web_port):
magic_text = "Completed initial Magic Folder setup"
proto = _MagicTextProtocol(magic_text)

coverage = request.config.getoption('coverage')
def optional(flag, elements):
if flag:
return elements
return []

args = [
sys.executable,
"-m",
] + optional(coverage, [
"coverage",
"run",
"-m",
]) + [
"magic_folder",
] + optional(coverage, [
"--coverage",
]) + [
"--node-directory",
node_dir,
"run",
"--web-port", web_port,
"--web-port",
web_port,
]
Message.log(
message_type=u"integration:run-magic-folder",
coverage=coverage,
args=args,
)
transport = reactor.spawnProcess(
proto,
sys.executable,
Expand Down
47 changes: 27 additions & 20 deletions integration/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,11 @@

import requests

from eliot import (
Message,
current_action,
)

from allmydata.util.configutil import (
get_config,
set_config,
Expand Down Expand Up @@ -95,19 +100,26 @@ def __init__(self, magic_text):
self.exited = Deferred()
self._magic_text = magic_text
self._output = StringIO()
self._action = current_action()

def processEnded(self, reason):
self.exited.callback(None)
with self._action.context():
Message.log(message_type=u"process-ended")
self.exited.callback(None)

def outReceived(self, data):
sys.stdout.write(data)
self._output.write(data)
if not self.magic_seen.called and self._magic_text in self._output.getvalue():
print("Saw '{}' in the logs".format(self._magic_text))
self.magic_seen.callback(self)
with self._action.context():
Message.log(message_type=u"out-received", data=data)
sys.stdout.write(data)
self._output.write(data)
if not self.magic_seen.called and self._magic_text in self._output.getvalue():
print("Saw '{}' in the logs".format(self._magic_text))
self.magic_seen.callback(self)

def errReceived(self, data):
sys.stdout.write(data)
with self._action.context():
Message.log(message_type=u"err-received", data=data)
sys.stdout.write(data)


def _cleanup_tahoe_process(tahoe_transport, exited):
Expand Down Expand Up @@ -141,16 +153,12 @@ def _magic_folder_runner(proto, reactor, request, other_args):
)


def _tahoe_runner_optional_coverage(proto, reactor, request, other_args):
def _tahoe_runner(proto, reactor, request, other_args):
"""
Internal helper. Calls spawnProcess with `-m
allmydata.scripts.runner` and `other_args`, optionally inserting a
`--coverage` option if the `request` indicates we should.
Internal helper. Calls spawnProcess with `-m allmydata.scripts.runner` and
`other_args`.
"""
if request.config.getoption('coverage'):
args = [sys.executable, '-m', 'coverage', 'run', '-m', 'allmydata.scripts.runner', '--coverage']
else:
args = [sys.executable, '-m', 'allmydata.scripts.runner']
args = [sys.executable, '-m', 'allmydata.scripts.runner']
args += other_args
return reactor.spawnProcess(
proto,
Expand Down Expand Up @@ -200,7 +208,7 @@ def _run_node(reactor, node_dir, request, magic_text):
# but on linux it means daemonize. "tahoe run" is consistent
# between platforms.

transport = _tahoe_runner_optional_coverage(
transport = _tahoe_runner(
protocol,
reactor,
request,
Expand Down Expand Up @@ -261,7 +269,7 @@ def _create_node(reactor, request, temp_dir, introducer_furl, flog_gatherer, nam
args.append('--no-storage')
args.append(node_dir)

_tahoe_runner_optional_coverage(done_proto, reactor, request, args)
_tahoe_runner(done_proto, reactor, request, args)
created_d = done_proto.done

def created(_):
Expand Down Expand Up @@ -396,11 +404,10 @@ def await_file_vanishes(path, timeout=10):

def cli(request, reactor, node_dir, *argv):
"""
Run a tahoe CLI subcommand for a given node, optionally running
under coverage if '--coverage' was supplied.
Run a tahoe CLI subcommand for a given node.
"""
proto = _CollectOutputProtocol()
_tahoe_runner_optional_coverage(
_tahoe_runner(
proto, reactor, request,
['--node-directory', node_dir] + list(argv),
)
Expand Down
Empty file added newsfragments/11.minor
Empty file.
38 changes: 38 additions & 0 deletions src/magic_folder/_coverage.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import os

from twisted.application.service import (
Service,
)

class _CoverageService(Service):
def startService(self):
import coverage

Service.startService(self)

# this doesn't change the shell's notion of the environment, but it make
# the test in process_startup() succeed, which is the goal here.
os.environ["COVERAGE_PROCESS_START"] = ".coveragerc"

# maybe-start the global coverage, unless it already got started
self.cov = coverage.process_startup()
if self.cov is None:
self.cov = coverage.process_startup.coverage

def stopService(self):
"""
Make sure that coverage has stopped; internally, it depends on ataxit
handlers running which doesn't always happen (Twisted's shutdown hook
also won't run if os._exit() is called, but it runs more-often than
atexit handlers).
"""
self.cov.stop()
self.cov.save()


def coverage_service():
"""
Return a service which will arrange for coverage to be collected (or fail
if the ``coverage`` package is not installed).
"""
return _CoverageService()
13 changes: 11 additions & 2 deletions src/magic_folder/scripts/magic_folder_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,10 @@
magic_folder_web_service,
)

from .._coverage import (
coverage_service,
)

INVITE_SEPARATOR = "+"

class CreateOptions(BasedirOptions):
Expand Down Expand Up @@ -995,6 +999,7 @@ class MagicFolderCommand(BaseOptions):
]
optFlags = [
["debug", "d", "Print full stack-traces"],
("coverage", None, "Enable coverage measurement."),
]
description = (
"A magic-folder has an owner who controls the writecap "
Expand Down Expand Up @@ -1095,12 +1100,16 @@ def makeService(options):
:return IService: An object providing ``IService`` which performs the
magic-folder operation requested by ``options`` when it is started.
"""
return _MagicFolderService(options)
service = MultiService()
_MagicFolderService(options).setServiceParent(service)
if options["coverage"]:
coverage_service().setServiceParent(service)
return service


def run():
"""
Implement the *magic_folder* console script declared in ``setup.py``.
Implement the *magic-folder* console script declared in ``setup.py``.
:return: ``None``
"""
Expand Down

0 comments on commit e1885cf

Please # to comment.