yellow team mailing list archive
-
yellow team
-
Mailing list archive
-
Message #00717
[Merge] lp:~frankban/launchpad/bug-974585-unclean-reactor-error into lp:launchpad
Francesco Banconi has proposed merging lp:~frankban/launchpad/bug-974585-unclean-reactor-error into lp:launchpad.
Requested reviews:
Launchpad Yellow Squad (yellow)
Related bugs:
Bug #974585 in Launchpad itself: "test_openFileInNonDirectory has UncleanReactorError"
https://bugs.launchpad.net/launchpad/+bug/974585
For more details, see:
https://code.launchpad.net/~frankban/launchpad/bug-974585-unclean-reactor-error/+merge/102473
= Summary =
Some tests using twisted leave delayed calls. If, later, another test uses
testtools an exception is raised if the reactor is not clean.
== Proposed fix ==
Use the clean_up_reactor function on the tearDown of tests leaving delayed
calls.
== Pre-implementation notes ==
I started a full test run temporary patching zope.testrunner to check and
notify not cancelled delayed calls after each test.
This way I spotted lp.buildmaster.tests.test_builder.TestSlaveConnectionTimeouts.test_connection_timeout as the only test leaving delayed calls. Hopefully, fixing that, we will not see the unclean reactor error again.
== Implementation details ==
- Move the `clean_up_reactor` function in a more generic place.
- And use it in
lp.buildmaster.tests.test_builder.TestSlaveConnectionTimeouts.tearDown.
- Lint updates for file `lib/lp/buildmaster/tests/test_builder.py`.
== Demo and Q/A ==
no-qa
== lint ==
Checking for conflicts and issues in changed files.
Linting changed files:
lib/lp/buildmaster/tests/test_builder.py
lib/lp/registry/tests/test_distributionmirror_prober.py
lib/lp/testing/__init__.py
--
https://code.launchpad.net/~frankban/launchpad/bug-974585-unclean-reactor-error/+merge/102473
Your team Launchpad Yellow Squad is requested to review the proposed merge of lp:~frankban/launchpad/bug-974585-unclean-reactor-error into lp:launchpad.
=== modified file 'lib/lp/buildmaster/tests/test_builder.py'
--- lib/lp/buildmaster/tests/test_builder.py 2012-02-09 23:09:36 +0000
+++ lib/lp/buildmaster/tests/test_builder.py 2012-04-18 09:45:27 +0000
@@ -78,6 +78,7 @@
BinaryPackageBuildBehavior,
)
from lp.testing import (
+ clean_up_reactor,
TestCase,
TestCaseWithFactory,
)
@@ -150,11 +151,13 @@
lostbuilding_builder = MockBuilder(
'Lost Building Broken Slave', slave, behavior=CorruptBehavior())
d = lostbuilding_builder.updateStatus(BufferLogger())
+
def check_slave_status(failure):
self.assertIn('abort', slave.call_log)
# 'Fault' comes from the LostBuildingBrokenSlave, this is
# just testing that the value is passed through.
self.assertIsInstance(failure.value, xmlrpclib.Fault)
+
return d.addBoth(check_slave_status)
def test_resumeSlaveHost_nonvirtual(self):
@@ -176,8 +179,10 @@
builder = self.factory.makeBuilder(virtualized=True, vm_host="pop")
d = builder.resumeSlaveHost()
+
def got_resume(output):
self.assertEqual(('parp', ''), output)
+
return d.addCallback(got_resume)
def test_resumeSlaveHost_command_failed(self):
@@ -252,9 +257,11 @@
removeSecurityProxy(builder)._findBuildCandidate = FakeMethod(
result=candidate)
d = builder.findAndStartJob()
+
def check_build_started(candidate):
self.assertEqual(candidate.builder, builder)
self.assertEqual(BuildStatus.BUILDING, build.status)
+
return d.addCallback(check_build_started)
def test_virtual_job_dispatch_pings_before_building(self):
@@ -265,9 +272,11 @@
removeSecurityProxy(builder)._findBuildCandidate = FakeMethod(
result=candidate)
d = builder.findAndStartJob()
+
def check_build_started(candidate):
self.assertIn(
('echo', 'ping'), removeSecurityProxy(builder.slave).call_log)
+
return d.addCallback(check_build_started)
def test_slave(self):
@@ -286,8 +295,10 @@
builder = MockBuilder("mock_builder", aborted_slave)
builder.currentjob = None
d = builder.rescueIfLost()
+
def check_slave_calls(ignored):
self.assertIn('clean', aborted_slave.call_log)
+
return d.addCallback(check_slave_calls)
def test_recovery_of_aborted_nonvirtual_slave(self):
@@ -300,9 +311,11 @@
builder.virtualized = False
builder.builderok = True
d = builder.rescueIfLost()
+
def check_failed(ignored):
self.assertFalse(builder.builderok)
self.assertNotIn('clean', aborted_slave.call_log)
+
return d.addCallback(check_failed)
def test_recover_ok_slave(self):
@@ -310,9 +323,11 @@
slave = OkSlave()
builder = MockBuilder("mock_builder", slave, TrivialBehavior())
d = builder.rescueIfLost()
+
def check_slave_calls(ignored):
self.assertNotIn('abort', slave.call_log)
self.assertNotIn('clean', slave.call_log)
+
return d.addCallback(check_slave_calls)
def test_recover_waiting_slave_with_good_id(self):
@@ -321,9 +336,11 @@
waiting_slave = WaitingSlave()
builder = MockBuilder("mock_builder", waiting_slave, TrivialBehavior())
d = builder.rescueIfLost()
+
def check_slave_calls(ignored):
self.assertNotIn('abort', waiting_slave.call_log)
self.assertNotIn('clean', waiting_slave.call_log)
+
return d.addCallback(check_slave_calls)
def test_recover_waiting_slave_with_bad_id(self):
@@ -335,31 +352,39 @@
waiting_slave = WaitingSlave()
builder = MockBuilder("mock_builder", waiting_slave, CorruptBehavior())
d = builder.rescueIfLost()
+
def check_slave_calls(ignored):
self.assertNotIn('abort', waiting_slave.call_log)
self.assertIn('clean', waiting_slave.call_log)
+
return d.addCallback(check_slave_calls)
def test_recover_building_slave_with_good_id(self):
# rescueIfLost does not attempt to abort or clean a builder that is
# BUILDING.
building_slave = BuildingSlave()
- builder = MockBuilder("mock_builder", building_slave, TrivialBehavior())
+ builder = MockBuilder(
+ "mock_builder", building_slave, TrivialBehavior())
d = builder.rescueIfLost()
+
def check_slave_calls(ignored):
self.assertNotIn('abort', building_slave.call_log)
self.assertNotIn('clean', building_slave.call_log)
+
return d.addCallback(check_slave_calls)
def test_recover_building_slave_with_bad_id(self):
# If a slave is BUILDING with a build id we don't recognize, then we
# abort the build, thus stopping it in its tracks.
building_slave = BuildingSlave()
- builder = MockBuilder("mock_builder", building_slave, CorruptBehavior())
+ builder = MockBuilder(
+ "mock_builder", building_slave, CorruptBehavior())
d = builder.rescueIfLost()
+
def check_slave_calls(ignored):
self.assertIn('abort', building_slave.call_log)
self.assertNotIn('clean', building_slave.call_log)
+
return d.addCallback(check_slave_calls)
def test_recover_building_slave_with_job_that_finished_elsewhere(self):
@@ -384,10 +409,12 @@
self.layer.txn.commit()
builder = getUtility(IBuilderSet)[builder.name]
d = builder.rescueIfLost()
+
def check_builder(ignored):
self.assertIsInstance(
removeSecurityProxy(builder.current_build_behavior),
IdleBuildBehavior)
+
return d.addCallback(check_builder)
@@ -930,10 +957,12 @@
build_id = 'status-build-id'
d = self.slave_helper.triggerGoodBuild(slave, build_id)
d.addCallback(lambda ignored: slave.status())
+
def check_status(status):
self.assertEqual([BuilderStatus.BUILDING, build_id], status[:2])
[log_file] = status[2:]
self.assertIsInstance(log_file, xmlrpclib.Binary)
+
return d.addCallback(check_status)
def test_ensurepresent_not_there(self):
@@ -964,9 +993,11 @@
slave = self.slave_helper.getClientSlave()
self.slave_helper.makeCacheFile(tachandler, 'blahblah')
d = slave.sendFileToSlave('blahblah', None, None, None)
+
def check_present(ignored):
d = slave.ensurepresent('blahblah', None, None, None)
return d.addCallback(self.assertEqual, [True, 'No URL'])
+
d.addCallback(check_present)
return d
@@ -1103,6 +1134,11 @@
self.slave = self.slave_helper.getClientSlave(
reactor=self.clock, proxy=self.proxy)
+ def tearDown(self):
+ # We need to remove any DelayedCalls that didn't actually get called.
+ clean_up_reactor()
+ super(TestSlaveConnectionTimeouts, self).tearDown()
+
def test_connection_timeout(self):
# The default timeout of 30 seconds should not cause a timeout,
# only the config value should.
@@ -1167,9 +1203,11 @@
slave = self.slave_helper.getClientSlave()
d = slave.ensurepresent(
lf.content.sha1, lf.http_url, "", "")
+
def check_file(ignored):
d = getPage(expected_url.encode('utf8'))
return d.addCallback(self.assertEqual, content)
+
return d.addCallback(check_file)
def test_getFiles(self):
@@ -1186,7 +1224,6 @@
def got_files(ignored):
# Called back when getFiles finishes. Make sure all the
# content is as expected.
- got_contents = []
for sha1 in filemap:
local_file = filemap[sha1]
file = open(local_file)
=== modified file 'lib/lp/registry/tests/test_distributionmirror_prober.py'
--- lib/lp/registry/tests/test_distributionmirror_prober.py 2012-04-02 20:56:02 +0000
+++ lib/lp/registry/tests/test_distributionmirror_prober.py 2012-04-18 09:45:27 +0000
@@ -60,6 +60,7 @@
from lp.services.config import config
from lp.services.daemons.tachandler import TacTestSetup
from lp.testing import (
+ clean_up_reactor,
TestCase,
TestCaseWithFactory,
)
@@ -69,14 +70,6 @@
)
-def clean_up_reactor():
- # XXX: JonathanLange 2010-11-22: These tests leave stacks of delayed
- # calls around. They need to be updated to use Twisted correctly.
- # For the meantime, just blat the reactor.
- for delayed_call in reactor.getDelayedCalls():
- delayed_call.cancel()
-
-
class HTTPServerTestSetup(TacTestSetup):
def setUpRoot(self):
=== modified file 'lib/lp/testing/__init__.py'
--- lib/lp/testing/__init__.py 2012-03-26 07:04:44 +0000
+++ lib/lp/testing/__init__.py 2012-04-18 09:45:27 +0000
@@ -15,6 +15,7 @@
'BrowserTestCase',
'build_yui_unittest_suite',
'celebrity_logged_in',
+ 'clean_up_reactor',
'ExpectedException',
'extract_lp_cache',
'FakeAdapterMixin',
@@ -1527,3 +1528,12 @@
self.addCleanup(
site_manager.registerUtility, current_commponent,
for_interface, name)
+
+
+def clean_up_reactor():
+ # XXX: JonathanLange 2010-11-22: These tests leave stacks of delayed
+ # calls around. They need to be updated to use Twisted correctly.
+ # For the meantime, just blat the reactor.
+ from twisted.internet import reactor
+ for delayed_call in reactor.getDelayedCalls():
+ delayed_call.cancel()