9
9
from textwrap import dedent
11
11
from testtools import TestCase
12
from testtools.deferredruntest import AsynchronousDeferredRunTest
13
from twisted.internet import defer
13
from lpbuildd.tests.fakeslave import FakeSlave
15
14
from lpbuildd.sourcepackagerecipe import (
16
15
RETCODE_FAILURE_INSTALL_BUILD_DEPS,
17
16
SourcePackageRecipeBuildManager,
18
17
SourcePackageRecipeBuildState,
20
from lpbuildd.tests.fakebuilder import FakeBuilder
21
from lpbuildd.tests.matchers import HasWaitingFiles
24
21
class MockBuildManager(SourcePackageRecipeBuildManager):
38
35
class TestSourcePackageRecipeBuildManagerIteration(TestCase):
39
36
"""Run SourcePackageRecipeBuildManager through its iteration steps."""
41
run_tests_with = AsynchronousDeferredRunTest.make_factory(timeout=5)
44
38
super(TestSourcePackageRecipeBuildManagerIteration, self).setUp()
45
39
self.working_dir = tempfile.mkdtemp()
46
40
self.addCleanup(lambda: shutil.rmtree(self.working_dir))
47
builder_dir = os.path.join(self.working_dir, 'builder')
41
slave_dir = os.path.join(self.working_dir, 'slave')
48
42
home_dir = os.path.join(self.working_dir, 'home')
49
for dir in (builder_dir, home_dir):
43
for dir in (slave_dir, home_dir):
51
self.builder = FakeBuilder(builder_dir)
45
self.slave = FakeSlave(slave_dir)
52
46
self.buildid = '123'
53
self.buildmanager = MockBuildManager(self.builder, self.buildid)
47
self.buildmanager = MockBuildManager(self.slave, self.buildid)
54
48
self.buildmanager.home = home_dir
55
self.buildmanager._cachepath = self.builder._cachepath
49
self.buildmanager._cachepath = self.slave._cachepath
56
50
self.chrootdir = os.path.join(
57
51
home_dir, 'build-%s' % self.buildid, 'chroot-autobuild')
89
81
# directly before BUILD_RECIPE.
90
82
self.buildmanager._state = SourcePackageRecipeBuildState.UPDATE
92
# BUILD_RECIPE: Run the builder's payload to build the source package.
93
yield self.buildmanager.iterate(0)
84
# BUILD_RECIPE: Run the slave's payload to build the source package.
85
self.buildmanager.iterate(0)
95
87
SourcePackageRecipeBuildState.BUILD_RECIPE, self.getState())
96
expected_command = ['sharepath/bin/buildrecipe', 'buildrecipe']
89
'sharepath/slavebin/buildrecipe', 'buildrecipe']
98
91
expected_command.append('--git')
99
92
expected_command.extend([
104
97
self.assertEqual(expected_command, self.buildmanager.commands[-1])
106
99
self.buildmanager.iterate, self.buildmanager.iterators[-1])
107
self.assertFalse(self.builder.wasCalled('chrootFail'))
100
self.assertFalse(self.slave.wasCalled('chrootFail'))
109
@defer.inlineCallbacks
110
102
def test_iterate(self):
111
103
# The build manager iterates a normal build from start to finish.
112
yield self.startBuild()
114
106
log_path = os.path.join(self.buildmanager._cachepath, 'buildlog')
115
with open(log_path, 'w') as log:
116
log.write("I am a build log.")
107
log = open(log_path, 'w')
108
log.write("I am a build log.")
118
111
changes_path = os.path.join(
119
112
self.buildmanager.home, 'build-%s' % self.buildid,
120
113
'foo_1_source.changes')
121
with open(changes_path, 'w') as changes:
122
changes.write("I am a changes file.")
114
changes = open(changes_path, 'w')
115
changes.write("I am a changes file.")
124
118
manifest_path = os.path.join(
125
119
self.buildmanager.home, 'build-%s' % self.buildid, 'manifest')
126
with open(manifest_path, 'w') as manifest:
127
manifest.write("I am a manifest file.")
120
manifest = open(manifest_path, 'w')
121
manifest.write("I am a manifest file.")
129
124
# After building the package, reap processes.
130
yield self.buildmanager.iterate(0)
125
self.buildmanager.iterate(0)
131
126
expected_command = [
132
'sharepath/bin/in-target', 'in-target', 'scan-for-processes',
133
'--backend=chroot', '--series=maverick', '--arch=i386',
127
'sharepath/slavebin/scan-for-processes', 'scan-for-processes',
136
130
self.assertEqual(
138
132
self.assertEqual(expected_command, self.buildmanager.commands[-1])
139
133
self.assertNotEqual(
140
134
self.buildmanager.iterate, self.buildmanager.iterators[-1])
141
self.assertFalse(self.builder.wasCalled('buildFail'))
142
self.assertThat(self.builder, HasWaitingFiles.byEquality({
143
'foo_1_source.changes': b'I am a changes file.',
144
'manifest': b'I am a manifest file.',
135
self.assertFalse(self.slave.wasCalled('buildFail'))
137
[((changes_path,), {}), ((manifest_path,), {})],
138
self.slave.addWaitingFile.calls)
147
140
# Control returns to the DebianBuildManager in the UMOUNT state.
148
141
self.buildmanager.iterateReap(self.getState(), 0)
149
142
expected_command = [
150
'sharepath/bin/in-target', 'in-target', 'umount-chroot',
151
'--backend=chroot', '--series=maverick', '--arch=i386',
143
'sharepath/slavebin/umount-chroot', 'umount-chroot', self.buildid
154
145
self.assertEqual(SourcePackageRecipeBuildState.UMOUNT, self.getState())
155
146
self.assertEqual(expected_command, self.buildmanager.commands[-1])
156
147
self.assertEqual(
157
148
self.buildmanager.iterate, self.buildmanager.iterators[-1])
158
self.assertFalse(self.builder.wasCalled('buildFail'))
149
self.assertFalse(self.slave.wasCalled('buildFail'))
160
@defer.inlineCallbacks
161
151
def test_iterate_BUILD_RECIPE_install_build_deps_depfail(self):
162
152
# The build manager can detect dependency wait states.
163
yield self.startBuild()
165
155
log_path = os.path.join(self.buildmanager._cachepath, 'buildlog')
166
with open(log_path, 'w') as log:
168
"The following packages have unmet dependencies:\n"
169
" pbuilder-satisfydepends-dummy :"
170
" Depends: base-files (>= 1000)"
171
" but it is not going to be installed.\n")
156
log = open(log_path, 'w')
158
"The following packages have unmet dependencies:\n"
159
" pbuilder-satisfydepends-dummy : Depends: base-files (>= 1000)"
160
" but it is not going to be installed.\n")
173
163
# The buildmanager calls depFail correctly and reaps processes.
174
yield self.buildmanager.iterate(RETCODE_FAILURE_INSTALL_BUILD_DEPS)
164
self.buildmanager.iterate(RETCODE_FAILURE_INSTALL_BUILD_DEPS)
175
165
expected_command = [
176
'sharepath/bin/in-target', 'in-target', 'scan-for-processes',
177
'--backend=chroot', '--series=maverick', '--arch=i386',
166
'sharepath/slavebin/scan-for-processes', 'scan-for-processes',
180
169
self.assertEqual(
182
171
self.assertEqual(expected_command, self.buildmanager.commands[-1])
183
172
self.assertNotEqual(
184
173
self.buildmanager.iterate, self.buildmanager.iterators[-1])
185
self.assertFalse(self.builder.wasCalled('buildFail'))
174
self.assertFalse(self.slave.wasCalled('buildFail'))
186
175
self.assertEqual(
187
[(("base-files (>= 1000)",), {})], self.builder.depFail.calls)
176
[(("base-files (>= 1000)",), {})], self.slave.depFail.calls)
189
178
# Control returns to the DebianBuildManager in the UMOUNT state.
190
179
self.buildmanager.iterateReap(self.getState(), 0)
191
180
expected_command = [
192
'sharepath/bin/in-target', 'in-target', 'umount-chroot',
193
'--backend=chroot', '--series=maverick', '--arch=i386',
181
'sharepath/slavebin/umount-chroot', 'umount-chroot', self.buildid,
196
183
self.assertEqual(SourcePackageRecipeBuildState.UMOUNT, self.getState())
197
184
self.assertEqual(expected_command, self.buildmanager.commands[-1])
198
185
self.assertEqual(
199
186
self.buildmanager.iterate, self.buildmanager.iterators[-1])
200
self.assertFalse(self.builder.wasCalled('buildFail'))
187
self.assertFalse(self.slave.wasCalled('buildFail'))
202
@defer.inlineCallbacks
203
189
def test_iterate_BUILD_RECIPE_install_build_deps_buildfail(self):
204
190
# If the build manager cannot detect a dependency wait from a
205
191
# build-dependency installation failure, it fails the build.
206
yield self.startBuild()
208
194
log_path = os.path.join(self.buildmanager._cachepath, 'buildlog')
209
with open(log_path, 'w') as log:
210
log.write("I am a failing build log.")
195
log = open(log_path, 'w')
196
log.write("I am a failing build log.")
212
199
# The buildmanager calls buildFail correctly and reaps processes.
213
yield self.buildmanager.iterate(RETCODE_FAILURE_INSTALL_BUILD_DEPS)
200
self.buildmanager.iterate(RETCODE_FAILURE_INSTALL_BUILD_DEPS)
214
201
expected_command = [
215
'sharepath/bin/in-target', 'in-target', 'scan-for-processes',
216
'--backend=chroot', '--series=maverick', '--arch=i386',
202
'sharepath/slavebin/scan-for-processes', 'scan-for-processes',
219
205
self.assertEqual(
221
207
self.assertEqual(expected_command, self.buildmanager.commands[-1])
222
208
self.assertNotEqual(
223
209
self.buildmanager.iterate, self.buildmanager.iterators[-1])
224
self.assertTrue(self.builder.wasCalled('buildFail'))
225
self.assertFalse(self.builder.wasCalled('depFail'))
210
self.assertTrue(self.slave.wasCalled('buildFail'))
211
self.assertFalse(self.slave.wasCalled('depFail'))
227
213
# Control returns to the DebianBuildManager in the UMOUNT state.
228
214
self.buildmanager.iterateReap(self.getState(), 0)
229
215
expected_command = [
230
'sharepath/bin/in-target', 'in-target', 'umount-chroot',
231
'--backend=chroot', '--series=maverick', '--arch=i386',
216
'sharepath/slavebin/umount-chroot', 'umount-chroot', self.buildid,
234
218
self.assertEqual(SourcePackageRecipeBuildState.UMOUNT, self.getState())
235
219
self.assertEqual(expected_command, self.buildmanager.commands[-1])
236
220
self.assertEqual(
237
221
self.buildmanager.iterate, self.buildmanager.iterators[-1])
239
@defer.inlineCallbacks
240
223
def test_iterate_git(self):
241
224
# Starting a git-based recipe build passes the correct option. (The
242
225
# rest of the build is identical to bzr-based recipe builds from the
243
226
# build manager's point of view.)
244
yield self.startBuild(git=True)
227
self.startBuild(git=True)