Merge lp:~mbp/launchpad/use-txfixtures into lp:launchpad

Proposed by Martin Pool
Status: Merged
Approved by: Brad Crittenden
Approved revision: no longer in the source branch.
Merged at revision: 14263
Proposed branch: lp:~mbp/launchpad/use-txfixtures
Merge into: lp:launchpad
Prerequisite: lp:~mbp/launchpad/800295-buildd-split
Diff against target: 249 lines (+16/-133)
5 files modified
lib/canonical/buildd/tests/harness.py (+4/-5)
lib/canonical/launchpad/daemons/tachandler.py (+9/-127)
lib/canonical/launchpad/daemons/tests/test_tachandler.py (+1/-1)
setup.py (+1/-0)
versions.cfg (+1/-0)
To merge this branch: bzr merge lp:~mbp/launchpad/use-txfixtures
Reviewer Review Type Date Requested Status
Brad Crittenden (community) code Approve
Review via email: mp+81243@code.launchpad.net

Commit message

[r=bac][no-qa] split out and depend upon txfixtures

Description of the change

This builds on <https://code.launchpad.net/~mbp/launchpad/800295-buildd-split/+merge/81111> to do more separation of the buildd code from the Launchpad tree.

The only remaining code dependency of the buildds is that they need a test fixture to run a Twisted application out-of-process. I have extracted out that code into lp:txfixtures.

This patch then:

 * makes Launchpad depend on txfixtures
 * makes txfixtures.TacTestFixture the base for Launchpad's own TacTestSetup
 * corrects in passing a test that counts on raising a DeprecationWarning for what's actually an operational warning (stale pid file) and that can be flaky on pythons where that is automatically suppressed
 * changes the buildd code to use txfixtures directly rather than canonical.launchpad.daemons.tachandler, so it no longer needs anything from the lp tree

I thought about changing buildd to use only relative imports so that it obviously didn't use anything from the rest of the tree and it could be more easily moved, but in the light of <https://code.launchpad.net/~abentley/launchpad/absolute-oops-import/+merge/71423> I decided not to.

This doesn't actually move the buildd out of tree but that should now be a small step.

I've left most of the tac fixture tests in the tree - they are somewhat redundant with tests now done inside txfixtures, but they're cheap and I think the integration testing is worthwhile, at least for now.

Launchpad's TacTestSetup could probably be slimmed down a bit more or even deleted entirely.

To post a comment you must log in.
Revision history for this message
Martin Pool (mbp) wrote :

At least one more thing is needed before this can land: package txfixtures
and make that a dpkg dependency of buildd.

Revision history for this message
Brad Crittenden (bac) wrote :

Hi Martin,

Thanks for the work you're doing here to untangle things. Your changes look good.

review: Approve (code)
Revision history for this message
Robert Collins (lifeless) wrote :

FTR you don't need to debian package txfixtures.

Add the egg to the download cache, and we're GTG.

Revision history for this message
Martin Pool (mbp) wrote :

On 7 November 2011 18:28, Robert Collins <email address hidden> wrote:
> FTR you don't need to debian package txfixtures.
>
> Add the egg to the download cache, and we're GTG.

For deployment into lp itself, yes. But this will also need to be
installed alongside buildds, which are installed from a package. So
we either need txfixtures packaged, or at least to get lamont & co to
agree to install it some other way.

Revision history for this message
Martin Pool (mbp) wrote :

I needed some additional changes in txfixtures to make this work on python<2.7, since they don't understand PYTHONWARNINGS and deprecation warnings make things fail.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'lib/canonical/buildd/tests/harness.py'
2--- lib/canonical/buildd/tests/harness.py 2011-11-04 00:43:11 +0000
3+++ lib/canonical/buildd/tests/harness.py 2011-11-07 07:50:31 +0000
4@@ -11,11 +11,9 @@
5 import unittest
6 from ConfigParser import SafeConfigParser
7
8-import canonical
9-import canonical.buildd
10+from txfixtures.tachandler import TacTestFixture
11
12 from canonical.buildd.slave import BuildDSlave
13-from canonical.launchpad.daemons.tachandler import TacTestSetup
14
15 from lp.services.osutils import remove_tree
16
17@@ -58,7 +56,7 @@
18 f.close()
19
20
21-class BuilddSlaveTestSetup(TacTestSetup):
22+class BuilddSlaveTestSetup(TacTestFixture):
23 r"""Setup BuildSlave for use by functional tests
24
25 >>> fixture = BuilddSlaveTestSetup()
26@@ -119,7 +117,8 @@
27 @property
28 def tacfile(self):
29 return os.path.abspath(os.path.join(
30- os.path.dirname(canonical.buildd.__file__),
31+ os.path.dirname(__file__),
32+ os.path.pardir,
33 'buildd-slave.tac'
34 ))
35
36
37=== modified file 'lib/canonical/launchpad/daemons/tachandler.py'
38--- lib/canonical/launchpad/daemons/tachandler.py 2011-11-04 00:43:11 +0000
39+++ lib/canonical/launchpad/daemons/tachandler.py 2011-11-07 07:50:31 +0000
40@@ -21,6 +21,11 @@
41
42 from fixtures import Fixture
43
44+from txfixtures.tachandler import (
45+ TacException,
46+ TacTestFixture,
47+ )
48+
49 from canonical.launchpad.daemons import readyservice
50 from lp.services.osutils import (
51 get_pid_from_file,
52@@ -36,74 +41,22 @@
53 os.pardir, os.pardir, os.pardir, os.pardir, 'bin', 'twistd'))
54
55
56-class TacException(Exception):
57- """Error raised by TacTestSetup."""
58-
59-
60-class TacTestSetup(Fixture):
61+class TacTestSetup(TacTestFixture):
62 """Setup an TAC file as daemon for use by functional tests.
63
64 You must override setUpRoot to set up a root directory for the daemon.
65 """
66
67 def setUp(self, spew=False, umask=None):
68- Fixture.setUp(self)
69- if get_pid_from_file(self.pidfile):
70- # An attempt to run while there was an existing live helper
71- # was made. Note that this races with helpers which use unique
72- # roots, so when moving/eliminating this code check subclasses
73- # for workarounds and remove those too.
74- pid = get_pid_from_file(self.pidfile)
75- warnings.warn("Attempt to start Tachandler with an existing "
76- "instance (%d) running in %s." % (pid, self.pidfile),
77- DeprecationWarning, stacklevel=2)
78- two_stage_kill(pid)
79- # If the pid file still exists, it may indicate that the process
80- # respawned itself, or that two processes were started (race?) and
81- # one is still running while the other has ended, or the process
82- # was killed but it didn't remove the pid file (bug), or the
83- # machine was hard-rebooted and the pid file was not cleaned up
84- # (bug again). In other words, it's not safe to assume that a
85- # stale pid file is safe to delete without human intervention.
86- if get_pid_from_file(self.pidfile):
87- raise TacException(
88- "Could not kill stale process %s." % (self.pidfile,))
89-
90 # setUp() watches the logfile to determine when the daemon has fully
91 # started. If it sees an old logfile, then it will find the
92 # readyservice.LOG_MAGIC string and return immediately, provoking
93 # hard-to-diagnose race conditions. Delete the logfile to make sure
94 # this does not happen.
95 remove_if_exists(self.logfile)
96-
97- self.setUpRoot()
98- args = [sys.executable,
99- # XXX: 2010-04-26, Salgado, bug=570246: Deprecation warnings
100- # in Twisted are not our problem. They also aren't easy to
101- # suppress, and cause test failures due to spurious stderr
102- # output. Just shut the whole bloody mess up.
103- '-Wignore::DeprecationWarning',
104- twistd_script, '-o', '-y', self.tacfile,
105- '--pidfile', self.pidfile, '--logfile', self.logfile]
106- if spew:
107- args.append('--spew')
108- if umask is not None:
109- args.extend(('--umask', umask))
110-
111- # Run twistd, and raise an error if the return value is non-zero or
112- # stdout/stderr are written to.
113- proc = subprocess.Popen(args, stdout=subprocess.PIPE,
114- stderr=subprocess.STDOUT)
115- self.addCleanup(self.killTac)
116- stdout = until_no_eintr(10, proc.stdout.read)
117- if stdout:
118- raise TacException('Error running %s: unclean stdout/err: %s'
119- % (args, stdout))
120- rv = proc.wait()
121- if rv != 0:
122- raise TacException('Error %d running %s' % (rv, args))
123-
124- self._waitForDaemonStartup()
125+ TacTestFixture.setUp(self,
126+ python_path=sys.executable,
127+ twistd_script=twistd_script)
128
129 def _hasDaemonStarted(self):
130 """Has the daemon started?
131@@ -117,61 +70,6 @@
132 else:
133 return False
134
135- def _isPortListening(self, host, port):
136- """True if a tcp port is accepting connections.
137-
138- This can be used by subclasses overriding _hasDaemonStarted, if they
139- want to check the port is up rather than for the contents of the log
140- file.
141- """
142- try:
143- s = socket.socket()
144- s.settimeout(2.0)
145- s.connect((host, port))
146- s.close()
147- return True
148- except socket.error, e:
149- if e.errno == errno.ECONNREFUSED:
150- return False
151- else:
152- raise
153-
154- def _waitForDaemonStartup(self):
155- """ Wait for the daemon to fully start.
156-
157- Times out after 20 seconds. If that happens, the log file content
158- will be included in the exception message for debugging purpose.
159-
160- :raises TacException: Timeout.
161- """
162- # Watch the log file for readyservice.LOG_MAGIC to signal that startup
163- # has completed.
164- now = time.time()
165- deadline = now + 20
166- while now < deadline and not self._hasDaemonStarted():
167- time.sleep(0.1)
168- now = time.time()
169-
170- if now >= deadline:
171- raise TacException('Unable to start %s. Content of %s:\n%s' % (
172- self.tacfile, self.logfile, open(self.logfile).read()))
173-
174- def tearDown(self):
175- # For compatibility - migrate to cleanUp.
176- self.cleanUp()
177-
178- def killTac(self):
179- """Kill the TAC file if it is running."""
180- pidfile = self.pidfile
181- kill_by_pidfile(pidfile)
182-
183- def sendSignal(self, sig):
184- """Send the given signal to the tac process."""
185- pid = get_pid_from_file(self.pidfile)
186- if pid is None:
187- return
188- os.kill(pid, sig)
189-
190 def truncateLog(self):
191 """Truncate the log file.
192
193@@ -198,19 +96,3 @@
194 test failure (e.g. log files might contain helpful tracebacks).
195 """
196 raise NotImplementedError
197-
198- @property
199- def root(self):
200- raise NotImplementedError
201-
202- @property
203- def tacfile(self):
204- raise NotImplementedError
205-
206- @property
207- def pidfile(self):
208- raise NotImplementedError
209-
210- @property
211- def logfile(self):
212- raise NotImplementedError
213
214=== modified file 'lib/canonical/launchpad/daemons/tests/test_tachandler.py'
215--- lib/canonical/launchpad/daemons/tests/test_tachandler.py 2011-08-19 15:36:31 +0000
216+++ lib/canonical/launchpad/daemons/tests/test_tachandler.py 2011-11-07 07:50:31 +0000
217@@ -132,7 +132,7 @@
218
219 # One deprecation warning is emitted.
220 self.assertEqual(1, len(warnings_log))
221- self.assertIs(DeprecationWarning, warnings_log[0].category)
222+ self.assertIs(UserWarning, warnings_log[0].category)
223
224 def test_truncateLog(self):
225 """
226
227=== modified file 'setup.py'
228--- setup.py 2011-11-03 23:03:26 +0000
229+++ setup.py 2011-11-07 07:50:31 +0000
230@@ -86,6 +86,7 @@
231 'timeline',
232 'transaction',
233 'Twisted',
234+ 'txfixtures',
235 'txlongpollfixture',
236 'wadllib',
237 'z3c.pt',
238
239=== modified file 'versions.cfg'
240--- versions.cfg 2011-11-07 03:44:49 +0000
241+++ versions.cfg 2011-11-07 07:50:31 +0000
242@@ -98,6 +98,7 @@
243 timeline = 0.0.1
244 transaction = 1.0.0
245 txamqp = 0.4
246+txfixtures = 0.1.4
247 txlongpoll = 0.2.9
248 txlongpollfixture = 0.1.3
249 Twisted = 11.1.0pre1