Merge lp:~robru/cupstream2distro/exception-tweaks into lp:cupstream2distro
- exception-tweaks
- Merge into trunk
Status: | Merged |
---|---|
Approved by: | Robert Bruce Park |
Approved revision: | 1202 |
Merged at revision: | 1195 |
Proposed branch: | lp:~robru/cupstream2distro/exception-tweaks |
Merge into: | lp:cupstream2distro |
Diff against target: |
667 lines (+198/-195) 14 files modified
citrain/build.py (+1/-2) citrain/merge_clean.py (+12/-27) citrain/migration.py (+5/-6) citrain/publisher.py (+18/-39) citrain/recipes/base.py (+0/-5) cupstream2distro/errors.py (+36/-14) cupstream2distro/silomanager.py (+32/-2) tests/unit/__init__.py (+0/-5) tests/unit/test_recipe_base.py (+0/-6) tests/unit/test_script_build.py (+1/-1) tests/unit/test_script_merge_clean.py (+11/-17) tests/unit/test_script_migration.py (+9/-7) tests/unit/test_script_publisher.py (+29/-62) tests/unit/test_silomanager.py (+44/-2) |
To merge this branch: | bzr merge lp:~robru/cupstream2distro/exception-tweaks |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
PS Jenkins bot | continuous-integration | Approve | |
CU2D maintainers | Pending | ||
Review via email: mp+276842@code.launchpad.net |
Commit message
Improve consistency of error handling & logging.
Description of the change
PS Jenkins bot (ps-jenkins) wrote : | # |
- 1196. By Robert Bruce Park
-
Support NoStatusError everywhere.
- 1197. By Robert Bruce Park
-
New test.
- 1198. By Robert Bruce Park
-
Pass the err to status.
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:1198
http://
Executed test runs:
Click here to trigger a rebuild:
http://
- 1199. By Robert Bruce Park
-
Logging tweaks.
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:1199
http://
Executed test runs:
Click here to trigger a rebuild:
http://
- 1200. By Robert Bruce Park
-
Unify merge_clean.main and publisher.main.
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:1200
http://
Executed test runs:
Click here to trigger a rebuild:
http://
- 1201. By Robert Bruce Park
-
Stop hard-coding return codes.
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:1201
http://
Executed test runs:
Click here to trigger a rebuild:
http://
- 1202. By Robert Bruce Park
-
Func name/docstring cleanup.
Preview Diff
1 | === modified file 'citrain/build.py' |
2 | --- citrain/build.py 2015-10-28 19:46:24 +0000 |
3 | +++ citrain/build.py 2015-11-06 15:09:47 +0000 |
4 | @@ -86,8 +86,7 @@ |
5 | Manager(silo_state).do('watch', 'diff') |
6 | silo_state.status = 'Packages built.' |
7 | except CITrainError as err: |
8 | - logging.error(str(err)) |
9 | - silo_state.status = 'Build failed: ' + str(err) |
10 | + silo_state.status = err |
11 | silo_state.mark_dirty() |
12 | silo_state.save_config() |
13 | return 1 |
14 | |
15 | === modified file 'citrain/merge_clean.py' |
16 | --- citrain/merge_clean.py 2015-10-28 19:46:24 +0000 |
17 | +++ citrain/merge_clean.py 2015-11-06 15:09:47 +0000 |
18 | @@ -31,31 +31,16 @@ |
19 | Enable debug infos |
20 | """ |
21 | |
22 | -import logging |
23 | - |
24 | from citrain.recipes.manager import Manager |
25 | -from cupstream2distro.errors import CITrainError |
26 | -from cupstream2distro.utils import env, run_script |
27 | -from cupstream2distro.silomanager import SiloState |
28 | - |
29 | - |
30 | -def main(): |
31 | - """Execute the merge & clean phases, logging & saving any errors. |
32 | - |
33 | - :returns: 0 on success, 1 on any failure. |
34 | - """ |
35 | - try: |
36 | - silo_state = SiloState(env.SILONAME, primary=True) |
37 | - Manager(silo_state).do( |
38 | - 'validate', 'enumeration', 'merge', 'push', 'nuke') |
39 | - silo_state.status = 'Landed' |
40 | - except CITrainError as err: |
41 | - logging.error(str(err)) |
42 | - silo_state.status = 'Merge&Clean failed: ' + str(err) |
43 | - silo_state.save_config() |
44 | - return 1 |
45 | - silo_state.save_config() |
46 | - return 0 |
47 | - |
48 | - |
49 | -run_script(__name__, __doc__, main) |
50 | +from cupstream2distro.utils import run_script |
51 | +from cupstream2distro.silomanager import stock_main |
52 | + |
53 | + |
54 | +def merge(silo_state): |
55 | + """Do the steps specific to merging & cleaning.""" |
56 | + Manager(silo_state).do( |
57 | + 'validate', 'enumeration', 'merge', 'push', 'nuke') |
58 | + silo_state.status = 'Landed' |
59 | + |
60 | + |
61 | +run_script(__name__, __doc__, lambda: stock_main(merge, mark_dirty=True)) |
62 | |
63 | === modified file 'citrain/migration.py' |
64 | --- citrain/migration.py 2015-11-04 15:43:33 +0000 |
65 | +++ citrain/migration.py 2015-11-06 15:09:47 +0000 |
66 | @@ -31,8 +31,8 @@ |
67 | from citrain.recipes.manager import Manager |
68 | from cupstream2distro.errors import CITrainError |
69 | from cupstream2distro.utils import env, run_script |
70 | -from cupstream2distro.silomanager import SiloState |
71 | -from citrain.merge_clean import main as merge_main |
72 | +from cupstream2distro.silomanager import SiloState, stock_main |
73 | +from citrain.merge_clean import merge |
74 | |
75 | |
76 | def main(): |
77 | @@ -57,11 +57,10 @@ |
78 | manager.do('unbuilt') |
79 | if silo_state.is_published: |
80 | manager.do('migration') |
81 | - merge_main() |
82 | + stock_main(merge, mark_dirty=True) |
83 | except CITrainError as err: |
84 | - logging.info(str(err) + '\n') |
85 | - if not silo_state.mark_dirty(): |
86 | - silo_state.status = 'Migration: ' + str(err) |
87 | + silo_state.status = err |
88 | + silo_state.mark_dirty() |
89 | silo_state.save_config() |
90 | return 0 |
91 | |
92 | |
93 | === modified file 'citrain/publisher.py' |
94 | --- citrain/publisher.py 2015-11-04 15:04:26 +0000 |
95 | +++ citrain/publisher.py 2015-11-06 15:09:47 +0000 |
96 | @@ -35,44 +35,23 @@ |
97 | Enable debug infos |
98 | """ |
99 | |
100 | -import logging |
101 | - |
102 | from citrain.recipes.manager import Manager |
103 | -from cupstream2distro.errors import CITrainError, PublishError |
104 | -from cupstream2distro.utils import env, run_script |
105 | -from cupstream2distro.silomanager import SiloState |
106 | - |
107 | - |
108 | -def main(): |
109 | - """Conduct a beautiful orchestra of publication.""" |
110 | - try: |
111 | - silo_state = SiloState(env.SILONAME, primary=True) |
112 | - if silo_state._series in ('wily+vivid', 'dual'): |
113 | - raise PublishError( |
114 | - 'This silo must be transitioned to xenial before publishing.') |
115 | - mgr = Manager(silo_state) |
116 | - mgr.do('checkstatus', 'validate', 'diff', 'checkupload', 'ackaging') |
117 | - silo_state.status = 'Publishing.' |
118 | - silo_state.save_config() |
119 | - mgr.do('dest_version_check', 'unapproved', 'unbuilt', 'publish') |
120 | - silo_state.set_published() |
121 | - except CITrainError as err: |
122 | - logging.error(str(err)) |
123 | - if err.loud: |
124 | - silo_state.status = 'Publish failed: ' + str(err) |
125 | - silo_state.save_config() |
126 | - return 1 |
127 | +from cupstream2distro.errors import PublishError |
128 | +from cupstream2distro.utils import run_script |
129 | +from cupstream2distro.silomanager import stock_main |
130 | + |
131 | + |
132 | +def publish(silo_state): |
133 | + """Do the actual specific steps required to publish.""" |
134 | + if silo_state._series in ('wily+vivid', 'dual'): |
135 | + raise PublishError( |
136 | + 'This silo must be transitioned to xenial before publishing.') |
137 | + mgr = Manager(silo_state) |
138 | + mgr.do('checkstatus', 'validate', 'diff', 'checkupload', 'ackaging') |
139 | + silo_state.status = 'Publishing.' |
140 | silo_state.save_config() |
141 | - # This bit needs to be in a separate block because it's so slow that it |
142 | - # makes the last call to save_config() end up losing races with |
143 | - # check-publication-migration. Then you get logs that say |
144 | - # 'Publishing. Migration:... Publishing. Migration:...' |
145 | - try: |
146 | - silo_state.mark_others_dirty() |
147 | - except Exception as err: |
148 | - logging.warning('Had trouble marking other silos dirty:') |
149 | - logging.warning(err) |
150 | - return 0 |
151 | - |
152 | - |
153 | -run_script(__name__, __doc__, main) |
154 | + mgr.do('dest_version_check', 'unapproved', 'unbuilt', 'publish') |
155 | + silo_state.set_published() |
156 | + |
157 | + |
158 | +run_script(__name__, __doc__, lambda: stock_main(publish, mark_dirty=True)) |
159 | |
160 | === modified file 'citrain/recipes/base.py' |
161 | --- citrain/recipes/base.py 2015-11-04 15:43:33 +0000 |
162 | +++ citrain/recipes/base.py 2015-11-06 15:09:47 +0000 |
163 | @@ -507,11 +507,6 @@ |
164 | silo_state.save_config() |
165 | |
166 | @staticmethod |
167 | - def post_push_phase(silo_state): |
168 | - """Mark other silos dirty once pushing is successful.""" |
169 | - silo_state.mark_others_dirty() |
170 | - |
171 | - @staticmethod |
172 | def post_nuke_phase(silo_state): |
173 | """Delete all packages from PPA and remove local silo dir.""" |
174 | logging.info('Cleaning %s ppa.', silo_state.ppa.web_link) |
175 | |
176 | === modified file 'cupstream2distro/errors.py' |
177 | --- cupstream2distro/errors.py 2015-11-04 15:04:26 +0000 |
178 | +++ cupstream2distro/errors.py 2015-11-06 15:09:47 +0000 |
179 | @@ -17,56 +17,78 @@ |
180 | """Exceptions raised by CI Train in various situations.""" |
181 | |
182 | |
183 | +COUNTER = iter(range(1, 100)) |
184 | + |
185 | + |
186 | class CITrainError(Exception): |
187 | """Base Exception class for all CI Train error conditions.""" |
188 | + prefix = 'Unknown error: ' |
189 | + code = next(COUNTER) |
190 | loud = True |
191 | + error = True |
192 | + |
193 | + def __str__(self): |
194 | + """Stringify this exception.""" |
195 | + return self.prefix + super().__str__() |
196 | + |
197 | + |
198 | +class NoStatusError(CITrainError): |
199 | + """Special Exception that when raised won't set status.""" |
200 | + code = next(COUNTER) |
201 | + loud = False |
202 | + prefix = '' |
203 | |
204 | |
205 | class ArchiveError(CITrainError): |
206 | """Exception raised when there's a problem with a Launchpad archive.""" |
207 | - pass |
208 | + prefix = 'Launchpad error: ' |
209 | + code = next(COUNTER) |
210 | |
211 | |
212 | class BranchError(CITrainError): |
213 | """Exception raised when there's a problem with a Bzr branch.""" |
214 | - pass |
215 | + prefix = 'Branch error: ' |
216 | + code = next(COUNTER) |
217 | |
218 | |
219 | class PackageError(CITrainError): |
220 | """Exception raised when there's a problem with a debian package.""" |
221 | - pass |
222 | + prefix = 'Package error: ' |
223 | + code = next(COUNTER) |
224 | |
225 | |
226 | class PrepError(CITrainError): |
227 | """Exception raised when there's a problem during preparation.""" |
228 | - pass |
229 | + prefix = 'Prepare failed: ' |
230 | + code = next(COUNTER) |
231 | |
232 | |
233 | class PublishError(CITrainError): |
234 | """Exception raised when there's a problem during publishing.""" |
235 | - pass |
236 | + prefix = 'Publish failed: ' |
237 | + code = next(COUNTER) |
238 | |
239 | |
240 | class BuildError(CITrainError): |
241 | """Exception raised when there's a problem with building.""" |
242 | - pass |
243 | + prefix = 'Build failed: ' |
244 | + code = next(COUNTER) |
245 | |
246 | |
247 | class MergeError(CITrainError): |
248 | """Exception raised when there's a problem with merging.""" |
249 | - pass |
250 | + prefix = 'Merge&Clean failed: ' |
251 | + code = next(COUNTER) |
252 | |
253 | |
254 | class MigrationError(CITrainError): |
255 | """Exception raised to report on the silo migration status.""" |
256 | - pass |
257 | + prefix = 'Migration: ' |
258 | + code = next(COUNTER) |
259 | + error = False |
260 | |
261 | |
262 | class RevertError(CITrainError): |
263 | """Exception raised when there's a problem with reverting.""" |
264 | - pass |
265 | - |
266 | - |
267 | -class NoStatusError(PublishError): |
268 | - """Special Exception that when raised won't set status.""" |
269 | - loud = False |
270 | + prefix = 'Revert failed: ' |
271 | + code = next(COUNTER) |
272 | |
273 | === modified file 'cupstream2distro/silomanager.py' |
274 | --- cupstream2distro/silomanager.py 2015-11-05 16:23:09 +0000 |
275 | +++ cupstream2distro/silomanager.py 2015-11-06 15:09:47 +0000 |
276 | @@ -36,7 +36,7 @@ |
277 | from lazr.restfulclient.errors import PreconditionFailed, NotFound |
278 | |
279 | from cupstream2distro.branchhandling import Branch |
280 | -from cupstream2distro.errors import PrepError |
281 | +from cupstream2distro.errors import CITrainError, PrepError |
282 | from cupstream2distro.launchpadmanager import lp |
283 | from cupstream2distro.settings import ( |
284 | BILETO_API, |
285 | @@ -152,6 +152,31 @@ |
286 | _bileto(COMMENT_API, **kwargs) |
287 | |
288 | |
289 | +def stock_main(body, mark_dirty=False): |
290 | + """Generic error handling for CI Train scripts.""" |
291 | + silo_state = SiloState(env.SILONAME, primary=True) |
292 | + try: |
293 | + body(silo_state) |
294 | + except CITrainError as err: |
295 | + silo_state.status = err |
296 | + silo_state.mark_dirty() |
297 | + silo_state.save_config() |
298 | + return err.code |
299 | + silo_state.mark_dirty() |
300 | + silo_state.save_config() |
301 | + # This bit needs to be in a separate block because it's so slow that it |
302 | + # makes the last call to save_config() end up losing races with |
303 | + # check-publication-migration. Then you get logs that say |
304 | + # 'Publishing. Migration:... Publishing. Migration:...' |
305 | + if mark_dirty: |
306 | + try: |
307 | + silo_state.mark_others_dirty() |
308 | + except Exception as err: |
309 | + logging.warning('Had trouble marking other silos dirty:') |
310 | + logging.warning(err) |
311 | + return 0 |
312 | + |
313 | + |
314 | class SiloState(object): |
315 | """This class stores and manipulates the silo config json blob.""" |
316 | REQUEST_ID_FILE = 'request_id_{}' |
317 | @@ -212,7 +237,12 @@ |
318 | def __setattr__(self, key, value): |
319 | """Set Bileto attributes in Bileto.""" |
320 | if key in ('status', 'job_log', 'published_versions'): |
321 | - self._bileto[key] = scrub(value) |
322 | + scrubbed = scrub(str(value)) |
323 | + if key == 'status': |
324 | + err = getattr(value, 'error', False) |
325 | + (logging.error if err else logging.info)(scrubbed) |
326 | + if getattr(value, 'loud', True): |
327 | + self._bileto[key] = scrubbed |
328 | else: |
329 | super().__setattr__(key, value) |
330 | |
331 | |
332 | === modified file 'tests/unit/__init__.py' |
333 | --- tests/unit/__init__.py 2015-11-04 01:46:45 +0000 |
334 | +++ tests/unit/__init__.py 2015-11-06 15:09:47 +0000 |
335 | @@ -174,8 +174,3 @@ |
336 | 'ubuntu/landing-{:03d}'.format(x) for x in range(0, 10)] |
337 | self.script.SILO_NAME_LIST = dict( |
338 | ubuntu=self.script.ALL_SILO_NAMES) |
339 | - |
340 | - def test_existence(self): |
341 | - """CI Train script has a main() func without SyntaxErrors.""" |
342 | - if self.script: |
343 | - self.assertTrue(self.script.main) |
344 | |
345 | === modified file 'tests/unit/test_recipe_base.py' |
346 | --- tests/unit/test_recipe_base.py 2015-11-04 01:46:45 +0000 |
347 | +++ tests/unit/test_recipe_base.py 2015-11-06 15:09:47 +0000 |
348 | @@ -772,12 +772,6 @@ |
349 | self.assertEqual(silo_state.status, 'Merging.') |
350 | silo_state.save_config.assert_called_once_with() |
351 | |
352 | - def test_buildbase_post_push_phase(self): |
353 | - """Mark other silos dirty after silo has merged.""" |
354 | - silo_state = Mock() |
355 | - BuildBase.post_push_phase(silo_state) |
356 | - silo_state.mark_others_dirty.assert_called_once_with() |
357 | - |
358 | @patch('citrain.recipes.base.mkdir') |
359 | @patch('citrain.recipes.base.shutil') |
360 | @patch('citrain.recipes.base.SILO_DIR') |
361 | |
362 | === modified file 'tests/unit/test_script_build.py' |
363 | --- tests/unit/test_script_build.py 2015-11-04 02:19:56 +0000 |
364 | +++ tests/unit/test_script_build.py 2015-11-06 15:09:47 +0000 |
365 | @@ -168,5 +168,5 @@ |
366 | bman.return_value.mock_calls, [ |
367 | call.do('validate'), |
368 | ]) |
369 | - self.assertEqual(silo_state.status, 'Build failed: It asploded!') |
370 | + self.assertEqual(str(silo_state.status), 'Build failed: It asploded!') |
371 | silo_state.save_config.assert_called_once_with() |
372 | |
373 | === modified file 'tests/unit/test_script_merge_clean.py' |
374 | --- tests/unit/test_script_merge_clean.py 2015-11-04 02:19:56 +0000 |
375 | +++ tests/unit/test_script_merge_clean.py 2015-11-06 15:09:47 +0000 |
376 | @@ -31,31 +31,25 @@ |
377 | """Test CI Train Merge & Clean script.""" |
378 | scriptname = 'merge_clean.py' |
379 | |
380 | - def test_main(self): |
381 | - """main() succeeds in the primary case.""" |
382 | - self.script.env.SILONAME = 'fred' |
383 | + def test_merge(self): |
384 | + """merge() succeeds in the primary case.""" |
385 | + silo_state = Mock() |
386 | self.script.Manager = Mock() |
387 | mergemanager = self.script.Manager.return_value |
388 | - self.assertEqual(self.script.main(), 0) |
389 | - self.script.SiloState.assert_called_once_with('fred', primary=True) |
390 | - self.script.Manager.assert_called_once_with( |
391 | - self.script.SiloState.return_value) |
392 | + self.assertIsNone(self.script.merge(silo_state)) |
393 | + self.script.Manager.assert_called_once_with(silo_state) |
394 | mergemanager.do.assert_called_once_with( |
395 | 'validate', 'enumeration', 'merge', 'push', 'nuke') |
396 | |
397 | - def test_main_failed(self): |
398 | - """main() returns 1 when a phase raises an exception.""" |
399 | - self.script.env.SILONAME = 'fred' |
400 | + def test_merge_failed(self): |
401 | + """merge() doesn't catch exceptions.""" |
402 | + silo_state = Mock() |
403 | self.script.Manager = Mock() |
404 | mergemanager = self.script.Manager.return_value |
405 | mergemanager.do.side_effect = MergeError('whoa buddy!') |
406 | silo_state = self.script.SiloState.return_value |
407 | - self.assertEqual(self.script.main(), 1) |
408 | - self.script.SiloState.assert_called_once_with('fred', primary=True) |
409 | - self.script.Manager.assert_called_once_with( |
410 | - self.script.SiloState.return_value) |
411 | + with self.assertRaisesRegexp(MergeError, 'Merge&Clean failed: whoa'): |
412 | + self.script.merge(silo_state) |
413 | + self.script.Manager.assert_called_once_with(silo_state) |
414 | mergemanager.do.assert_called_once_with( |
415 | 'validate', 'enumeration', 'merge', 'push', 'nuke') |
416 | - self.script.logging.error.assert_called_once_with('whoa buddy!') |
417 | - self.assertEqual(silo_state.status, 'Merge&Clean failed: whoa buddy!') |
418 | - silo_state.save_config.assert_called_once_with() |
419 | |
420 | === modified file 'tests/unit/test_script_migration.py' |
421 | --- tests/unit/test_script_migration.py 2015-11-04 02:19:56 +0000 |
422 | +++ tests/unit/test_script_migration.py 2015-11-06 15:09:47 +0000 |
423 | @@ -36,7 +36,7 @@ |
424 | def setUp(self): |
425 | super().setUp() |
426 | self.script.Manager = Mock() |
427 | - self.script.merge_main = Mock() |
428 | + self.script.stock_main = Mock() |
429 | self.script.glob.return_value = [ |
430 | join(SILOS_DIR, 'ubuntu', 'landing-00{}'.format(x)) |
431 | for x in range(3)] |
432 | @@ -64,14 +64,16 @@ |
433 | call().do('unbuilt'), |
434 | call().do('migration'), |
435 | ]) |
436 | - self.assertEqual(self.script.merge_main.mock_calls, [call()] * 3) |
437 | + self.assertEqual(self.script.stock_main.mock_calls, [ |
438 | + call(self.script.merge, mark_dirty=True) |
439 | + ] * 3) |
440 | |
441 | def test_main_skip_empty_silos(self): |
442 | """Don't check silos that are empty.""" |
443 | self.script.SiloState.iterate.return_value = [] |
444 | self.assertEqual(self.script.main(), 0) |
445 | self.assertEqual(self.script.Manager.mock_calls, []) |
446 | - self.assertEqual(self.script.merge_main.mock_calls, []) |
447 | + self.assertEqual(self.script.stock_main.mock_calls, []) |
448 | |
449 | def test_main_skip_locked_silos(self): |
450 | """Skip over silos that are currently used by other processes.""" |
451 | @@ -79,7 +81,7 @@ |
452 | states[0].enforce_lock.side_effect = BlockingIOError |
453 | self.assertEqual(self.script.main(), 0) |
454 | self.assertEqual(self.script.Manager.mock_calls, []) |
455 | - self.assertEqual(self.script.merge_main.mock_calls, []) |
456 | + self.assertEqual(self.script.stock_main.mock_calls, []) |
457 | self.assertEqual(states[0].mock_calls, [call.enforce_lock()]) |
458 | |
459 | def test_main_skip_unpublished_silos(self): |
460 | @@ -96,7 +98,7 @@ |
461 | call(states[2]), |
462 | call().do('unbuilt'), |
463 | ]) |
464 | - self.assertEqual(self.script.merge_main.mock_calls, []) |
465 | + self.assertEqual(self.script.stock_main.mock_calls, []) |
466 | for state in states: |
467 | self.assertEqual(state.set_migrating.mock_calls, []) |
468 | self.assertEqual(state.save_config.mock_calls, []) |
469 | @@ -115,7 +117,7 @@ |
470 | self.script.Manager.return_value.do.side_effect = set_side_effect |
471 | self.assertEqual(self.script.main(), 0) |
472 | self.assertEqual( |
473 | - states[0].status, 'Migration: foo is in the Proposed pocket.') |
474 | + str(states[0].status), 'Migration: foo is in the Proposed pocket.') |
475 | self.assertEqual(states[0].mock_calls, [ |
476 | call.enforce_lock(), |
477 | call.lock_fd.close(), |
478 | @@ -128,4 +130,4 @@ |
479 | call().do('unbuilt'), |
480 | call().do('migration'), |
481 | ]) |
482 | - self.assertEqual(self.script.merge_main.mock_calls, []) |
483 | + self.assertEqual(self.script.stock_main.mock_calls, []) |
484 | |
485 | === modified file 'tests/unit/test_script_publisher.py' |
486 | --- tests/unit/test_script_publisher.py 2015-10-28 19:40:12 +0000 |
487 | +++ tests/unit/test_script_publisher.py 2015-11-06 15:09:47 +0000 |
488 | @@ -21,10 +21,7 @@ |
489 | from tests.unit import CITrainScriptTestCase |
490 | |
491 | from cupstream2distro.utils import env |
492 | -from cupstream2distro.errors import PublishError, NoStatusError |
493 | - |
494 | - |
495 | -N = '\n' |
496 | +from cupstream2distro.errors import PublishError |
497 | |
498 | |
499 | class PublisherTestCase(CITrainScriptTestCase): |
500 | @@ -37,67 +34,37 @@ |
501 | env.DISTRO = 'ubuntu' |
502 | env.SERIES = 'vivid' |
503 | |
504 | - def mock_main(self): |
505 | - """Ensure enough of the publisher is mocked out for testing main().""" |
506 | + def test_publish_success(self): |
507 | + """publisher.publish() should call the right functions.""" |
508 | + silo_state = Mock() |
509 | self.script.Manager = Mock() |
510 | - self.script.SiloState.return_value.series = \ |
511 | - 'https://api.launchpad.net/devel/ubuntu/vivid' |
512 | - |
513 | - def test_main_success(self): |
514 | - """publisher.main() should call the right functions.""" |
515 | - self.mock_main() |
516 | - state = self.script.SiloState() |
517 | - self.assertEqual(self.script.main(), 0) |
518 | - self.script.Manager.assert_called_once_with(state) |
519 | + self.assertIsNone(self.script.publish(silo_state)) |
520 | + self.script.Manager.assert_called_once_with(silo_state) |
521 | self.assertEqual(self.script.Manager.return_value.do.mock_calls, [ |
522 | call('checkstatus', 'validate', 'diff', 'checkupload', 'ackaging'), |
523 | call('dest_version_check', 'unapproved', 'unbuilt', 'publish'), |
524 | ]) |
525 | - self.assertEqual(state.status, 'Publishing.') |
526 | - state.set_published.assert_called_once_with() |
527 | - self.assertEqual(state.save_config.mock_calls, [call(), call()]) |
528 | - state.mark_others_dirty.assert_called_once_with() |
529 | - |
530 | - def test_main_failure(self): |
531 | - """publisher.main() should handle errors.""" |
532 | - self.mock_main() |
533 | - state = self.script.SiloState() |
534 | - self.script.Manager.return_value.do.side_effect = PublishError('Oops') |
535 | - self.assertEqual(self.script.main(), 1) |
536 | - self.assertEqual(state.status, "Publish failed: Oops") |
537 | - self.assertEqual(state.set_published.call_count, 0) |
538 | - self.script.logging.error.assert_called_once_with('Oops') |
539 | - self.assertEqual(state.save_config.mock_calls, [call()]) |
540 | - self.assertEqual(state.mark_others_dirty.mock_calls, []) |
541 | - |
542 | - def test_main_failure_nostatus(self): |
543 | - """publisher.main() shouldn't set status inappropriately.""" |
544 | - self.mock_main() |
545 | - state = self.script.SiloState() |
546 | - state.status = 'Preserved' |
547 | - self.script.Manager.return_value.do = Mock( |
548 | - side_effect=NoStatusError('Whoops')) |
549 | - self.assertEqual(self.script.main(), 1) |
550 | - self.assertEqual(state.status, 'Preserved') |
551 | - |
552 | - def test_main_failure_dirty(self): |
553 | - """publisher.main() should call save_config sooner.""" |
554 | - self.mock_main() |
555 | - state = self.script.SiloState() |
556 | - state.status = 'Packages built.' |
557 | - self.script.SiloState.return_value.mark_others_dirty = Mock( |
558 | - side_effect=Exception('Whoops')) |
559 | - self.assertEqual(self.script.main(), 0) |
560 | - self.assertEqual(state.status, 'Publishing.') |
561 | - |
562 | - def test_main_failure_wily(self): |
563 | + self.assertEqual(silo_state.status, 'Publishing.') |
564 | + self.assertEqual(silo_state.mock_calls, [ |
565 | + call.save_config(), |
566 | + call.set_published(), |
567 | + ]) |
568 | + |
569 | + def test_publish_failure(self): |
570 | + """publisher.publish() should raise errors.""" |
571 | + silo_state = Mock() |
572 | + self.script.Manager = Mock() |
573 | + err = PublishError('Oops') |
574 | + self.script.Manager.return_value.do.side_effect = err |
575 | + with self.assertRaisesRegexp(PublishError, 'Publish failed: Oops'): |
576 | + self.script.publish(silo_state) |
577 | + self.assertEqual(silo_state.mock_calls, []) |
578 | + |
579 | + def test_publish_failure_wily(self): |
580 | """Prevent publishing dual silos to wily.""" |
581 | - self.mock_main() |
582 | - state = self.script.SiloState() |
583 | - state.status = 'Packages built.' |
584 | - state._series = 'dual' |
585 | - self.assertEqual(self.script.main(), 1) |
586 | - self.assertEqual( |
587 | - state.status, |
588 | - 'Publish failed: This silo must be transitioned to xenial ' |
589 | - 'before publishing.') |
590 | + silo_state = Mock() |
591 | + silo_state.status = 'Packages built.' |
592 | + silo_state._series = 'dual' |
593 | + self.script.Manager = Mock() |
594 | + with self.assertRaisesRegexp(PublishError, 'must be transitioned'): |
595 | + self.script.publish(silo_state) |
596 | |
597 | === modified file 'tests/unit/test_silomanager.py' |
598 | --- tests/unit/test_silomanager.py 2015-11-05 16:32:14 +0000 |
599 | +++ tests/unit/test_silomanager.py 2015-11-06 15:09:47 +0000 |
600 | @@ -28,9 +28,9 @@ |
601 | from tests.unit import DirectoryAwareTestCase |
602 | |
603 | from cupstream2distro import silomanager |
604 | -from cupstream2distro.silomanager import SiloState, splitter |
605 | +from cupstream2distro.silomanager import SiloState, splitter, stock_main |
606 | from cupstream2distro.utils import env, utf8_open |
607 | -from cupstream2distro.errors import PrepError |
608 | +from cupstream2distro.errors import PrepError, NoStatusError |
609 | |
610 | |
611 | SOURCES = 'bar bat foo zing'.split() |
612 | @@ -114,6 +114,42 @@ |
613 | expected = [['robru', 'sil2100']] * 7 + [[]] |
614 | self.assertEqual([splitter(case) for case in cases], expected) |
615 | |
616 | + @patch('cupstream2distro.silomanager.SiloState') |
617 | + def test_stock_main(self, state_mock): |
618 | + """Ensure that the stock main() function works.""" |
619 | + self.assertEqual(stock_main(lambda state: state, True), 0) |
620 | + self.assertEqual(state_mock.mock_calls, [ |
621 | + call(self.tempdir, primary=True), |
622 | + call().mark_dirty(), |
623 | + call().save_config(), |
624 | + call().mark_others_dirty(), |
625 | + ]) |
626 | + |
627 | + @patch('cupstream2distro.silomanager.SiloState') |
628 | + def test_stock_main_fail(self, state_mock): |
629 | + """Ensure that the stock main() function handles exceptions.""" |
630 | + def body(silo_state): |
631 | + """Raise a little hell.""" |
632 | + raise PrepError('Boo!') |
633 | + self.assertEqual(stock_main(body, True), PrepError.code) |
634 | + self.assertEqual(state_mock.mock_calls, [ |
635 | + call(self.tempdir, primary=True), |
636 | + call().mark_dirty(), |
637 | + call().save_config(), |
638 | + ]) |
639 | + |
640 | + @patch('cupstream2distro.silomanager.SiloState') |
641 | + def test_stock_main_dirty_fail(self, state_mock): |
642 | + """Ensure that the stock main() function handles dirty exceptions.""" |
643 | + state_mock.return_value.mark_others_dirty.side_effect = Exception('Oh') |
644 | + self.assertEqual(stock_main(lambda state: state, True), 0) |
645 | + self.assertEqual(state_mock.mock_calls, [ |
646 | + call(self.tempdir, primary=True), |
647 | + call().mark_dirty(), |
648 | + call().save_config(), |
649 | + call().mark_others_dirty(), |
650 | + ]) |
651 | + |
652 | def test_silostate_init(self): |
653 | """Ensure that init is sensible.""" |
654 | state = SiloState('ubuntu/landing-123') |
655 | @@ -375,6 +411,12 @@ |
656 | self.assertEqual(self.state.landers, 'm') |
657 | self.state.load_bileto.assert_called_once_with() |
658 | |
659 | + def test_silostate_setattr_nostateerror(self): |
660 | + """Don't set status with NoStatusError.""" |
661 | + self.state.status = 'Preserved' |
662 | + self.state.status = NoStatusError('No') |
663 | + self.assertEquals(self.state.status, 'Preserved') |
664 | + |
665 | @patch('cupstream2distro.silomanager.SiloState.series') |
666 | @patch('cupstream2distro.silomanager.SiloState.iterate') |
667 | @patch('cupstream2distro.silomanager.SiloState.all_projects', ['qtmir']) |
PASSED: Continuous integration, rev:1195 jenkins. qa.ubuntu. com/job/ cu2d-choo- choo-ci/ 889/
http://
Executed test runs:
Click here to trigger a rebuild: s-jenkins. ubuntu- ci:8080/ job/cu2d- choo-choo- ci/889/ rebuild
http://