Merge lp:~cjwatson/launchpad/preload-git-landing-targets-candidates into lp:launchpad

Proposed by Colin Watson
Status: Merged
Merged at revision: 18285
Proposed branch: lp:~cjwatson/launchpad/preload-git-landing-targets-candidates
Merge into: lp:launchpad
Prerequisite: lp:~cjwatson/launchpad/preload-branch-landing-targets
Diff against target: 541 lines (+268/-48)
9 files modified
lib/lp/_schema_circular_imports.py (+6/-4)
lib/lp/code/browser/gitref.py (+4/-2)
lib/lp/code/browser/tests/test_gitref.py (+36/-0)
lib/lp/code/interfaces/gitref.py (+40/-16)
lib/lp/code/interfaces/gitrepository.py (+40/-16)
lib/lp/code/model/gitref.py (+20/-10)
lib/lp/code/model/gitrepository.py (+20/-0)
lib/lp/code/model/tests/test_gitref.py (+51/-0)
lib/lp/code/model/tests/test_gitrepository.py (+51/-0)
To merge this branch: bzr merge lp:~cjwatson/launchpad/preload-git-landing-targets-candidates
Reviewer Review Type Date Requested Status
William Grant code Approve
Review via email: mp+310655@code.launchpad.net

Commit message

Preload {GitRepository,GitRef}.{landing_candidates,landing_targets}.

Description of the change

This is in line with similar handling in Branch.

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

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'lib/lp/_schema_circular_imports.py'
--- lib/lp/_schema_circular_imports.py 2016-11-11 15:10:55 +0000
+++ lib/lp/_schema_circular_imports.py 2016-11-11 15:10:56 +0000
@@ -566,8 +566,10 @@
566 IGitRef, 'createMergeProposal', 'merge_target', IGitRef)566 IGitRef, 'createMergeProposal', 'merge_target', IGitRef)
567patch_plain_parameter_type(567patch_plain_parameter_type(
568 IGitRef, 'createMergeProposal', 'merge_prerequisite', IGitRef)568 IGitRef, 'createMergeProposal', 'merge_prerequisite', IGitRef)
569patch_collection_property(IGitRef, 'landing_targets', IBranchMergeProposal)569patch_collection_property(
570patch_collection_property(IGitRef, 'landing_candidates', IBranchMergeProposal)570 IGitRef, '_api_landing_targets', IBranchMergeProposal)
571patch_collection_property(
572 IGitRef, '_api_landing_candidates', IBranchMergeProposal)
571patch_collection_property(IGitRef, 'dependent_landings', IBranchMergeProposal)573patch_collection_property(IGitRef, 'dependent_landings', IBranchMergeProposal)
572patch_entry_return_type(IGitRef, 'createMergeProposal', IBranchMergeProposal)574patch_entry_return_type(IGitRef, 'createMergeProposal', IBranchMergeProposal)
573patch_collection_return_type(575patch_collection_return_type(
@@ -581,9 +583,9 @@
581patch_entry_return_type(IGitRepository, 'getSubscription', IGitSubscription)583patch_entry_return_type(IGitRepository, 'getSubscription', IGitSubscription)
582patch_reference_property(IGitRepository, 'code_import', ICodeImport)584patch_reference_property(IGitRepository, 'code_import', ICodeImport)
583patch_collection_property(585patch_collection_property(
584 IGitRepository, 'landing_targets', IBranchMergeProposal)586 IGitRepository, '_api_landing_targets', IBranchMergeProposal)
585patch_collection_property(587patch_collection_property(
586 IGitRepository, 'landing_candidates', IBranchMergeProposal)588 IGitRepository, '_api_landing_candidates', IBranchMergeProposal)
587patch_collection_property(589patch_collection_property(
588 IGitRepository, 'dependent_landings', IBranchMergeProposal)590 IGitRepository, 'dependent_landings', IBranchMergeProposal)
589591
590592
=== modified file 'lib/lp/code/browser/gitref.py'
--- lib/lp/code/browser/gitref.py 2016-10-14 16:16:18 +0000
+++ lib/lp/code/browser/gitref.py 2016-11-11 15:10:56 +0000
@@ -133,12 +133,14 @@
133 @cachedproperty133 @cachedproperty
134 def landing_targets(self):134 def landing_targets(self):
135 """Return a filtered list of landing targets."""135 """Return a filtered list of landing targets."""
136 return latest_proposals_for_each_branch(self.context.landing_targets)136 targets = self.context.getPrecachedLandingTargets(self.user)
137 return latest_proposals_for_each_branch(targets)
137138
138 @cachedproperty139 @cachedproperty
139 def landing_candidates(self):140 def landing_candidates(self):
140 """Return a decorated list of landing candidates."""141 """Return a decorated list of landing candidates."""
141 return [proposal for proposal in self.context.landing_candidates142 candidates = self.context.getPrecachedLandingCandidates(self.user)
143 return [proposal for proposal in candidates
142 if check_permission("launchpad.View", proposal)]144 if check_permission("launchpad.View", proposal)]
143145
144 def _getBranchCountText(self, count):146 def _getBranchCountText(self, count):
145147
=== modified file 'lib/lp/code/browser/tests/test_gitref.py'
--- lib/lp/code/browser/tests/test_gitref.py 2016-09-07 11:12:58 +0000
+++ lib/lp/code/browser/tests/test_gitref.py 2016-11-11 15:10:56 +0000
@@ -12,6 +12,8 @@
12from BeautifulSoup import BeautifulSoup12from BeautifulSoup import BeautifulSoup
13import pytz13import pytz
14import soupmatchers14import soupmatchers
15from storm.store import Store
16from testtools.matchers import Equals
15from zope.component import getUtility17from zope.component import getUtility
16from zope.security.proxy import removeSecurityProxy18from zope.security.proxy import removeSecurityProxy
1719
@@ -23,9 +25,11 @@
23from lp.testing import (25from lp.testing import (
24 admin_logged_in,26 admin_logged_in,
25 BrowserTestCase,27 BrowserTestCase,
28 StormStatementRecorder,
26 )29 )
27from lp.testing.dbuser import dbuser30from lp.testing.dbuser import dbuser
28from lp.testing.layers import LaunchpadFunctionalLayer31from lp.testing.layers import LaunchpadFunctionalLayer
32from lp.testing.matchers import HasQueryCount
29from lp.testing.pages import (33from lp.testing.pages import (
30 extract_text,34 extract_text,
31 find_tags_by_class,35 find_tags_by_class,
@@ -220,3 +224,35 @@
220 soupmatchers.Tag(224 soupmatchers.Tag(
221 'all commits link', 'a', text='All commits',225 'all commits link', 'a', text='All commits',
222 attrs={'href': expected_url}))))226 attrs={'href': expected_url}))))
227
228 def test_query_count_landing_candidates(self):
229 project = self.factory.makeProduct()
230 [ref] = self.factory.makeGitRefs(target=project)
231 for i in range(10):
232 self.factory.makeBranchMergeProposalForGit(target_ref=ref)
233 [source] = self.factory.makeGitRefs(target=project)
234 [prereq] = self.factory.makeGitRefs(target=project)
235 self.factory.makeBranchMergeProposalForGit(
236 source_ref=source, target_ref=ref, prerequisite_ref=prereq)
237 Store.of(ref).flush()
238 Store.of(ref).invalidate()
239 view = create_view(ref, '+index')
240 with StormStatementRecorder() as recorder:
241 view.landing_candidates
242 self.assertThat(recorder, HasQueryCount(Equals(11)))
243
244 def test_query_count_landing_targets(self):
245 project = self.factory.makeProduct()
246 [ref] = self.factory.makeGitRefs(target=project)
247 for i in range(10):
248 self.factory.makeBranchMergeProposalForGit(source_ref=ref)
249 [target] = self.factory.makeGitRefs(target=project)
250 [prereq] = self.factory.makeGitRefs(target=project)
251 self.factory.makeBranchMergeProposalForGit(
252 source_ref=ref, target_ref=target, prerequisite_ref=prereq)
253 Store.of(ref).flush()
254 Store.of(ref).invalidate()
255 view = create_view(ref, '+index')
256 with StormStatementRecorder() as recorder:
257 view.landing_targets
258 self.assertThat(recorder, HasQueryCount(Equals(11)))
223259
=== modified file 'lib/lp/code/interfaces/gitref.py'
--- lib/lp/code/interfaces/gitref.py 2016-11-09 17:18:21 +0000
+++ lib/lp/code/interfaces/gitref.py 2016-11-11 15:10:56 +0000
@@ -221,22 +221,34 @@
221 and their subscriptions.221 and their subscriptions.
222 """222 """
223223
224 landing_targets = exported(CollectionField(224 landing_targets = Attribute(
225 title=_("Landing targets"),225 "A collection of the merge proposals where this reference is "
226 description=_(226 "the source.")
227 "A collection of the merge proposals where this reference is the "227 _api_landing_targets = exported(
228 "source."),228 CollectionField(
229 readonly=True,229 title=_("Landing targets"),
230 # Really IBranchMergeProposal, patched in _schema_circular_imports.py.230 description=_(
231 value_type=Reference(Interface)))231 "A collection of the merge proposals where this reference is "
232 landing_candidates = exported(CollectionField(232 "the source."),
233 title=_("Landing candidates"),233 readonly=True,
234 description=_(234 # Really IBranchMergeProposal, patched in
235 "A collection of the merge proposals where this reference is the "235 # _schema_circular_imports.py.
236 "target."),236 value_type=Reference(Interface)),
237 readonly=True,237 exported_as="landing_targets")
238 # Really IBranchMergeProposal, patched in _schema_circular_imports.py.238 landing_candidates = Attribute(
239 value_type=Reference(Interface)))239 "A collection of the merge proposals where this reference is "
240 "the target.")
241 _api_landing_candidates = exported(
242 CollectionField(
243 title=_("Landing candidates"),
244 description=_(
245 "A collection of the merge proposals where this reference is "
246 "the target."),
247 readonly=True,
248 # Really IBranchMergeProposal, patched in
249 # _schema_circular_imports.py.
250 value_type=Reference(Interface)),
251 exported_as="landing_candidates")
240 dependent_landings = exported(CollectionField(252 dependent_landings = exported(CollectionField(
241 title=_("Dependent landings"),253 title=_("Dependent landings"),
242 description=_(254 description=_(
@@ -246,6 +258,18 @@
246 # Really IBranchMergeProposal, patched in _schema_circular_imports.py.258 # Really IBranchMergeProposal, patched in _schema_circular_imports.py.
247 value_type=Reference(Interface)))259 value_type=Reference(Interface)))
248260
261 def getPrecachedLandingTargets(user):
262 """Return precached landing targets.
263
264 Target and prerequisite repositories are preloaded.
265 """
266
267 def getPrecachedLandingCandidates(user):
268 """Return precached landing candidates.
269
270 Source and prerequisite repositories are preloaded.
271 """
272
249 # XXX cjwatson 2015-04-16: Rename in line with landing_targets above273 # XXX cjwatson 2015-04-16: Rename in line with landing_targets above
250 # once we have a better name.274 # once we have a better name.
251 def addLandingTarget(registrant, merge_target, merge_prerequisite=None,275 def addLandingTarget(registrant, merge_target, merge_prerequisite=None,
252276
=== modified file 'lib/lp/code/interfaces/gitrepository.py'
--- lib/lp/code/interfaces/gitrepository.py 2016-10-14 15:08:36 +0000
+++ lib/lp/code/interfaces/gitrepository.py 2016-11-11 15:10:56 +0000
@@ -476,22 +476,34 @@
476 and their subscriptions.476 and their subscriptions.
477 """477 """
478478
479 landing_targets = exported(CollectionField(479 landing_targets = Attribute(
480 title=_("Landing targets"),480 "A collection of the merge proposals where this repository is "
481 description=_(481 "the source.")
482 "A collection of the merge proposals where this repository is the "482 _api_landing_targets = exported(
483 "source."),483 CollectionField(
484 readonly=True,484 title=_("Landing targets"),
485 # Really IBranchMergeProposal, patched in _schema_circular_imports.py.485 description=_(
486 value_type=Reference(Interface)))486 "A collection of the merge proposals where this repository is "
487 landing_candidates = exported(CollectionField(487 "the source."),
488 title=_("Landing candidates"),488 readonly=True,
489 description=_(489 # Really IBranchMergeProposal, patched in
490 "A collection of the merge proposals where this repository is the "490 # _schema_circular_imports.py.
491 "target."),491 value_type=Reference(Interface)),
492 readonly=True,492 exported_as="landing_targets")
493 # Really IBranchMergeProposal, patched in _schema_circular_imports.py.493 landing_candidates = Attribute(
494 value_type=Reference(Interface)))494 "A collection of the merge proposals where this repository is "
495 "the target.")
496 _api_landing_candidates = exported(
497 CollectionField(
498 title=_("Landing candidates"),
499 description=_(
500 "A collection of the merge proposals where this repository is "
501 "the target."),
502 readonly=True,
503 # Really IBranchMergeProposal, patched in
504 # _schema_circular_imports.py.
505 value_type=Reference(Interface)),
506 exported_as="landing_candidates")
495 dependent_landings = exported(CollectionField(507 dependent_landings = exported(CollectionField(
496 title=_("Dependent landings"),508 title=_("Dependent landings"),
497 description=_(509 description=_(
@@ -501,6 +513,18 @@
501 # Really IBranchMergeProposal, patched in _schema_circular_imports.py.513 # Really IBranchMergeProposal, patched in _schema_circular_imports.py.
502 value_type=Reference(Interface)))514 value_type=Reference(Interface)))
503515
516 def getPrecachedLandingTargets(user):
517 """Return precached landing targets.
518
519 Target and prerequisite repositories are preloaded.
520 """
521
522 def getPrecachedLandingCandidates(user):
523 """Return precached landing candidates.
524
525 Source and prerequisite repositories are preloaded.
526 """
527
504 def getMergeProposalByID(id):528 def getMergeProposalByID(id):
505 """Return this repository's merge proposal with this id, or None."""529 """Return this repository's merge proposal with this id, or None."""
506530
507531
=== modified file 'lib/lp/code/model/gitref.py'
--- lib/lp/code/model/gitref.py 2016-11-09 17:18:21 +0000
+++ lib/lp/code/model/gitref.py 2016-11-11 15:10:56 +0000
@@ -8,6 +8,7 @@
8 ]8 ]
99
10from datetime import datetime10from datetime import datetime
11from functools import partial
11import json12import json
12from urllib import quote_plus13from urllib import quote_plus
13from urlparse import urlsplit14from urlparse import urlsplit
@@ -51,7 +52,6 @@
51 BranchMergeProposalGetter,52 BranchMergeProposalGetter,
52 )53 )
53from lp.services.config import config54from lp.services.config import config
54from lp.services.database.bulk import load_related
55from lp.services.database.constants import UTC_NOW55from lp.services.database.constants import UTC_NOW
56from lp.services.database.decoratedresultset import DecoratedResultSet56from lp.services.database.decoratedresultset import DecoratedResultSet
57from lp.services.database.enumcol import EnumCol57from lp.services.database.enumcol import EnumCol
@@ -59,6 +59,7 @@
59from lp.services.database.stormbase import StormBase59from lp.services.database.stormbase import StormBase
60from lp.services.features import getFeatureFlag60from lp.services.features import getFeatureFlag
61from lp.services.memcache.interfaces import IMemcacheClient61from lp.services.memcache.interfaces import IMemcacheClient
62from lp.services.webapp.interfaces import ILaunchBag
6263
6364
64class GitRefMixin:65class GitRefMixin:
@@ -194,25 +195,34 @@
194 BranchMergeProposal.source_git_repository == self.repository,195 BranchMergeProposal.source_git_repository == self.repository,
195 BranchMergeProposal.source_git_path == self.path)196 BranchMergeProposal.source_git_path == self.path)
196197
198 def getPrecachedLandingTargets(self, user):
199 """See `IGitRef`."""
200 loader = partial(BranchMergeProposal.preloadDataForBMPs, user=user)
201 return DecoratedResultSet(self.landing_targets, pre_iter_hook=loader)
202
203 @property
204 def _api_landing_targets(self):
205 return self.getPrecachedLandingTargets(getUtility(ILaunchBag).user)
206
197 @property207 @property
198 def landing_candidates(self):208 def landing_candidates(self):
199 """See `IGitRef`."""209 """See `IGitRef`."""
200 # Circular import.210 return Store.of(self).find(
201 from lp.code.model.gitrepository import GitRepository
202
203 result = Store.of(self).find(
204 BranchMergeProposal,211 BranchMergeProposal,
205 BranchMergeProposal.target_git_repository == self.repository,212 BranchMergeProposal.target_git_repository == self.repository,
206 BranchMergeProposal.target_git_path == self.path,213 BranchMergeProposal.target_git_path == self.path,
207 Not(BranchMergeProposal.queue_status.is_in(214 Not(BranchMergeProposal.queue_status.is_in(
208 BRANCH_MERGE_PROPOSAL_FINAL_STATES)))215 BRANCH_MERGE_PROPOSAL_FINAL_STATES)))
209216
210 def eager_load(rows):217 def getPrecachedLandingCandidates(self, user):
211 load_related(218 """See `IGitRef`."""
212 GitRepository, rows,219 loader = partial(BranchMergeProposal.preloadDataForBMPs, user=user)
213 ["source_git_repositoryID", "prerequisite_git_repositoryID"])220 return DecoratedResultSet(
221 self.landing_candidates, pre_iter_hook=loader)
214222
215 return DecoratedResultSet(result, pre_iter_hook=eager_load)223 @property
224 def _api_landing_candidates(self):
225 return self.getPrecachedLandingCandidates(getUtility(ILaunchBag).user)
216226
217 @property227 @property
218 def dependent_landings(self):228 def dependent_landings(self):
219229
=== modified file 'lib/lp/code/model/gitrepository.py'
--- lib/lp/code/model/gitrepository.py 2016-10-14 15:08:58 +0000
+++ lib/lp/code/model/gitrepository.py 2016-11-11 15:10:56 +0000
@@ -155,6 +155,7 @@
155 get_property_cache,155 get_property_cache,
156 )156 )
157from lp.services.webapp.authorization import available_with_permission157from lp.services.webapp.authorization import available_with_permission
158from lp.services.webapp.interfaces import ILaunchBag
158from lp.services.webhooks.interfaces import IWebhookSet159from lp.services.webhooks.interfaces import IWebhookSet
159from lp.services.webhooks.model import WebhookTargetMixin160from lp.services.webhooks.model import WebhookTargetMixin
160from lp.snappy.interfaces.snap import ISnapSet161from lp.snappy.interfaces.snap import ISnapSet
@@ -887,6 +888,15 @@
887 BranchMergeProposal,888 BranchMergeProposal,
888 BranchMergeProposal.source_git_repository == self)889 BranchMergeProposal.source_git_repository == self)
889890
891 def getPrecachedLandingTargets(self, user):
892 """See `IGitRef`."""
893 loader = partial(BranchMergeProposal.preloadDataForBMPs, user=user)
894 return DecoratedResultSet(self.landing_targets, pre_iter_hook=loader)
895
896 @property
897 def _api_landing_targets(self):
898 return self.getPrecachedLandingTargets(getUtility(ILaunchBag).user)
899
890 def getActiveLandingTargets(self, paths):900 def getActiveLandingTargets(self, paths):
891 """Merge proposals not in final states where these refs are source."""901 """Merge proposals not in final states where these refs are source."""
892 return Store.of(self).find(902 return Store.of(self).find(
@@ -905,6 +915,16 @@
905 Not(BranchMergeProposal.queue_status.is_in(915 Not(BranchMergeProposal.queue_status.is_in(
906 BRANCH_MERGE_PROPOSAL_FINAL_STATES)))916 BRANCH_MERGE_PROPOSAL_FINAL_STATES)))
907917
918 def getPrecachedLandingCandidates(self, user):
919 """See `IGitRef`."""
920 loader = partial(BranchMergeProposal.preloadDataForBMPs, user=user)
921 return DecoratedResultSet(
922 self.landing_candidates, pre_iter_hook=loader)
923
924 @property
925 def _api_landing_candidates(self):
926 return self.getPrecachedLandingCandidates(getUtility(ILaunchBag).user)
927
908 def getActiveLandingCandidates(self, paths):928 def getActiveLandingCandidates(self, paths):
909 """Merge proposals not in final states where these refs are target."""929 """Merge proposals not in final states where these refs are target."""
910 return Store.of(self).find(930 return Store.of(self).find(
911931
=== modified file 'lib/lp/code/model/tests/test_gitref.py'
--- lib/lp/code/model/tests/test_gitref.py 2016-09-07 11:12:58 +0000
+++ lib/lp/code/model/tests/test_gitref.py 2016-11-11 15:10:56 +0000
@@ -18,6 +18,7 @@
18 EndsWith,18 EndsWith,
19 Equals,19 Equals,
20 Is,20 Is,
21 LessThan,
21 MatchesListwise,22 MatchesListwise,
22 MatchesStructure,23 MatchesStructure,
23 )24 )
@@ -36,6 +37,7 @@
36 ANONYMOUS,37 ANONYMOUS,
37 api_url,38 api_url,
38 person_logged_in,39 person_logged_in,
40 record_two_runs,
39 TestCaseWithFactory,41 TestCaseWithFactory,
40 verifyObject,42 verifyObject,
41 )43 )
@@ -43,6 +45,7 @@
43 DatabaseFunctionalLayer,45 DatabaseFunctionalLayer,
44 LaunchpadFunctionalLayer,46 LaunchpadFunctionalLayer,
45 )47 )
48from lp.testing.matchers import HasQueryCount
46from lp.testing.pages import webservice_for_person49from lp.testing.pages import webservice_for_person
4750
4851
@@ -461,6 +464,30 @@
461 self.assertThat(464 self.assertThat(
462 landing_candidates["entries"][0]["self_link"], EndsWith(bmp_url))465 landing_candidates["entries"][0]["self_link"], EndsWith(bmp_url))
463466
467 def test_landing_candidates_constant_queries(self):
468 project = self.factory.makeProduct()
469 with person_logged_in(project.owner):
470 [trunk] = self.factory.makeGitRefs(target=project)
471 trunk_url = api_url(trunk)
472 webservice = webservice_for_person(
473 project.owner, permission=OAuthPermission.WRITE_PRIVATE)
474
475 def create_mp():
476 with admin_logged_in():
477 [ref] = self.factory.makeGitRefs(
478 target=project,
479 information_type=InformationType.PRIVATESECURITY)
480 self.factory.makeBranchMergeProposalForGit(
481 source_ref=ref, target_ref=trunk)
482
483 def list_mps():
484 webservice.get(trunk_url + "/landing_candidates")
485
486 list_mps()
487 recorder1, recorder2 = record_two_runs(list_mps, create_mp, 2)
488 self.assertThat(recorder1, HasQueryCount(LessThan(30)))
489 self.assertThat(recorder2, HasQueryCount.byEquality(recorder1))
490
464 def test_landing_targets(self):491 def test_landing_targets(self):
465 bmp_db = self.factory.makeBranchMergeProposalForGit()492 bmp_db = self.factory.makeBranchMergeProposalForGit()
466 with person_logged_in(bmp_db.registrant):493 with person_logged_in(bmp_db.registrant):
@@ -476,6 +503,30 @@
476 self.assertThat(503 self.assertThat(
477 landing_targets["entries"][0]["self_link"], EndsWith(bmp_url))504 landing_targets["entries"][0]["self_link"], EndsWith(bmp_url))
478505
506 def test_landing_targets_constant_queries(self):
507 project = self.factory.makeProduct()
508 with person_logged_in(project.owner):
509 [source] = self.factory.makeGitRefs(target=project)
510 source_url = api_url(source)
511 webservice = webservice_for_person(
512 project.owner, permission=OAuthPermission.WRITE_PRIVATE)
513
514 def create_mp():
515 with admin_logged_in():
516 [ref] = self.factory.makeGitRefs(
517 target=project,
518 information_type=InformationType.PRIVATESECURITY)
519 self.factory.makeBranchMergeProposalForGit(
520 source_ref=source, target_ref=ref)
521
522 def list_mps():
523 webservice.get(source_url + "/landing_targets")
524
525 list_mps()
526 recorder1, recorder2 = record_two_runs(list_mps, create_mp, 2)
527 self.assertThat(recorder1, HasQueryCount(LessThan(30)))
528 self.assertThat(recorder2, HasQueryCount.byEquality(recorder1))
529
479 def test_dependent_landings(self):530 def test_dependent_landings(self):
480 [ref] = self.factory.makeGitRefs()531 [ref] = self.factory.makeGitRefs()
481 bmp_db = self.factory.makeBranchMergeProposalForGit(532 bmp_db = self.factory.makeBranchMergeProposalForGit(
482533
=== modified file 'lib/lp/code/model/tests/test_gitrepository.py'
--- lib/lp/code/model/tests/test_gitrepository.py 2016-10-14 16:16:18 +0000
+++ lib/lp/code/model/tests/test_gitrepository.py 2016-11-11 15:10:56 +0000
@@ -20,6 +20,7 @@
20from storm.store import Store20from storm.store import Store
21from testtools.matchers import (21from testtools.matchers import (
22 EndsWith,22 EndsWith,
23 LessThan,
23 MatchesSetwise,24 MatchesSetwise,
24 MatchesStructure,25 MatchesStructure,
25 )26 )
@@ -2800,6 +2801,31 @@
2800 self.assertThat(2801 self.assertThat(
2801 landing_candidates["entries"][0]["self_link"], EndsWith(bmp_url))2802 landing_candidates["entries"][0]["self_link"], EndsWith(bmp_url))
28022803
2804 def test_landing_candidates_constant_queries(self):
2805 project = self.factory.makeProduct()
2806 with person_logged_in(project.owner):
2807 repository = self.factory.makeGitRepository(target=project)
2808 repository_url = api_url(repository)
2809 webservice = webservice_for_person(
2810 project.owner, permission=OAuthPermission.WRITE_PRIVATE)
2811
2812 def create_mp():
2813 with admin_logged_in():
2814 [target] = self.factory.makeGitRefs(repository=repository)
2815 [source] = self.factory.makeGitRefs(
2816 target=project,
2817 information_type=InformationType.PRIVATESECURITY)
2818 self.factory.makeBranchMergeProposalForGit(
2819 source_ref=source, target_ref=target)
2820
2821 def list_mps():
2822 webservice.get(repository_url + "/landing_candidates")
2823
2824 list_mps()
2825 recorder1, recorder2 = record_two_runs(list_mps, create_mp, 2)
2826 self.assertThat(recorder1, HasQueryCount(LessThan(40)))
2827 self.assertThat(recorder2, HasQueryCount.byEquality(recorder1))
2828
2803 def test_landing_targets(self):2829 def test_landing_targets(self):
2804 bmp_db = self.factory.makeBranchMergeProposalForGit()2830 bmp_db = self.factory.makeBranchMergeProposalForGit()
2805 with person_logged_in(bmp_db.registrant):2831 with person_logged_in(bmp_db.registrant):
@@ -2815,6 +2841,31 @@
2815 self.assertThat(2841 self.assertThat(
2816 landing_targets["entries"][0]["self_link"], EndsWith(bmp_url))2842 landing_targets["entries"][0]["self_link"], EndsWith(bmp_url))
28172843
2844 def test_landing_targets_constant_queries(self):
2845 project = self.factory.makeProduct()
2846 with person_logged_in(project.owner):
2847 repository = self.factory.makeGitRepository(target=project)
2848 repository_url = api_url(repository)
2849 webservice = webservice_for_person(
2850 project.owner, permission=OAuthPermission.WRITE_PRIVATE)
2851
2852 def create_mp():
2853 with admin_logged_in():
2854 [source] = self.factory.makeGitRefs(repository=repository)
2855 [target] = self.factory.makeGitRefs(
2856 target=project,
2857 information_type=InformationType.PRIVATESECURITY)
2858 self.factory.makeBranchMergeProposalForGit(
2859 source_ref=source, target_ref=target)
2860
2861 def list_mps():
2862 webservice.get(repository_url + "/landing_targets")
2863
2864 list_mps()
2865 recorder1, recorder2 = record_two_runs(list_mps, create_mp, 2)
2866 self.assertThat(recorder1, HasQueryCount(LessThan(30)))
2867 self.assertThat(recorder2, HasQueryCount.byEquality(recorder1))
2868
2818 def test_dependent_landings(self):2869 def test_dependent_landings(self):
2819 [ref] = self.factory.makeGitRefs()2870 [ref] = self.factory.makeGitRefs()
2820 bmp_db = self.factory.makeBranchMergeProposalForGit(2871 bmp_db = self.factory.makeBranchMergeProposalForGit(