/usr/lib/python3/dist-packages/txfixtures/tachandler.py is in python3-txfixtures 0.2.6-1.
This file is owned by root:root, with mode 0o644.
The actual contents of the file can be viewed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 | # Copyright 2009-2011 Canonical Ltd. This software is licensed under the
# GNU General Public License version 3.
"""Test harness for TAC (Twisted Application Configuration) files."""
__metaclass__ = type
__all__ = [
'TacException',
'TacTestFixture',
]
import errno
import os
import socket
import subprocess
import sys
import time
import warnings
from fixtures import Fixture
from testtools.content import content_from_file
from txfixtures.osutils import (
get_pid_from_file,
kill_by_pidfile,
two_stage_kill,
until_no_eintr,
)
class TacException(Exception):
"""Error raised by TacTestSetup."""
class TacTestFixture(Fixture):
"""Setup an TAC file as daemon for use by functional tests.
You must override setUpRoot to set up a root directory for the daemon.
You may override _hasDaemonStarted, typically by calling _isPortListening,
to tell how long to wait before the daemon is available.
"""
_proc = None
def setUp(self, spew=False, umask=None,
python_path=None, twistd_script=None):
"""Initialize a new TacTestFixture fixture.
:param python_path: If set, run twistd under this Python interpreter.
:param twistd_script: If set, run this twistd script rather than the
system default. Must be provided if python_path is given.
"""
super(TacTestFixture, self).setUp()
if get_pid_from_file(self.pidfile):
# An attempt to run while there was an existing live helper
# was made. Note that this races with helpers which use unique
# roots, so when moving/eliminating this code check subclasses
# for workarounds and remove those too.
pid = get_pid_from_file(self.pidfile)
warnings.warn("Attempt to start Tachandler %r with an existing "
"instance (%d) running in %s." % (
self.tacfile, pid, self.pidfile),
UserWarning, stacklevel=2)
two_stage_kill(pid)
# If the pid file still exists, it may indicate that the process
# respawned itself, or that two processes were started (race?) and
# one is still running while the other has ended, or the process
# was killed but it didn't remove the pid file (bug), or the
# machine was hard-rebooted and the pid file was not cleaned up
# (bug again). In other words, it's not safe to assume that a
# stale pid file is safe to delete without human intervention.
stale_pid = get_pid_from_file(self.pidfile)
if stale_pid:
raise TacException(
"Could not kill stale process %s from %s." % (
stale_pid, self.pidfile,))
self.setUpRoot()
if python_path is None:
python_path = sys.executable
if twistd_script is None:
twistd_script = '/usr/bin/twistd'
args = [python_path,
'-Wignore::DeprecationWarning',
twistd_script,
'-o', '-y', self.tacfile, '--pidfile', self.pidfile,
'--logfile', self.logfile]
if spew:
args.append('--spew')
if umask is not None:
args.extend(('--umask', umask))
# 2010-04-26, Salgado, http://pad.lv/570246: Deprecation warnings
# in Twisted are not our problem. They also aren't easy to suppress,
# and cause test failures due to spurious stderr output. Just shut
# the whole bloody mess up.
# Run twistd, and raise an error if the return value is non-zero or
# stdout/stderr are written to.
self._proc = subprocess.Popen(
args,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
)
self.addCleanup(self.killTac)
stdout = until_no_eintr(10, self._proc.stdout.read)
if stdout:
raise TacException('Error running %s: unclean stdout/err: %s'
% (args, stdout))
rv = self._proc.wait()
# twistd will normally fork off into the background with the
# originally-spawned process exiting 0.
if rv != 0:
raise TacException('Error %d running %s' % (rv, args))
self.addDetail(self.logfile, content_from_file(self.logfile))
self._waitForDaemonStartup()
def _hasDaemonStarted(self):
"""Has the daemon started?
"""
return self._isPortListening('localhost', self.daemon_port)
def _isPortListening(self, host, port):
"""True if a tcp port is accepting connections.
This can be used by subclasses overriding _hasDaemonStarted, if they
want to check the port is up rather than for the contents of the log
file.
"""
try:
s = socket.socket()
s.settimeout(2.0)
s.connect((host, port))
s.close()
return True
except socket.error as e:
if e.errno == errno.ECONNREFUSED:
return False
else:
raise
def _waitForDaemonStartup(self):
""" Wait for the daemon to fully start.
Times out after 20 seconds. If that happens, the log file content
will be included in the exception message for debugging purpose.
:raises TacException: Timeout.
"""
# Watch the log file for readyservice.LOG_MAGIC to signal that startup
# has completed.
now = time.time()
deadline = now + 20
while now < deadline and not self._hasDaemonStarted():
time.sleep(0.1)
now = time.time()
if now >= deadline:
raise TacException('Unable to start %s.' % self.tacfile)
def tearDown(self):
# For compatibility - migrate to cleanUp.
self.cleanUp()
def killTac(self):
"""Kill the TAC file if it is running."""
pidfile = self.pidfile
kill_by_pidfile(pidfile)
if self._proc:
# Close the pipe
self._proc.stdout.close()
def sendSignal(self, sig):
"""Send the given signal to the tac process."""
pid = get_pid_from_file(self.pidfile)
if pid is None:
return
os.kill(pid, sig)
def setUpRoot(self):
"""Override this.
This should be able to cope with the root already existing, because it
will be left behind after each test in case it's needed to diagnose a
test failure (e.g. log files might contain helpful tracebacks).
"""
raise NotImplementedError
@property
def root(self):
raise NotImplementedError
@property
def tacfile(self):
raise NotImplementedError
@property
def pidfile(self):
raise NotImplementedError
@property
def logfile(self):
raise NotImplementedError
@property
def daemon_port(self):
raise NotImplementedError
|