Merge lp:~gmb/launchpad/remove-calculatebugheatjob-bug-588186 into lp:launchpad/db-devel
- remove-calculatebugheatjob-bug-588186
- Merge into db-devel
Proposed by
Graham Binns
Status: | Merged |
---|---|
Approved by: | Graham Binns |
Approved revision: | no longer in the source branch. |
Merged at revision: | 9421 |
Proposed branch: | lp:~gmb/launchpad/remove-calculatebugheatjob-bug-588186 |
Merge into: | lp:launchpad/db-devel |
Diff against target: |
670 lines (+2/-578) 9 files modified
cronscripts/calculate-bug-heat.py (+0/-33) lib/canonical/launchpad/scripts/garbo.py (+0/-1) lib/lp/bugs/configure.zcml (+0/-12) lib/lp/bugs/interfaces/bugjob.py (+1/-11) lib/lp/bugs/model/bug.py (+0/-1) lib/lp/bugs/model/bugheat.py (+0/-54) lib/lp/bugs/scripts/bugheat.py (+0/-108) lib/lp/bugs/scripts/tests/test_bugheat.py (+0/-256) lib/lp/bugs/tests/test_bugheat.py (+1/-102) |
To merge this branch: | bzr merge lp:~gmb/launchpad/remove-calculatebugheatjob-bug-588186 |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Michael Nelson (community) | Approve | ||
Jelmer Vernooij (community) | code* | Approve | |
Review via email: mp+26684@code.launchpad.net |
Commit message
All CalculateBugHeatJob code has been removed since it is not longer used.
Description of the change
This branch removes all the CalculateBugHeatJob code since we no longer use it (bug heat calculation is now done by a stored procedure in the database).
To post a comment you must log in.
Revision history for this message
Jelmer Vernooij (jelmer) : | # |
review:
Approve
(code*)
Revision history for this message
Michael Nelson (michael.nelson) : | # |
review:
Approve
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === removed file 'cronscripts/calculate-bug-heat.py' |
2 | --- cronscripts/calculate-bug-heat.py 2010-04-22 17:30:35 +0000 |
3 | +++ cronscripts/calculate-bug-heat.py 1970-01-01 00:00:00 +0000 |
4 | @@ -1,33 +0,0 @@ |
5 | -#!/usr/bin/python2.5 -S |
6 | -# |
7 | -# Copyright 2010 Canonical Ltd. This software is licensed under the |
8 | -# GNU Affero General Public License version 3 (see the file LICENSE). |
9 | - |
10 | -# pylint: disable-msg=W0403 |
11 | - |
12 | -"""Calculate bug heat.""" |
13 | - |
14 | -__metaclass__ = type |
15 | - |
16 | -import _pythonpath |
17 | - |
18 | -from canonical.launchpad.webapp import errorlog |
19 | - |
20 | -from lp.services.job.runner import JobCronScript |
21 | -from lp.bugs.interfaces.bugjob import ICalculateBugHeatJobSource |
22 | - |
23 | - |
24 | -class RunCalculateBugHeat(JobCronScript): |
25 | - """Run BranchScanJob jobs.""" |
26 | - |
27 | - config_name = 'calculate_bug_heat' |
28 | - source_interface = ICalculateBugHeatJobSource |
29 | - |
30 | - def main(self): |
31 | - errorlog.globalErrorUtility.configure(self.config_name) |
32 | - return super(RunCalculateBugHeat, self).main() |
33 | - |
34 | - |
35 | -if __name__ == '__main__': |
36 | - script = RunCalculateBugHeat() |
37 | - script.lock_and_run() |
38 | |
39 | === modified file 'lib/canonical/launchpad/scripts/garbo.py' |
40 | --- lib/canonical/launchpad/scripts/garbo.py 2010-05-27 16:49:09 +0000 |
41 | +++ lib/canonical/launchpad/scripts/garbo.py 2010-06-03 10:55:38 +0000 |
42 | @@ -33,7 +33,6 @@ |
43 | from lp.bugs.interfaces.bug import IBugSet |
44 | from lp.bugs.model.bug import Bug |
45 | from lp.bugs.model.bugattachment import BugAttachment |
46 | -from lp.bugs.interfaces.bugjob import ICalculateBugHeatJobSource |
47 | from lp.bugs.model.bugnotification import BugNotification |
48 | from lp.bugs.model.bugwatch import BugWatch |
49 | from lp.bugs.scripts.checkwatches.scheduler import ( |
50 | |
51 | === modified file 'lib/lp/bugs/configure.zcml' |
52 | --- lib/lp/bugs/configure.zcml 2010-05-26 15:24:23 +0000 |
53 | +++ lib/lp/bugs/configure.zcml 2010-06-03 10:55:38 +0000 |
54 | @@ -967,18 +967,6 @@ |
55 | factory="lp.bugs.browser.bugtarget.BugsVHostBreadcrumb" |
56 | permission="zope.Public"/> |
57 | |
58 | - <!-- CalculateBugHeatJobs --> |
59 | - <class class="lp.bugs.model.bugheat.CalculateBugHeatJob"> |
60 | - <allow interface="lp.bugs.interfaces.bugjob.IBugJob" /> |
61 | - <allow interface="lp.bugs.interfaces.bugjob.ICalculateBugHeatJob"/> |
62 | - </class> |
63 | - <securedutility |
64 | - component="lp.bugs.model.bugheat.CalculateBugHeatJob" |
65 | - provides="lp.bugs.interfaces.bugjob.ICalculateBugHeatJobSource"> |
66 | - <allow |
67 | - interface="lp.bugs.interfaces.bugjob.ICalculateBugHeatJobSource"/> |
68 | - </securedutility> |
69 | - |
70 | <!-- ProcessApportBlobJobs --> |
71 | <class class="lp.bugs.model.apportjob.ProcessApportBlobJob"> |
72 | <allow interface="lp.bugs.interfaces.apportjob.IApportJob" /> |
73 | |
74 | === modified file 'lib/lp/bugs/interfaces/bugjob.py' |
75 | --- lib/lp/bugs/interfaces/bugjob.py 2010-01-22 21:44:19 +0000 |
76 | +++ lib/lp/bugs/interfaces/bugjob.py 2010-06-03 10:55:38 +0000 |
77 | @@ -8,8 +8,6 @@ |
78 | 'BugJobType', |
79 | 'IBugJob', |
80 | 'IBugJobSource', |
81 | - 'ICalculateBugHeatJob', |
82 | - 'ICalculateBugHeatJobSource', |
83 | ] |
84 | |
85 | from zope.interface import Attribute, Interface |
86 | @@ -19,7 +17,7 @@ |
87 | |
88 | from lazr.enum import DBEnumeratedType, DBItem |
89 | from lp.bugs.interfaces.bug import IBug |
90 | -from lp.services.job.interfaces.job import IJob, IJobSource, IRunnableJob |
91 | +from lp.services.job.interfaces.job import IJob, IJobSource |
92 | |
93 | |
94 | class BugJobType(DBEnumeratedType): |
95 | @@ -57,11 +55,3 @@ |
96 | |
97 | def create(bug): |
98 | """Create a new IBugJob for a bug.""" |
99 | - |
100 | - |
101 | -class ICalculateBugHeatJob(IRunnableJob): |
102 | - """A Job to calculate bug heat.""" |
103 | - |
104 | - |
105 | -class ICalculateBugHeatJobSource(IBugJobSource): |
106 | - """Interface for acquiring CalculateBugHeatJobs.""" |
107 | |
108 | === modified file 'lib/lp/bugs/model/bug.py' |
109 | --- lib/lp/bugs/model/bug.py 2010-05-30 04:06:48 +0000 |
110 | +++ lib/lp/bugs/model/bug.py 2010-06-03 10:55:38 +0000 |
111 | @@ -83,7 +83,6 @@ |
112 | from lp.bugs.interfaces.bugtracker import BugTrackerType |
113 | from lp.bugs.interfaces.bugwatch import IBugWatchSet |
114 | from lp.bugs.interfaces.cve import ICveSet |
115 | -from lp.bugs.scripts.bugheat import BugHeatConstants |
116 | from lp.bugs.model.bugattachment import BugAttachment |
117 | from lp.bugs.model.bugbranch import BugBranch |
118 | from lp.bugs.model.bugcve import BugCve |
119 | |
120 | === removed file 'lib/lp/bugs/model/bugheat.py' |
121 | --- lib/lp/bugs/model/bugheat.py 2010-01-21 20:46:03 +0000 |
122 | +++ lib/lp/bugs/model/bugheat.py 1970-01-01 00:00:00 +0000 |
123 | @@ -1,54 +0,0 @@ |
124 | -# Copyright 2010 Canonical Ltd. This software is licensed under the |
125 | -# GNU Affero General Public License version 3 (see the file LICENSE). |
126 | - |
127 | -"""Job classes related to BugJobs are in here.""" |
128 | - |
129 | -__metaclass__ = type |
130 | -__all__ = [ |
131 | - 'CalculateBugHeatJob', |
132 | - ] |
133 | - |
134 | -from zope.component import getUtility |
135 | -from zope.interface import classProvides, implements |
136 | - |
137 | -from canonical.launchpad.webapp.interfaces import ( |
138 | - DEFAULT_FLAVOR, IStoreSelector, MAIN_STORE) |
139 | - |
140 | -from lp.bugs.interfaces.bugjob import ( |
141 | - BugJobType, ICalculateBugHeatJob, ICalculateBugHeatJobSource) |
142 | -from lp.bugs.model.bugjob import BugJob, BugJobDerived |
143 | -from lp.bugs.scripts.bugheat import BugHeatCalculator |
144 | -from lp.services.job.model.job import Job |
145 | - |
146 | - |
147 | -class CalculateBugHeatJob(BugJobDerived): |
148 | - """A Job to calculate bug heat.""" |
149 | - implements(ICalculateBugHeatJob) |
150 | - |
151 | - class_job_type = BugJobType.UPDATE_HEAT |
152 | - classProvides(ICalculateBugHeatJobSource) |
153 | - |
154 | - def run(self): |
155 | - """See `IRunnableJob`.""" |
156 | - calculator = BugHeatCalculator(self.bug) |
157 | - calculated_heat = calculator.getBugHeat() |
158 | - self.bug.setHeat(calculated_heat) |
159 | - |
160 | - @classmethod |
161 | - def create(cls, bug): |
162 | - """See `ICalculateBugHeatJobSource`.""" |
163 | - # If there's already a job for the bug, don't create a new one. |
164 | - store = getUtility(IStoreSelector).get(MAIN_STORE, DEFAULT_FLAVOR) |
165 | - job_for_bug = store.find( |
166 | - BugJob, |
167 | - BugJob.bug == bug, |
168 | - BugJob.job_type == cls.class_job_type, |
169 | - BugJob.job == Job.id, |
170 | - Job.id.is_in(Job.ready_jobs) |
171 | - ).any() |
172 | - |
173 | - if job_for_bug is not None: |
174 | - return cls(job_for_bug) |
175 | - else: |
176 | - return super(CalculateBugHeatJob, cls).create(bug) |
177 | - |
178 | |
179 | === removed file 'lib/lp/bugs/scripts/bugheat.py' |
180 | --- lib/lp/bugs/scripts/bugheat.py 2010-04-29 11:31:49 +0000 |
181 | +++ lib/lp/bugs/scripts/bugheat.py 1970-01-01 00:00:00 +0000 |
182 | @@ -1,108 +0,0 @@ |
183 | -# Copyright 2010 Canonical Ltd. This software is licensed under the |
184 | -# GNU Affero General Public License version 3 (see the file LICENSE). |
185 | - |
186 | -"""The innards of the Bug Heat cronscript.""" |
187 | - |
188 | -__metaclass__ = type |
189 | -__all__ = [ |
190 | - 'BugHeatCalculator', |
191 | - 'BugHeatConstants', |
192 | - ] |
193 | - |
194 | -from datetime import datetime |
195 | - |
196 | -from lp.bugs.interfaces.bugtask import RESOLVED_BUGTASK_STATUSES |
197 | - |
198 | -class BugHeatConstants: |
199 | - |
200 | - PRIVACY = 150 |
201 | - SECURITY = 250 |
202 | - DUPLICATE = 6 |
203 | - AFFECTED_USER = 4 |
204 | - SUBSCRIBER = 2 |
205 | - |
206 | - |
207 | -class BugHeatCalculator: |
208 | - """A class to calculate the heat for a bug.""" |
209 | - # If you change the way that bug heat is calculated, remember to update |
210 | - # the description of how it is calculated at |
211 | - # /lib/lp/bugs/help/bug-heat.html and |
212 | - # https://help.launchpad.net/Bugs/BugHeat |
213 | - |
214 | - def __init__(self, bug): |
215 | - self.bug = bug |
216 | - |
217 | - def _getHeatFromPrivacy(self): |
218 | - """Return the heat generated by the bug's `private` attribute.""" |
219 | - if self.bug.private: |
220 | - return BugHeatConstants.PRIVACY |
221 | - else: |
222 | - return 0 |
223 | - |
224 | - def _getHeatFromSecurity(self): |
225 | - """Return the heat generated if the bug is security related.""" |
226 | - if self.bug.security_related: |
227 | - return BugHeatConstants.SECURITY |
228 | - else: |
229 | - return 0 |
230 | - |
231 | - def _getHeatFromDuplicates(self): |
232 | - """Return the heat generated by the bug's duplicates.""" |
233 | - return self.bug.duplicates.count() * BugHeatConstants.DUPLICATE |
234 | - |
235 | - def _getHeatFromAffectedUsers(self): |
236 | - """Return the heat generated by the bug's affected users.""" |
237 | - return ( |
238 | - self.bug.users_affected_count_with_dupes * |
239 | - BugHeatConstants.AFFECTED_USER) |
240 | - |
241 | - def _getHeatFromSubscribers(self): |
242 | - """Return the heat generated by the bug's subscribers.""" |
243 | - direct_subscribers = self.bug.getDirectSubscribers() |
244 | - subscribers_from_dupes = self.bug.getSubscribersFromDuplicates() |
245 | - |
246 | - subscriber_count = ( |
247 | - len(direct_subscribers) + len(subscribers_from_dupes)) |
248 | - return subscriber_count * BugHeatConstants.SUBSCRIBER |
249 | - |
250 | - def _bugIsComplete(self): |
251 | - """Are all the tasks for this bug resolved?""" |
252 | - return all([(task.status in RESOLVED_BUGTASK_STATUSES) |
253 | - for task in self.bug.bugtasks]) |
254 | - |
255 | - def getBugHeat(self): |
256 | - """Return the total heat for the current bug.""" |
257 | - if self._bugIsComplete(): |
258 | - return 0 |
259 | - |
260 | - total_heat = sum([ |
261 | - self._getHeatFromAffectedUsers(), |
262 | - self._getHeatFromDuplicates(), |
263 | - self._getHeatFromPrivacy(), |
264 | - self._getHeatFromSecurity(), |
265 | - self._getHeatFromSubscribers(), |
266 | - ]) |
267 | - |
268 | - # Bugs decay over time. Every day the bug isn't touched its heat |
269 | - # decreases by 1%. |
270 | - days = ( |
271 | - datetime.utcnow() - |
272 | - self.bug.date_last_updated.replace(tzinfo=None)).days |
273 | - total_heat = int(total_heat * (0.99 ** days)) |
274 | - |
275 | - if days > 0: |
276 | - # Bug heat increases by a quarter of the maximum bug heat divided |
277 | - # by the number of days since the bug's creation date. |
278 | - days_since_last_activity = ( |
279 | - datetime.utcnow() - |
280 | - max(self.bug.date_last_updated.replace(tzinfo=None), |
281 | - self.bug.date_last_message.replace(tzinfo=None))).days |
282 | - days_since_created = ( |
283 | - datetime.utcnow() - self.bug.datecreated.replace(tzinfo=None)).days |
284 | - max_heat = max( |
285 | - task.target.max_bug_heat for task in self.bug.bugtasks) |
286 | - if max_heat is not None and days_since_created > 0: |
287 | - total_heat = total_heat + (max_heat * 0.25 / days_since_created) |
288 | - |
289 | - return int(total_heat) |
290 | - |
291 | |
292 | === removed file 'lib/lp/bugs/scripts/tests/test_bugheat.py' |
293 | --- lib/lp/bugs/scripts/tests/test_bugheat.py 2010-04-29 11:31:49 +0000 |
294 | +++ lib/lp/bugs/scripts/tests/test_bugheat.py 1970-01-01 00:00:00 +0000 |
295 | @@ -1,256 +0,0 @@ |
296 | -# Copyright 2010 Canonical Ltd. This software is licensed under the |
297 | -# GNU Affero General Public License version 3 (see the file LICENSE). |
298 | - |
299 | -"""Module docstring goes here.""" |
300 | - |
301 | -__metaclass__ = type |
302 | - |
303 | -import unittest |
304 | - |
305 | -from datetime import datetime, timedelta |
306 | - |
307 | -from canonical.testing import LaunchpadZopelessLayer |
308 | - |
309 | -from lp.bugs.interfaces.bugtask import BugTaskStatus |
310 | -from lp.bugs.scripts.bugheat import BugHeatCalculator, BugHeatConstants |
311 | -from lp.testing import TestCaseWithFactory |
312 | - |
313 | -from zope.security.proxy import removeSecurityProxy |
314 | - |
315 | - |
316 | -class TestBugHeatCalculator(TestCaseWithFactory): |
317 | - """Tests for the BugHeatCalculator class.""" |
318 | - # If you change the way that bug heat is calculated, remember to update |
319 | - # the description of how it is calculated at |
320 | - # /lib/lp/bugs/help/bug-heat.html and |
321 | - # https://help.launchpad.net/Bugs/BugHeat |
322 | - |
323 | - layer = LaunchpadZopelessLayer |
324 | - |
325 | - def setUp(self): |
326 | - super(TestBugHeatCalculator, self).setUp() |
327 | - self.bug = self.factory.makeBug() |
328 | - self.calculator = BugHeatCalculator(self.bug) |
329 | - |
330 | - def test__getHeatFromDuplicates(self): |
331 | - # BugHeatCalculator._getHeatFromDuplicates() returns the bug |
332 | - # heat generated by duplicates of a bug. |
333 | - # By default, the bug has no heat from dupes |
334 | - self.assertEqual(0, self.calculator._getHeatFromDuplicates()) |
335 | - |
336 | - # If adding duplicates, the heat generated by them will be n * |
337 | - # BugHeatConstants.DUPLICATE, where n is the number of |
338 | - # duplicates. |
339 | - for i in range(5): |
340 | - dupe = self.factory.makeBug() |
341 | - dupe.duplicateof = self.bug |
342 | - |
343 | - expected_heat = BugHeatConstants.DUPLICATE * 5 |
344 | - actual_heat = self.calculator._getHeatFromDuplicates() |
345 | - self.assertEqual( |
346 | - expected_heat, actual_heat, |
347 | - "Heat from duplicates does not match expected heat. " |
348 | - "Expected %s, got %s" % (expected_heat, actual_heat)) |
349 | - |
350 | - def test__getHeatFromAffectedUsers(self): |
351 | - # BugHeatCalculator._getHeatFromAffectedUsers() returns the bug |
352 | - # heat generated by users affected by the bug and by duplicate bugs. |
353 | - # By default, the heat will be BugHeatConstants.AFFECTED_USER, since |
354 | - # there will be one affected user (the user who filed the bug). |
355 | - self.assertEqual( |
356 | - BugHeatConstants.AFFECTED_USER, |
357 | - self.calculator._getHeatFromAffectedUsers()) |
358 | - |
359 | - # As the number of affected users increases, the heat generated |
360 | - # will be n * BugHeatConstants.AFFECTED_USER, where n is the number |
361 | - # of affected users. |
362 | - for i in range(5): |
363 | - person = self.factory.makePerson() |
364 | - self.bug.markUserAffected(person) |
365 | - |
366 | - expected_heat = BugHeatConstants.AFFECTED_USER * 6 |
367 | - actual_heat = self.calculator._getHeatFromAffectedUsers() |
368 | - self.assertEqual( |
369 | - expected_heat, actual_heat, |
370 | - "Heat from affected users does not match expected heat. " |
371 | - "Expected %s, got %s" % (expected_heat, actual_heat)) |
372 | - |
373 | - # When our bug has duplicates, users affected by these duplicates |
374 | - # are included in _getHeatFromAffectedUsers() of the main bug. |
375 | - for i in range(3): |
376 | - dupe = self.factory.makeBug() |
377 | - dupe.duplicateof = self.bug |
378 | - # Each bug reporter is by default also marked as being affected |
379 | - # by the bug, so we have three additional affected users. |
380 | - expected_heat += BugHeatConstants.AFFECTED_USER * 3 |
381 | - |
382 | - person = self.factory.makePerson() |
383 | - dupe.markUserAffected(person) |
384 | - expected_heat += BugHeatConstants.AFFECTED_USER |
385 | - actual_heat = self.calculator._getHeatFromAffectedUsers() |
386 | - self.assertEqual( |
387 | - expected_heat, actual_heat, |
388 | - "Heat from users affected by duplicate bugs does not match " |
389 | - "expected heat. Expected %s, got %s" |
390 | - % (expected_heat, actual_heat)) |
391 | - |
392 | - def test__getHeatFromSubscribers(self): |
393 | - # BugHeatCalculator._getHeatFromSubscribers() returns the bug |
394 | - # heat generated by users subscribed tothe bug. |
395 | - # By default, the heat will be BugHeatConstants.SUBSCRIBER, |
396 | - # since there will be one direct subscriber (the user who filed |
397 | - # the bug). |
398 | - self.assertEqual( |
399 | - BugHeatConstants.SUBSCRIBER, |
400 | - self.calculator._getHeatFromSubscribers()) |
401 | - |
402 | - # As the number of subscribers increases, the heat generated |
403 | - # will be n * BugHeatConstants.SUBSCRIBER, where n is the number |
404 | - # of subscribers. |
405 | - for i in range(5): |
406 | - person = self.factory.makePerson() |
407 | - self.bug.subscribe(person, person) |
408 | - |
409 | - expected_heat = BugHeatConstants.SUBSCRIBER * 6 |
410 | - actual_heat = self.calculator._getHeatFromSubscribers() |
411 | - self.assertEqual( |
412 | - expected_heat, actual_heat, |
413 | - "Heat from subscribers does not match expected heat. " |
414 | - "Expected %s, got %s" % (expected_heat, actual_heat)) |
415 | - |
416 | - # Subscribers from duplicates are included in the heat returned |
417 | - # by _getHeatFromSubscribers() |
418 | - dupe = self.factory.makeBug() |
419 | - dupe.duplicateof = self.bug |
420 | - expected_heat = BugHeatConstants.SUBSCRIBER * 7 |
421 | - actual_heat = self.calculator._getHeatFromSubscribers() |
422 | - self.assertEqual( |
423 | - expected_heat, actual_heat, |
424 | - "Heat from subscribers (including duplicate-subscribers) " |
425 | - "does not match expected heat. Expected %s, got %s" % |
426 | - (expected_heat, actual_heat)) |
427 | - |
428 | - # Seting the bug to private will increase its heat from |
429 | - # subscribers by 1 * BugHeatConstants.SUBSCRIBER, as the project |
430 | - # owner will now be directly subscribed to it. |
431 | - self.bug.setPrivate(True, self.bug.owner) |
432 | - expected_heat = BugHeatConstants.SUBSCRIBER * 8 |
433 | - actual_heat = self.calculator._getHeatFromSubscribers() |
434 | - self.assertEqual( |
435 | - expected_heat, actual_heat, |
436 | - "Heat from subscribers to private bug does not match expected " |
437 | - "heat. Expected %s, got %s" % (expected_heat, actual_heat)) |
438 | - |
439 | - def test__getHeatFromPrivacy(self): |
440 | - # BugHeatCalculator._getHeatFromPrivacy() returns the heat |
441 | - # generated by the bug's private attribute. If the bug is |
442 | - # public, this will be 0. |
443 | - self.assertEqual(0, self.calculator._getHeatFromPrivacy()) |
444 | - |
445 | - # However, if the bug is private, _getHeatFromPrivacy() will |
446 | - # return BugHeatConstants.PRIVACY. |
447 | - self.bug.setPrivate(True, self.bug.owner) |
448 | - self.assertEqual( |
449 | - BugHeatConstants.PRIVACY, self.calculator._getHeatFromPrivacy()) |
450 | - |
451 | - def test__getHeatFromSecurity(self): |
452 | - # BugHeatCalculator._getHeatFromSecurity() returns the heat |
453 | - # generated by the bug's security_related attribute. If the bug |
454 | - # is not security related, _getHeatFromSecurity() will return 0. |
455 | - self.assertEqual(0, self.calculator._getHeatFromPrivacy()) |
456 | - |
457 | - |
458 | - # If, on the other hand, the bug is security_related, |
459 | - # _getHeatFromSecurity() will return BugHeatConstants.SECURITY |
460 | - self.bug.setSecurityRelated(True) |
461 | - self.assertEqual( |
462 | - BugHeatConstants.SECURITY, self.calculator._getHeatFromSecurity()) |
463 | - |
464 | - def test_getBugHeat(self): |
465 | - # BugHeatCalculator.getBugHeat() returns the total heat for a |
466 | - # given bug as the sum of the results of all _getHeatFrom*() |
467 | - # methods. |
468 | - # By default this will be (BugHeatConstants.AFFECTED_USER + |
469 | - # BugHeatConstants.SUBSCRIBER) since there will be one |
470 | - # subscriber and one affected user only. |
471 | - expected_heat = ( |
472 | - BugHeatConstants.AFFECTED_USER + BugHeatConstants.SUBSCRIBER) |
473 | - actual_heat = self.calculator.getBugHeat() |
474 | - self.assertEqual( |
475 | - expected_heat, actual_heat, |
476 | - "Expected bug heat did not match actual bug heat. " |
477 | - "Expected %s, got %s" % (expected_heat, actual_heat)) |
478 | - |
479 | - # Adding a duplicate and making the bug private and security |
480 | - # related will increase its heat. |
481 | - dupe = self.factory.makeBug() |
482 | - dupe.duplicateof = self.bug |
483 | - self.bug.setPrivate(True, self.bug.owner) |
484 | - self.bug.setSecurityRelated(True) |
485 | - |
486 | - expected_heat += ( |
487 | - BugHeatConstants.DUPLICATE + |
488 | - BugHeatConstants.PRIVACY + |
489 | - BugHeatConstants.SECURITY + |
490 | - BugHeatConstants.AFFECTED_USER |
491 | - ) |
492 | - |
493 | - # Adding the duplicate and making the bug private means it gets |
494 | - # two new subscribers, the project owner and the duplicate's |
495 | - # direct subscriber. |
496 | - expected_heat += BugHeatConstants.SUBSCRIBER * 2 |
497 | - actual_heat = self.calculator.getBugHeat() |
498 | - self.assertEqual( |
499 | - expected_heat, actual_heat, |
500 | - "Expected bug heat did not match actual bug heat. " |
501 | - "Expected %s, got %s" % (expected_heat, actual_heat)) |
502 | - |
503 | - def test_getBugHeat_complete_bugs(self): |
504 | - # Bug which are in a resolved status don't have heat at all. |
505 | - complete_bug = self.factory.makeBug() |
506 | - heat = BugHeatCalculator(complete_bug).getBugHeat() |
507 | - self.assertNotEqual( |
508 | - 0, heat, |
509 | - "Expected bug heat did not match actual bug heat. " |
510 | - "Expected a positive value, got 0") |
511 | - complete_bug.bugtasks[0].transitionToStatus( |
512 | - BugTaskStatus.INVALID, complete_bug.owner) |
513 | - heat = BugHeatCalculator(complete_bug).getBugHeat() |
514 | - self.assertEqual( |
515 | - 0, heat, |
516 | - "Expected bug heat did not match actual bug heat. " |
517 | - "Expected %s, got %s" % (0, heat)) |
518 | - |
519 | - def test_getBugHeat_decay(self): |
520 | - # Every day, a bug that wasn't touched has its heat reduced by 1%. |
521 | - aging_bug = self.factory.makeBug() |
522 | - fresh_heat = BugHeatCalculator(aging_bug).getBugHeat() |
523 | - aging_bug.date_last_updated = ( |
524 | - aging_bug.date_last_updated - timedelta(days=1)) |
525 | - expected = int(fresh_heat * 0.99) |
526 | - heat = BugHeatCalculator(aging_bug).getBugHeat() |
527 | - self.assertEqual( |
528 | - expected, heat, |
529 | - "Expected bug heat did not match actual bug heat. " |
530 | - "Expected %s, got %s" % (expected, heat)) |
531 | - |
532 | - def test_getBugHeat_activity(self): |
533 | - # Bug heat increases by a quarter of the maximum bug heat divided by |
534 | - # the number of days between the bug's creating and its last activity. |
535 | - active_bug = removeSecurityProxy(self.factory.makeBug()) |
536 | - fresh_heat = BugHeatCalculator(active_bug).getBugHeat() |
537 | - active_bug.date_last_updated = ( |
538 | - active_bug.date_last_updated - timedelta(days=10)) |
539 | - active_bug.datecreated = (active_bug.datecreated - timedelta(days=20)) |
540 | - active_bug.default_bugtask.target.setMaxBugHeat(100) |
541 | - expected = int((fresh_heat * (0.99 ** 20)) + (100 * 0.25 / 20)) |
542 | - heat = BugHeatCalculator(active_bug).getBugHeat() |
543 | - self.assertEqual( |
544 | - expected, heat, |
545 | - "Expected bug heat did not match actual bug heat. " |
546 | - "Expected %s, got %s" % (expected, heat)) |
547 | - |
548 | - |
549 | - |
550 | -def test_suite(): |
551 | - return unittest.TestLoader().loadTestsFromName(__name__) |
552 | |
553 | === modified file 'lib/lp/bugs/tests/test_bugheat.py' |
554 | --- lib/lp/bugs/tests/test_bugheat.py 2010-05-27 13:56:03 +0000 |
555 | +++ lib/lp/bugs/tests/test_bugheat.py 2010-06-03 10:55:38 +0000 |
556 | @@ -5,114 +5,13 @@ |
557 | |
558 | __metaclass__ = type |
559 | |
560 | -import pytz |
561 | -import transaction |
562 | import unittest |
563 | -from datetime import datetime |
564 | - |
565 | -from zope.component import getUtility |
566 | - |
567 | -from canonical.launchpad.scripts.tests import run_script |
568 | + |
569 | from canonical.testing import LaunchpadZopelessLayer |
570 | |
571 | -from lp.bugs.adapters.bugchange import BugDescriptionChange |
572 | -from lp.bugs.interfaces.bugjob import ICalculateBugHeatJobSource |
573 | -from lp.bugs.model.bugheat import CalculateBugHeatJob |
574 | -from lp.bugs.scripts.bugheat import BugHeatCalculator |
575 | -from lp.testing import TestCaseWithFactory |
576 | from lp.testing.factory import LaunchpadObjectFactory |
577 | |
578 | |
579 | -class CalculateBugHeatJobTestCase(TestCaseWithFactory): |
580 | - """Test case for CalculateBugHeatJob.""" |
581 | - |
582 | - layer = LaunchpadZopelessLayer |
583 | - |
584 | - def setUp(self): |
585 | - super(CalculateBugHeatJobTestCase, self).setUp() |
586 | - self.bug = self.factory.makeBug() |
587 | - |
588 | - # NB: This looks like it should go in the teardown, however |
589 | - # creating the bug causes a job to be added for it. We clear |
590 | - # this out so that our tests are consistent. |
591 | - self._completeJobsAndAssertQueueEmpty() |
592 | - |
593 | - def _completeJobsAndAssertQueueEmpty(self): |
594 | - """Make sure that all the CalculateBugHeatJobs are completed.""" |
595 | - for bug_job in getUtility(ICalculateBugHeatJobSource).iterReady(): |
596 | - bug_job.job.start() |
597 | - bug_job.job.complete() |
598 | - self.assertEqual(0, self._getJobCount()) |
599 | - |
600 | - def _getJobCount(self): |
601 | - """Return the number of CalculateBugHeatJobs in the queue.""" |
602 | - return len(self._getJobs()) |
603 | - |
604 | - def _getJobs(self): |
605 | - """Return the pending CalculateBugHeatJobs as a list.""" |
606 | - return list(CalculateBugHeatJob.iterReady()) |
607 | - |
608 | - def test_run(self): |
609 | - # CalculateBugHeatJob.run() sets calculates and sets the heat |
610 | - # for a bug. |
611 | - job = CalculateBugHeatJob.create(self.bug) |
612 | - bug_heat_calculator = BugHeatCalculator(self.bug) |
613 | - |
614 | - job.run() |
615 | - self.assertEqual( |
616 | - bug_heat_calculator.getBugHeat(), self.bug.heat) |
617 | - |
618 | - def test_utility(self): |
619 | - # CalculateBugHeatJobSource is a utility for acquiring |
620 | - # CalculateBugHeatJobs. |
621 | - utility = getUtility(ICalculateBugHeatJobSource) |
622 | - self.assertTrue( |
623 | - ICalculateBugHeatJobSource.providedBy(utility)) |
624 | - |
625 | - def test_create_only_creates_one(self): |
626 | - # If there's already a CalculateBugHeatJob for a bug, |
627 | - # CalculateBugHeatJob.create() won't create a new one. |
628 | - job = CalculateBugHeatJob.create(self.bug) |
629 | - |
630 | - # There will now be one job in the queue. |
631 | - self.assertEqual(1, self._getJobCount()) |
632 | - |
633 | - new_job = CalculateBugHeatJob.create(self.bug) |
634 | - |
635 | - # The two jobs will in fact be the same job. |
636 | - self.assertEqual(job, new_job) |
637 | - |
638 | - # And the queue will still have a length of 1. |
639 | - self.assertEqual(1, self._getJobCount()) |
640 | - |
641 | - def test_cronscript_succeeds(self): |
642 | - # The calculate-bug-heat cronscript will run all pending |
643 | - # CalculateBugHeatJobs. |
644 | - CalculateBugHeatJob.create(self.bug) |
645 | - transaction.commit() |
646 | - |
647 | - retcode, stdout, stderr = run_script( |
648 | - 'cronscripts/calculate-bug-heat.py', [], |
649 | - expect_returncode=0) |
650 | - self.assertEqual('', stdout) |
651 | - self.assertIn( |
652 | - 'INFO Ran 1 CalculateBugHeatJob jobs.\n', stderr) |
653 | - |
654 | - def test_getOopsVars(self): |
655 | - # BugJobDerived.getOopsVars() returns the variables to be used |
656 | - # when logging an OOPS for a bug job. We test this using |
657 | - # CalculateBugHeatJob because BugJobDerived doesn't let us |
658 | - # create() jobs. |
659 | - job = CalculateBugHeatJob.create(self.bug) |
660 | - vars = job.getOopsVars() |
661 | - |
662 | - # The Bug ID, BugJob ID and BugJob type will be returned by |
663 | - # getOopsVars(). |
664 | - self.assertIn(('bug_id', self.bug.id), vars) |
665 | - self.assertIn(('bug_job_id', job.context.id), vars) |
666 | - self.assertIn(('bug_job_type', job.context.job_type.title), vars) |
667 | - |
668 | - |
669 | class MaxHeatByTargetBase: |
670 | """Base class for testing a bug target's max_bug_heat attribute.""" |
671 |