Merge lp:~stevenk/launchpad/switch-bmp-to-previewdiff-merge_proposal into lp:launchpad

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
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.branch_merge_proposal, and remove BranchMergeProposal.preview_diff in preparation for dropping the column.

Description of the change

Switch everything over to using PreviewDiff.merge_propsal, and remove BranchMergeProposal.preview_diff in preparation for dropping the column.

To post a comment you must log in.
Revision history for this message
William Grant (wgrant) wrote :

207 + @cachedproperty
208 + def preview_diffs(self):
209 + return Store.of(self).find(
210 + PreviewDiff, PreviewDiff.merge_proposal_id == self.id).order_by(
211 + PreviewDiff.date_created)

Caching a ResultSet achieves nothing. You probably want to cache a listified version instead.

213 + @property
214 + def preview_diff(self):
215 + diffs = list(self.preview_diffs)
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:
293 + cache = get_property_cache(previewdiff.merge_proposal)
294 + cache.preview_diffs.append(previewdiff)

Is preloading preview_diffs valuable here, or would it be sufficient to simply preload the latest, preview_diff?

371 + @property
372 def branch_merge_proposal(self):
373 - # This can turn into return self.merge_proposal when it's populated.
374 - if self.merge_proposal:
375 - return self.merge_proposal
376 - return self._branch_merge_proposal
377 + return self.merge_proposal

Why does this still exist?

review: Approve (code)

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,