Merge lp:~robru/cupstream2distro/exception-tweaks into lp:cupstream2distro

Proposed by Robert Bruce Park
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
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.

To post a comment you must log in.
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :

PASSED: Continuous integration, rev:1195
http://jenkins.qa.ubuntu.com/job/cu2d-choo-choo-ci/889/
Executed test runs:

Click here to trigger a rebuild:
http://s-jenkins.ubuntu-ci:8080/job/cu2d-choo-choo-ci/889/rebuild

review: Approve (continuous-integration)
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.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :

FAILED: Continuous integration, rev:1198
http://jenkins.qa.ubuntu.com/job/cu2d-choo-choo-ci/890/
Executed test runs:

Click here to trigger a rebuild:
http://s-jenkins.ubuntu-ci:8080/job/cu2d-choo-choo-ci/890/rebuild

review: Needs Fixing (continuous-integration)
1199. By Robert Bruce Park

Logging tweaks.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :

FAILED: Continuous integration, rev:1199
http://jenkins.qa.ubuntu.com/job/cu2d-choo-choo-ci/891/
Executed test runs:

Click here to trigger a rebuild:
http://s-jenkins.ubuntu-ci:8080/job/cu2d-choo-choo-ci/891/rebuild

review: Needs Fixing (continuous-integration)
1200. By Robert Bruce Park

Unify merge_clean.main and publisher.main.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :

PASSED: Continuous integration, rev:1200
http://jenkins.qa.ubuntu.com/job/cu2d-choo-choo-ci/892/
Executed test runs:

Click here to trigger a rebuild:
http://s-jenkins.ubuntu-ci:8080/job/cu2d-choo-choo-ci/892/rebuild

review: Approve (continuous-integration)
1201. By Robert Bruce Park

Stop hard-coding return codes.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :

PASSED: Continuous integration, rev:1201
http://jenkins.qa.ubuntu.com/job/cu2d-choo-choo-ci/893/
Executed test runs:

Click here to trigger a rebuild:
http://s-jenkins.ubuntu-ci:8080/job/cu2d-choo-choo-ci/893/rebuild

review: Approve (continuous-integration)
1202. By Robert Bruce Park

Func name/docstring cleanup.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
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'])

Subscribers

People subscribed via source and target branches