Merge lp:~stevenk/launchpad/switch-bmp-to-previewdiff-merge_proposal into lp:launchpad
- switch-bmp-to-previewdiff-merge_proposal
- Merge into devel
Proposed by
Steve Kowalik
Status: | Merged | ||||||||
---|---|---|---|---|---|---|---|---|---|
Approved by: | William Grant | ||||||||
Approved revision: | no longer in the source branch. | ||||||||
Merged at revision: | 16622 | ||||||||
Proposed branch: | lp:~stevenk/launchpad/switch-bmp-to-previewdiff-merge_proposal | ||||||||
Merge into: | lp:launchpad | ||||||||
Prerequisite: | lp:~stevenk/launchpad/index-previewdiff-merge_proposal | ||||||||
Diff against target: |
660 lines (+135/-140) 14 files modified
lib/lp/code/browser/tests/test_branchmergeproposal.py (+4/-6) lib/lp/code/browser/tests/test_branchmergeproposallisting.py (+1/-1) lib/lp/code/doc/branch-merge-proposal-notifications.txt (+9/-12) lib/lp/code/interfaces/branchmergeproposal.py (+2/-0) lib/lp/code/mail/tests/test_branchmergeproposal.py (+6/-9) lib/lp/code/mail/tests/test_codehandler.py (+2/-7) lib/lp/code/model/branchmergeproposal.py (+35/-29) lib/lp/code/model/branchmergeproposaljob.py (+3/-5) lib/lp/code/model/diff.py (+9/-35) lib/lp/code/model/tests/test_branchcollection.py (+45/-12) lib/lp/code/model/tests/test_branchmergeproposal.py (+1/-1) lib/lp/code/stories/branches/xx-branchmergeproposal-listings.txt (+2/-5) lib/lp/code/stories/branches/xx-branchmergeproposals.txt (+8/-10) lib/lp/testing/factory.py (+8/-8) |
||||||||
To merge this branch: | bzr merge lp:~stevenk/launchpad/switch-bmp-to-previewdiff-merge_proposal | ||||||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
William Grant | code | Approve | |
Review via email: mp+162916@code.launchpad.net |
Commit message
Switch everything over to using PreviewDiff.
Description of the change
Switch everything over to using PreviewDiff.
To post a comment you must log in.
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'lib/lp/code/browser/tests/test_branchmergeproposal.py' |
2 | --- lib/lp/code/browser/tests/test_branchmergeproposal.py 2013-05-13 06:57:37 +0000 |
3 | +++ lib/lp/code/browser/tests/test_branchmergeproposal.py 2013-05-13 06:57:38 +0000 |
4 | @@ -871,25 +871,23 @@ |
5 | # librarian content. |
6 | text = ''.join(chr(x) for x in range(255)) |
7 | diff_bytes = ''.join(unified_diff('', text)) |
8 | - self.setPreviewDiff(diff_bytes) |
9 | + preview_diff = self.setPreviewDiff(diff_bytes) |
10 | transaction.commit() |
11 | |
12 | def fake_open(*args): |
13 | raise LibrarianServerError |
14 | |
15 | - lfa = removeSecurityProxy(self.bmp.preview_diff).diff.diff_text |
16 | + lfa = preview_diff.diff.diff_text |
17 | with monkey_patch(lfa, open=fake_open): |
18 | - view = create_initialized_view(self.bmp.preview_diff, '+diff') |
19 | + view = create_initialized_view(preview_diff, '+diff') |
20 | self.assertEqual('', view.preview_diff_text) |
21 | self.assertFalse(view.diff_available) |
22 | markup = view() |
23 | self.assertIn('The diff is not available at this time.', markup) |
24 | |
25 | def setPreviewDiff(self, preview_diff_bytes): |
26 | - preview_diff = PreviewDiff.create( |
27 | + return PreviewDiff.create( |
28 | self.bmp, preview_diff_bytes, u'a', u'b', None, u'') |
29 | - removeSecurityProxy(self.bmp).preview_diff = preview_diff |
30 | - return preview_diff |
31 | |
32 | def test_linked_bugs_excludes_mutual_bugs(self): |
33 | """List bugs that are linked to the source only.""" |
34 | |
35 | === modified file 'lib/lp/code/browser/tests/test_branchmergeproposallisting.py' |
36 | --- lib/lp/code/browser/tests/test_branchmergeproposallisting.py 2013-03-15 01:27:19 +0000 |
37 | +++ lib/lp/code/browser/tests/test_branchmergeproposallisting.py 2013-05-13 06:57:38 +0000 |
38 | @@ -207,7 +207,7 @@ |
39 | with StormStatementRecorder() as recorder: |
40 | self.getViewBrowser( |
41 | product, '+merges', rootsite='code', user=product.owner) |
42 | - self.assertThat(recorder, HasQueryCount(Equals(40))) |
43 | + self.assertThat(recorder, HasQueryCount(Equals(41))) |
44 | |
45 | def test_productseries(self): |
46 | target = self.factory.makeBranch() |
47 | |
48 | === modified file 'lib/lp/code/doc/branch-merge-proposal-notifications.txt' |
49 | --- lib/lp/code/doc/branch-merge-proposal-notifications.txt 2013-05-13 06:57:37 +0000 |
50 | +++ lib/lp/code/doc/branch-merge-proposal-notifications.txt 2013-05-13 06:57:38 +0000 |
51 | @@ -19,20 +19,16 @@ |
52 | >>> from lp.code.interfaces.branchmergeproposal import ( |
53 | ... IBranchMergeProposalJobSource) |
54 | >>> from lp.code.model.diff import PreviewDiff |
55 | - >>> from lp.code.tests.helpers import ( |
56 | - ... make_merge_proposal_without_reviewers) |
57 | + >>> from lp.code.tests.helpers import make_merge_proposal_without_reviewers |
58 | >>> from lp.testing.mail_helpers import pop_notifications |
59 | >>> import transaction |
60 | >>> login('test@canonical.com') |
61 | - >>> diff_bytes = ''.join(unified_diff('', 'c')) |
62 | - >>> preview_diff = PreviewDiff.create( |
63 | - ... None, diff_bytes, u'a', u'b', None, None) |
64 | - >>> # Make Librarian happy. |
65 | - >>> transaction.commit() |
66 | >>> target_owner = factory.makePerson(email='target_owner@example.com') |
67 | >>> target_branch = factory.makeBranch(owner=target_owner) |
68 | >>> bmp = make_merge_proposal_without_reviewers( |
69 | ... factory, target_branch=target_branch) |
70 | + >>> previewdiff = factory.makePreviewDiff(merge_proposal=bmp) |
71 | + >>> transaction.commit() |
72 | >>> source_subscriber = factory.makePerson( |
73 | ... email='source@example.com', displayname='Source Subscriber') |
74 | >>> _unused = bmp.source_branch.subscribe(source_subscriber, |
75 | @@ -111,7 +107,8 @@ |
76 | >>> # To avoid needing to access branches, pre-populate diffs. |
77 | >>> bmp = source_branch.addLandingTarget( |
78 | ... registrant, target_branch, needs_review=True) |
79 | - >>> removeSecurityProxy(bmp).preview_diff = preview_diff |
80 | + >>> previewdiff = factory.makePreviewDiff(merge_proposal=bmp) |
81 | + >>> transaction.commit() |
82 | >>> # Fake the update preview diff as done. |
83 | >>> bmp.next_preview_diff_job.start() |
84 | >>> bmp.next_preview_diff_job.complete() |
85 | @@ -165,10 +162,10 @@ |
86 | ... """) |
87 | >>> bmp.deleteProposal() |
88 | >>> bmp = source_branch.addLandingTarget( |
89 | - ... registrant, target_branch, |
90 | - ... description=initial_comment, review_requests=reviewers, |
91 | - ... needs_review=True) |
92 | - >>> removeSecurityProxy(bmp).preview_diff = preview_diff |
93 | + ... registrant, target_branch, description=initial_comment, |
94 | + ... review_requests=reviewers, needs_review=True) |
95 | + >>> previewdiff = factory.makePreviewDiff(merge_proposal=bmp) |
96 | + >>> transaction.commit() |
97 | >>> # Fake the update preview diff as done. |
98 | >>> bmp.next_preview_diff_job.start() |
99 | >>> bmp.next_preview_diff_job.complete() |
100 | |
101 | === modified file 'lib/lp/code/interfaces/branchmergeproposal.py' |
102 | --- lib/lp/code/interfaces/branchmergeproposal.py 2013-02-28 01:02:50 +0000 |
103 | +++ lib/lp/code/interfaces/branchmergeproposal.py 2013-05-13 06:57:38 +0000 |
104 | @@ -174,6 +174,8 @@ |
105 | next_preview_diff_job = Attribute( |
106 | 'The next BranchMergeProposalJob that will update a preview diff.') |
107 | |
108 | + preview_diffs = Attribute('All preview diffs for this merge proposal.') |
109 | + |
110 | preview_diff = exported( |
111 | Reference( |
112 | IPreviewDiff, |
113 | |
114 | === modified file 'lib/lp/code/mail/tests/test_branchmergeproposal.py' |
115 | --- lib/lp/code/mail/tests/test_branchmergeproposal.py 2013-05-13 06:57:37 +0000 |
116 | +++ lib/lp/code/mail/tests/test_branchmergeproposal.py 2013-05-13 06:57:38 +0000 |
117 | @@ -1,4 +1,4 @@ |
118 | -# Copyright 2009-2012 Canonical Ltd. This software is licensed under the |
119 | +# Copyright 2009-2013 Canonical Ltd. This software is licensed under the |
120 | # GNU Affero General Public License version 3 (see the file LICENSE). |
121 | |
122 | """Tests for BranchMergeProposal mailings""" |
123 | @@ -67,15 +67,12 @@ |
124 | else: |
125 | initial_status = BranchMergeProposalStatus.WORK_IN_PROGRESS |
126 | bmp = self.factory.makeBranchMergeProposal( |
127 | - registrant=registrant, product=product, |
128 | - set_state=initial_status, |
129 | + registrant=registrant, product=product, set_state=initial_status, |
130 | prerequisite_branch=prerequisite_branch, |
131 | - initial_comment=initial_comment, |
132 | - reviewer=reviewer) |
133 | - if diff_text is not None: |
134 | - removeSecurityProxy(bmp).preview_diff = PreviewDiff.create( |
135 | - bmp, diff_text, |
136 | - unicode(self.factory.getUniqueString('revid')), |
137 | + initial_comment=initial_comment, reviewer=reviewer) |
138 | + if diff_text: |
139 | + PreviewDiff.create( |
140 | + bmp, diff_text, unicode(self.factory.getUniqueString('revid')), |
141 | unicode(self.factory.getUniqueString('revid')), None, None) |
142 | transaction.commit() |
143 | subscriber = self.factory.makePerson(displayname='Baz Quxx', |
144 | |
145 | === modified file 'lib/lp/code/mail/tests/test_codehandler.py' |
146 | --- lib/lp/code/mail/tests/test_codehandler.py 2013-05-13 06:57:37 +0000 |
147 | +++ lib/lp/code/mail/tests/test_codehandler.py 2013-05-13 06:57:38 +0000 |
148 | @@ -381,15 +381,10 @@ |
149 | |
150 | def test_reviewer_with_diff(self): |
151 | """Requesting a review with a diff works.""" |
152 | - diff_text = ''.join(unified_diff('', 'Fake diff')) |
153 | - preview_diff = PreviewDiff.create( |
154 | - None, diff_text, |
155 | - unicode(self.factory.getUniqueString('revid')), |
156 | - unicode(self.factory.getUniqueString('revid')), None, None) |
157 | + bmp = make_merge_proposal_without_reviewers(self.factory) |
158 | + preview_diff = self.factory.makePreviewDiff(merge_proposal=bmp) |
159 | # To record the diff in the librarian. |
160 | transaction.commit() |
161 | - bmp = make_merge_proposal_without_reviewers( |
162 | - self.factory, preview_diff=preview_diff) |
163 | eric = self.factory.makePerson(name="eric", email="eric@example.com") |
164 | mail = self.factory.makeSignedMessage(body=' reviewer eric') |
165 | email_addr = bmp.address |
166 | |
167 | === modified file 'lib/lp/code/model/branchmergeproposal.py' |
168 | --- lib/lp/code/model/branchmergeproposal.py 2013-05-13 06:57:37 +0000 |
169 | +++ lib/lp/code/model/branchmergeproposal.py 2013-05-13 06:57:38 +0000 |
170 | @@ -27,10 +27,7 @@ |
171 | Select, |
172 | SQL, |
173 | ) |
174 | -from storm.locals import ( |
175 | - Int, |
176 | - Reference, |
177 | - ) |
178 | +from storm.locals import Reference |
179 | from storm.store import Store |
180 | from zope.component import getUtility |
181 | from zope.event import notify |
182 | @@ -104,6 +101,10 @@ |
183 | from lp.services.job.interfaces.job import JobStatus |
184 | from lp.services.job.model.job import Job |
185 | from lp.services.mail.sendmail import validate_message |
186 | +from lp.services.propertycache import ( |
187 | + cachedproperty, |
188 | + get_property_cache, |
189 | + ) |
190 | |
191 | |
192 | def is_valid_transition(proposal, from_state, next_state, user=None): |
193 | @@ -226,9 +227,6 @@ |
194 | else: |
195 | return None |
196 | |
197 | - preview_diff_id = Int(name='merge_diff') |
198 | - preview_diff = Reference(preview_diff_id, 'PreviewDiff.id') |
199 | - |
200 | reviewed_revision_id = StringCol(default=None) |
201 | |
202 | commit_message = StringCol(default=None) |
203 | @@ -309,6 +307,21 @@ |
204 | raise WrongBranchMergeProposal |
205 | return vote |
206 | |
207 | + @property |
208 | + def _preview_diffs(self): |
209 | + return Store.of(self).find( |
210 | + PreviewDiff, |
211 | + PreviewDiff.branch_merge_proposal_id == self.id).order_by( |
212 | + PreviewDiff.date_created) |
213 | + |
214 | + @cachedproperty |
215 | + def preview_diffs(self): |
216 | + return list(self._preview_diffs) |
217 | + |
218 | + @cachedproperty |
219 | + def preview_diff(self): |
220 | + return self._preview_diffs.first() |
221 | + |
222 | date_queued = UtcDateTimeCol(notNull=False, default=None) |
223 | |
224 | votes = SQLMultipleJoin( |
225 | @@ -706,11 +719,11 @@ |
226 | comment.destroySelf() |
227 | # Delete all jobs referring to the BranchMergeProposal, whether |
228 | # or not they have completed. |
229 | - from lp.code.model.branchmergeproposaljob import ( |
230 | - BranchMergeProposalJob) |
231 | + from lp.code.model.branchmergeproposaljob import BranchMergeProposalJob |
232 | for job in BranchMergeProposalJob.selectBy( |
233 | branch_merge_proposal=self.id): |
234 | job.destroySelf() |
235 | + self.preview_diffs.remove() |
236 | self.destroySelf() |
237 | |
238 | def getUnlandedSourceBranchRevisions(self): |
239 | @@ -860,18 +873,10 @@ |
240 | target_revision_id, prerequisite_revision_id=None, |
241 | conflicts=None): |
242 | """See `IBranchMergeProposal`.""" |
243 | - # Create the PreviewDiff. |
244 | - self.preview_diff = PreviewDiff.create( |
245 | + return PreviewDiff.create( |
246 | self, diff_content, source_revision_id, target_revision_id, |
247 | prerequisite_revision_id, conflicts) |
248 | |
249 | - # XXX: TimPenhey 2009-02-19 bug 324724 |
250 | - # Since the branch_merge_proposal attribute of the preview_diff |
251 | - # is a on_remote reference, it may not be found unless we flush |
252 | - # the storm store. |
253 | - Store.of(self).flush() |
254 | - return self.preview_diff |
255 | - |
256 | def getIncrementalDiffRanges(self): |
257 | groups = self.getRevisionsSinceReviewStart() |
258 | return [ |
259 | @@ -965,14 +970,14 @@ |
260 | from lp.registry.model.product import Product |
261 | from lp.registry.model.distroseries import DistroSeries |
262 | |
263 | + ids = set() |
264 | source_branch_ids = set() |
265 | person_ids = set() |
266 | - diff_ids = set() |
267 | for mp in branch_merge_proposals: |
268 | + ids.add(mp.id) |
269 | source_branch_ids.add(mp.source_branchID) |
270 | person_ids.add(mp.registrantID) |
271 | person_ids.add(mp.merge_reporterID) |
272 | - diff_ids.add(mp.preview_diff_id) |
273 | |
274 | branches = load_related( |
275 | Branch, branch_merge_proposals, ( |
276 | @@ -985,16 +990,17 @@ |
277 | if len(branches) == 0: |
278 | return |
279 | |
280 | - store = IStore(BranchMergeProposal) |
281 | - |
282 | # Pre-load PreviewDiffs and Diffs. |
283 | - preview_diffs_and_diffs = list(store.find( |
284 | - (PreviewDiff, Diff), |
285 | - PreviewDiff.id.is_in(diff_ids), |
286 | - Diff.id == PreviewDiff.diff_id)) |
287 | - PreviewDiff.preloadData( |
288 | - [preview_diff_and_diff[0] for preview_diff_and_diff |
289 | - in preview_diffs_and_diffs]) |
290 | + preview_diffs = IStore(BranchMergeProposal).find( |
291 | + PreviewDiff, |
292 | + PreviewDiff.branch_merge_proposal_id.is_in(ids)).order_by( |
293 | + PreviewDiff.branch_merge_proposal_id, |
294 | + Desc(PreviewDiff.date_created)).config( |
295 | + distinct=[PreviewDiff.branch_merge_proposal_id]) |
296 | + load_related(Diff, preview_diffs, ['diff_id']) |
297 | + for previewdiff in preview_diffs: |
298 | + cache = get_property_cache(previewdiff.branch_merge_proposal) |
299 | + cache.preview_diff = previewdiff |
300 | |
301 | # Add source branch owners' to the list of pre-loaded persons. |
302 | person_ids.update( |
303 | |
304 | === modified file 'lib/lp/code/model/branchmergeproposaljob.py' |
305 | --- lib/lp/code/model/branchmergeproposaljob.py 2012-11-30 05:10:47 +0000 |
306 | +++ lib/lp/code/model/branchmergeproposaljob.py 2013-05-13 06:57:38 +0000 |
307 | @@ -1,4 +1,4 @@ |
308 | -# Copyright 2009-2011 Canonical Ltd. This software is licensed under the |
309 | +# Copyright 2009-2013 Canonical Ltd. This software is licensed under the |
310 | # GNU Affero General Public License version 3 (see the file LICENSE). |
311 | |
312 | """Job classes related to BranchMergeProposals are in here. |
313 | @@ -371,10 +371,8 @@ |
314 | """See `IRunnableJob`.""" |
315 | self.checkReady() |
316 | with server(get_ro_server(), no_replace=True): |
317 | - preview = PreviewDiff.fromBranchMergeProposal( |
318 | - self.branch_merge_proposal) |
319 | - with BranchMergeProposalDelta.monitor(self.branch_merge_proposal): |
320 | - self.branch_merge_proposal.preview_diff = preview |
321 | + with BranchMergeProposalDelta.monitor(self.branch_merge_proposal): |
322 | + PreviewDiff.fromBranchMergeProposal(self.branch_merge_proposal) |
323 | |
324 | def getOperationDescription(self): |
325 | return ('generating the diff for a merge proposal') |
326 | |
327 | === modified file 'lib/lp/code/model/diff.py' |
328 | --- lib/lp/code/model/diff.py 2013-05-13 06:57:37 +0000 |
329 | +++ lib/lp/code/model/diff.py 2013-05-13 06:57:38 +0000 |
330 | @@ -1,4 +1,4 @@ |
331 | -# Copyright 2009-2011 Canonical Ltd. This software is licensed under the |
332 | +# Copyright 2009-2013 Canonical Ltd. This software is licensed under the |
333 | # GNU Affero General Public License version 3 (see the file LICENSE). |
334 | |
335 | """Implementation classes for IDiff, etc.""" |
336 | @@ -48,7 +48,6 @@ |
337 | ) |
338 | from lp.codehosting.bzrutils import read_locked |
339 | from lp.services.config import config |
340 | -from lp.services.database.bulk import load_referencing |
341 | from lp.services.database.constants import UTC_NOW |
342 | from lp.services.database.datetimecol import UtcDateTimeCol |
343 | from lp.services.database.sqlbase import SQLBase |
344 | @@ -56,10 +55,7 @@ |
345 | from lp.services.librarian.interfaces.client import ( |
346 | LIBRARIAN_SERVER_DEFAULT_TIMEOUT, |
347 | ) |
348 | -from lp.services.propertycache import ( |
349 | - cachedproperty, |
350 | - get_property_cache, |
351 | - ) |
352 | +from lp.services.propertycache import get_property_cache |
353 | from lp.services.webapp.adapter import get_request_remaining_seconds |
354 | |
355 | |
356 | @@ -364,11 +360,13 @@ |
357 | |
358 | prerequisite_revision_id = Unicode(name='dependent_revision_id') |
359 | |
360 | - branch_merge_proposal_id = Int(name='branch_merge_proposal') |
361 | - _new_branch_merge_proposal = Reference( |
362 | + branch_merge_proposal_id = Int( |
363 | + name='branch_merge_proposal', allow_none=False) |
364 | + branch_merge_proposal = Reference( |
365 | branch_merge_proposal_id, 'BranchMergeProposal.id') |
366 | |
367 | - date_created = UtcDateTimeCol(dbName='date_created', default=UTC_NOW) |
368 | + date_created = UtcDateTimeCol( |
369 | + dbName='date_created', default=UTC_NOW, notNull=True) |
370 | |
371 | conflicts = Unicode() |
372 | |
373 | @@ -376,29 +374,6 @@ |
374 | def has_conflicts(self): |
375 | return self.conflicts is not None and self.conflicts != '' |
376 | |
377 | - @staticmethod |
378 | - def preloadData(preview_diffs): |
379 | - # Circular imports. |
380 | - from lp.code.model.branchmergeproposal import BranchMergeProposal |
381 | - bmps = load_referencing( |
382 | - BranchMergeProposal, preview_diffs, ['preview_diff_id']) |
383 | - bmps_preview = dict((bmp.preview_diff_id, bmp) for bmp in bmps) |
384 | - for preview_diff in preview_diffs: |
385 | - cache = get_property_cache(preview_diff) |
386 | - cache.branch_merge_proposal = bmps_preview[preview_diff.id] |
387 | - |
388 | - _branch_merge_proposal = Reference( |
389 | - "PreviewDiff.id", "BranchMergeProposal.preview_diff_id", |
390 | - on_remote=True) |
391 | - |
392 | - @cachedproperty |
393 | - def branch_merge_proposal(self): |
394 | - # This property can die when self._new_branch_merge_proposal is |
395 | - # populated. |
396 | - if self._new_branch_merge_proposal: |
397 | - return self._new_branch_merge_proposal |
398 | - return self._branch_merge_proposal |
399 | - |
400 | @classmethod |
401 | def fromBranchMergeProposal(cls, bmp): |
402 | """Create a `PreviewDiff` from a `BranchMergeProposal`. |
403 | @@ -416,9 +391,7 @@ |
404 | else: |
405 | prerequisite_branch = None |
406 | diff, conflicts = Diff.mergePreviewFromBranches( |
407 | - source_branch, source_revision, target_branch, |
408 | - prerequisite_branch) |
409 | - |
410 | + source_branch, source_revision, target_branch, prerequisite_branch) |
411 | preview = cls() |
412 | preview.source_revision_id = source_revision.decode('utf-8') |
413 | preview.target_revision_id = target_revision.decode('utf-8') |
414 | @@ -426,6 +399,7 @@ |
415 | preview.diff = diff |
416 | preview.conflicts = u''.join( |
417 | unicode(conflict) + '\n' for conflict in conflicts) |
418 | + del get_property_cache(bmp).preview_diffs |
419 | return preview |
420 | |
421 | @classmethod |
422 | |
423 | === modified file 'lib/lp/code/model/tests/test_branchcollection.py' |
424 | --- lib/lp/code/model/tests/test_branchcollection.py 2013-02-14 03:53:10 +0000 |
425 | +++ lib/lp/code/model/tests/test_branchcollection.py 2013-05-13 06:57:38 +0000 |
426 | @@ -5,10 +5,16 @@ |
427 | |
428 | __metaclass__ = type |
429 | |
430 | -from datetime import datetime |
431 | +from datetime import ( |
432 | + datetime, |
433 | + timedelta, |
434 | + ) |
435 | |
436 | import pytz |
437 | -from storm.store import EmptyResultSet |
438 | +from storm.store import ( |
439 | + EmptyResultSet, |
440 | + Store, |
441 | + ) |
442 | from testtools.matchers import Equals |
443 | from zope.component import getUtility |
444 | from zope.security.proxy import removeSecurityProxy |
445 | @@ -36,11 +42,7 @@ |
446 | from lp.registry.enums import PersonVisibility |
447 | from lp.registry.interfaces.person import TeamMembershipPolicy |
448 | from lp.registry.interfaces.pocket import PackagePublishingPocket |
449 | -from lp.services.database.interfaces import ( |
450 | - DEFAULT_FLAVOR, |
451 | - IStoreSelector, |
452 | - MAIN_STORE, |
453 | - ) |
454 | +from lp.services.database.lpstorm import IStore |
455 | from lp.services.webapp.publisher import canonical_url |
456 | from lp.testing import ( |
457 | person_logged_in, |
458 | @@ -48,7 +50,10 @@ |
459 | StormStatementRecorder, |
460 | TestCaseWithFactory, |
461 | ) |
462 | -from lp.testing.layers import DatabaseFunctionalLayer |
463 | +from lp.testing.layers import ( |
464 | + DatabaseFunctionalLayer, |
465 | + LaunchpadFunctionalLayer, |
466 | + ) |
467 | from lp.testing.matchers import HasQueryCount |
468 | |
469 | |
470 | @@ -94,10 +99,9 @@ |
471 | layer = DatabaseFunctionalLayer |
472 | |
473 | def setUp(self): |
474 | - TestCaseWithFactory.setUp(self) |
475 | + super(TestGenericBranchCollection, self).setUp() |
476 | remove_all_sample_data_branches() |
477 | - self.store = getUtility(IStoreSelector).get( |
478 | - MAIN_STORE, DEFAULT_FLAVOR) |
479 | + self.store = IStore(Branch) |
480 | |
481 | def test_provides_branchcollection(self): |
482 | # `GenericBranchCollection` provides the `IBranchCollection` |
483 | @@ -879,7 +883,7 @@ |
484 | |
485 | class TestBranchMergeProposals(TestCaseWithFactory): |
486 | |
487 | - layer = DatabaseFunctionalLayer |
488 | + layer = LaunchpadFunctionalLayer |
489 | |
490 | def setUp(self): |
491 | TestCaseWithFactory.setUp(self) |
492 | @@ -940,6 +944,35 @@ |
493 | proposals = collection.getMergeProposals() |
494 | self.assertEqual(sorted([mp1, mp2]), sorted(proposals)) |
495 | |
496 | + def test_preloading_for_previewdiff(self): |
497 | + product = self.factory.makeProduct() |
498 | + target = self.factory.makeBranch(product=product) |
499 | + owner = self.factory.makePerson() |
500 | + branch1 = self.factory.makeBranch(product=product, owner=owner) |
501 | + branch2 = self.factory.makeBranch(product=product, owner=owner) |
502 | + bmp1 = self.factory.makeBranchMergeProposal( |
503 | + target_branch=target, source_branch=branch1) |
504 | + bmp2 = self.factory.makeBranchMergeProposal( |
505 | + target_branch=target, source_branch=branch2) |
506 | + old_date = datetime.now(pytz.UTC) - timedelta(hours=1) |
507 | + self.factory.makePreviewDiff( |
508 | + merge_proposal=bmp1, date_created=old_date) |
509 | + previewdiff1 = self.factory.makePreviewDiff(merge_proposal=bmp1) |
510 | + self.factory.makePreviewDiff( |
511 | + merge_proposal=bmp2, date_created=old_date) |
512 | + previewdiff2 = self.factory.makePreviewDiff(merge_proposal=bmp2) |
513 | + Store.of(bmp1).flush() |
514 | + Store.of(bmp1).invalidate() |
515 | + collection = self.all_branches.ownedBy(owner) |
516 | + [pre_bmp1, pre_bmp2] = sorted( |
517 | + collection.getMergeProposals(eager_load=True)) |
518 | + with StormStatementRecorder() as recorder: |
519 | + self.assertEqual( |
520 | + removeSecurityProxy(pre_bmp1.preview_diff).id, previewdiff1.id) |
521 | + self.assertEqual( |
522 | + removeSecurityProxy(pre_bmp2.preview_diff).id, previewdiff2.id) |
523 | + self.assertThat(recorder, HasQueryCount(Equals(0))) |
524 | + |
525 | def test_merge_proposals_in_product(self): |
526 | mp1 = self.factory.makeBranchMergeProposal() |
527 | self.factory.makeBranchMergeProposal() |
528 | |
529 | === modified file 'lib/lp/code/model/tests/test_branchmergeproposal.py' |
530 | --- lib/lp/code/model/tests/test_branchmergeproposal.py 2013-05-13 06:57:37 +0000 |
531 | +++ lib/lp/code/model/tests/test_branchmergeproposal.py 2013-05-13 06:57:38 +0000 |
532 | @@ -2085,6 +2085,6 @@ |
533 | # A previewdiff with an empty diffstat doesn't crash when fetched. |
534 | previewdiff = self.factory.makePreviewDiff() |
535 | previewdiff.diff.diffstat = None |
536 | - user = previewdiff._new_branch_merge_proposal.target_branch.owner |
537 | + user = previewdiff.branch_merge_proposal.target_branch.owner |
538 | ws_previewdiff = self.wsObject(previewdiff, user=user) |
539 | self.assertIsNone(ws_previewdiff.diffstat) |
540 | |
541 | === modified file 'lib/lp/code/stories/branches/xx-branchmergeproposal-listings.txt' |
542 | --- lib/lp/code/stories/branches/xx-branchmergeproposal-listings.txt 2013-05-13 06:57:37 +0000 |
543 | +++ lib/lp/code/stories/branches/xx-branchmergeproposal-listings.txt 2013-05-13 06:57:38 +0000 |
544 | @@ -173,14 +173,11 @@ |
545 | |
546 | After a preview diff is added, it has a line count. |
547 | |
548 | - >>> from lp.code.model.diff import PreviewDiff |
549 | >>> login('foo.bar@canonical.com') |
550 | - >>> removeSecurityProxy(proposal).preview_diff = PreviewDiff.create( |
551 | - ... proposal, 'a\n\b\n', u'source-id', u'target-id', u'prereq-id', |
552 | - ... u'conflicts') |
553 | + >>> diff = factory.makePreviewDiff(merge_proposal=proposal) |
554 | >>> logout() |
555 | >>> browser.open(url) |
556 | >>> print_tag_with_id(browser.contents, 'proposals') |
557 | Other reviews I am not actively reviewing |
558 | Branch Merge Proposal Requested By Lines Activity |
559 | - lp://dev/... ... 2 None |
560 | + lp://dev/... ... 14 None |
561 | |
562 | === modified file 'lib/lp/code/stories/branches/xx-branchmergeproposals.txt' |
563 | --- lib/lp/code/stories/branches/xx-branchmergeproposals.txt 2013-05-13 06:57:37 +0000 |
564 | +++ lib/lp/code/stories/branches/xx-branchmergeproposals.txt 2013-05-13 06:57:38 +0000 |
565 | @@ -609,11 +609,10 @@ |
566 | >>> from lp.code.model.diff import PreviewDiff |
567 | >>> diff_text = ''.join( |
568 | ... unified_diff('', ['Fake Diff' + u'\u1010'.encode('utf-8')])) |
569 | + >>> bmp = factory.makeBranchMergeProposal() |
570 | >>> preview_diff = PreviewDiff.create( |
571 | - ... None, diff_text, u'a', u'b', None, u'') |
572 | + ... bmp, diff_text, u'a', u'b', None, u'') |
573 | >>> transaction.commit() |
574 | - >>> bmp = factory.makeBranchMergeProposal() |
575 | - >>> removeSecurityProxy(bmp).preview_diff = preview_diff |
576 | >>> url = canonical_url(bmp) |
577 | >>> logout() |
578 | >>> def get_review_diff(): |
579 | @@ -642,17 +641,17 @@ |
580 | |
581 | If no diff is present, nothing is shown. |
582 | |
583 | - >>> login('admin@canonical.com') |
584 | - >>> removeSecurityProxy(bmp).preview_diff = None |
585 | - >>> logout() |
586 | + >>> from storm.store import Store |
587 | + >>> Store.of(preview_diff).remove(preview_diff) |
588 | + >>> from lp.services.propertycache import get_property_cache |
589 | + >>> del get_property_cache(bmp).preview_diffs |
590 | >>> print get_review_diff() |
591 | None |
592 | |
593 | If the review diff is empty, then we say it is empty. |
594 | |
595 | >>> login('admin@canonical.com') |
596 | - >>> removeSecurityProxy(bmp).preview_diff = PreviewDiff.create( |
597 | - ... bmp, '', u'c', u'd', None, u'') |
598 | + >>> preview_diff = PreviewDiff.create(bmp, '', u'c', u'd', None, u'') |
599 | >>> logout() |
600 | >>> print extract_text(get_review_diff()) |
601 | Preview Diff |
602 | @@ -662,8 +661,7 @@ |
603 | Preview diff generation status |
604 | ------------------------------ |
605 | |
606 | - >>> update = find_tag_by_id( |
607 | - ... nopriv_browser.contents, 'diff-pending-update') |
608 | + >>> update = find_tag_by_id(nopriv_browser.contents, 'diff-pending-update') |
609 | >>> print extract_text(update) |
610 | Updating diff... |
611 | An updated diff will be available in a few minutes. Reload to see the |
612 | |
613 | === modified file 'lib/lp/testing/factory.py' |
614 | --- lib/lp/testing/factory.py 2013-05-13 06:57:37 +0000 |
615 | +++ lib/lp/testing/factory.py 2013-05-13 06:57:38 +0000 |
616 | @@ -1483,9 +1483,9 @@ |
617 | def makeBranchMergeProposal(self, target_branch=None, registrant=None, |
618 | set_state=None, prerequisite_branch=None, |
619 | product=None, initial_comment=None, |
620 | - source_branch=None, preview_diff=None, |
621 | - date_created=None, description=None, |
622 | - reviewer=None, merged_revno=None): |
623 | + source_branch=None, date_created=None, |
624 | + description=None, reviewer=None, |
625 | + merged_revno=None): |
626 | """Create a proposal to merge based on anonymous branches.""" |
627 | if target_branch is not None: |
628 | target_branch = removeSecurityProxy(target_branch) |
629 | @@ -1521,8 +1521,6 @@ |
630 | |
631 | unsafe_proposal = removeSecurityProxy(proposal) |
632 | unsafe_proposal.merged_revno = merged_revno |
633 | - if preview_diff is not None: |
634 | - unsafe_proposal.preview_diff = preview_diff |
635 | if (set_state is None or |
636 | set_state == BranchMergeProposalStatus.WORK_IN_PROGRESS): |
637 | # The initial state is work in progress, so do nothing. |
638 | @@ -1568,17 +1566,19 @@ |
639 | return ProxyFactory( |
640 | Diff.fromFile(StringIO(diff_text), len(diff_text))) |
641 | |
642 | - def makePreviewDiff(self, conflicts=u'', merge_proposal=None): |
643 | + def makePreviewDiff(self, conflicts=u'', merge_proposal=None, |
644 | + date_created=None): |
645 | diff = self.makeDiff() |
646 | if merge_proposal is None: |
647 | merge_proposal = self.makeBranchMergeProposal() |
648 | preview_diff = PreviewDiff() |
649 | - preview_diff._new_branch_merge_proposal = merge_proposal |
650 | + preview_diff.branch_merge_proposal = merge_proposal |
651 | preview_diff.conflicts = conflicts |
652 | preview_diff.diff = diff |
653 | preview_diff.source_revision_id = self.getUniqueUnicode() |
654 | preview_diff.target_revision_id = self.getUniqueUnicode() |
655 | - removeSecurityProxy(merge_proposal).preview_diff = preview_diff |
656 | + if date_created: |
657 | + preview_diff.date_created = date_created |
658 | return preview_diff |
659 | |
660 | def makeIncrementalDiff(self, merge_proposal=None, old_revision=None, |
207 + @cachedproperty diffs(self) : self).find( merge_proposal_ id == self.id).order_by( date_created)
208 + def preview_
209 + return Store.of(
210 + PreviewDiff, PreviewDiff.
211 + PreviewDiff.
Caching a ResultSet achieves nothing. You probably want to cache a listified version instead.
213 + @property preview_ diffs)
214 + def preview_diff(self):
215 + diffs = list(self.
216 + if diffs:
217 + return diffs[-1]
218 + return None
Materialising all of the PreviewDiffs just for this is probably pointless, particularly given how cheap it is to find just the latest. I'd probably move the ResultSet into _preview_diffs, then have preview_diffs listify and cache it, and preview_diff grab the first and cache it. If you wanted to be excessively performant you could even have preview_diff check is preview_diffs was cached, and if so just grab the first from there, but that's probably pointless.
292 + for previewdiff in preview_diffs: cache(previewdi ff.merge_ proposal) diffs.append( previewdiff)
293 + cache = get_property_
294 + cache.preview_
Is preloading preview_diffs valuable here, or would it be sufficient to simply preload the latest, preview_diff?
371 + @property merge_proposal( self): proposal: merge_proposal
372 def branch_
373 - # This can turn into return self.merge_proposal when it's populated.
374 - if self.merge_
375 - return self.merge_proposal
376 - return self._branch_
377 + return self.merge_proposal
Why does this still exist?