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
=== modified file 'citrain/build.py'
--- citrain/build.py 2015-10-28 19:46:24 +0000
+++ citrain/build.py 2015-11-06 15:09:47 +0000
@@ -86,8 +86,7 @@
86 Manager(silo_state).do('watch', 'diff')86 Manager(silo_state).do('watch', 'diff')
87 silo_state.status = 'Packages built.'87 silo_state.status = 'Packages built.'
88 except CITrainError as err:88 except CITrainError as err:
89 logging.error(str(err))89 silo_state.status = err
90 silo_state.status = 'Build failed: ' + str(err)
91 silo_state.mark_dirty()90 silo_state.mark_dirty()
92 silo_state.save_config()91 silo_state.save_config()
93 return 192 return 1
9493
=== modified file 'citrain/merge_clean.py'
--- citrain/merge_clean.py 2015-10-28 19:46:24 +0000
+++ citrain/merge_clean.py 2015-11-06 15:09:47 +0000
@@ -31,31 +31,16 @@
31 Enable debug infos31 Enable debug infos
32"""32"""
3333
34import logging
35
36from citrain.recipes.manager import Manager34from citrain.recipes.manager import Manager
37from cupstream2distro.errors import CITrainError35from cupstream2distro.utils import run_script
38from cupstream2distro.utils import env, run_script36from cupstream2distro.silomanager import stock_main
39from cupstream2distro.silomanager import SiloState37
4038
4139def merge(silo_state):
42def main():40 """Do the steps specific to merging & cleaning."""
43 """Execute the merge & clean phases, logging & saving any errors.41 Manager(silo_state).do(
4442 'validate', 'enumeration', 'merge', 'push', 'nuke')
45 :returns: 0 on success, 1 on any failure.43 silo_state.status = 'Landed'
46 """44
47 try:45
48 silo_state = SiloState(env.SILONAME, primary=True)46run_script(__name__, __doc__, lambda: stock_main(merge, mark_dirty=True))
49 Manager(silo_state).do(
50 'validate', 'enumeration', 'merge', 'push', 'nuke')
51 silo_state.status = 'Landed'
52 except CITrainError as err:
53 logging.error(str(err))
54 silo_state.status = 'Merge&Clean failed: ' + str(err)
55 silo_state.save_config()
56 return 1
57 silo_state.save_config()
58 return 0
59
60
61run_script(__name__, __doc__, main)
6247
=== modified file 'citrain/migration.py'
--- citrain/migration.py 2015-11-04 15:43:33 +0000
+++ citrain/migration.py 2015-11-06 15:09:47 +0000
@@ -31,8 +31,8 @@
31from citrain.recipes.manager import Manager31from citrain.recipes.manager import Manager
32from cupstream2distro.errors import CITrainError32from cupstream2distro.errors import CITrainError
33from cupstream2distro.utils import env, run_script33from cupstream2distro.utils import env, run_script
34from cupstream2distro.silomanager import SiloState34from cupstream2distro.silomanager import SiloState, stock_main
35from citrain.merge_clean import main as merge_main35from citrain.merge_clean import merge
3636
3737
38def main():38def main():
@@ -57,11 +57,10 @@
57 manager.do('unbuilt')57 manager.do('unbuilt')
58 if silo_state.is_published:58 if silo_state.is_published:
59 manager.do('migration')59 manager.do('migration')
60 merge_main()60 stock_main(merge, mark_dirty=True)
61 except CITrainError as err:61 except CITrainError as err:
62 logging.info(str(err) + '\n')62 silo_state.status = err
63 if not silo_state.mark_dirty():63 silo_state.mark_dirty()
64 silo_state.status = 'Migration: ' + str(err)
65 silo_state.save_config()64 silo_state.save_config()
66 return 065 return 0
6766
6867
=== modified file 'citrain/publisher.py'
--- citrain/publisher.py 2015-11-04 15:04:26 +0000
+++ citrain/publisher.py 2015-11-06 15:09:47 +0000
@@ -35,44 +35,23 @@
35 Enable debug infos35 Enable debug infos
36"""36"""
3737
38import logging
39
40from citrain.recipes.manager import Manager38from citrain.recipes.manager import Manager
41from cupstream2distro.errors import CITrainError, PublishError39from cupstream2distro.errors import PublishError
42from cupstream2distro.utils import env, run_script40from cupstream2distro.utils import run_script
43from cupstream2distro.silomanager import SiloState41from cupstream2distro.silomanager import stock_main
4442
4543
46def main():44def publish(silo_state):
47 """Conduct a beautiful orchestra of publication."""45 """Do the actual specific steps required to publish."""
48 try:46 if silo_state._series in ('wily+vivid', 'dual'):
49 silo_state = SiloState(env.SILONAME, primary=True)47 raise PublishError(
50 if silo_state._series in ('wily+vivid', 'dual'):48 'This silo must be transitioned to xenial before publishing.')
51 raise PublishError(49 mgr = Manager(silo_state)
52 'This silo must be transitioned to xenial before publishing.')50 mgr.do('checkstatus', 'validate', 'diff', 'checkupload', 'ackaging')
53 mgr = Manager(silo_state)51 silo_state.status = 'Publishing.'
54 mgr.do('checkstatus', 'validate', 'diff', 'checkupload', 'ackaging')
55 silo_state.status = 'Publishing.'
56 silo_state.save_config()
57 mgr.do('dest_version_check', 'unapproved', 'unbuilt', 'publish')
58 silo_state.set_published()
59 except CITrainError as err:
60 logging.error(str(err))
61 if err.loud:
62 silo_state.status = 'Publish failed: ' + str(err)
63 silo_state.save_config()
64 return 1
65 silo_state.save_config()52 silo_state.save_config()
66 # This bit needs to be in a separate block because it's so slow that it53 mgr.do('dest_version_check', 'unapproved', 'unbuilt', 'publish')
67 # makes the last call to save_config() end up losing races with54 silo_state.set_published()
68 # check-publication-migration. Then you get logs that say55
69 # 'Publishing. Migration:... Publishing. Migration:...'56
70 try:57run_script(__name__, __doc__, lambda: stock_main(publish, mark_dirty=True))
71 silo_state.mark_others_dirty()
72 except Exception as err:
73 logging.warning('Had trouble marking other silos dirty:')
74 logging.warning(err)
75 return 0
76
77
78run_script(__name__, __doc__, main)
7958
=== modified file 'citrain/recipes/base.py'
--- citrain/recipes/base.py 2015-11-04 15:43:33 +0000
+++ citrain/recipes/base.py 2015-11-06 15:09:47 +0000
@@ -507,11 +507,6 @@
507 silo_state.save_config()507 silo_state.save_config()
508508
509 @staticmethod509 @staticmethod
510 def post_push_phase(silo_state):
511 """Mark other silos dirty once pushing is successful."""
512 silo_state.mark_others_dirty()
513
514 @staticmethod
515 def post_nuke_phase(silo_state):510 def post_nuke_phase(silo_state):
516 """Delete all packages from PPA and remove local silo dir."""511 """Delete all packages from PPA and remove local silo dir."""
517 logging.info('Cleaning %s ppa.', silo_state.ppa.web_link)512 logging.info('Cleaning %s ppa.', silo_state.ppa.web_link)
518513
=== modified file 'cupstream2distro/errors.py'
--- cupstream2distro/errors.py 2015-11-04 15:04:26 +0000
+++ cupstream2distro/errors.py 2015-11-06 15:09:47 +0000
@@ -17,56 +17,78 @@
17"""Exceptions raised by CI Train in various situations."""17"""Exceptions raised by CI Train in various situations."""
1818
1919
20COUNTER = iter(range(1, 100))
21
22
20class CITrainError(Exception):23class CITrainError(Exception):
21 """Base Exception class for all CI Train error conditions."""24 """Base Exception class for all CI Train error conditions."""
25 prefix = 'Unknown error: '
26 code = next(COUNTER)
22 loud = True27 loud = True
28 error = True
29
30 def __str__(self):
31 """Stringify this exception."""
32 return self.prefix + super().__str__()
33
34
35class NoStatusError(CITrainError):
36 """Special Exception that when raised won't set status."""
37 code = next(COUNTER)
38 loud = False
39 prefix = ''
2340
2441
25class ArchiveError(CITrainError):42class ArchiveError(CITrainError):
26 """Exception raised when there's a problem with a Launchpad archive."""43 """Exception raised when there's a problem with a Launchpad archive."""
27 pass44 prefix = 'Launchpad error: '
45 code = next(COUNTER)
2846
2947
30class BranchError(CITrainError):48class BranchError(CITrainError):
31 """Exception raised when there's a problem with a Bzr branch."""49 """Exception raised when there's a problem with a Bzr branch."""
32 pass50 prefix = 'Branch error: '
51 code = next(COUNTER)
3352
3453
35class PackageError(CITrainError):54class PackageError(CITrainError):
36 """Exception raised when there's a problem with a debian package."""55 """Exception raised when there's a problem with a debian package."""
37 pass56 prefix = 'Package error: '
57 code = next(COUNTER)
3858
3959
40class PrepError(CITrainError):60class PrepError(CITrainError):
41 """Exception raised when there's a problem during preparation."""61 """Exception raised when there's a problem during preparation."""
42 pass62 prefix = 'Prepare failed: '
63 code = next(COUNTER)
4364
4465
45class PublishError(CITrainError):66class PublishError(CITrainError):
46 """Exception raised when there's a problem during publishing."""67 """Exception raised when there's a problem during publishing."""
47 pass68 prefix = 'Publish failed: '
69 code = next(COUNTER)
4870
4971
50class BuildError(CITrainError):72class BuildError(CITrainError):
51 """Exception raised when there's a problem with building."""73 """Exception raised when there's a problem with building."""
52 pass74 prefix = 'Build failed: '
75 code = next(COUNTER)
5376
5477
55class MergeError(CITrainError):78class MergeError(CITrainError):
56 """Exception raised when there's a problem with merging."""79 """Exception raised when there's a problem with merging."""
57 pass80 prefix = 'Merge&Clean failed: '
81 code = next(COUNTER)
5882
5983
60class MigrationError(CITrainError):84class MigrationError(CITrainError):
61 """Exception raised to report on the silo migration status."""85 """Exception raised to report on the silo migration status."""
62 pass86 prefix = 'Migration: '
87 code = next(COUNTER)
88 error = False
6389
6490
65class RevertError(CITrainError):91class RevertError(CITrainError):
66 """Exception raised when there's a problem with reverting."""92 """Exception raised when there's a problem with reverting."""
67 pass93 prefix = 'Revert failed: '
6894 code = next(COUNTER)
69
70class NoStatusError(PublishError):
71 """Special Exception that when raised won't set status."""
72 loud = False
7395
=== modified file 'cupstream2distro/silomanager.py'
--- cupstream2distro/silomanager.py 2015-11-05 16:23:09 +0000
+++ cupstream2distro/silomanager.py 2015-11-06 15:09:47 +0000
@@ -36,7 +36,7 @@
36from lazr.restfulclient.errors import PreconditionFailed, NotFound36from lazr.restfulclient.errors import PreconditionFailed, NotFound
3737
38from cupstream2distro.branchhandling import Branch38from cupstream2distro.branchhandling import Branch
39from cupstream2distro.errors import PrepError39from cupstream2distro.errors import CITrainError, PrepError
40from cupstream2distro.launchpadmanager import lp40from cupstream2distro.launchpadmanager import lp
41from cupstream2distro.settings import (41from cupstream2distro.settings import (
42 BILETO_API,42 BILETO_API,
@@ -152,6 +152,31 @@
152 _bileto(COMMENT_API, **kwargs)152 _bileto(COMMENT_API, **kwargs)
153153
154154
155def stock_main(body, mark_dirty=False):
156 """Generic error handling for CI Train scripts."""
157 silo_state = SiloState(env.SILONAME, primary=True)
158 try:
159 body(silo_state)
160 except CITrainError as err:
161 silo_state.status = err
162 silo_state.mark_dirty()
163 silo_state.save_config()
164 return err.code
165 silo_state.mark_dirty()
166 silo_state.save_config()
167 # This bit needs to be in a separate block because it's so slow that it
168 # makes the last call to save_config() end up losing races with
169 # check-publication-migration. Then you get logs that say
170 # 'Publishing. Migration:... Publishing. Migration:...'
171 if mark_dirty:
172 try:
173 silo_state.mark_others_dirty()
174 except Exception as err:
175 logging.warning('Had trouble marking other silos dirty:')
176 logging.warning(err)
177 return 0
178
179
155class SiloState(object):180class SiloState(object):
156 """This class stores and manipulates the silo config json blob."""181 """This class stores and manipulates the silo config json blob."""
157 REQUEST_ID_FILE = 'request_id_{}'182 REQUEST_ID_FILE = 'request_id_{}'
@@ -212,7 +237,12 @@
212 def __setattr__(self, key, value):237 def __setattr__(self, key, value):
213 """Set Bileto attributes in Bileto."""238 """Set Bileto attributes in Bileto."""
214 if key in ('status', 'job_log', 'published_versions'):239 if key in ('status', 'job_log', 'published_versions'):
215 self._bileto[key] = scrub(value)240 scrubbed = scrub(str(value))
241 if key == 'status':
242 err = getattr(value, 'error', False)
243 (logging.error if err else logging.info)(scrubbed)
244 if getattr(value, 'loud', True):
245 self._bileto[key] = scrubbed
216 else:246 else:
217 super().__setattr__(key, value)247 super().__setattr__(key, value)
218248
219249
=== modified file 'tests/unit/__init__.py'
--- tests/unit/__init__.py 2015-11-04 01:46:45 +0000
+++ tests/unit/__init__.py 2015-11-06 15:09:47 +0000
@@ -174,8 +174,3 @@
174 'ubuntu/landing-{:03d}'.format(x) for x in range(0, 10)]174 'ubuntu/landing-{:03d}'.format(x) for x in range(0, 10)]
175 self.script.SILO_NAME_LIST = dict(175 self.script.SILO_NAME_LIST = dict(
176 ubuntu=self.script.ALL_SILO_NAMES)176 ubuntu=self.script.ALL_SILO_NAMES)
177
178 def test_existence(self):
179 """CI Train script has a main() func without SyntaxErrors."""
180 if self.script:
181 self.assertTrue(self.script.main)
182177
=== modified file 'tests/unit/test_recipe_base.py'
--- tests/unit/test_recipe_base.py 2015-11-04 01:46:45 +0000
+++ tests/unit/test_recipe_base.py 2015-11-06 15:09:47 +0000
@@ -772,12 +772,6 @@
772 self.assertEqual(silo_state.status, 'Merging.')772 self.assertEqual(silo_state.status, 'Merging.')
773 silo_state.save_config.assert_called_once_with()773 silo_state.save_config.assert_called_once_with()
774774
775 def test_buildbase_post_push_phase(self):
776 """Mark other silos dirty after silo has merged."""
777 silo_state = Mock()
778 BuildBase.post_push_phase(silo_state)
779 silo_state.mark_others_dirty.assert_called_once_with()
780
781 @patch('citrain.recipes.base.mkdir')775 @patch('citrain.recipes.base.mkdir')
782 @patch('citrain.recipes.base.shutil')776 @patch('citrain.recipes.base.shutil')
783 @patch('citrain.recipes.base.SILO_DIR')777 @patch('citrain.recipes.base.SILO_DIR')
784778
=== modified file 'tests/unit/test_script_build.py'
--- tests/unit/test_script_build.py 2015-11-04 02:19:56 +0000
+++ tests/unit/test_script_build.py 2015-11-06 15:09:47 +0000
@@ -168,5 +168,5 @@
168 bman.return_value.mock_calls, [168 bman.return_value.mock_calls, [
169 call.do('validate'),169 call.do('validate'),
170 ])170 ])
171 self.assertEqual(silo_state.status, 'Build failed: It asploded!')171 self.assertEqual(str(silo_state.status), 'Build failed: It asploded!')
172 silo_state.save_config.assert_called_once_with()172 silo_state.save_config.assert_called_once_with()
173173
=== modified file 'tests/unit/test_script_merge_clean.py'
--- tests/unit/test_script_merge_clean.py 2015-11-04 02:19:56 +0000
+++ tests/unit/test_script_merge_clean.py 2015-11-06 15:09:47 +0000
@@ -31,31 +31,25 @@
31 """Test CI Train Merge & Clean script."""31 """Test CI Train Merge & Clean script."""
32 scriptname = 'merge_clean.py'32 scriptname = 'merge_clean.py'
3333
34 def test_main(self):34 def test_merge(self):
35 """main() succeeds in the primary case."""35 """merge() succeeds in the primary case."""
36 self.script.env.SILONAME = 'fred'36 silo_state = Mock()
37 self.script.Manager = Mock()37 self.script.Manager = Mock()
38 mergemanager = self.script.Manager.return_value38 mergemanager = self.script.Manager.return_value
39 self.assertEqual(self.script.main(), 0)39 self.assertIsNone(self.script.merge(silo_state))
40 self.script.SiloState.assert_called_once_with('fred', primary=True)40 self.script.Manager.assert_called_once_with(silo_state)
41 self.script.Manager.assert_called_once_with(
42 self.script.SiloState.return_value)
43 mergemanager.do.assert_called_once_with(41 mergemanager.do.assert_called_once_with(
44 'validate', 'enumeration', 'merge', 'push', 'nuke')42 'validate', 'enumeration', 'merge', 'push', 'nuke')
4543
46 def test_main_failed(self):44 def test_merge_failed(self):
47 """main() returns 1 when a phase raises an exception."""45 """merge() doesn't catch exceptions."""
48 self.script.env.SILONAME = 'fred'46 silo_state = Mock()
49 self.script.Manager = Mock()47 self.script.Manager = Mock()
50 mergemanager = self.script.Manager.return_value48 mergemanager = self.script.Manager.return_value
51 mergemanager.do.side_effect = MergeError('whoa buddy!')49 mergemanager.do.side_effect = MergeError('whoa buddy!')
52 silo_state = self.script.SiloState.return_value50 silo_state = self.script.SiloState.return_value
53 self.assertEqual(self.script.main(), 1)51 with self.assertRaisesRegexp(MergeError, 'Merge&Clean failed: whoa'):
54 self.script.SiloState.assert_called_once_with('fred', primary=True)52 self.script.merge(silo_state)
55 self.script.Manager.assert_called_once_with(53 self.script.Manager.assert_called_once_with(silo_state)
56 self.script.SiloState.return_value)
57 mergemanager.do.assert_called_once_with(54 mergemanager.do.assert_called_once_with(
58 'validate', 'enumeration', 'merge', 'push', 'nuke')55 'validate', 'enumeration', 'merge', 'push', 'nuke')
59 self.script.logging.error.assert_called_once_with('whoa buddy!')
60 self.assertEqual(silo_state.status, 'Merge&Clean failed: whoa buddy!')
61 silo_state.save_config.assert_called_once_with()
6256
=== modified file 'tests/unit/test_script_migration.py'
--- tests/unit/test_script_migration.py 2015-11-04 02:19:56 +0000
+++ tests/unit/test_script_migration.py 2015-11-06 15:09:47 +0000
@@ -36,7 +36,7 @@
36 def setUp(self):36 def setUp(self):
37 super().setUp()37 super().setUp()
38 self.script.Manager = Mock()38 self.script.Manager = Mock()
39 self.script.merge_main = Mock()39 self.script.stock_main = Mock()
40 self.script.glob.return_value = [40 self.script.glob.return_value = [
41 join(SILOS_DIR, 'ubuntu', 'landing-00{}'.format(x))41 join(SILOS_DIR, 'ubuntu', 'landing-00{}'.format(x))
42 for x in range(3)]42 for x in range(3)]
@@ -64,14 +64,16 @@
64 call().do('unbuilt'),64 call().do('unbuilt'),
65 call().do('migration'),65 call().do('migration'),
66 ])66 ])
67 self.assertEqual(self.script.merge_main.mock_calls, [call()] * 3)67 self.assertEqual(self.script.stock_main.mock_calls, [
68 call(self.script.merge, mark_dirty=True)
69 ] * 3)
6870
69 def test_main_skip_empty_silos(self):71 def test_main_skip_empty_silos(self):
70 """Don't check silos that are empty."""72 """Don't check silos that are empty."""
71 self.script.SiloState.iterate.return_value = []73 self.script.SiloState.iterate.return_value = []
72 self.assertEqual(self.script.main(), 0)74 self.assertEqual(self.script.main(), 0)
73 self.assertEqual(self.script.Manager.mock_calls, [])75 self.assertEqual(self.script.Manager.mock_calls, [])
74 self.assertEqual(self.script.merge_main.mock_calls, [])76 self.assertEqual(self.script.stock_main.mock_calls, [])
7577
76 def test_main_skip_locked_silos(self):78 def test_main_skip_locked_silos(self):
77 """Skip over silos that are currently used by other processes."""79 """Skip over silos that are currently used by other processes."""
@@ -79,7 +81,7 @@
79 states[0].enforce_lock.side_effect = BlockingIOError81 states[0].enforce_lock.side_effect = BlockingIOError
80 self.assertEqual(self.script.main(), 0)82 self.assertEqual(self.script.main(), 0)
81 self.assertEqual(self.script.Manager.mock_calls, [])83 self.assertEqual(self.script.Manager.mock_calls, [])
82 self.assertEqual(self.script.merge_main.mock_calls, [])84 self.assertEqual(self.script.stock_main.mock_calls, [])
83 self.assertEqual(states[0].mock_calls, [call.enforce_lock()])85 self.assertEqual(states[0].mock_calls, [call.enforce_lock()])
8486
85 def test_main_skip_unpublished_silos(self):87 def test_main_skip_unpublished_silos(self):
@@ -96,7 +98,7 @@
96 call(states[2]),98 call(states[2]),
97 call().do('unbuilt'),99 call().do('unbuilt'),
98 ])100 ])
99 self.assertEqual(self.script.merge_main.mock_calls, [])101 self.assertEqual(self.script.stock_main.mock_calls, [])
100 for state in states:102 for state in states:
101 self.assertEqual(state.set_migrating.mock_calls, [])103 self.assertEqual(state.set_migrating.mock_calls, [])
102 self.assertEqual(state.save_config.mock_calls, [])104 self.assertEqual(state.save_config.mock_calls, [])
@@ -115,7 +117,7 @@
115 self.script.Manager.return_value.do.side_effect = set_side_effect117 self.script.Manager.return_value.do.side_effect = set_side_effect
116 self.assertEqual(self.script.main(), 0)118 self.assertEqual(self.script.main(), 0)
117 self.assertEqual(119 self.assertEqual(
118 states[0].status, 'Migration: foo is in the Proposed pocket.')120 str(states[0].status), 'Migration: foo is in the Proposed pocket.')
119 self.assertEqual(states[0].mock_calls, [121 self.assertEqual(states[0].mock_calls, [
120 call.enforce_lock(),122 call.enforce_lock(),
121 call.lock_fd.close(),123 call.lock_fd.close(),
@@ -128,4 +130,4 @@
128 call().do('unbuilt'),130 call().do('unbuilt'),
129 call().do('migration'),131 call().do('migration'),
130 ])132 ])
131 self.assertEqual(self.script.merge_main.mock_calls, [])133 self.assertEqual(self.script.stock_main.mock_calls, [])
132134
=== modified file 'tests/unit/test_script_publisher.py'
--- tests/unit/test_script_publisher.py 2015-10-28 19:40:12 +0000
+++ tests/unit/test_script_publisher.py 2015-11-06 15:09:47 +0000
@@ -21,10 +21,7 @@
21from tests.unit import CITrainScriptTestCase21from tests.unit import CITrainScriptTestCase
2222
23from cupstream2distro.utils import env23from cupstream2distro.utils import env
24from cupstream2distro.errors import PublishError, NoStatusError24from cupstream2distro.errors import PublishError
25
26
27N = '\n'
2825
2926
30class PublisherTestCase(CITrainScriptTestCase):27class PublisherTestCase(CITrainScriptTestCase):
@@ -37,67 +34,37 @@
37 env.DISTRO = 'ubuntu'34 env.DISTRO = 'ubuntu'
38 env.SERIES = 'vivid'35 env.SERIES = 'vivid'
3936
40 def mock_main(self):37 def test_publish_success(self):
41 """Ensure enough of the publisher is mocked out for testing main()."""38 """publisher.publish() should call the right functions."""
39 silo_state = Mock()
42 self.script.Manager = Mock()40 self.script.Manager = Mock()
43 self.script.SiloState.return_value.series = \41 self.assertIsNone(self.script.publish(silo_state))
44 'https://api.launchpad.net/devel/ubuntu/vivid'42 self.script.Manager.assert_called_once_with(silo_state)
45
46 def test_main_success(self):
47 """publisher.main() should call the right functions."""
48 self.mock_main()
49 state = self.script.SiloState()
50 self.assertEqual(self.script.main(), 0)
51 self.script.Manager.assert_called_once_with(state)
52 self.assertEqual(self.script.Manager.return_value.do.mock_calls, [43 self.assertEqual(self.script.Manager.return_value.do.mock_calls, [
53 call('checkstatus', 'validate', 'diff', 'checkupload', 'ackaging'),44 call('checkstatus', 'validate', 'diff', 'checkupload', 'ackaging'),
54 call('dest_version_check', 'unapproved', 'unbuilt', 'publish'),45 call('dest_version_check', 'unapproved', 'unbuilt', 'publish'),
55 ])46 ])
56 self.assertEqual(state.status, 'Publishing.')47 self.assertEqual(silo_state.status, 'Publishing.')
57 state.set_published.assert_called_once_with()48 self.assertEqual(silo_state.mock_calls, [
58 self.assertEqual(state.save_config.mock_calls, [call(), call()])49 call.save_config(),
59 state.mark_others_dirty.assert_called_once_with()50 call.set_published(),
6051 ])
61 def test_main_failure(self):52
62 """publisher.main() should handle errors."""53 def test_publish_failure(self):
63 self.mock_main()54 """publisher.publish() should raise errors."""
64 state = self.script.SiloState()55 silo_state = Mock()
65 self.script.Manager.return_value.do.side_effect = PublishError('Oops')56 self.script.Manager = Mock()
66 self.assertEqual(self.script.main(), 1)57 err = PublishError('Oops')
67 self.assertEqual(state.status, "Publish failed: Oops")58 self.script.Manager.return_value.do.side_effect = err
68 self.assertEqual(state.set_published.call_count, 0)59 with self.assertRaisesRegexp(PublishError, 'Publish failed: Oops'):
69 self.script.logging.error.assert_called_once_with('Oops')60 self.script.publish(silo_state)
70 self.assertEqual(state.save_config.mock_calls, [call()])61 self.assertEqual(silo_state.mock_calls, [])
71 self.assertEqual(state.mark_others_dirty.mock_calls, [])62
7263 def test_publish_failure_wily(self):
73 def test_main_failure_nostatus(self):
74 """publisher.main() shouldn't set status inappropriately."""
75 self.mock_main()
76 state = self.script.SiloState()
77 state.status = 'Preserved'
78 self.script.Manager.return_value.do = Mock(
79 side_effect=NoStatusError('Whoops'))
80 self.assertEqual(self.script.main(), 1)
81 self.assertEqual(state.status, 'Preserved')
82
83 def test_main_failure_dirty(self):
84 """publisher.main() should call save_config sooner."""
85 self.mock_main()
86 state = self.script.SiloState()
87 state.status = 'Packages built.'
88 self.script.SiloState.return_value.mark_others_dirty = Mock(
89 side_effect=Exception('Whoops'))
90 self.assertEqual(self.script.main(), 0)
91 self.assertEqual(state.status, 'Publishing.')
92
93 def test_main_failure_wily(self):
94 """Prevent publishing dual silos to wily."""64 """Prevent publishing dual silos to wily."""
95 self.mock_main()65 silo_state = Mock()
96 state = self.script.SiloState()66 silo_state.status = 'Packages built.'
97 state.status = 'Packages built.'67 silo_state._series = 'dual'
98 state._series = 'dual'68 self.script.Manager = Mock()
99 self.assertEqual(self.script.main(), 1)69 with self.assertRaisesRegexp(PublishError, 'must be transitioned'):
100 self.assertEqual(70 self.script.publish(silo_state)
101 state.status,
102 'Publish failed: This silo must be transitioned to xenial '
103 'before publishing.')
10471
=== modified file 'tests/unit/test_silomanager.py'
--- tests/unit/test_silomanager.py 2015-11-05 16:32:14 +0000
+++ tests/unit/test_silomanager.py 2015-11-06 15:09:47 +0000
@@ -28,9 +28,9 @@
28from tests.unit import DirectoryAwareTestCase28from tests.unit import DirectoryAwareTestCase
2929
30from cupstream2distro import silomanager30from cupstream2distro import silomanager
31from cupstream2distro.silomanager import SiloState, splitter31from cupstream2distro.silomanager import SiloState, splitter, stock_main
32from cupstream2distro.utils import env, utf8_open32from cupstream2distro.utils import env, utf8_open
33from cupstream2distro.errors import PrepError33from cupstream2distro.errors import PrepError, NoStatusError
3434
3535
36SOURCES = 'bar bat foo zing'.split()36SOURCES = 'bar bat foo zing'.split()
@@ -114,6 +114,42 @@
114 expected = [['robru', 'sil2100']] * 7 + [[]]114 expected = [['robru', 'sil2100']] * 7 + [[]]
115 self.assertEqual([splitter(case) for case in cases], expected)115 self.assertEqual([splitter(case) for case in cases], expected)
116116
117 @patch('cupstream2distro.silomanager.SiloState')
118 def test_stock_main(self, state_mock):
119 """Ensure that the stock main() function works."""
120 self.assertEqual(stock_main(lambda state: state, True), 0)
121 self.assertEqual(state_mock.mock_calls, [
122 call(self.tempdir, primary=True),
123 call().mark_dirty(),
124 call().save_config(),
125 call().mark_others_dirty(),
126 ])
127
128 @patch('cupstream2distro.silomanager.SiloState')
129 def test_stock_main_fail(self, state_mock):
130 """Ensure that the stock main() function handles exceptions."""
131 def body(silo_state):
132 """Raise a little hell."""
133 raise PrepError('Boo!')
134 self.assertEqual(stock_main(body, True), PrepError.code)
135 self.assertEqual(state_mock.mock_calls, [
136 call(self.tempdir, primary=True),
137 call().mark_dirty(),
138 call().save_config(),
139 ])
140
141 @patch('cupstream2distro.silomanager.SiloState')
142 def test_stock_main_dirty_fail(self, state_mock):
143 """Ensure that the stock main() function handles dirty exceptions."""
144 state_mock.return_value.mark_others_dirty.side_effect = Exception('Oh')
145 self.assertEqual(stock_main(lambda state: state, True), 0)
146 self.assertEqual(state_mock.mock_calls, [
147 call(self.tempdir, primary=True),
148 call().mark_dirty(),
149 call().save_config(),
150 call().mark_others_dirty(),
151 ])
152
117 def test_silostate_init(self):153 def test_silostate_init(self):
118 """Ensure that init is sensible."""154 """Ensure that init is sensible."""
119 state = SiloState('ubuntu/landing-123')155 state = SiloState('ubuntu/landing-123')
@@ -375,6 +411,12 @@
375 self.assertEqual(self.state.landers, 'm')411 self.assertEqual(self.state.landers, 'm')
376 self.state.load_bileto.assert_called_once_with()412 self.state.load_bileto.assert_called_once_with()
377413
414 def test_silostate_setattr_nostateerror(self):
415 """Don't set status with NoStatusError."""
416 self.state.status = 'Preserved'
417 self.state.status = NoStatusError('No')
418 self.assertEquals(self.state.status, 'Preserved')
419
378 @patch('cupstream2distro.silomanager.SiloState.series')420 @patch('cupstream2distro.silomanager.SiloState.series')
379 @patch('cupstream2distro.silomanager.SiloState.iterate')421 @patch('cupstream2distro.silomanager.SiloState.iterate')
380 @patch('cupstream2distro.silomanager.SiloState.all_projects', ['qtmir'])422 @patch('cupstream2distro.silomanager.SiloState.all_projects', ['qtmir'])

Subscribers

People subscribed via source and target branches