Merge lp:~stevenk/launchpad/preload-landing_candidates into lp:launchpad

Proposed by Steve Kowalik
Status: Merged
Merged at revision: 16507
Proposed branch: lp:~stevenk/launchpad/preload-landing_candidates
Merge into: lp:launchpad
Diff against target: 502 lines (+80/-95)
3 files modified
lib/lp/code/browser/branch.py (+8/-1)
lib/lp/code/browser/tests/test_branch.py (+69/-93)
lib/lp/code/configure.zcml (+3/-1)
To merge this branch: bzr merge lp:~stevenk/launchpad/preload-landing_candidates
Reviewer Review Type Date Requested Status
William Grant code Approve
Review via email: mp+150255@code.launchpad.net

Commit message

Preload candidate branches in BranchView.landing_candidates to not cause O(n) queries when checking 1-2 branches per MP targeted to the branch being viewed.

Description of the change

Preload candidate branches in BranchView.landing_candidates before checking for launchpad.View. Since APGs and AAGs are denormalized onto Branch, we only need to load them all in one load_related call to pre-cache the access check.

Clean up a bunch of test_branch, having it use create_view or create_initialized_view rather than calling out the views manually.

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/code/browser/branch.py'
--- lib/lp/code/browser/branch.py 2013-02-21 05:43:21 +0000
+++ lib/lp/code/browser/branch.py 2013-02-25 05:26:21 +0000
@@ -127,11 +127,14 @@
127from lp.code.interfaces.branchmergeproposal import IBranchMergeProposal127from lp.code.interfaces.branchmergeproposal import IBranchMergeProposal
128from lp.code.interfaces.branchtarget import IBranchTarget128from lp.code.interfaces.branchtarget import IBranchTarget
129from lp.code.interfaces.codereviewvote import ICodeReviewVoteReference129from lp.code.interfaces.codereviewvote import ICodeReviewVoteReference
130from lp.code.model.branch import Branch
131from lp.code.model.branchcollection import GenericBranchCollection
130from lp.registry.interfaces.person import IPersonSet132from lp.registry.interfaces.person import IPersonSet
131from lp.registry.interfaces.productseries import IProductSeries133from lp.registry.interfaces.productseries import IProductSeries
132from lp.registry.vocabularies import UserTeamsParticipationPlusSelfVocabulary134from lp.registry.vocabularies import UserTeamsParticipationPlusSelfVocabulary
133from lp.services import searchbuilder135from lp.services import searchbuilder
134from lp.services.config import config136from lp.services.config import config
137from lp.services.database.bulk import load_related
135from lp.services.database.constants import UTC_NOW138from lp.services.database.constants import UTC_NOW
136from lp.services.feeds.browser import (139from lp.services.feeds.browser import (
137 BranchFeedLink,140 BranchFeedLink,
@@ -554,7 +557,11 @@
554 @cachedproperty557 @cachedproperty
555 def landing_candidates(self):558 def landing_candidates(self):
556 """Return a decorated list of landing candidates."""559 """Return a decorated list of landing candidates."""
557 candidates = self.context.landing_candidates560 candidates = list(self.context.landing_candidates)
561 branches = load_related(
562 Branch, candidates, ['source_branchID', 'prerequisite_branchID'])
563 GenericBranchCollection.preloadVisibleStackedOnBranches(
564 branches, self.user)
558 return [proposal for proposal in candidates565 return [proposal for proposal in candidates
559 if check_permission('launchpad.View', proposal)]566 if check_permission('launchpad.View', proposal)]
560567
561568
=== modified file 'lib/lp/code/browser/tests/test_branch.py'
--- lib/lp/code/browser/tests/test_branch.py 2012-11-14 15:45:10 +0000
+++ lib/lp/code/browser/tests/test_branch.py 2013-02-25 05:26:21 +0000
@@ -1,4 +1,4 @@
1# Copyright 2009-2012 Canonical Ltd. This software is licensed under the1# Copyright 2009-2013 Canonical Ltd. This software is licensed under the
2# GNU Affero General Public License version 3 (see the file LICENSE).2# GNU Affero General Public License version 3 (see the file LICENSE).
33
4"""Unit tests for BranchView."""4"""Unit tests for BranchView."""
@@ -10,6 +10,8 @@
1010
11from BeautifulSoup import BeautifulSoup11from BeautifulSoup import BeautifulSoup
12import pytz12import pytz
13from storm.store import Store
14from testtools.matchers import Equals
13from zope.component import getUtility15from zope.component import getUtility
14from zope.publisher.interfaces import NotFound16from zope.publisher.interfaces import NotFound
15from zope.security.proxy import removeSecurityProxy17from zope.security.proxy import removeSecurityProxy
@@ -22,12 +24,7 @@
22 BugTaskStatus,24 BugTaskStatus,
23 UNRESOLVED_BUGTASK_STATUSES,25 UNRESOLVED_BUGTASK_STATUSES,
24 )26 )
25from lp.code.browser.branch import (27from lp.code.browser.branch import BranchMirrorStatusView
26 BranchMirrorStatusView,
27 BranchReviewerEditView,
28 BranchView,
29 )
30from lp.code.browser.branchlisting import PersonOwnedBranchesView
31from lp.code.bzr import (28from lp.code.bzr import (
32 BranchFormat,29 BranchFormat,
33 ControlFormat,30 ControlFormat,
@@ -48,6 +45,7 @@
48 login_person,45 login_person,
49 logout,46 logout,
50 person_logged_in,47 person_logged_in,
48 StormStatementRecorder,
51 TestCaseWithFactory,49 TestCaseWithFactory,
52 )50 )
53from lp.testing.layers import (51from lp.testing.layers import (
@@ -57,6 +55,7 @@
57from lp.testing.matchers import (55from lp.testing.matchers import (
58 BrowsesWithQueryLimit,56 BrowsesWithQueryLimit,
59 Contains,57 Contains,
58 HasQueryCount,
60 )59 )
61from lp.testing.pages import (60from lp.testing.pages import (
62 extract_text,61 extract_text,
@@ -76,7 +75,7 @@
76 layer = LaunchpadFunctionalLayer75 layer = LaunchpadFunctionalLayer
7776
78 def setUp(self):77 def setUp(self):
79 TestCaseWithFactory.setUp(self)78 super(TestBranchMirrorHidden, self).setUp()
80 config.push(79 config.push(
81 "test", dedent("""\80 "test", dedent("""\
82 [codehosting]81 [codehosting]
@@ -85,15 +84,14 @@
8584
86 def tearDown(self):85 def tearDown(self):
87 config.pop("test")86 config.pop("test")
88 TestCaseWithFactory.tearDown(self)87 super(TestBranchMirrorHidden, self).tearDown()
8988
90 def testNormalBranch(self):89 def testNormalBranch(self):
91 # A branch from a normal location is fine.90 # A branch from a normal location is fine.
92 branch = self.factory.makeAnyBranch(91 branch = self.factory.makeAnyBranch(
93 branch_type=BranchType.MIRRORED,92 branch_type=BranchType.MIRRORED,
94 url="http://example.com/good/mirror")93 url="http://example.com/good/mirror")
95 view = BranchView(branch, LaunchpadTestRequest())94 view = create_initialized_view(branch, '+index')
96 view.initialize()
97 self.assertTrue(view.user is None)95 self.assertTrue(view.user is None)
98 self.assertEqual(96 self.assertEqual(
99 "http://example.com/good/mirror", view.mirror_location)97 "http://example.com/good/mirror", view.mirror_location)
@@ -101,10 +99,8 @@
101 def testLocationlessRemoteBranch(self):99 def testLocationlessRemoteBranch(self):
102 # A branch from a normal location is fine.100 # A branch from a normal location is fine.
103 branch = self.factory.makeAnyBranch(101 branch = self.factory.makeAnyBranch(
104 branch_type=BranchType.REMOTE,102 branch_type=BranchType.REMOTE, url=None)
105 url=None)103 view = create_initialized_view(branch, '+index')
106 view = BranchView(branch, LaunchpadTestRequest())
107 view.initialize()
108 self.assertTrue(view.user is None)104 self.assertTrue(view.user is None)
109 self.assertIs(None, view.mirror_location)105 self.assertIs(None, view.mirror_location)
110106
@@ -114,11 +110,9 @@
114 branch = self.factory.makeAnyBranch(110 branch = self.factory.makeAnyBranch(
115 branch_type=BranchType.MIRRORED,111 branch_type=BranchType.MIRRORED,
116 url="http://private.example.com/bzr-mysql/mysql-5.0")112 url="http://private.example.com/bzr-mysql/mysql-5.0")
117 view = BranchView(branch, LaunchpadTestRequest())113 view = create_initialized_view(branch, '+index')
118 view.initialize()
119 self.assertTrue(view.user is None)114 self.assertTrue(view.user is None)
120 self.assertEqual(115 self.assertEqual("<private server>", view.mirror_location)
121 "<private server>", view.mirror_location)
122116
123 def testHiddenBranchAsBranchOwner(self):117 def testHiddenBranchAsBranchOwner(self):
124 # A branch location with a defined private host is visible to the118 # A branch location with a defined private host is visible to the
@@ -126,12 +120,10 @@
126 owner = self.factory.makePerson(email="eric@example.com")120 owner = self.factory.makePerson(email="eric@example.com")
127 branch = self.factory.makeAnyBranch(121 branch = self.factory.makeAnyBranch(
128 branch_type=BranchType.MIRRORED,122 branch_type=BranchType.MIRRORED,
129 owner=owner,123 owner=owner, url="http://private.example.com/bzr-mysql/mysql-5.0")
130 url="http://private.example.com/bzr-mysql/mysql-5.0")
131 # Now log in the owner.124 # Now log in the owner.
132 login('eric@example.com')125 login('eric@example.com')
133 view = BranchView(branch, LaunchpadTestRequest())126 view = create_initialized_view(branch, '+index')
134 view.initialize()
135 self.assertEqual(view.user, owner)127 self.assertEqual(view.user, owner)
136 self.assertEqual(128 self.assertEqual(
137 "http://private.example.com/bzr-mysql/mysql-5.0",129 "http://private.example.com/bzr-mysql/mysql-5.0",
@@ -143,33 +135,26 @@
143 owner = self.factory.makePerson(email="eric@example.com")135 owner = self.factory.makePerson(email="eric@example.com")
144 other = self.factory.makePerson(email="other@example.com")136 other = self.factory.makePerson(email="other@example.com")
145 branch = self.factory.makeAnyBranch(137 branch = self.factory.makeAnyBranch(
146 branch_type=BranchType.MIRRORED,138 branch_type=BranchType.MIRRORED, owner=owner,
147 owner=owner,
148 url="http://private.example.com/bzr-mysql/mysql-5.0")139 url="http://private.example.com/bzr-mysql/mysql-5.0")
149 # Now log in the other person.140 # Now log in the other person.
150 login('other@example.com')141 login('other@example.com')
151 view = BranchView(branch, LaunchpadTestRequest())142 view = create_initialized_view(branch, '+index')
152 view.initialize()
153 self.assertEqual(view.user, other)143 self.assertEqual(view.user, other)
154 self.assertEqual(144 self.assertEqual("<private server>", view.mirror_location)
155 "<private server>", view.mirror_location)
156145
157146
158class TestBranchView(BrowserTestCase):147class TestBranchView(BrowserTestCase):
159148
160 layer = DatabaseFunctionalLayer149 layer = DatabaseFunctionalLayer
161150
162 def setUp(self):
163 super(TestBranchView, self).setUp()
164 self.request = LaunchpadTestRequest()
165
166 def testMirrorStatusMessageIsTruncated(self):151 def testMirrorStatusMessageIsTruncated(self):
167 """mirror_status_message is truncated if the text is overly long."""152 """mirror_status_message is truncated if the text is overly long."""
168 branch = self.factory.makeBranch(branch_type=BranchType.MIRRORED)153 branch = self.factory.makeBranch(branch_type=BranchType.MIRRORED)
169 branch.mirrorFailed(154 branch.mirrorFailed(
170 "on quick brown fox the dog jumps to" *155 "on quick brown fox the dog jumps to" *
171 BranchMirrorStatusView.MAXIMUM_STATUS_MESSAGE_LENGTH)156 BranchMirrorStatusView.MAXIMUM_STATUS_MESSAGE_LENGTH)
172 branch_view = BranchMirrorStatusView(branch, self.request)157 branch_view = create_view(branch, '+mirror-status')
173 self.assertEqual(158 self.assertEqual(
174 truncate_text(branch.mirror_status_message,159 truncate_text(branch.mirror_status_message,
175 branch_view.MAXIMUM_STATUS_MESSAGE_LENGTH) + ' ...',160 branch_view.MAXIMUM_STATUS_MESSAGE_LENGTH) + ' ...',
@@ -179,7 +164,7 @@
179 """mirror_status_message on the view is the same as on the branch."""164 """mirror_status_message on the view is the same as on the branch."""
180 branch = self.factory.makeBranch(branch_type=BranchType.MIRRORED)165 branch = self.factory.makeBranch(branch_type=BranchType.MIRRORED)
181 branch.mirrorFailed("This is a short error message.")166 branch.mirrorFailed("This is a short error message.")
182 branch_view = BranchMirrorStatusView(branch, self.request)167 branch_view = create_view(branch, '+mirror-status')
183 self.assertTrue(168 self.assertTrue(
184 len(branch.mirror_status_message)169 len(branch.mirror_status_message)
185 <= branch_view.MAXIMUM_STATUS_MESSAGE_LENGTH,170 <= branch_view.MAXIMUM_STATUS_MESSAGE_LENGTH,
@@ -194,18 +179,16 @@
194 def testShowMergeLinksOnManyBranchProject(self):179 def testShowMergeLinksOnManyBranchProject(self):
195 # The merge links are shown on projects that have multiple branches.180 # The merge links are shown on projects that have multiple branches.
196 product = self.factory.makeProduct(name='super-awesome-project')181 product = self.factory.makeProduct(name='super-awesome-project')
197 branch1 = self.factory.makeAnyBranch(product=product)182 branch = self.factory.makeAnyBranch(product=product)
198 self.factory.makeAnyBranch(product=product)183 self.factory.makeAnyBranch(product=product)
199 view = BranchView(branch1, self.request)184 view = create_initialized_view(branch, '+index')
200 view.initialize()
201 self.assertTrue(view.show_merge_links)185 self.assertTrue(view.show_merge_links)
202186
203 def testShowMergeLinksOnJunkBranch(self):187 def testShowMergeLinksOnJunkBranch(self):
204 # The merge links are not shown on junk branches because they do not188 # The merge links are not shown on junk branches because they do not
205 # support merge proposals.189 # support merge proposals.
206 junk_branch = self.factory.makeBranch(product=None)190 junk_branch = self.factory.makeBranch(product=None)
207 view = BranchView(junk_branch, self.request)191 view = create_initialized_view(junk_branch, '+index')
208 view.initialize()
209 self.assertFalse(view.show_merge_links)192 self.assertFalse(view.show_merge_links)
210193
211 def testShowMergeLinksOnSingleBranchProject(self):194 def testShowMergeLinksOnSingleBranchProject(self):
@@ -213,17 +196,14 @@
213 # only has one branch because it's pointless to propose it for merging196 # only has one branch because it's pointless to propose it for merging
214 # if there's nothing to merge into.197 # if there's nothing to merge into.
215 branch = self.factory.makeAnyBranch()198 branch = self.factory.makeAnyBranch()
216 view = BranchView(branch, self.request)199 view = create_initialized_view(branch, '+index')
217 view.initialize()
218 self.assertFalse(view.show_merge_links)200 self.assertFalse(view.show_merge_links)
219201
220 def testNoProductSeriesPushingTranslations(self):202 def testNoProductSeriesPushingTranslations(self):
221 # By default, a branch view shows no product series pushing203 # By default, a branch view shows no product series pushing
222 # translations to the branch.204 # translations to the branch.
223 branch = self.factory.makeBranch()205 branch = self.factory.makeBranch()
224206 view = create_initialized_view(branch, '+index')
225 view = BranchView(branch, self.request)
226 view.initialize()
227 self.assertEqual(list(view.translations_sources()), [])207 self.assertEqual(list(view.translations_sources()), [])
228208
229 def testProductSeriesPushingTranslations(self):209 def testProductSeriesPushingTranslations(self):
@@ -233,16 +213,13 @@
233 trunk = product.getSeries('trunk')213 trunk = product.getSeries('trunk')
234 branch = self.factory.makeBranch(owner=product.owner)214 branch = self.factory.makeBranch(owner=product.owner)
235 removeSecurityProxy(trunk).translations_branch = branch215 removeSecurityProxy(trunk).translations_branch = branch
236216 view = create_initialized_view(branch, '+index')
237 view = BranchView(branch, self.request)
238 view.initialize()
239 self.assertEqual(list(view.translations_sources()), [trunk])217 self.assertEqual(list(view.translations_sources()), [trunk])
240218
241 def test_is_empty_directory(self):219 def test_is_empty_directory(self):
242 # Branches are considered empty until they get a control format.220 # Branches are considered empty until they get a control format.
243 branch = self.factory.makeBranch()221 branch = self.factory.makeBranch()
244 view = BranchView(branch, self.request)222 view = create_initialized_view(branch, '+index')
245 view.initialize()
246 self.assertTrue(view.is_empty_directory)223 self.assertTrue(view.is_empty_directory)
247 with person_logged_in(branch.owner):224 with person_logged_in(branch.owner):
248 # Make it look as though the branch has been pushed.225 # Make it look as though the branch has been pushed.
@@ -541,6 +518,24 @@
541 browser = self.getUserBrowser(url, user=user)518 browser = self.getUserBrowser(url, user=user)
542 self.assertIn(product_name, browser.contents)519 self.assertIn(product_name, browser.contents)
543520
521 def test_query_count_landing_candidates(self):
522 product = self.factory.makeProduct()
523 branch = self.factory.makeBranch(product=product)
524 for i in range(10):
525 self.factory.makeBranchMergeProposal(target_branch=branch)
526 stacked = self.factory.makeBranch(product=product)
527 source = self.factory.makeBranch(stacked_on=stacked, product=product)
528 prereq = self.factory.makeBranch(product=product)
529 self.factory.makeBranchMergeProposal(
530 source_branch=source, target_branch=branch,
531 prerequisite_branch=prereq)
532 Store.of(branch).flush()
533 Store.of(branch).invalidate()
534 view = create_view(branch, '+index')
535 with StormStatementRecorder() as recorder:
536 view.landing_candidates
537 self.assertThat(recorder, HasQueryCount(Equals(5)))
538
544539
545class TestBranchViewPrivateArtifacts(BrowserTestCase):540class TestBranchViewPrivateArtifacts(BrowserTestCase):
546 """ Tests that branches with private team artifacts can be viewed.541 """ Tests that branches with private team artifacts can be viewed.
@@ -733,28 +728,23 @@
733 # the branch.728 # the branch.
734 branch = self.factory.makeAnyBranch()729 branch = self.factory.makeAnyBranch()
735 self.assertIs(None, branch.reviewer)730 self.assertIs(None, branch.reviewer)
736 view = BranchReviewerEditView(branch, LaunchpadTestRequest())731 view = create_view(branch, '+reviewer')
737 self.assertEqual(732 self.assertEqual(branch.owner, view.initial_values['reviewer'])
738 branch.owner,
739 view.initial_values['reviewer'])
740733
741 def test_initial_reviewer_set(self):734 def test_initial_reviewer_set(self):
742 # If the reviewer has been set, it is shown as the initial value.735 # If the reviewer has been set, it is shown as the initial value.
743 branch = self.factory.makeAnyBranch()736 branch = self.factory.makeAnyBranch()
744 login_person(branch.owner)737 login_person(branch.owner)
745 branch.reviewer = self.factory.makePerson()738 branch.reviewer = self.factory.makePerson()
746 view = BranchReviewerEditView(branch, LaunchpadTestRequest())739 view = create_view(branch, '+reviewer')
747 self.assertEqual(740 self.assertEqual(branch.reviewer, view.initial_values['reviewer'])
748 branch.reviewer,
749 view.initial_values['reviewer'])
750741
751 def test_set_reviewer(self):742 def test_set_reviewer(self):
752 # Test setting the reviewer.743 # Test setting the reviewer.
753 branch = self.factory.makeAnyBranch()744 branch = self.factory.makeAnyBranch()
754 reviewer = self.factory.makePerson()745 reviewer = self.factory.makePerson()
755 login_person(branch.owner)746 login_person(branch.owner)
756 view = BranchReviewerEditView(branch, LaunchpadTestRequest())747 view = create_initialized_view(branch, '+reviewer')
757 view.initialize()
758 view.change_action.success({'reviewer': reviewer})748 view.change_action.success({'reviewer': reviewer})
759 self.assertEqual(reviewer, branch.reviewer)749 self.assertEqual(reviewer, branch.reviewer)
760 # Last modified has been updated.750 # Last modified has been updated.
@@ -767,8 +757,7 @@
767 branch = self.factory.makeAnyBranch()757 branch = self.factory.makeAnyBranch()
768 login_person(branch.owner)758 login_person(branch.owner)
769 branch.reviewer = self.factory.makePerson()759 branch.reviewer = self.factory.makePerson()
770 view = BranchReviewerEditView(branch, LaunchpadTestRequest())760 view = create_initialized_view(branch, '+reviewer')
771 view.initialize()
772 view.change_action.success({'reviewer': branch.owner})761 view.change_action.success({'reviewer': branch.owner})
773 self.assertIs(None, branch.reviewer)762 self.assertIs(None, branch.reviewer)
774 # Last modified has been updated.763 # Last modified has been updated.
@@ -781,8 +770,7 @@
781 # modified is not updated.770 # modified is not updated.
782 modified_date = datetime(2007, 1, 1, tzinfo=pytz.UTC)771 modified_date = datetime(2007, 1, 1, tzinfo=pytz.UTC)
783 branch = self.factory.makeAnyBranch(date_created=modified_date)772 branch = self.factory.makeAnyBranch(date_created=modified_date)
784 view = BranchReviewerEditView(branch, LaunchpadTestRequest())773 view = create_initialized_view(branch, '+reviewer')
785 view.initialize()
786 view.change_action.success({'reviewer': branch.owner})774 view.change_action.success({'reviewer': branch.owner})
787 self.assertIs(None, branch.reviewer)775 self.assertIs(None, branch.reviewer)
788 # Last modified has not been updated.776 # Last modified has not been updated.
@@ -803,8 +791,8 @@
803 # the development focus branch.791 # the development focus branch.
804 login_person(product.owner)792 login_person(product.owner)
805 product.development_focus.branch = branch793 product.development_focus.branch = branch
806 view = PersonOwnedBranchesView(branch.owner, LaunchpadTestRequest())794 view = create_initialized_view(
807 view.initialize()795 branch.owner, '+ownedbranches', rootsite='code')
808 navigator = view.branches()796 navigator = view.branches()
809 [decorated_branch] = navigator.branches797 [decorated_branch] = navigator.branches
810 self.assertEqual("lp://dev/fooix", decorated_branch.bzr_identity)798 self.assertEqual("lp://dev/fooix", decorated_branch.bzr_identity)
@@ -815,15 +803,12 @@
815803
816 layer = DatabaseFunctionalLayer804 layer = DatabaseFunctionalLayer
817805
818 def setUp(self):
819 TestCaseWithFactory.setUp(self)
820
821 def test_public_target(self):806 def test_public_target(self):
822 # If the user can see the target, then there are merges, and the807 # If the user can see the target, then there are merges, and the
823 # landing_target is available for the template rendering.808 # landing_target is available for the template rendering.
824 bmp = self.factory.makeBranchMergeProposal()809 bmp = self.factory.makeBranchMergeProposal()
825 branch = bmp.source_branch810 branch = bmp.source_branch
826 view = BranchView(branch, LaunchpadTestRequest())811 view = create_view(branch, '+index')
827 self.assertFalse(view.no_merges)812 self.assertFalse(view.no_merges)
828 [target] = view.landing_targets813 [target] = view.landing_targets
829 # Check the ids as the target is a DecoratedMergeProposal.814 # Check the ids as the target is a DecoratedMergeProposal.
@@ -835,7 +820,7 @@
835 branch = bmp.source_branch820 branch = bmp.source_branch
836 removeSecurityProxy(bmp.target_branch).information_type = (821 removeSecurityProxy(bmp.target_branch).information_type = (
837 InformationType.USERDATA)822 InformationType.USERDATA)
838 view = BranchView(branch, LaunchpadTestRequest())823 view = create_view(branch, '+index')
839 self.assertTrue(view.no_merges)824 self.assertTrue(view.no_merges)
840 self.assertEqual([], view.landing_targets)825 self.assertEqual([], view.landing_targets)
841826
@@ -844,7 +829,7 @@
844 # landing_candidate is available for the template rendering.829 # landing_candidate is available for the template rendering.
845 bmp = self.factory.makeBranchMergeProposal()830 bmp = self.factory.makeBranchMergeProposal()
846 branch = bmp.target_branch831 branch = bmp.target_branch
847 view = BranchView(branch, LaunchpadTestRequest())832 view = create_view(branch, '+index')
848 self.assertFalse(view.no_merges)833 self.assertFalse(view.no_merges)
849 [candidate] = view.landing_candidates834 [candidate] = view.landing_candidates
850 # Check the ids as the target is a DecoratedMergeProposal.835 # Check the ids as the target is a DecoratedMergeProposal.
@@ -857,7 +842,7 @@
857 branch = bmp.target_branch842 branch = bmp.target_branch
858 removeSecurityProxy(bmp.source_branch).information_type = (843 removeSecurityProxy(bmp.source_branch).information_type = (
859 InformationType.USERDATA)844 InformationType.USERDATA)
860 view = BranchView(branch, LaunchpadTestRequest())845 view = create_view(branch, '+index')
861 self.assertTrue(view.no_merges)846 self.assertTrue(view.no_merges)
862 self.assertEqual([], view.landing_candidates)847 self.assertEqual([], view.landing_candidates)
863848
@@ -866,7 +851,7 @@
866 # there are merges.851 # there are merges.
867 branch = self.factory.makeProductBranch()852 branch = self.factory.makeProductBranch()
868 bmp = self.factory.makeBranchMergeProposal(prerequisite_branch=branch)853 bmp = self.factory.makeBranchMergeProposal(prerequisite_branch=branch)
869 view = BranchView(branch, LaunchpadTestRequest())854 view = create_view(branch, '+index')
870 self.assertFalse(view.no_merges)855 self.assertFalse(view.no_merges)
871 [proposal] = view.dependent_branches856 [proposal] = view.dependent_branches
872 self.assertEqual(bmp, proposal)857 self.assertEqual(bmp, proposal)
@@ -878,7 +863,7 @@
878 bmp = self.factory.makeBranchMergeProposal(prerequisite_branch=branch)863 bmp = self.factory.makeBranchMergeProposal(prerequisite_branch=branch)
879 removeSecurityProxy(bmp.source_branch).information_type = (864 removeSecurityProxy(bmp.source_branch).information_type = (
880 InformationType.USERDATA)865 InformationType.USERDATA)
881 view = BranchView(branch, LaunchpadTestRequest())866 view = create_view(branch, '+index')
882 self.assertTrue(view.no_merges)867 self.assertTrue(view.no_merges)
883 self.assertEqual([], view.dependent_branches)868 self.assertEqual([], view.dependent_branches)
884869
@@ -1009,8 +994,7 @@
1009 browser.getControl("Change Branch").click()994 browser.getControl("Change Branch").click()
1010 self.assertThat(995 self.assertThat(
1011 browser.contents,996 browser.contents,
1012 Contains(997 Contains('Public branches are not allowed for target Commercial.'))
1013 'Public branches are not allowed for target Commercial.'))
1014 with person_logged_in(owner):998 with person_logged_in(owner):
1015 self.assertEquals(initial_target, branch.target.context)999 self.assertEquals(initial_target, branch.target.context)
10161000
@@ -1026,8 +1010,7 @@
1026 browser.getControl("Private", index=1).click()1010 browser.getControl("Private", index=1).click()
1027 browser.getControl("Change Branch").click()1011 browser.getControl("Change Branch").click()
1028 with person_logged_in(person):1012 with person_logged_in(person):
1029 self.assertEqual(1013 self.assertEqual(InformationType.USERDATA, branch.information_type)
1030 InformationType.USERDATA, branch.information_type)
10311014
1032 def test_can_not_change_privacy_of_stacked_on_private(self):1015 def test_can_not_change_privacy_of_stacked_on_private(self):
1033 # The privacy field is not shown if the branch is stacked on a1016 # The privacy field is not shown if the branch is stacked on a
@@ -1042,8 +1025,7 @@
1042 with person_logged_in(owner):1025 with person_logged_in(owner):
1043 browser = self.getUserBrowser(1026 browser = self.getUserBrowser(
1044 canonical_url(branch) + '/+edit', user=owner)1027 canonical_url(branch) + '/+edit', user=owner)
1045 self.assertRaises(1028 self.assertRaises(LookupError, browser.getControl, "Information Type")
1046 LookupError, browser.getControl, "Information Type")
10471029
1048 def test_edit_view_ajax_render(self):1030 def test_edit_view_ajax_render(self):
1049 # An information type change request is processed as expected when an1031 # An information type change request is processed as expected when an
@@ -1058,11 +1040,10 @@
1058 'field.information_type': 'PUBLICSECURITY'},1040 'field.information_type': 'PUBLICSECURITY'},
1059 **extra)1041 **extra)
1060 with person_logged_in(person):1042 with person_logged_in(person):
1061 view = create_view(1043 view = create_initialized_view(
1062 branch, name='+edit-information-type',1044 branch, name='+edit-information-type',
1063 request=request, principal=person)1045 request=request, principal=person)
1064 request.traversed_objects = [person, branch.product, branch, view]1046 request.traversed_objects = [person, branch.product, branch, view]
1065 view.initialize()
1066 result = view.render()1047 result = view.render()
1067 self.assertEqual('', result)1048 self.assertEqual('', result)
1068 self.assertEqual(1049 self.assertEqual(
@@ -1088,10 +1069,8 @@
1088 branch = self.factory.makeBranch(1069 branch = self.factory.makeBranch(
1089 information_type=InformationType.PUBLIC)1070 information_type=InformationType.PUBLIC)
1090 self.assertShownTypes(1071 self.assertShownTypes(
1091 [InformationType.PUBLIC,1072 [InformationType.PUBLIC, InformationType.PUBLICSECURITY,
1092 InformationType.PUBLICSECURITY,1073 InformationType.PRIVATESECURITY, InformationType.USERDATA],
1093 InformationType.PRIVATESECURITY,
1094 InformationType.USERDATA],
1095 branch)1074 branch)
10961075
1097 def test_branch_with_disallowed_type(self):1076 def test_branch_with_disallowed_type(self):
@@ -1103,12 +1082,9 @@
1103 branch = self.factory.makeBranch(1082 branch = self.factory.makeBranch(
1104 product=product, information_type=InformationType.PROPRIETARY)1083 product=product, information_type=InformationType.PROPRIETARY)
1105 self.assertShownTypes(1084 self.assertShownTypes(
1106 [InformationType.PUBLIC,1085 [InformationType.PUBLIC, InformationType.PUBLICSECURITY,
1107 InformationType.PUBLICSECURITY,1086 InformationType.PRIVATESECURITY, InformationType.USERDATA,
1108 InformationType.PRIVATESECURITY,1087 InformationType.PROPRIETARY], branch)
1109 InformationType.USERDATA,
1110 InformationType.PROPRIETARY],
1111 branch)
11121088
1113 def test_stacked_on_private(self):1089 def test_stacked_on_private(self):
1114 # A branch stacked on a private branch has its choices limited1090 # A branch stacked on a private branch has its choices limited
11151091
=== modified file 'lib/lp/code/configure.zcml'
--- lib/lp/code/configure.zcml 2013-01-22 02:06:41 +0000
+++ lib/lp/code/configure.zcml 2013-02-25 05:26:21 +0000
@@ -1,4 +1,4 @@
1<!-- Copyright 2009-2011 Canonical Ltd. This software is licensed under the1<!-- Copyright 2009-2013 Canonical Ltd. This software is licensed under the
2 GNU Affero General Public License version 3 (see the file LICENSE).2 GNU Affero General Public License version 3 (see the file LICENSE).
3-->3-->
44
@@ -223,8 +223,10 @@
223 id223 id
224 registrant224 registrant
225 source_branch225 source_branch
226 source_branchID
226 target_branch227 target_branch
227 prerequisite_branch228 prerequisite_branch
229 prerequisite_branchID
228 description230 description
229 whiteboard231 whiteboard
230 queue_status232 queue_status