82
85
TestCaseWithFactory,
84
87
from lp.testing.fakemethod import FakeMethod
85
from lp.testing.layers import (
86
DatabaseFunctionalLayer,
87
LaunchpadZopelessLayer,
88
from lp.testing.layers import LaunchpadZopelessLayer
91
91
class TestBuilderBasics(TestCaseWithFactory):
92
92
"""Basic unit tests for `Builder`."""
94
layer = DatabaseFunctionalLayer
94
layer = LaunchpadZopelessLayer
97
super(TestBuilderBasics, self).setUp()
98
self.useFixture(BuilddManagerTestFixture())
96
100
def test_providesInterface(self):
97
101
# Builder provides IBuilder
98
builder = self.factory.makeBuilder()
102
with BuilddManagerTestFixture.extraSetUp():
103
builder = self.factory.makeBuilder()
99
104
self.assertProvides(builder, IBuilder)
101
106
def test_default_values(self):
102
builder = self.factory.makeBuilder()
107
with BuilddManagerTestFixture.extraSetUp():
108
builder = self.factory.makeBuilder()
103
109
# Make sure the Storm cache gets the values that the database
105
111
flush_database_updates()
112
self.useFixture(BuilddManagerTestFixture())
106
113
self.assertEqual(0, builder.failure_count)
108
115
def test_getCurrentBuildFarmJob(self):
109
bq = self.factory.makeSourcePackageRecipeBuildJob(3333)
110
builder = self.factory.makeBuilder()
111
bq.markAsBuilding(builder)
116
with BuilddManagerTestFixture.extraSetUp():
117
bq = self.factory.makeSourcePackageRecipeBuildJob(3333)
118
builder = self.factory.makeBuilder()
119
bq.markAsBuilding(builder)
120
self.useFixture(BuilddManagerTestFixture())
112
121
self.assertEqual(
113
122
bq, builder.getCurrentBuildFarmJob().buildqueue_record)
115
124
def test_getBuildQueue(self):
116
buildqueueset = getUtility(IBuildQueueSet)
117
active_jobs = buildqueueset.getActiveBuildJobs()
118
[active_job] = active_jobs
119
builder = active_job.builder
125
with BuilddManagerTestFixture.extraSetUp():
126
buildqueueset = getUtility(IBuildQueueSet)
127
active_jobs = buildqueueset.getActiveBuildJobs()
128
[active_job] = active_jobs
129
builder = active_job.builder
121
131
bq = builder.getBuildQueue()
122
132
self.assertEqual(active_job, bq)
124
active_job.builder = None
134
with BuilddManagerTestFixture.extraSetUp():
135
active_job.builder = None
125
137
bq = builder.getBuildQueue()
126
138
self.assertIs(None, bq)
128
140
def test_setting_builderok_resets_failure_count(self):
129
builder = removeSecurityProxy(self.factory.makeBuilder())
130
builder.failure_count = 1
131
builder.builderok = False
141
with BuilddManagerTestFixture.extraSetUp():
142
builder = removeSecurityProxy(self.factory.makeBuilder())
143
builder.failure_count = 1
144
builder.builderok = False
132
146
self.assertEqual(1, builder.failure_count)
133
builder.builderok = True
148
with BuilddManagerTestFixture.extraSetUp():
149
builder.builderok = True
134
151
self.assertEqual(0, builder.failure_count)
152
170
d = lostbuilding_builder.updateStatus(BufferLogger())
153
171
def check_slave_status(failure):
154
172
self.assertIn('abort', slave.call_log)
155
# 'Fault' comes from the LostBuildingBrokenSlave, this is
173
# 'Fault' comes from the LostBuildingBrokenSlave. This is
156
174
# just testing that the value is passed through.
157
175
self.assertIsInstance(failure.value, xmlrpclib.Fault)
158
176
return d.addBoth(check_slave_status)
160
178
def test_resumeSlaveHost_nonvirtual(self):
161
builder = self.factory.makeBuilder(virtualized=False)
179
with BuilddManagerTestFixture.extraSetUp():
180
builder = self.factory.makeBuilder(virtualized=False)
162
181
d = builder.resumeSlaveHost()
163
182
return assert_fails_with(d, CannotResumeHost)
165
184
def test_resumeSlaveHost_no_vmhost(self):
166
builder = self.factory.makeBuilder(virtualized=True, vm_host=None)
185
with BuilddManagerTestFixture.extraSetUp():
186
builder = self.factory.makeBuilder(virtualized=True, vm_host=None)
167
187
d = builder.resumeSlaveHost()
168
188
return assert_fails_with(d, CannotResumeHost)
174
194
config.push('reset', reset_config)
175
195
self.addCleanup(config.pop, 'reset')
177
builder = self.factory.makeBuilder(virtualized=True, vm_host="pop")
197
with BuilddManagerTestFixture.extraSetUp():
198
builder = self.factory.makeBuilder(
199
virtualized=True, vm_host="pop")
178
200
d = builder.resumeSlaveHost()
179
201
def got_resume(output):
180
202
self.assertEqual(('parp', ''), output)
186
208
vm_resume_command: /bin/false"""
187
209
config.push('reset fail', reset_fail_config)
188
210
self.addCleanup(config.pop, 'reset fail')
189
builder = self.factory.makeBuilder(virtualized=True, vm_host="pop")
211
with BuilddManagerTestFixture.extraSetUp():
212
builder = self.factory.makeBuilder(
213
virtualized=True, vm_host="pop")
190
214
d = builder.resumeSlaveHost()
191
215
return assert_fails_with(d, CannotResumeHost)
196
220
vm_resume_command: /bin/false"""
197
221
config.push('reset fail', reset_fail_config)
198
222
self.addCleanup(config.pop, 'reset fail')
199
builder = self.factory.makeBuilder(virtualized=True, vm_host="pop")
200
builder.builderok = True
223
with BuilddManagerTestFixture.extraSetUp():
224
builder = self.factory.makeBuilder(
225
virtualized=True, vm_host="pop")
226
builder.builderok = True
201
227
d = builder.handleTimeout(BufferLogger(), 'blah')
202
228
return assert_fails_with(d, CannotResumeHost)
230
@BuilddManagerTestFixture.extraSetUp
204
231
def _setupBuilder(self):
205
232
processor = self.factory.makeProcessor(name="i386")
206
233
builder = self.factory.makeBuilder(
235
264
# findAndStartJob finds the next queued job using _findBuildCandidate.
236
265
# We don't care about the type of build at all.
237
266
builder, build = self._setupRecipeBuildAndBuilder()
238
candidate = build.queueBuild()
267
with BuilddManagerTestFixture.extraSetUp():
268
candidate = build.queueBuild()
239
269
# _findBuildCandidate is tested elsewhere, we just make sure that
240
270
# findAndStartJob delegates to it.
241
271
removeSecurityProxy(builder)._findBuildCandidate = FakeMethod(
319
352
# rescueIfLost does not attempt to abort or clean a builder that is
321
354
waiting_slave = WaitingSlave()
322
builder = MockBuilder("mock_builder", waiting_slave, TrivialBehavior())
355
builder = MockBuilder(
356
"mock_builder", waiting_slave, TrivialBehavior())
323
357
d = builder.rescueIfLost()
324
358
def check_slave_calls(ignored):
325
359
self.assertNotIn('abort', waiting_slave.call_log)
333
367
# builder is reset for a new build, and the corrupt build is
335
369
waiting_slave = WaitingSlave()
336
builder = MockBuilder("mock_builder", waiting_slave, CorruptBehavior())
370
builder = MockBuilder(
371
"mock_builder", waiting_slave, CorruptBehavior())
337
372
d = builder.rescueIfLost()
338
373
def check_slave_calls(ignored):
339
374
self.assertNotIn('abort', waiting_slave.call_log)
344
379
# rescueIfLost does not attempt to abort or clean a builder that is
346
381
building_slave = BuildingSlave()
347
builder = MockBuilder("mock_builder", building_slave, TrivialBehavior())
382
builder = MockBuilder(
383
"mock_builder", building_slave, TrivialBehavior())
348
384
d = builder.rescueIfLost()
349
385
def check_slave_calls(ignored):
350
386
self.assertNotIn('abort', building_slave.call_log)
355
391
# If a slave is BUILDING with a build id we don't recognize, then we
356
392
# abort the build, thus stopping it in its tracks.
357
393
building_slave = BuildingSlave()
358
builder = MockBuilder("mock_builder", building_slave, CorruptBehavior())
394
builder = MockBuilder(
395
"mock_builder", building_slave, CorruptBehavior())
359
396
d = builder.rescueIfLost()
360
397
def check_slave_calls(ignored):
361
398
self.assertIn('abort', building_slave.call_log)
368
405
# too so that we don't traceback when the wrong behaviour tries
369
406
# to access a non-existent job.
370
407
builder, build = self._setupBinaryBuildAndBuilder()
371
candidate = build.queueBuild()
372
building_slave = BuildingSlave()
373
builder.setSlaveForTesting(building_slave)
374
candidate.markAsBuilding(builder)
408
with BuilddManagerTestFixture.extraSetUp():
409
candidate = build.queueBuild()
410
building_slave = BuildingSlave()
411
builder.setSlaveForTesting(building_slave)
412
candidate.markAsBuilding(builder)
376
414
# At this point we should see a valid behaviour on the builder:
377
415
self.assertFalse(
401
439
super(TestBuilderSlaveStatus, self).setUp()
402
440
self.slave_helper = self.useFixture(SlaveTestHelpers())
441
self.builder = self.factory.makeBuilder()
442
self.useFixture(BuilddManagerTestFixture())
404
444
def assertStatus(self, slave, builder_status=None,
405
445
build_status=None, logtail=False, filemap=None,
406
446
dependencies=None):
407
builder = self.factory.makeBuilder()
408
builder.setSlaveForTesting(slave)
409
d = builder.slaveStatus()
447
self.builder.setSlaveForTesting(slave)
448
d = self.builder.slaveStatus()
411
450
def got_status(status_dict):
452
491
def test_isAvailable_with_not_builderok(self):
453
492
# isAvailable() is a wrapper around slaveStatusSentence()
454
builder = self.factory.makeBuilder()
455
builder.builderok = False
456
d = builder.isAvailable()
493
with BuilddManagerTestFixture.extraSetUp():
494
self.builder.builderok = False
495
d = self.builder.isAvailable()
457
496
return d.addCallback(self.assertFalse)
459
498
def test_isAvailable_with_slave_fault(self):
460
builder = self.factory.makeBuilder()
461
builder.setSlaveForTesting(BrokenSlave())
462
d = builder.isAvailable()
499
self.builder.setSlaveForTesting(BrokenSlave())
500
d = self.builder.isAvailable()
463
501
return d.addCallback(self.assertFalse)
465
503
def test_isAvailable_with_slave_idle(self):
466
builder = self.factory.makeBuilder()
467
builder.setSlaveForTesting(OkSlave())
468
d = builder.isAvailable()
504
self.builder.setSlaveForTesting(OkSlave())
505
d = self.builder.isAvailable()
469
506
return d.addCallback(self.assertTrue)
507
546
# IBuilder._findBuildCandidate identifies if there are builds
508
547
# for superseded source package releases in the queue and marks
509
548
# the corresponding build record as SUPERSEDED.
510
archive = self.factory.makeArchive()
511
self.publisher.getPubSource(
512
sourcename="gedit", status=PackagePublishingStatus.PUBLISHED,
513
archive=archive).createMissingBuilds()
549
with BuilddManagerTestFixture.extraSetUp():
550
archive = self.factory.makeArchive()
551
self.publisher.getPubSource(
552
sourcename="gedit", status=PackagePublishingStatus.PUBLISHED,
553
archive=archive).createMissingBuilds()
514
554
old_candidate = removeSecurityProxy(
515
555
self.frog_builder)._findBuildCandidate()
531
572
# And the old_candidate is superseded:
532
573
self.assertEqual(BuildStatus.SUPERSEDED, build.status)
575
def test_findBuildCandidate_postprocesses_in_read_write_policy(self):
576
# _findBuildCandidate invokes BuildFarmJob.postprocessCandidate,
577
# which may modify the database. This happens in a read-write
578
# transaction even if _findBuildCandidate itself runs in a
579
# read-only transaction policy.
581
# PackageBuildJob.postprocessCandidate will attempt to delete
583
with BuilddManagerTestFixture.extraSetUp():
584
pub = self.publisher.getPubSource(
585
sourcename="gedit", status=PackagePublishingStatus.PUBLISHED,
586
archive=self.factory.makeArchive(),
587
pocket=PackagePublishingPocket.SECURITY)
588
pub.createMissingBuilds()
589
removeSecurityProxy(self.frog_builder)._findBuildCandidate()
590
# Passes without a "transaction is read-only" error...
534
593
def test_acquireBuildCandidate_marks_building(self):
535
594
# acquireBuildCandidate() should call _findBuildCandidate and
536
595
# mark the build as building.
537
archive = self.factory.makeArchive()
538
self.publisher.getPubSource(
539
sourcename="gedit", status=PackagePublishingStatus.PUBLISHED,
540
archive=archive).createMissingBuilds()
596
with BuilddManagerTestFixture.extraSetUp():
597
archive = self.factory.makeArchive()
598
self.publisher.getPubSource(
599
sourcename="gedit", status=PackagePublishingStatus.PUBLISHED,
600
archive=archive).createMissingBuilds()
541
601
candidate = removeSecurityProxy(
542
602
self.frog_builder).acquireBuildCandidate()
543
603
self.assertEqual(JobStatus.RUNNING, candidate.job.status)
580
642
# If bob is in a failed state the joesppa build is still
582
self.bob_builder.builderok = False
583
self.bob_builder.manual = False
644
with BuilddManagerTestFixture.extraSetUp():
645
self.bob_builder.builderok = False
646
self.bob_builder.manual = False
584
647
next_job = removeSecurityProxy(
585
648
self.frog_builder)._findBuildCandidate()
586
649
build = getUtility(IBinaryPackageBuildSet).getByQueueEntry(next_job)
670
737
def test_findBuildCandidate_first_build_finished(self):
671
738
# When joe's first ppa build finishes, his fourth i386 build
672
739
# will be the next build candidate.
673
self.joe_builds[0].status = BuildStatus.FAILEDTOBUILD
740
with BuilddManagerTestFixture.extraSetUp():
741
self.joe_builds[0].status = BuildStatus.FAILEDTOBUILD
674
742
next_job = removeSecurityProxy(self.builder4)._findBuildCandidate()
675
743
build = getUtility(IBinaryPackageBuildSet).getByQueueEntry(next_job)
676
744
self.failUnlessEqual('joesppa', build.archive.name)
678
746
def test_findBuildCandidate_with_disabled_archive(self):
679
747
# Disabled archives should not be considered for dispatching
681
disabled_job = removeSecurityProxy(self.builder4)._findBuildCandidate()
682
build = getUtility(IBinaryPackageBuildSet).getByQueueEntry(
684
build.archive.disable()
749
disabled_job = removeSecurityProxy(
750
self.builder4)._findBuildCandidate()
751
with BuilddManagerTestFixture.extraSetUp():
752
build = getUtility(IBinaryPackageBuildSet).getByQueueEntry(
754
build.archive.disable()
685
755
next_job = removeSecurityProxy(self.builder4)._findBuildCandidate()
686
756
self.assertNotEqual(disabled_job, next_job)
701
771
# dispatched because the builder has to fetch the source files
702
772
# from the (password protected) repo area, not the librarian.
703
773
pub = build.current_source_publication
704
pub.status = PackagePublishingStatus.PENDING
774
with BuilddManagerTestFixture.extraSetUp():
775
pub.status = PackagePublishingStatus.PENDING
705
776
candidate = removeSecurityProxy(self.builder4)._findBuildCandidate()
706
777
self.assertNotEqual(next_job.id, candidate.id)
736
810
# Now even if we set the build building, we'll still get the
737
811
# second non-ppa build for the same archive as the next candidate.
738
build.status = BuildStatus.BUILDING
739
build.builder = self.frog_builder
812
with BuilddManagerTestFixture.extraSetUp():
813
build.status = BuildStatus.BUILDING
814
build.builder = self.frog_builder
740
815
next_job = removeSecurityProxy(
741
816
self.frog_builder)._findBuildCandidate()
742
817
build = getUtility(IBinaryPackageBuildSet).getByQueueEntry(next_job)
1134
1219
super(TestSlaveWithLibrarian, self).setUp()
1135
1220
self.slave_helper = self.useFixture(SlaveTestHelpers())
1222
self.useFixture(BuilddManagerTestFixture())
1137
1224
def test_ensurepresent_librarian(self):
1138
1225
# ensurepresent, when given an http URL for a file will download the
1139
1226
# file from that URL and report that the file is present, and it was
1142
1229
# Use the Librarian because it's a "convenient" web server.
1143
lf = self.factory.makeLibraryFileAlias(
1144
'HelloWorld.txt', content="Hello World")
1145
self.layer.txn.commit()
1230
with BuilddManagerTestFixture.extraSetUp():
1231
lf = self.factory.makeLibraryFileAlias(
1232
'HelloWorld.txt', content="Hello World")
1146
1233
self.slave_helper.getServerSlave()
1147
1234
slave = self.slave_helper.getClientSlave()
1148
1235
d = slave.ensurepresent(
1155
1242
# filename made from the sha1 of the content underneath the
1156
1243
# 'filecache' directory.
1157
1244
content = "Hello World"
1158
lf = self.factory.makeLibraryFileAlias(
1159
'HelloWorld.txt', content=content)
1160
self.layer.txn.commit()
1245
with BuilddManagerTestFixture.extraSetUp():
1246
lf = self.factory.makeLibraryFileAlias(
1247
'HelloWorld.txt', content=content)
1161
1248
expected_url = '%s/filecache/%s' % (
1162
1249
self.slave_helper.BASE_URL, lf.content.sha1)
1163
1250
self.slave_helper.getServerSlave()
1200
1286
for content in contents:
1201
1287
filename = content + '.txt'
1202
lf = self.factory.makeLibraryFileAlias(filename, content=content)
1288
with BuilddManagerTestFixture.extraSetUp():
1289
lf = self.factory.makeLibraryFileAlias(
1290
filename, content=content)
1203
1291
content_map[lf.content.sha1] = content
1204
1292
fd, filemap[lf.content.sha1] = tempfile.mkstemp()
1205
1293
self.addCleanup(os.remove, filemap[lf.content.sha1])
1206
self.layer.txn.commit()
1207
1294
d = slave.ensurepresent(lf.content.sha1, lf.http_url, "", "")