Repository URL to install this package:
|
Version:
1.26.0.dev0+git7a2db260 ▾
|
# Copyright 2019 Pants project contributors (see CONTRIBUTORS.md).
# Licensed under the Apache License, Version 2.0 (see LICENSE).
import logging
import sys
from contextlib import contextmanager
from twitter.common.collections import maybe_list
from pants.base.workunit import WorkUnit
from pants.build_graph.target import Target
from pants.goal.context import Context
from pants.goal.run_tracker import RunTrackerLogger
class TestContext(Context):
"""A Context to use during unittesting.
:API: public
Stubs out various dependencies that we don't want to introduce in unit tests.
TODO: Instead of extending the runtime Context class, create a Context interface and have
TestContext and a runtime Context implementation extend that. This will also allow us to
isolate the parts of the interface that a Task is allowed to use vs. the parts that the
task-running machinery is allowed to use.
"""
class DummyWorkUnit:
"""A workunit stand-in that sends all output to stderr.
These outputs are typically only used by subprocesses spawned by code under test, not
the code under test itself, and would otherwise go into some reporting black hole. The
testing framework will only display the stderr output when a test fails.
Provides no other tracking/labeling/reporting functionality. Does not require "opening"
or "closing".
"""
def output(self, name):
return sys.stderr.buffer
def set_outcome(self, outcome):
return sys.stderr.buffer.write(
f"\nWorkUnit outcome: {WorkUnit.outcome_string(outcome)}\n".encode()
)
class DummyRunTracker:
"""A runtracker stand-in that does no actual tracking."""
def __init__(self):
self.logger = RunTrackerLogger(self)
class DummyArtifactCacheStats:
def add_hits(self, cache_name, targets):
pass
def add_misses(self, cache_name, targets, causes):
pass
artifact_cache_stats = DummyArtifactCacheStats()
def report_target_info(self, scope, target, keys, val):
pass
class TestLogger(logging.getLoggerClass()): # type: ignore[misc] # MyPy does't understand this dynamic base class
"""A logger that converts our structured records into flat ones.
This is so we can use a regular logger in tests instead of our reporting machinery.
"""
def makeRecord(
self, name, lvl, fn, lno, msg, args, exc_info, func=None, extra=None, sinfo=None
):
msg = "".join([msg] + [a[0] if isinstance(a, (list, tuple)) else a for a in args])
args = []
return super().makeRecord(name, lvl, fn, lno, msg, args, exc_info, func, extra, sinfo)
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
logger_cls = logging.getLoggerClass()
try:
logging.setLoggerClass(self.TestLogger)
self._logger = logging.getLogger("test")
finally:
logging.setLoggerClass(logger_cls)
@contextmanager
def new_workunit(self, name, labels=None, cmd="", log_config=None):
"""
:API: public
"""
sys.stderr.write(f"\nStarting workunit {name}\n")
yield TestContext.DummyWorkUnit()
@property
def log(self):
"""
:API: public
"""
return self._logger
def submit_background_work_chain(self, work_chain, parent_workunit_name=None):
"""
:API: public
"""
# Just do the work synchronously, so we don't need a run tracker, background workers and so on.
for work in work_chain:
for args_tuple in work.args_tuples:
work.func(*args_tuple)
def subproc_map(self, f, items):
"""
:API: public
"""
# Just execute in-process.
return list(map(f, items))
def create_context_from_options(
options,
target_roots=None,
build_graph=None,
build_configuration=None,
address_mapper=None,
console_outstream=None,
workspace=None,
scheduler=None,
):
"""Creates a ``Context`` with the given options and no targets by default.
:param options: An :class:`pants.option.options.Option`-alike object that supports read methods.
Other params are as for ``Context``.
"""
run_tracker = TestContext.DummyRunTracker()
target_roots = maybe_list(target_roots, Target) if target_roots else []
return TestContext(
options=options,
run_tracker=run_tracker,
target_roots=target_roots,
build_graph=build_graph,
build_configuration=build_configuration,
address_mapper=address_mapper,
console_outstream=console_outstream,
workspace=workspace,
scheduler=scheduler,
)