Merge lp:~mabac/svammel/bug-749561-set-series-and-milestone into lp:svammel
- bug-749561-set-series-and-milestone
- Merge into trunk
Status: | Merged |
---|---|
Approved by: | Mattias Backman |
Approved revision: | 102 |
Merged at revision: | 99 |
Proposed branch: | lp:~mabac/svammel/bug-749561-set-series-and-milestone |
Merge into: | lp:svammel |
Diff against target: |
472 lines (+260/-39) 4 files modified
bug_reporting.py (+93/-7) config.py (+6/-0) file-failures.py (+7/-3) tests/test_fail_filer.py (+154/-29) |
To merge this branch: | bzr merge lp:~mabac/svammel/bug-749561-set-series-and-milestone |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
James Westby (community) | Approve | ||
Matthias Klose | Pending | ||
Review via email: mp+57514@code.launchpad.net |
Commit message
Description of the change
Hi,
This branch adds the option to set milestones and series on newly created bugs.
--milestone and --target-series options are added.
Milestone and series are looked up in Ubuntu and on any error setting them, the bug is closed. I have limited permissions for nominating series, so everything is not thoroughly tested.
Thanks,
Mattias
Mattias Backman (mabac) wrote : | # |
Mattias Backman (mabac) wrote : | # |
Try something like this
$ python file-failures.py --archive test-rebuild-
There is a namespace clash on the command line with --series and --target-series which probably increases confusion.
If setting either milestone or series fails the bug will be closed and the script bails. Unless --ignore-errors is passed.
Matthias Klose (doko) wrote : | # |
looks fine.
+ # createBug creates only one bugtask, but addNomination
+ # may add another, so we use the last one.
+ set_bugtask_
just wondering how robust this is ...
Mattias Backman (mabac) wrote : | # |
> + # createBug creates only one bugtask, but addNomination
> + # may add another, so we use the last one.
> + set_bugtask_
>
> just wondering how robust this is ...
Can't say that writing that was my proudest moment... I hope that we
can clarify how addNomination works before this feature is done. I
have however not been able to fully test this since I don't seem to
have proper permissions anywhere in the system.
Thanks,
Mattias
James Westby (james-w) wrote : | # |
Hi,
Sorry for the delay in reviewing this.
8 +class AttributeError(
Could we avoid re-defining a standard exception, that's only going to lead to confusion.
If the standard exception doesn't do what you want please change the name of
this one.
59 + set_bugtask_
Like Mattias I am uneasy about this, it looks very fragile. Is there
some rule you can verbalise about which task is needed at this point?
If so then we can try and code that up, rather than relying on ordering
of the tasks here.
Everything else looks good.
Thanks,
James
- 97. By Mattias Backman
-
Remove exception which collided with standard Exception and fix tests.
- 98. By Mattias Backman
-
Find correct bugtask for milestone instead of assuming that it is the last one.
Mattias Backman (mabac) wrote : | # |
> Sorry for the delay in reviewing this.
No problem. I had left it in status "work in progress" until recently too.
>
> 8 +class AttributeError(
>
> Could we avoid re-defining a standard exception, that's only going to lead to confusion.
That's my mistake, didn't check for a standard exception with that
name. Removed.
> 59 + set_bugtask_
>
> Like Mattias I am uneasy about this, it looks very fragile. Is there
> some rule you can verbalise about which task is needed at this point?
> If so then we can try and code that up, rather than relying on ordering
> of the tasks here.
That rule should be to use the only bug_task if no series has been
nominated, and to use the bug_task that actually is targeted to the
specified series otherwise. Trivial now that I think about it, thanks.
:) I've changed this as well.
Thanks,
Mattias
- 99. By Mattias Backman
-
Automatically approve series nomination, to be able to proceed with setting milestone on bug_task.
- 100. By Mattias Backman
-
Only auto approve if user is allowed to do so.
Mattias Backman (mabac) wrote : | # |
Another piece of the puzzle is that the user might be able to add a nomination but is not allowed to approve it. The new bugtask is only created when the nomination is approved.
Now the script will add a nomination and approve it immediately if the user has the permission to do so. If the nomination is added but cannot be approved, setting a milestone will fail, and I think that has to be since the user tried to do something she hasn't proper permissions for.
Just adding nominations in one pass would work, and setting milestones without nominating a series first is also ok. Only nominating a series and setting a milestone will require special permissions, I think.
James Westby (james-w) wrote : | # |
53 + set_bugtask_
I would probably add a check here whether the bugtask was found, asking them to
report a bug if not, otherwise they will get a confusing error.
This looks much better now, thanks.
> Another piece of the puzzle is that the user might be able to add a nomination but is not allowed to
> approve it. The new bugtask is only created when the nomination is approved.
I think an error if they request an impossible situation is fine. Wouldn't the --continue-on-error
support deal with that anyway if they wanted?
As it stands though I don't think it will continue if the person can't do it, as the code
for finding the task now assumes that a series task will be created if --series is specified.
Perhaps it should not assume that and then decide what to do based on --continue-
Thanks,
James
- 101. By Mattias Backman
-
Move getting bug_task for milestone to immediately after nomination.
- 102. By Mattias Backman
-
Continue after failed addNomination if --ignore-errors passed.
Mattias Backman (mabac) wrote : | # |
> 53 + set_bugtask_
>
> I would probably add a check here whether the bugtask was found, asking them
> to
> report a bug if not, otherwise they will get a confusing error.
Good idea, I have added that.
> > Another piece of the puzzle is that the user might be able to add a
> nomination but is not allowed to
> > approve it. The new bugtask is only created when the nomination is approved.
>
> I think an error if they request an impossible situation is fine. Wouldn't the
> --continue-on-error
> support deal with that anyway if they wanted?
Yes it would. I changed it a bit so now series nomination can fail in a couple of ways and still try to set milestone if --ignore-errors is passed.
>
> As it stands though I don't think it will continue if the person can't do it,
> as the code
> for finding the task now assumes that a series task will be created if
> --series is specified.
>
> Perhaps it should not assume that and then decide what to do based on
> --continue-
Now it doesn't assume that and will set milestone even if the user has no permissions for targeting series.
I'll merge this in the morning if no-one comments on the last revisions.
Thanks,
Mattias
James Westby (james-w) wrote : | # |
This looks good to me, so merge whenever you like :-)
Preview Diff
1 | === modified file 'bug_reporting.py' |
2 | --- bug_reporting.py 2011-04-15 06:59:55 +0000 |
3 | +++ bug_reporting.py 2011-04-20 14:50:23 +0000 |
4 | @@ -36,34 +36,114 @@ |
5 | return project.self_link |
6 | |
7 | |
8 | -def file_bug_by_url(target_url, description, title, tags, lp, importance=None): |
9 | +def file_bug_by_url(target_url, description, title, tags, lp, importance=None, |
10 | + milestone_name=None, series_name=None): |
11 | """ File a bug on the bug target specified by target_url. |
12 | |
13 | Returns the newly created bug or None if no bug was filed. |
14 | """ |
15 | + logger.debug("lp.bugs.createBug(description=%s..., title=%s, target=%s, " \ |
16 | + "tags=%s)" % (description[:20], title, target_url, tags)) |
17 | + |
18 | bug = lp.bugs.createBug(description=description, |
19 | title=title, |
20 | target=target_url, |
21 | tags=tags) |
22 | if bug is not None: |
23 | logger.info('Filed bug %i: %s' % (bug.id, bug.web_link)) |
24 | - if importance is not None: |
25 | - set_bugtask_importance(bug, importance) |
26 | + try: |
27 | + target = lp.distributions['ubuntu'] |
28 | + logger.debug("Series and milestone target is %s." % target) |
29 | + ms_bugtask = None |
30 | + if series_name is not None: |
31 | + series = target.getSeries(name_or_version=series_name) |
32 | + logger.debug("Adding nomination for series %s." % series) |
33 | + try: |
34 | + nomination = bug.addNomination(target=series) |
35 | + except: |
36 | + if continue_on_error(): |
37 | + logger.warning("Cannot add nomination for series %s.", |
38 | + series) |
39 | + nomination = None |
40 | + else: |
41 | + raise |
42 | + |
43 | + if nomination is not None and nomination.canApprove(): |
44 | + logger.debug("Approving nomination %s." % nomination) |
45 | + nomination.approve() |
46 | + for candidate_bugtask in bug.bug_tasks: |
47 | + if candidate_bugtask.target == series: |
48 | + ms_bugtask = candidate_bugtask |
49 | + if ms_bugtask is None: |
50 | + raise Exception( |
51 | + "A bug task to set milestone was not found, " \ |
52 | + "please report this bug on the svammel project.") |
53 | + |
54 | + elif continue_on_error(): |
55 | + logger.warning("Cannot approve nomination %s.", nomination) |
56 | + else: |
57 | + raise AttributeError( |
58 | + "Unable to approve nomination %s." % nomination) |
59 | + |
60 | + if milestone_name is not None: |
61 | + milestone = target.getMilestone(name=milestone_name) |
62 | + logger.debug("Target milestone is %s." % milestone) |
63 | + if milestone is None: |
64 | + raise AttributeError( |
65 | + "Milestone '%s' not valid for target '%s'." % \ |
66 | + (milestone_name, target.name)) |
67 | + |
68 | + if ms_bugtask is None: |
69 | + # If series nomination is done the bug_task |
70 | + # has already been found. |
71 | + ms_bugtask = bug.bug_tasks[0] |
72 | + set_bugtask_milestone(ms_bugtask, milestone) |
73 | + |
74 | + if importance is not None: |
75 | + set_bug_importance(bug, importance) |
76 | + except Exception, e: |
77 | + if continue_on_error(): |
78 | + logger.error("Error '%s' but will proceed.", e) |
79 | + else: |
80 | + logger.critical( |
81 | + "Closing bug before terminating, due to '%s'.", e) |
82 | + for bug_task in bug.bug_tasks: |
83 | + close_bug(bug_task, BUG_INVALID_STATUS) |
84 | + raise |
85 | else: |
86 | logger.error("Unable to file bug '%s'." % title) |
87 | |
88 | return bug |
89 | |
90 | |
91 | -def set_bugtask_importance(bug, importance): |
92 | +def set_bug_importance(bug, importance): |
93 | + """Sets importance of all bug_tasks belonging to a bug.""" |
94 | try: |
95 | for bug_task in bug.bug_tasks: |
96 | + logger.debug("Setting importance %s for %s" % \ |
97 | + (importance, bug_task.title)) |
98 | bug_task.importance = importance |
99 | bug_task.lp_save() |
100 | except: |
101 | if continue_on_error(): |
102 | logger.error("Unable to set bug importance to '%s' but " \ |
103 | - "will proceed." % importance) |
104 | + "will proceed." % importance) |
105 | + else: |
106 | + raise |
107 | + |
108 | + |
109 | +def set_bugtask_milestone(bug_task, milestone): |
110 | + """Sets the milestone for a bugtask.""" |
111 | + try: |
112 | + logger.debug("Setting milestone %s for %s" % \ |
113 | + (milestone.name, bug_task.title)) |
114 | + |
115 | + bug_task.milestone = milestone |
116 | + bug_task.lp_save() |
117 | + except: |
118 | + if continue_on_error(): |
119 | + logger.error("Unable to set milestone '%s' but will " \ |
120 | + "proceed." % (milestone.name)) |
121 | else: |
122 | raise |
123 | |
124 | @@ -80,10 +160,16 @@ |
125 | return len(dupe_bugs[:1]) > 0 |
126 | |
127 | |
128 | -def close_bug(url, close_status, lp): |
129 | +def close_bug_by_url(url, close_status, lp): |
130 | + """ Close the bugtask by setting the status.""" |
131 | + |
132 | + bug_task = lp.load(url) |
133 | + close_bug(bug_task, close_status) |
134 | + |
135 | + |
136 | +def close_bug(bug_task, close_status): |
137 | """ Close the bugtask by setting status to Invalid.""" |
138 | |
139 | - bug_task = lp.load(url) |
140 | logger.info("Changing status for %s [%s ==> %s]" % \ |
141 | (bug_task.title, bug_task.status, close_status)) |
142 | bug_task.status = close_status |
143 | |
144 | === modified file 'config.py' |
145 | --- config.py 2011-04-15 06:47:57 +0000 |
146 | +++ config.py 2011-04-20 14:50:23 +0000 |
147 | @@ -168,5 +168,11 @@ |
148 | '--ignore-errors', action='store_true', dest='ignoreerrors', |
149 | help='Ignore errors that do not prevent proper completion, such as ' \ |
150 | 'failure to set bug attributes.') |
151 | + parser.add_argument( |
152 | + '--milestone', |
153 | + help='If specified, will be set on all created bugs.') |
154 | + parser.add_argument( |
155 | + '--target-series', dest='targetseries', |
156 | + help='If specified, will be nominated for all created bugs.') |
157 | |
158 | return parser |
159 | |
160 | === modified file 'file-failures.py' |
161 | --- file-failures.py 2011-04-15 07:40:10 +0000 |
162 | +++ file-failures.py 2011-04-20 14:50:23 +0000 |
163 | @@ -31,7 +31,7 @@ |
164 | from bug_reporting import ( |
165 | file_bug_by_url, |
166 | bug_already_filed_by_url, |
167 | - close_bug, |
168 | + close_bug_by_url, |
169 | BUG_INVALID_STATUS, |
170 | BUG_FIXED_STATUS, |
171 | ) |
172 | @@ -139,7 +139,10 @@ |
173 | (spph.package_name, spph.version, bug_tags)) |
174 | if not args.dryrun: |
175 | bug = file_bug_by_url(spph.url, bug_description, bug_title, |
176 | - bug_tags, get_launchpad(), args.importance) |
177 | + bug_tags, get_launchpad(), |
178 | + importance=args.importance, |
179 | + milestone_name=args.milestone, |
180 | + series_name=args.targetseries) |
181 | if bug is not None and args.logfile is not None: |
182 | bug_log.write_entry( |
183 | LogEntry(spph.package_name, spph.version, fail_platform, |
184 | @@ -161,7 +164,8 @@ |
185 | new_status = BUG_INVALID_STATUS |
186 | else: |
187 | new_status = BUG_FIXED_STATUS |
188 | - close_bug(log_entry.bugtask_url, new_status, get_launchpad()) |
189 | + close_bug_by_url(log_entry.bugtask_url, new_status, |
190 | + get_launchpad()) |
191 | else: |
192 | bug = get_launchpad().load(log_entry.bugtask_url) |
193 | for bug_task in bug.bug_tasks: |
194 | |
195 | === modified file 'tests/test_fail_filer.py' |
196 | --- tests/test_fail_filer.py 2011-04-15 10:16:29 +0000 |
197 | +++ tests/test_fail_filer.py 2011-04-20 14:50:23 +0000 |
198 | @@ -37,9 +37,14 @@ |
199 | filter_spph, |
200 | get_build_list, |
201 | ) |
202 | +import bug_reporting |
203 | from bug_reporting import ( |
204 | bug_already_filed_by_url, |
205 | - set_bugtask_importance, |
206 | + set_bug_importance, |
207 | + set_bugtask_milestone, |
208 | + close_bug, |
209 | + file_bug_by_url, |
210 | + BUG_INVALID_STATUS, |
211 | ) |
212 | from config import ( |
213 | get_service_root, |
214 | @@ -117,7 +122,6 @@ |
215 | Launchpad, 'login_with', classmethod(mock_func))) |
216 | init('staging', 'dummyapp', 'dummycache') |
217 | |
218 | - |
219 | def test_filter_only_checks_other_archives(self): |
220 | self.init_dummy_config() |
221 | archive_with_failure = self.MockArchive('failarchive') |
222 | @@ -129,7 +133,8 @@ |
223 | ] |
224 | spph = SPPH('packagetotest', '1') |
225 | failrecord = self.MockRecord(TEST_FAIL_ARCH, datetime.now(), |
226 | - FAILED_BUILD_STATE, archive = archive_with_failure) |
227 | + FAILED_BUILD_STATE, |
228 | + archive=archive_with_failure) |
229 | spph.add_build_record(failrecord) |
230 | spph_list = dict() |
231 | spph_list[spph.package_name] = spph |
232 | @@ -139,7 +144,6 @@ |
233 | self.assertTrue(other_archive_2.has_been_called) |
234 | self.assertFalse(archive_with_failure.has_been_called) |
235 | |
236 | - |
237 | def test_filter_fail_record_not_filtered(self): |
238 | self.init_dummy_config() |
239 | spph = SPPH('packagefail', '1') |
240 | @@ -189,10 +193,12 @@ |
241 | packagename = 'packagesubstring' |
242 | archive = self.MockArchive('archivename') |
243 | exactrecord = self.MockRecord(TEST_FAIL_ARCH, datetime.now(), |
244 | - SUCCESS_BUILD_STATE, packagename, archive) |
245 | + SUCCESS_BUILD_STATE, packagename, |
246 | + archive) |
247 | nonexactrecord = self.MockRecord(TEST_FAIL_ARCH, datetime.now(), |
248 | SUCCESS_BUILD_STATE, |
249 | - 'pre' + packagename + 'post', archive) |
250 | + 'pre' + packagename + 'post', |
251 | + archive) |
252 | archive.setRecordsToReturn([exactrecord, nonexactrecord]) |
253 | build_records = get_build_list(archive, SUCCESS_BUILD_STATE, |
254 | packagename) |
255 | @@ -248,7 +254,8 @@ |
256 | fail_spph = SPPH(packagename, version) |
257 | archive = self.MockArchive('archivename') |
258 | failrecord = self.MockRecord(TEST_FAIL_ARCH, datetime.now(), |
259 | - FAILED_BUILD_STATE, packagename, version, archive) |
260 | + FAILED_BUILD_STATE, packagename, version, |
261 | + archive) |
262 | successrecord = self.MockRecord(TEST_OK_ARCHS[0], datetime.now(), |
263 | SUCCESS_BUILD_STATE, packagename, |
264 | version, archive) |
265 | @@ -526,19 +533,85 @@ |
266 | self.assertTrue(get_highest_version([None, None]) is None) |
267 | |
268 | |
269 | -class TestFileBug(TestCase): |
270 | +class TestFileBug(TestCaseWithFixtures): |
271 | + |
272 | + class TestFailError(Exception): |
273 | + def __init__(self, value): |
274 | + self.value = value |
275 | + |
276 | + def __str__(self): |
277 | + return repr(self.value) |
278 | + |
279 | + class MockLaunchpad(object): |
280 | + class MockProject(object): |
281 | + def searchTasks(self, tags, tags_combinator): |
282 | + return self.bug_tasks |
283 | + |
284 | + def __init__(self, bug_tasks): |
285 | + self.bug_tasks = bug_tasks |
286 | + |
287 | + class Bugs(object): |
288 | + def createBug(self, description, title, target, tags): |
289 | + bug = TestFileBug.MockBug() |
290 | + bug_task1 = TestFileBug.MockBugTask(False) |
291 | + bug_task1.status = 'New' |
292 | + bug.add_bug_task(bug_task1) |
293 | + bug_task2 = TestFileBug.MockBugTask(False) |
294 | + bug_task2.status = 'New' |
295 | + bug.add_bug_task(bug_task2) |
296 | + return bug |
297 | + |
298 | + def __init__(self, bug_tasks): |
299 | + self.bugs = self.Bugs() |
300 | + self.project = self.MockProject(bug_tasks) |
301 | + self.distributions = dict() |
302 | + self.distributions['ubuntu'] = TestFileBug.MockTarget() |
303 | + |
304 | + def load(self, url): |
305 | + return self.project |
306 | + |
307 | + class MockMilestone: |
308 | + def __init__(self, name): |
309 | + self.name = name |
310 | |
311 | class MockBug: |
312 | def __init__(self): |
313 | self.bug_tasks = [] |
314 | + self.id = 1 |
315 | + self.web_link = 'http://dummy' |
316 | + self.title = 'Test bug' |
317 | + |
318 | def add_bug_task(self, bug_task): |
319 | self.bug_tasks.append(bug_task) |
320 | |
321 | + class MockTarget: |
322 | + class MockSeries: |
323 | + def __init__(self): |
324 | + self.name = 'Series' |
325 | + |
326 | + def getMilestone(self, name): |
327 | + return TestFileBug.MockMilestone(name) |
328 | + |
329 | + def __init__(self): |
330 | + self.name = 'Target' |
331 | + self.current_series = self.MockSeries() |
332 | + |
333 | + def getMilestone(self, name): |
334 | + return TestFileBug.MockMilestone(name) |
335 | + |
336 | + def getSeries(self, name): |
337 | + return self.MockSeries() |
338 | + |
339 | class MockBugTask: |
340 | + |
341 | def __init__(self, will_fail_on_save): |
342 | self.will_fail_on_save = will_fail_on_save |
343 | self.importance = 'Undecided' |
344 | self.has_saved = False |
345 | + self.target = TestFileBug.MockTarget() |
346 | + self.title = '' |
347 | + self.status = '' |
348 | + |
349 | def lp_save(self): |
350 | if self.will_fail_on_save: |
351 | raise Exception() |
352 | @@ -549,47 +622,99 @@ |
353 | super(TestFileBug, self).setUp() |
354 | |
355 | def test_duplicate_exists(self): |
356 | - class MockLaunchpad(object): |
357 | - |
358 | - def load(obj, url): |
359 | - class MockProject(object): |
360 | - def searchTasks(obj, tags, tags_combinator): |
361 | - return ['something here'] |
362 | - return MockProject() |
363 | - |
364 | + lp = self.MockLaunchpad(['something here']) |
365 | self.assertTrue(bug_already_filed_by_url('https://dummy', [], |
366 | - MockLaunchpad())) |
367 | + lp)) |
368 | |
369 | def test_duplicate_does_not_exist(self): |
370 | - class MockLaunchpad(object): |
371 | - |
372 | - def load(obj, url): |
373 | - class MockProject(object): |
374 | - def searchTasks(obj, tags, tags_combinator): |
375 | - return [] |
376 | - return MockProject() |
377 | - |
378 | + lp = self.MockLaunchpad([]) |
379 | self.assertFalse(bug_already_filed_by_url('https://dummy', [], |
380 | - MockLaunchpad())) |
381 | + lp)) |
382 | + |
383 | + def test_close_bug_if_setting_importance_fails(self): |
384 | + |
385 | + def raise_func(bug, importance): |
386 | + self.saved_bug = bug |
387 | + raise self.TestFailError('Setting importance failed.') |
388 | + |
389 | + self.useFixture(MockSomethingFixture( |
390 | + bug_reporting, 'set_bug_importance', raise_func)) |
391 | + try: |
392 | + self.saved_bug = None |
393 | + bug = file_bug_by_url('http://dummy', 'description', 'title', [], |
394 | + self.MockLaunchpad([]), importance='Low') |
395 | + self.fail('Error in test case, file_bug_by_url should have '\ |
396 | + 'raised an error.') |
397 | + except self.TestFailError: |
398 | + for bug_task in self.saved_bug.bug_tasks: |
399 | + self.assertEquals(BUG_INVALID_STATUS, bug_task.status) |
400 | + |
401 | + def test_close_bug_if_setting_milestone_fails(self): |
402 | + |
403 | + def raise_func(bug_task, milestone): |
404 | + self.saved_bug_task = bug_task |
405 | + raise self.TestFailError('Setting milestone failed.') |
406 | + |
407 | + self.useFixture(MockSomethingFixture( |
408 | + bug_reporting, 'set_bugtask_milestone', raise_func)) |
409 | + try: |
410 | + self.saved_bug_task = None |
411 | + bug = file_bug_by_url('http://dummy', 'description', 'title', [], |
412 | + self.MockLaunchpad([]), |
413 | + milestone_name='1.0.0') |
414 | + self.fail('Error in test case, file_bug_by_url should have ' \ |
415 | + 'raised an error.') |
416 | + except self.TestFailError: |
417 | + self.assertEquals(BUG_INVALID_STATUS, self.saved_bug_task.status) |
418 | + |
419 | + def test_close_bug_saves(self): |
420 | + bug_task = self.MockBugTask(False) |
421 | + close_bug(bug_task, 'status') |
422 | + self.assertEquals(True, bug_task.has_saved) |
423 | |
424 | def test_set_importance_saves(self): |
425 | bug = self.MockBug() |
426 | bug_task = self.MockBugTask(False) |
427 | bug.add_bug_task(bug_task) |
428 | - set_bugtask_importance(bug, 'High') |
429 | + set_bug_importance(bug, 'High') |
430 | self.assertEquals(True, bug_task.has_saved) |
431 | |
432 | def test_set_importance_can_ignore_errors(self): |
433 | set_ignore_errors(True) |
434 | bug = self.MockBug() |
435 | bug.add_bug_task(self.MockBugTask(True)) |
436 | - set_bugtask_importance(bug, 'High') |
437 | + set_bug_importance(bug, 'High') |
438 | |
439 | def test_set_importance_raises(self): |
440 | set_ignore_errors(False) |
441 | bug = self.MockBug() |
442 | bug.add_bug_task(self.MockBugTask(True)) |
443 | - self.assertRaises(Exception, set_bugtask_importance, bug, 'High') |
444 | + self.assertRaises(Exception, set_bug_importance, bug, 'High') |
445 | + |
446 | + def test_set_milestone_saves(self): |
447 | + bug_task = self.MockBugTask(False) |
448 | + milestone = self.MockMilestone('0.5.0') |
449 | + set_bugtask_milestone(bug_task, milestone) |
450 | + self.assertEquals(milestone, bug_task.milestone) |
451 | + self.assertTrue(bug_task.has_saved) |
452 | + |
453 | + def test_set_milestone_can_ignore_errors(self): |
454 | + set_ignore_errors(True) |
455 | + bug_task = self.MockBugTask(True) |
456 | + milestone = self.MockMilestone('0.5.0') |
457 | + set_bugtask_milestone(bug_task, milestone) |
458 | + |
459 | + def test_set_milestone_save_fail_raises(self): |
460 | + set_ignore_errors(False) |
461 | + bug_task = self.MockBugTask(True) |
462 | + milestone = self.MockMilestone('0.5.0') |
463 | + self.assertRaises(Exception, set_bugtask_milestone, bug_task, |
464 | + milestone) |
465 | + |
466 | + def test_set_milestone_no_milestone_raises(self): |
467 | + set_ignore_errors(False) |
468 | + bug_task = self.MockBugTask(True) |
469 | + self.assertRaises(Exception, set_bugtask_milestone, bug_task, None) |
470 | |
471 | |
472 | class TestConfigInit(TestCaseWithFixtures): |
Reminder to self:
147 + importance= args.importance , milestone= args.milestone,
should be milestone_name=