Merge lp://staging/~jml/launchpad/ubuntu-package-bug-345737 into lp://staging/launchpad
- ubuntu-package-bug-345737
- Merge into devel
Status: | Merged | ||||
---|---|---|---|---|---|
Merged at revision: | not available | ||||
Proposed branch: | lp://staging/~jml/launchpad/ubuntu-package-bug-345737 | ||||
Merge into: | lp://staging/launchpad | ||||
Diff against target: | None lines | ||||
To merge this branch: | bzr merge lp://staging/~jml/launchpad/ubuntu-package-bug-345737 | ||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Tim Penhey (community) | Approve | ||
Review via email:
|
Commit message
Description of the change
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Jonathan Lange (jml) wrote : | # |
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Tim Penhey (thumper) wrote : | # |
> bazaar_identity has been changed to return 'lp:ubuntu/package' for source
> package branches associated to the development focus package.
My first thought on reading this was "for just the release pocket?", but I
guess we'll see.
> === modified file 'lib/lp/
> --- lib/lp/
> +++ lib/lp/
> @@ -1131,6 +1130,10 @@
> 'series': use_series.name}
>
> if branch.
> + distro_package = branch.
> + linked_branch = ICanHasLinkedBr
> + if linked_
> + return lp_prefix + linked_
What object traversal is really going on here? And how will it impact getting
bazaar_identities for branch listings?
branch.
.distribution_
Adaption -> other queries?
> === modified file 'lib/lp/
> --- lib/lp/
> +++ lib/lp/
> @@ -30,6 +33,16 @@
> """See `ICanHasLinkedB
> return self.product_
>
> + @property
> + def bzr_identity(self):
> + """See `ICanHasLinkedB
> + return '/'.join(
> + [self.product_
I think that the bzr_identity here (and in following adapters) should really
have the lp: prefix too (from the config variable). Otherwise bzr_identity
functions in different places return different types of strings.
Also, it shouldn't really return anything if there is not a branch set.
> @@ -45,6 +58,15 @@
> """See `ICanHasLinkedB
> return ICanHasLinkedBr
>
> + @property
> + def bzr_identity(self):
> + """See `ICanHasLinkedB
> + return self.product.name
> +
> + def setBranch(self, branch, registrant=None):
> + """See `ICanHasLinkedB
> +
ICanHasLinkedBr
I think you should also pass the registrant through. If we start to record
it, it'll save changing this later.
> +
>
> class PackageLinkedBr
> """Implement a linked branch for a source package pocket."""
> @@ -61,3 +83,50 @@
> package = self.suite_
> pocket = self.suite_
> return package.
> +
> + @property
> + def bzr_identity(self):
> + """See `ICanHasLinkedB
> + return self.suite_
> +
> + def setBranch(self, branch, registrant):
> + """See `ICanHasLinkedB
> + package = self.suite_
> + pocket = self.suite_
> + package.
> +
> +
> +class DistributionPac
> + """Implement a linked branch for an `IDistributionS
> +
...
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Jonathan Lange (jml) wrote : | # |
On Fri, Jul 17, 2009 at 11:00 AM, Tim Penhey<email address hidden> wrote:
> Review: Needs Information
>> bazaar_identity has been changed to return 'lp:ubuntu/package' for source
>> package branches associated to the development focus package.
>
> My first thought on reading this was "for just the release pocket?", but I
> guess we'll see.
>
Yeah, just for the release pocket.
>> === modified file 'lib/lp/
>> --- lib/lp/
>> +++ lib/lp/
>> @@ -1131,6 +1130,10 @@
>> 'series': use_series.name}
>>
>> if branch.
>> + distro_package = branch.
>> + linked_branch = ICanHasLinkedBr
>> + if linked_
>> + return lp_prefix + linked_
>
> What object traversal is really going on here? And how will it impact getting
> bazaar_identities for branch listings?
>
> branch.
> .distribution_
No queries, it's a virtual object.
> Adaption -> other queries?
>
No queries in adaptation either.
However, getting the bzr_identity from the adapted object requires
loading the currentseries from the DistroSeries table. You probably
want to have a look at Distribution.
some of your other questions.
If possible, I'd rather land this branch now with some performance
issues than in 2 months time.
>> === modified file 'lib/lp/
>> --- lib/lp/
>> +++ lib/lp/
>> @@ -30,6 +33,16 @@
>> """See `ICanHasLinkedB
>> return self.product_
>>
>> + @property
>> + def bzr_identity(self):
>> + """See `ICanHasLinkedB
>> + return '/'.join(
>> + [self.product_
>
> I think that the bzr_identity here (and in following adapters) should really
> have the lp: prefix too (from the config variable). Otherwise bzr_identity
> functions in different places return different types of strings.
>
Fair enough, although in some ways I'd prefer keeping the current
behaviour and picking a different name. It seems wrong to imbue this
code with knowledge of the name of the server.
Oh, I see later on you suggest the names 'bzr_path' and
'bzr_identity_path' for a property with the current behaviour. I like
that.
Changed.
> Also, it shouldn't really return anything if there is not a branch set.
>
Why do you say that?
>> @@ -45,6 +58,15 @@
>> """See `ICanHasLinkedB
>> return ICanHasLinkedBr
>>
>> + @property
>> + def bzr_identity(self):
>> + """See `ICanHasLinkedB
>> + return self.product.name
>> +
>> + def setBranch(self, branch, registrant=None):
>> + """See `ICanHasLinkedB
>> +
> ICanHasLinkedBr
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Tim Penhey (thumper) wrote : | # |
On Fri, 17 Jul 2009 20:03:07 Jonathan Lange wrote:
> If possible, I'd rather land this branch now with some performance
> issues than in 2 months time.
I agree.
> > Also, it shouldn't really return anything if there is not a branch set.
>
> Why do you say that?
OK, I guess not. As long as the documentation says that the bzr_path is the
path of the branch if there was a branch set.
> > ICanHasLinkedBr
>
> Your wrapping here is very weird.
I blame my mail client.
> > This one has always bothered me. We should not allow junk branches to be
> > linked rather than just saying that their links don't show it. What do
> > you think?
>
> As a general principle, I think we should do as little special-casing
> of +junk as possible. It always comes back to bite us.
>
> I personally think that we can do without this special case at all.
> However, if pressed, I'd favour simply preventing junk branches from
> being linked. I think it's for another branch though.
Definitely another branch.
> > To save with the login, logout dance, how about we just remove the
> > security proxy as with the other tests, but still pass the registrant
> > through.
>
> Doesn't work. Internally, the link is made by code that looks like:
> getUtility(
> distroseries, sourcepackagename)
>
> getUtility guarantees that the object is secured, and 'new' requires
> lp.Edit permissions.
Aah, ok. I wish we had a nicer way to test this without the logins, but not
for this branch.
> > The main questions I have resolve around the currentseries for a
> > distribution, and the actual content of ICanHasLinkedBr
>
> Cool. I hope they're addressed here.
Yes. I wish that distribution always had one series by default like a
Product, but I think you've done as well as you can given that "some"
distributions may well not have set any series.
review approve
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Jonathan Lange (jml) wrote : | # |
On Mon, Jul 20, 2009 at 8:48 AM, Tim Penhey<email address hidden> wrote:
> Review: Approve
> On Fri, 17 Jul 2009 20:03:07 Jonathan Lange wrote:
>> If possible, I'd rather land this branch now with some performance
>> issues than in 2 months time.
>
> I agree.
>
>> > Also, it shouldn't really return anything if there is not a branch set.
>>
>> Why do you say that?
>
> OK, I guess not. As long as the documentation says that the bzr_path is the
> path of the branch if there was a branch set.
>
Done.
Landing now.
jml
Preview Diff
1 | === modified file 'lib/lp/code/configure.zcml' | |||
2 | --- lib/lp/code/configure.zcml 2009-07-15 01:45:35 +0000 | |||
3 | +++ lib/lp/code/configure.zcml 2009-07-16 04:47:24 +0000 | |||
4 | @@ -801,6 +801,7 @@ | |||
5 | 801 | <adapter factory="lp.code.model.linkedbranch.ProductSeriesLinkedBranch" /> | 801 | <adapter factory="lp.code.model.linkedbranch.ProductSeriesLinkedBranch" /> |
6 | 802 | <adapter factory="lp.code.model.linkedbranch.ProductLinkedBranch" /> | 802 | <adapter factory="lp.code.model.linkedbranch.ProductLinkedBranch" /> |
7 | 803 | <adapter factory="lp.code.model.linkedbranch.PackageLinkedBranch" /> | 803 | <adapter factory="lp.code.model.linkedbranch.PackageLinkedBranch" /> |
8 | 804 | <adapter factory="lp.code.model.linkedbranch.DistributionPackageLinkedBranch" /> | ||
9 | 804 | 805 | ||
10 | 805 | <lp:help-folder | 806 | <lp:help-folder |
11 | 806 | folder="help" type="canonical.launchpad.layers.CodeLayer" /> | 807 | folder="help" type="canonical.launchpad.layers.CodeLayer" /> |
12 | 807 | 808 | ||
13 | === modified file 'lib/lp/code/interfaces/branch.py' | |||
14 | --- lib/lp/code/interfaces/branch.py 2009-07-15 01:45:35 +0000 | |||
15 | +++ lib/lp/code/interfaces/branch.py 2009-07-16 04:47:24 +0000 | |||
16 | @@ -64,6 +64,7 @@ | |||
17 | 64 | ) | 64 | ) |
18 | 65 | from lp.code.interfaces.branchlookup import IBranchLookup | 65 | from lp.code.interfaces.branchlookup import IBranchLookup |
19 | 66 | from lp.code.interfaces.branchtarget import IHasBranchTarget | 66 | from lp.code.interfaces.branchtarget import IHasBranchTarget |
20 | 67 | from lp.code.interfaces.linkedbranch import ICanHasLinkedBranch | ||
21 | 67 | from canonical.launchpad.interfaces.launchpad import ILaunchpadCelebrities | 68 | from canonical.launchpad.interfaces.launchpad import ILaunchpadCelebrities |
22 | 68 | from lp.registry.interfaces.role import IHasOwner | 69 | from lp.registry.interfaces.role import IHasOwner |
23 | 69 | from lp.registry.interfaces.person import IPerson | 70 | from lp.registry.interfaces.person import IPerson |
24 | @@ -81,6 +82,7 @@ | |||
25 | 81 | class BranchCreationException(Exception): | 82 | class BranchCreationException(Exception): |
26 | 82 | """Base class for branch creation exceptions.""" | 83 | """Base class for branch creation exceptions.""" |
27 | 83 | 84 | ||
28 | 85 | |||
29 | 84 | class BranchExists(BranchCreationException): | 86 | class BranchExists(BranchCreationException): |
30 | 85 | """Raised when creating a branch that already exists.""" | 87 | """Raised when creating a branch that already exists.""" |
31 | 86 | 88 | ||
32 | @@ -738,8 +740,7 @@ | |||
33 | 738 | 'development focus, then the result should be lp:product. ' | 740 | 'development focus, then the result should be lp:product. ' |
34 | 739 | 'If the branch is related to a series, then ' | 741 | 'If the branch is related to a series, then ' |
35 | 740 | 'lp:product/series. Otherwise the result is ' | 742 | 'lp:product/series. Otherwise the result is ' |
38 | 741 | 'lp:~user/product/branch-name.' | 743 | 'lp:~user/product/branch-name.'))) |
37 | 742 | ))) | ||
39 | 743 | 744 | ||
40 | 744 | def addToLaunchBag(launchbag): | 745 | def addToLaunchBag(launchbag): |
41 | 745 | """Add information about this branch to `launchbag'. | 746 | """Add information about this branch to `launchbag'. |
42 | @@ -1083,8 +1084,6 @@ | |||
43 | 1083 | last_scanned_id = Attribute("The revision id of the tip revision.") | 1084 | last_scanned_id = Attribute("The revision id of the tip revision.") |
44 | 1084 | 1085 | ||
45 | 1085 | 1086 | ||
46 | 1086 | |||
47 | 1087 | |||
48 | 1088 | class IBranchCloud(Interface): | 1087 | class IBranchCloud(Interface): |
49 | 1089 | """A utility to generate data for branch clouds. | 1088 | """A utility to generate data for branch clouds. |
50 | 1090 | 1089 | ||
51 | @@ -1131,6 +1130,10 @@ | |||
52 | 1131 | 'series': use_series.name} | 1130 | 'series': use_series.name} |
53 | 1132 | 1131 | ||
54 | 1133 | if branch.sourcepackage is not None: | 1132 | if branch.sourcepackage is not None: |
55 | 1133 | distro_package = branch.sourcepackage.distribution_sourcepackage | ||
56 | 1134 | linked_branch = ICanHasLinkedBranch(distro_package) | ||
57 | 1135 | if linked_branch.branch == branch: | ||
58 | 1136 | return lp_prefix + linked_branch.bzr_identity | ||
59 | 1134 | suite_sourcepackages = branch.associatedSuiteSourcePackages() | 1137 | suite_sourcepackages = branch.associatedSuiteSourcePackages() |
60 | 1135 | # Take the first link if there is one. | 1138 | # Take the first link if there is one. |
61 | 1136 | if len(suite_sourcepackages) > 0: | 1139 | if len(suite_sourcepackages) > 0: |
62 | 1137 | 1140 | ||
63 | === modified file 'lib/lp/code/interfaces/branchlookup.py' | |||
64 | --- lib/lp/code/interfaces/branchlookup.py 2009-05-15 03:16:18 +0000 | |||
65 | +++ lib/lp/code/interfaces/branchlookup.py 2009-07-16 05:00:18 +0000 | |||
66 | @@ -16,8 +16,14 @@ | |||
67 | 16 | class ILinkedBranchTraversable(Interface): | 16 | class ILinkedBranchTraversable(Interface): |
68 | 17 | """A thing that can be traversed to find a thing linked to a branch.""" | 17 | """A thing that can be traversed to find a thing linked to a branch.""" |
69 | 18 | 18 | ||
72 | 19 | def traverse(self, name): | 19 | def traverse(self, name, segments): |
73 | 20 | """Return the object beneath this one that matches 'name'.""" | 20 | """Return the object beneath this one that matches 'name'. |
74 | 21 | |||
75 | 22 | :param name: The name of the object being traversed to. | ||
76 | 23 | :param segments: Remaining path segments. | ||
77 | 24 | :return: An `ILinkedBranchTraversable` object if traversing should | ||
78 | 25 | continue, an `ICanHasLinkedBranch` object otherwise. | ||
79 | 26 | """ | ||
80 | 21 | 27 | ||
81 | 22 | 28 | ||
82 | 23 | class ILinkedBranchTraverser(Interface): | 29 | class ILinkedBranchTraverser(Interface): |
83 | @@ -40,7 +46,7 @@ | |||
84 | 40 | :return: One of | 46 | :return: One of |
85 | 41 | * `IProduct` | 47 | * `IProduct` |
86 | 42 | * `IProductSeries` | 48 | * `IProductSeries` |
88 | 43 | * (ISourcePackage, PackagePublishingPocket) | 49 | * `ISuiteSourcePackage` |
89 | 44 | * `IDistributionSourcePackage` | 50 | * `IDistributionSourcePackage` |
90 | 45 | """ | 51 | """ |
91 | 46 | 52 | ||
92 | 47 | 53 | ||
93 | === modified file 'lib/lp/code/interfaces/linkedbranch.py' | |||
94 | --- lib/lp/code/interfaces/linkedbranch.py 2009-05-24 18:09:51 +0000 | |||
95 | +++ lib/lp/code/interfaces/linkedbranch.py 2009-07-16 01:58:59 +0000 | |||
96 | @@ -23,6 +23,16 @@ | |||
97 | 23 | """Something that has a linked branch.""" | 23 | """Something that has a linked branch.""" |
98 | 24 | 24 | ||
99 | 25 | branch = Attribute("The linked branch.") | 25 | branch = Attribute("The linked branch.") |
100 | 26 | bzr_identity = Attribute('The Bazaar branch path for the linked branch.') | ||
101 | 27 | |||
102 | 28 | def setBranch(branch, registrant=None): | ||
103 | 29 | """Set the linked branch. | ||
104 | 30 | |||
105 | 31 | :param branch: An `IBranch`. After calling this, | ||
106 | 32 | `ICanHasLinkedBranch.branch` will be 'branch'. | ||
107 | 33 | :param registrant: The `IPerson` linking the branch. Not used by all | ||
108 | 34 | implementations. | ||
109 | 35 | """ | ||
110 | 26 | 36 | ||
111 | 27 | 37 | ||
112 | 28 | class CannotHaveLinkedBranch(Exception): | 38 | class CannotHaveLinkedBranch(Exception): |
113 | 29 | 39 | ||
114 | === modified file 'lib/lp/code/model/branchlookup.py' | |||
115 | --- lib/lp/code/model/branchlookup.py 2009-05-15 03:16:18 +0000 | |||
116 | +++ lib/lp/code/model/branchlookup.py 2009-07-16 05:39:51 +0000 | |||
117 | @@ -29,7 +29,7 @@ | |||
118 | 29 | from lp.code.interfaces.linkedbranch import get_linked_branch, NoLinkedBranch | 29 | from lp.code.interfaces.linkedbranch import get_linked_branch, NoLinkedBranch |
119 | 30 | from lp.registry.interfaces.distribution import IDistribution | 30 | from lp.registry.interfaces.distribution import IDistribution |
120 | 31 | from lp.registry.interfaces.distroseries import ( | 31 | from lp.registry.interfaces.distroseries import ( |
122 | 32 | IDistroSeries, IDistroSeriesSet) | 32 | IDistroSeries, IDistroSeriesSet, NoSuchDistroSeries) |
123 | 33 | from lp.registry.interfaces.pillar import IPillarNameSet | 33 | from lp.registry.interfaces.pillar import IPillarNameSet |
124 | 34 | from lp.registry.interfaces.product import ( | 34 | from lp.registry.interfaces.product import ( |
125 | 35 | InvalidProductName, IProduct, NoSuchProduct) | 35 | InvalidProductName, IProduct, NoSuchProduct) |
126 | @@ -65,7 +65,7 @@ | |||
127 | 65 | 65 | ||
128 | 66 | implements(ILinkedBranchTraversable) | 66 | implements(ILinkedBranchTraversable) |
129 | 67 | 67 | ||
131 | 68 | def traverse(self, name): | 68 | def traverse(self, name, segments): |
132 | 69 | """See `ITraversable`. | 69 | """See `ITraversable`. |
133 | 70 | 70 | ||
134 | 71 | :raise NoSuchProduct: If 'name' doesn't match an existing pillar. | 71 | :raise NoSuchProduct: If 'name' doesn't match an existing pillar. |
135 | @@ -102,7 +102,7 @@ | |||
136 | 102 | adapts(IProduct) | 102 | adapts(IProduct) |
137 | 103 | implements(ILinkedBranchTraversable) | 103 | implements(ILinkedBranchTraversable) |
138 | 104 | 104 | ||
140 | 105 | def traverse(self, name): | 105 | def traverse(self, name, segments): |
141 | 106 | """See `ITraversable`. | 106 | """See `ITraversable`. |
142 | 107 | 107 | ||
143 | 108 | :raises NoSuchProductSeries: if 'name' doesn't match an existing | 108 | :raises NoSuchProductSeries: if 'name' doesn't match an existing |
144 | @@ -124,12 +124,18 @@ | |||
145 | 124 | adapts(IDistribution) | 124 | adapts(IDistribution) |
146 | 125 | implements(ILinkedBranchTraversable) | 125 | implements(ILinkedBranchTraversable) |
147 | 126 | 126 | ||
149 | 127 | def traverse(self, name): | 127 | def traverse(self, name, segments): |
150 | 128 | """See `ITraversable`.""" | 128 | """See `ITraversable`.""" |
155 | 129 | # XXX: JonathanLange 2009-03-20 spec=package-branches bug=345737: This | 129 | try: |
156 | 130 | # could also try to find a package and then return a reference to its | 130 | return getUtility(IDistroSeriesSet).fromSuite(self.context, name) |
157 | 131 | # development focus. | 131 | except NoSuchDistroSeries: |
158 | 132 | return getUtility(IDistroSeriesSet).fromSuite(self.context, name) | 132 | sourcepackage = self.context.getSourcePackage(name) |
159 | 133 | if sourcepackage is None: | ||
160 | 134 | if segments: | ||
161 | 135 | raise | ||
162 | 136 | else: | ||
163 | 137 | raise NoSuchSourcePackageName(name) | ||
164 | 138 | return sourcepackage | ||
165 | 133 | 139 | ||
166 | 134 | 140 | ||
167 | 135 | class DistroSeriesTraversable: | 141 | class DistroSeriesTraversable: |
168 | @@ -145,7 +151,7 @@ | |||
169 | 145 | self.distroseries = distroseries | 151 | self.distroseries = distroseries |
170 | 146 | self.pocket = pocket | 152 | self.pocket = pocket |
171 | 147 | 153 | ||
173 | 148 | def traverse(self, name): | 154 | def traverse(self, name, segments): |
174 | 149 | """See `ITraversable`.""" | 155 | """See `ITraversable`.""" |
175 | 150 | sourcepackage = self.distroseries.getSourcePackage(name) | 156 | sourcepackage = self.distroseries.getSourcePackage(name) |
176 | 151 | if sourcepackage is None: | 157 | if sourcepackage is None: |
177 | @@ -170,7 +176,7 @@ | |||
178 | 170 | traversable = RootTraversable() | 176 | traversable = RootTraversable() |
179 | 171 | while segments: | 177 | while segments: |
180 | 172 | name = segments.pop(0) | 178 | name = segments.pop(0) |
182 | 173 | context = traversable.traverse(name) | 179 | context = traversable.traverse(name, segments) |
183 | 174 | traversable = adapt(context, ILinkedBranchTraversable) | 180 | traversable = adapt(context, ILinkedBranchTraversable) |
184 | 175 | if traversable is None: | 181 | if traversable is None: |
185 | 176 | break | 182 | break |
186 | @@ -332,7 +338,7 @@ | |||
187 | 332 | namespace_set = getUtility(IBranchNamespaceSet) | 338 | namespace_set = getUtility(IBranchNamespaceSet) |
188 | 333 | segments = iter(path.lstrip('~').split('/')) | 339 | segments = iter(path.lstrip('~').split('/')) |
189 | 334 | branch = namespace_set.traverse(segments) | 340 | branch = namespace_set.traverse(segments) |
191 | 335 | suffix = '/'.join(segments) | 341 | suffix = '/'.join(segments) |
192 | 336 | if not check_permission('launchpad.View', branch): | 342 | if not check_permission('launchpad.View', branch): |
193 | 337 | raise NoSuchBranch(path) | 343 | raise NoSuchBranch(path) |
194 | 338 | if suffix == '': | 344 | if suffix == '': |
195 | 339 | 345 | ||
196 | === modified file 'lib/lp/code/model/linkedbranch.py' | |||
197 | --- lib/lp/code/model/linkedbranch.py 2009-05-15 03:16:18 +0000 | |||
198 | +++ lib/lp/code/model/linkedbranch.py 2009-07-16 05:38:41 +0000 | |||
199 | @@ -11,9 +11,12 @@ | |||
200 | 11 | from zope.interface import implements | 11 | from zope.interface import implements |
201 | 12 | 12 | ||
202 | 13 | from lp.code.interfaces.linkedbranch import ICanHasLinkedBranch | 13 | from lp.code.interfaces.linkedbranch import ICanHasLinkedBranch |
203 | 14 | from lp.registry.interfaces.distributionsourcepackage import ( | ||
204 | 15 | IDistributionSourcePackage) | ||
205 | 14 | from lp.registry.interfaces.product import IProduct | 16 | from lp.registry.interfaces.product import IProduct |
206 | 15 | from lp.registry.interfaces.productseries import IProductSeries | 17 | from lp.registry.interfaces.productseries import IProductSeries |
207 | 16 | from lp.registry.interfaces.suitesourcepackage import ISuiteSourcePackage | 18 | from lp.registry.interfaces.suitesourcepackage import ISuiteSourcePackage |
208 | 19 | from lp.soyuz.interfaces.publishing import PackagePublishingPocket | ||
209 | 17 | 20 | ||
210 | 18 | 21 | ||
211 | 19 | class ProductSeriesLinkedBranch: | 22 | class ProductSeriesLinkedBranch: |
212 | @@ -30,6 +33,16 @@ | |||
213 | 30 | """See `ICanHasLinkedBranch`.""" | 33 | """See `ICanHasLinkedBranch`.""" |
214 | 31 | return self.product_series.branch | 34 | return self.product_series.branch |
215 | 32 | 35 | ||
216 | 36 | @property | ||
217 | 37 | def bzr_identity(self): | ||
218 | 38 | """See `ICanHasLinkedBranch`.""" | ||
219 | 39 | return '/'.join( | ||
220 | 40 | [self.product_series.product.name, self.product_series.name]) | ||
221 | 41 | |||
222 | 42 | def setBranch(self, branch, registrant=None): | ||
223 | 43 | """See `ICanHasLinkedBranch`.""" | ||
224 | 44 | self.product_series.branch = branch | ||
225 | 45 | |||
226 | 33 | 46 | ||
227 | 34 | class ProductLinkedBranch: | 47 | class ProductLinkedBranch: |
228 | 35 | """Implement a linked branch for a product.""" | 48 | """Implement a linked branch for a product.""" |
229 | @@ -45,6 +58,15 @@ | |||
230 | 45 | """See `ICanHasLinkedBranch`.""" | 58 | """See `ICanHasLinkedBranch`.""" |
231 | 46 | return ICanHasLinkedBranch(self.product.development_focus).branch | 59 | return ICanHasLinkedBranch(self.product.development_focus).branch |
232 | 47 | 60 | ||
233 | 61 | @property | ||
234 | 62 | def bzr_identity(self): | ||
235 | 63 | """See `ICanHasLinkedBranch`.""" | ||
236 | 64 | return self.product.name | ||
237 | 65 | |||
238 | 66 | def setBranch(self, branch, registrant=None): | ||
239 | 67 | """See `ICanHasLinkedBranch`.""" | ||
240 | 68 | ICanHasLinkedBranch(self.product.development_focus).setBranch(branch) | ||
241 | 69 | |||
242 | 48 | 70 | ||
243 | 49 | class PackageLinkedBranch: | 71 | class PackageLinkedBranch: |
244 | 50 | """Implement a linked branch for a source package pocket.""" | 72 | """Implement a linked branch for a source package pocket.""" |
245 | @@ -61,3 +83,50 @@ | |||
246 | 61 | package = self.suite_sourcepackage.sourcepackage | 83 | package = self.suite_sourcepackage.sourcepackage |
247 | 62 | pocket = self.suite_sourcepackage.pocket | 84 | pocket = self.suite_sourcepackage.pocket |
248 | 63 | return package.getBranch(pocket) | 85 | return package.getBranch(pocket) |
249 | 86 | |||
250 | 87 | @property | ||
251 | 88 | def bzr_identity(self): | ||
252 | 89 | """See `ICanHasLinkedBranch`.""" | ||
253 | 90 | return self.suite_sourcepackage.path | ||
254 | 91 | |||
255 | 92 | def setBranch(self, branch, registrant): | ||
256 | 93 | """See `ICanHasLinkedBranch`.""" | ||
257 | 94 | package = self.suite_sourcepackage.sourcepackage | ||
258 | 95 | pocket = self.suite_sourcepackage.pocket | ||
259 | 96 | package.setBranch(pocket, branch, registrant) | ||
260 | 97 | |||
261 | 98 | |||
262 | 99 | class DistributionPackageLinkedBranch: | ||
263 | 100 | """Implement a linked branch for an `IDistributionSourcePackage`.""" | ||
264 | 101 | |||
265 | 102 | adapts(IDistributionSourcePackage) | ||
266 | 103 | implements(ICanHasLinkedBranch) | ||
267 | 104 | |||
268 | 105 | def __init__(self, distribution_sourcepackage): | ||
269 | 106 | self._distribution_sourcepackage = distribution_sourcepackage | ||
270 | 107 | |||
271 | 108 | @property | ||
272 | 109 | def branch(self): | ||
273 | 110 | """See `ICanHasLinkedBranch`.""" | ||
274 | 111 | development_package = ( | ||
275 | 112 | self._distribution_sourcepackage.development_version) | ||
276 | 113 | if development_package is None: | ||
277 | 114 | return None | ||
278 | 115 | suite_sourcepackage = development_package.getSuiteSourcePackage( | ||
279 | 116 | PackagePublishingPocket.RELEASE) | ||
280 | 117 | return ICanHasLinkedBranch(suite_sourcepackage).branch | ||
281 | 118 | |||
282 | 119 | @property | ||
283 | 120 | def bzr_identity(self): | ||
284 | 121 | """See `ICanHasLinkedBranch`.""" | ||
285 | 122 | return '/'.join( | ||
286 | 123 | [self._distribution_sourcepackage.distribution.name, | ||
287 | 124 | self._distribution_sourcepackage.sourcepackagename.name]) | ||
288 | 125 | |||
289 | 126 | def setBranch(self, branch, registrant): | ||
290 | 127 | """See `ICanHasLinkedBranch`.""" | ||
291 | 128 | development_package = ( | ||
292 | 129 | self._distribution_sourcepackage.development_version) | ||
293 | 130 | suite_sourcepackage = development_package.getSuiteSourcePackage( | ||
294 | 131 | PackagePublishingPocket.RELEASE) | ||
295 | 132 | ICanHasLinkedBranch(suite_sourcepackage).setBranch(branch, registrant) | ||
296 | 64 | 133 | ||
297 | === modified file 'lib/lp/code/model/tests/test_branch.py' | |||
298 | --- lib/lp/code/model/tests/test_branch.py 2009-07-08 04:57:51 +0000 | |||
299 | +++ lib/lp/code/model/tests/test_branch.py 2009-07-16 03:22:24 +0000 | |||
300 | @@ -20,49 +20,49 @@ | |||
301 | 20 | from canonical.config import config | 20 | from canonical.config import config |
302 | 21 | from canonical.database.constants import UTC_NOW | 21 | from canonical.database.constants import UTC_NOW |
303 | 22 | from canonical.launchpad import _ | 22 | from canonical.launchpad import _ |
304 | 23 | from lp.code.model.branch import ( | ||
305 | 24 | ClearDependentBranch, ClearOfficialPackageBranch, ClearSeriesBranch, | ||
306 | 25 | DeleteCodeImport, DeletionCallable, DeletionOperation, | ||
307 | 26 | update_trigger_modified_fields) | ||
308 | 27 | from lp.code.model.branchjob import ( | ||
309 | 28 | BranchDiffJob, BranchJob, BranchJobType, ReclaimBranchSpaceJob) | ||
310 | 29 | from lp.code.model.branchmergeproposal import ( | ||
311 | 30 | BranchMergeProposal) | ||
312 | 31 | from lp.bugs.model.bugbranch import BugBranch | ||
313 | 32 | from lp.code.model.codeimport import CodeImport, CodeImportSet | ||
314 | 33 | from lp.code.model.codereviewcomment import CodeReviewComment | ||
315 | 34 | from lp.registry.model.product import ProductSet | ||
316 | 35 | from lp.blueprints.model.specificationbranch import ( | ||
317 | 36 | SpecificationBranch) | ||
318 | 37 | from lp.registry.model.sourcepackage import SourcePackage | ||
319 | 38 | from canonical.launchpad.ftests import ( | 23 | from canonical.launchpad.ftests import ( |
320 | 39 | ANONYMOUS, login, login_person, logout, syncUpdate) | 24 | ANONYMOUS, login, login_person, logout, syncUpdate) |
322 | 40 | from lp.bugs.interfaces.bug import CreateBugParams, IBugSet | 25 | from canonical.launchpad.interfaces.launchpad import ILaunchpadCelebrities |
323 | 26 | from canonical.launchpad.webapp.interfaces import IOpenLaunchBag | ||
324 | 27 | from canonical.testing import DatabaseFunctionalLayer, LaunchpadZopelessLayer | ||
325 | 28 | |||
326 | 41 | from lp.blueprints.interfaces.specification import ( | 29 | from lp.blueprints.interfaces.specification import ( |
327 | 42 | ISpecificationSet, SpecificationDefinitionStatus) | 30 | ISpecificationSet, SpecificationDefinitionStatus) |
328 | 31 | from lp.blueprints.model.specificationbranch import ( | ||
329 | 32 | SpecificationBranch) | ||
330 | 33 | from lp.bugs.interfaces.bug import CreateBugParams, IBugSet | ||
331 | 34 | from lp.bugs.model.bugbranch import BugBranch | ||
332 | 43 | from lp.code.bzr import BranchFormat, RepositoryFormat | 35 | from lp.code.bzr import BranchFormat, RepositoryFormat |
333 | 44 | from lp.code.enums import ( | 36 | from lp.code.enums import ( |
334 | 45 | BranchLifecycleStatus, BranchSubscriptionNotificationLevel, BranchType, | 37 | BranchLifecycleStatus, BranchSubscriptionNotificationLevel, BranchType, |
335 | 46 | BranchVisibilityRule, CodeReviewNotificationLevel) | 38 | BranchVisibilityRule, CodeReviewNotificationLevel) |
336 | 47 | from lp.code.interfaces.branch import ( | 39 | from lp.code.interfaces.branch import ( |
337 | 48 | BranchCannotBePrivate, BranchCannotBePublic, | 40 | BranchCannotBePrivate, BranchCannotBePublic, |
339 | 49 | CannotDeleteBranch) | 41 | CannotDeleteBranch, DEFAULT_BRANCH_STATUS_IN_LISTING) |
340 | 42 | from lp.code.interfaces.branchlookup import IBranchLookup | ||
341 | 43 | from lp.code.interfaces.branchnamespace import IBranchNamespaceSet | ||
342 | 50 | from lp.code.interfaces.branchmergeproposal import InvalidBranchMergeProposal | 44 | from lp.code.interfaces.branchmergeproposal import InvalidBranchMergeProposal |
343 | 45 | from lp.code.interfaces.linkedbranch import ICanHasLinkedBranch | ||
344 | 51 | from lp.code.interfaces.seriessourcepackagebranch import ( | 46 | from lp.code.interfaces.seriessourcepackagebranch import ( |
345 | 52 | IFindOfficialBranchLinks) | 47 | IFindOfficialBranchLinks) |
346 | 48 | from lp.code.model.branch import ( | ||
347 | 49 | ClearDependentBranch, ClearOfficialPackageBranch, ClearSeriesBranch, | ||
348 | 50 | DeleteCodeImport, DeletionCallable, DeletionOperation, | ||
349 | 51 | update_trigger_modified_fields) | ||
350 | 52 | from lp.code.model.branchjob import ( | ||
351 | 53 | BranchDiffJob, BranchJob, BranchJobType, ReclaimBranchSpaceJob) | ||
352 | 54 | from lp.code.model.branchmergeproposal import ( | ||
353 | 55 | BranchMergeProposal) | ||
354 | 56 | from lp.code.model.codeimport import CodeImport, CodeImportSet | ||
355 | 57 | from lp.code.model.codereviewcomment import CodeReviewComment | ||
356 | 53 | from lp.registry.interfaces.person import IPersonSet | 58 | from lp.registry.interfaces.person import IPersonSet |
357 | 54 | from lp.registry.interfaces.product import IProductSet | 59 | from lp.registry.interfaces.product import IProductSet |
362 | 55 | from lp.code.interfaces.branch import DEFAULT_BRANCH_STATUS_IN_LISTING | 60 | from lp.registry.model.product import ProductSet |
363 | 56 | from lp.code.interfaces.branchlookup import IBranchLookup | 61 | from lp.registry.model.sourcepackage import SourcePackage |
360 | 57 | from lp.code.interfaces.branchnamespace import IBranchNamespaceSet | ||
361 | 58 | from canonical.launchpad.interfaces.launchpad import ILaunchpadCelebrities | ||
364 | 59 | from lp.soyuz.interfaces.publishing import PackagePublishingPocket | 62 | from lp.soyuz.interfaces.publishing import PackagePublishingPocket |
365 | 60 | from lp.testing import ( | 63 | from lp.testing import ( |
366 | 61 | run_with_login, TestCase, TestCaseWithFactory, time_counter) | 64 | run_with_login, TestCase, TestCaseWithFactory, time_counter) |
367 | 62 | from lp.testing.factory import LaunchpadObjectFactory | 65 | from lp.testing.factory import LaunchpadObjectFactory |
368 | 63 | from canonical.launchpad.webapp.interfaces import IOpenLaunchBag | ||
369 | 64 | |||
370 | 65 | from canonical.testing import DatabaseFunctionalLayer, LaunchpadZopelessLayer | ||
371 | 66 | 66 | ||
372 | 67 | 67 | ||
373 | 68 | class TestCodeImport(TestCase): | 68 | class TestCodeImport(TestCase): |
374 | @@ -94,7 +94,7 @@ | |||
375 | 94 | def _makeRevision(self, revno): | 94 | def _makeRevision(self, revno): |
376 | 95 | # Make a revision and add it to the branch. | 95 | # Make a revision and add it to the branch. |
377 | 96 | rev = self.factory.makeRevision() | 96 | rev = self.factory.makeRevision() |
379 | 97 | br = self.branch.createBranchRevision(revno, rev) | 97 | self.branch.createBranchRevision(revno, rev) |
380 | 98 | return rev | 98 | return rev |
381 | 99 | 99 | ||
382 | 100 | def testGetBySequenceNumber(self): | 100 | def testGetBySequenceNumber(self): |
383 | @@ -117,7 +117,7 @@ | |||
384 | 117 | self.assertEqual(1, branch_revision.sequence) | 117 | self.assertEqual(1, branch_revision.sequence) |
385 | 118 | 118 | ||
386 | 119 | def testNonExistant(self): | 119 | def testNonExistant(self): |
388 | 120 | rev1 = self._makeRevision(1) | 120 | self._makeRevision(1) |
389 | 121 | self.assertTrue(self.branch.getBranchRevision(sequence=2) is None) | 121 | self.assertTrue(self.branch.getBranchRevision(sequence=2) is None) |
390 | 122 | rev2 = self.factory.makeRevision() | 122 | rev2 = self.factory.makeRevision() |
391 | 123 | self.assertTrue(self.branch.getBranchRevision(revision=rev2) is None) | 123 | self.assertTrue(self.branch.getBranchRevision(revision=rev2) is None) |
392 | @@ -418,9 +418,10 @@ | |||
393 | 418 | # If a branch is the development focus branch for a product, then it's | 418 | # If a branch is the development focus branch for a product, then it's |
394 | 419 | # bzr identity is lp:product. | 419 | # bzr identity is lp:product. |
395 | 420 | branch = self.factory.makeProductBranch() | 420 | branch = self.factory.makeProductBranch() |
399 | 421 | product = branch.product | 421 | product = removeSecurityProxy(branch.product) |
400 | 422 | removeSecurityProxy(product).development_focus.branch = branch | 422 | linked_branch = ICanHasLinkedBranch(product) |
401 | 423 | self.assertBzrIdentity(branch, product.name) | 423 | linked_branch.setBranch(branch) |
402 | 424 | self.assertBzrIdentity(branch, linked_branch.bzr_identity) | ||
403 | 424 | 425 | ||
404 | 425 | def test_linked_to_product_series(self): | 426 | def test_linked_to_product_series(self): |
405 | 426 | # If a branch is the development focus branch for a product series, | 427 | # If a branch is the development focus branch for a product series, |
406 | @@ -428,8 +429,9 @@ | |||
407 | 428 | branch = self.factory.makeProductBranch() | 429 | branch = self.factory.makeProductBranch() |
408 | 429 | product = branch.product | 430 | product = branch.product |
409 | 430 | series = self.factory.makeProductSeries(product=product) | 431 | series = self.factory.makeProductSeries(product=product) |
412 | 431 | series.branch = branch | 432 | linked_branch = ICanHasLinkedBranch(series) |
413 | 432 | self.assertBzrIdentity(branch, '%s/%s' % (product.name, series.name)) | 433 | linked_branch.setBranch(branch) |
414 | 434 | self.assertBzrIdentity(branch, linked_branch.bzr_identity) | ||
415 | 433 | 435 | ||
416 | 434 | def test_private_linked_to_product(self): | 436 | def test_private_linked_to_product(self): |
417 | 435 | # If a branch is private, then the bzr identity is the unique name, | 437 | # If a branch is private, then the bzr identity is the unique name, |
418 | @@ -439,8 +441,8 @@ | |||
419 | 439 | owner = removeSecurityProxy(branch).owner | 441 | owner = removeSecurityProxy(branch).owner |
420 | 440 | login_person(owner) | 442 | login_person(owner) |
421 | 441 | self.addCleanup(logout) | 443 | self.addCleanup(logout) |
424 | 442 | product = branch.product | 444 | product = removeSecurityProxy(branch.product) |
425 | 443 | removeSecurityProxy(product).development_focus.branch = branch | 445 | ICanHasLinkedBranch(product).setBranch(branch) |
426 | 444 | self.assertBzrIdentity(branch, branch.unique_name) | 446 | self.assertBzrIdentity(branch, branch.unique_name) |
427 | 445 | 447 | ||
428 | 446 | def test_linked_to_series_and_dev_focus(self): | 448 | def test_linked_to_series_and_dev_focus(self): |
429 | @@ -448,33 +450,54 @@ | |||
430 | 448 | # branch for a series, the bzr identity will be the storter of the two | 450 | # branch for a series, the bzr identity will be the storter of the two |
431 | 449 | # URLs. | 451 | # URLs. |
432 | 450 | branch = self.factory.makeProductBranch() | 452 | branch = self.factory.makeProductBranch() |
438 | 451 | product = branch.product | 453 | series = self.factory.makeProductSeries(product=branch.product) |
439 | 452 | removeSecurityProxy(product).development_focus.branch = branch | 454 | product_link = ICanHasLinkedBranch( |
440 | 453 | series = self.factory.makeProductSeries(product=product) | 455 | removeSecurityProxy(branch.product)) |
441 | 454 | series.branch = branch | 456 | series_link = ICanHasLinkedBranch(series) |
442 | 455 | self.assertBzrIdentity(branch, product.name) | 457 | product_link.setBranch(branch) |
443 | 458 | series_link.setBranch(branch) | ||
444 | 459 | self.assertBzrIdentity(branch, product_link.bzr_identity) | ||
445 | 456 | 460 | ||
446 | 457 | def test_junk_branch_always_unique_name(self): | 461 | def test_junk_branch_always_unique_name(self): |
447 | 458 | # For junk branches, the bzr identity is always based on the unique | 462 | # For junk branches, the bzr identity is always based on the unique |
448 | 459 | # name of the branch, even if it's linked to a product, product series | 463 | # name of the branch, even if it's linked to a product, product series |
449 | 460 | # or whatever. | 464 | # or whatever. |
450 | 461 | branch = self.factory.makePersonalBranch() | 465 | branch = self.factory.makePersonalBranch() |
453 | 462 | product = self.factory.makeProduct() | 466 | product = removeSecurityProxy(self.factory.makeProduct()) |
454 | 463 | removeSecurityProxy(product).development_focus.branch = branch | 467 | ICanHasLinkedBranch(product).setBranch(branch) |
455 | 464 | self.assertBzrIdentity(branch, branch.unique_name) | 468 | self.assertBzrIdentity(branch, branch.unique_name) |
456 | 465 | 469 | ||
459 | 466 | def test_linked_to_package_release(self): | 470 | def test_linked_to_package(self): |
460 | 467 | # If a branch is linked to the release pocket of a package, then the | 471 | # If a branch is linked to a pocket of a package, then the |
461 | 468 | # bzr identity is the path to that package. | 472 | # bzr identity is the path to that package. |
462 | 469 | branch = self.factory.makePackageBranch() | 473 | branch = self.factory.makePackageBranch() |
463 | 474 | # Have to pick something that's not RELEASE in order to guarantee that | ||
464 | 475 | # it's not the dev focus source package. | ||
465 | 476 | pocket = PackagePublishingPocket.BACKPORTS | ||
466 | 477 | linked_branch = ICanHasLinkedBranch( | ||
467 | 478 | branch.sourcepackage.getSuiteSourcePackage(pocket)) | ||
468 | 470 | registrant = getUtility( | 479 | registrant = getUtility( |
469 | 471 | ILaunchpadCelebrities).ubuntu_branches.teamowner | 480 | ILaunchpadCelebrities).ubuntu_branches.teamowner |
470 | 472 | login_person(registrant) | 481 | login_person(registrant) |
473 | 473 | branch.sourcepackage.setBranch( | 482 | linked_branch.setBranch(branch, registrant) |
472 | 474 | PackagePublishingPocket.RELEASE, branch, registrant) | ||
474 | 475 | logout() | 483 | logout() |
475 | 476 | login(ANONYMOUS) | 484 | login(ANONYMOUS) |
477 | 477 | self.assertBzrIdentity(branch, branch.sourcepackage.path) | 485 | self.assertBzrIdentity(branch, linked_branch.bzr_identity) |
478 | 486 | |||
479 | 487 | def test_linked_to_dev_package(self): | ||
480 | 488 | # If a branch is linked to the development focus version of a package | ||
481 | 489 | # then the bzr identity is distro/package. | ||
482 | 490 | sourcepackage = self.factory.makeSourcePackage() | ||
483 | 491 | distro_package = sourcepackage.distribution_sourcepackage | ||
484 | 492 | branch = self.factory.makePackageBranch( | ||
485 | 493 | sourcepackage=distro_package.development_version) | ||
486 | 494 | linked_branch = ICanHasLinkedBranch(distro_package) | ||
487 | 495 | registrant = getUtility( | ||
488 | 496 | ILaunchpadCelebrities).ubuntu_branches.teamowner | ||
489 | 497 | run_with_login( | ||
490 | 498 | registrant, | ||
491 | 499 | linked_branch.setBranch, branch, registrant) | ||
492 | 500 | self.assertBzrIdentity(branch, linked_branch.bzr_identity) | ||
493 | 478 | 501 | ||
494 | 479 | 502 | ||
495 | 480 | class TestBranchDeletion(TestCaseWithFactory): | 503 | class TestBranchDeletion(TestCaseWithFactory): |
496 | @@ -506,7 +529,7 @@ | |||
497 | 506 | 529 | ||
498 | 507 | def test_stackedBranchDisablesDeletion(self): | 530 | def test_stackedBranchDisablesDeletion(self): |
499 | 508 | # A branch that is stacked upon cannot be deleted. | 531 | # A branch that is stacked upon cannot be deleted. |
501 | 509 | branch = self.factory.makeAnyBranch(stacked_on=self.branch) | 532 | self.factory.makeAnyBranch(stacked_on=self.branch) |
502 | 510 | self.assertFalse(self.branch.canBeDeleted()) | 533 | self.assertFalse(self.branch.canBeDeleted()) |
503 | 511 | 534 | ||
504 | 512 | def test_subscriptionDoesntDisableDeletion(self): | 535 | def test_subscriptionDoesntDisableDeletion(self): |
505 | @@ -679,8 +702,7 @@ | |||
506 | 679 | ' proposal.')), | 702 | ' proposal.')), |
507 | 680 | merge_proposal2: | 703 | merge_proposal2: |
508 | 681 | ('delete', _('This branch is the source branch of this merge' | 704 | ('delete', _('This branch is the source branch of this merge' |
511 | 682 | ' proposal.')) | 705 | ' proposal.'))}, |
510 | 683 | }, | ||
512 | 684 | self.branch.deletionRequirements()) | 706 | self.branch.deletionRequirements()) |
513 | 685 | self.assertEqual({ | 707 | self.assertEqual({ |
514 | 686 | merge_proposal1: | 708 | merge_proposal1: |
515 | @@ -688,8 +710,7 @@ | |||
516 | 688 | ' proposal.')), | 710 | ' proposal.')), |
517 | 689 | merge_proposal2: | 711 | merge_proposal2: |
518 | 690 | ('delete', _('This branch is the target branch of this merge' | 712 | ('delete', _('This branch is the target branch of this merge' |
521 | 691 | ' proposal.')) | 713 | ' proposal.'))}, |
520 | 692 | }, | ||
522 | 693 | merge_proposal1.target_branch.deletionRequirements()) | 714 | merge_proposal1.target_branch.deletionRequirements()) |
523 | 694 | self.assertEqual({ | 715 | self.assertEqual({ |
524 | 695 | merge_proposal1: | 716 | merge_proposal1: |
525 | @@ -697,8 +718,7 @@ | |||
526 | 697 | ' proposal.')), | 718 | ' proposal.')), |
527 | 698 | merge_proposal2: | 719 | merge_proposal2: |
528 | 699 | ('alter', _('This branch is the dependent branch of this merge' | 720 | ('alter', _('This branch is the dependent branch of this merge' |
531 | 700 | ' proposal.')) | 721 | ' proposal.'))}, |
530 | 701 | }, | ||
532 | 702 | merge_proposal1.dependent_branch.deletionRequirements()) | 722 | merge_proposal1.dependent_branch.deletionRequirements()) |
533 | 703 | 723 | ||
534 | 704 | def test_deleteMergeProposalSource(self): | 724 | def test_deleteMergeProposalSource(self): |
535 | @@ -722,7 +742,6 @@ | |||
536 | 722 | def test_deleteMergeProposalDependent(self): | 742 | def test_deleteMergeProposalDependent(self): |
537 | 723 | """break_links enables deleting merge proposal dependant branches.""" | 743 | """break_links enables deleting merge proposal dependant branches.""" |
538 | 724 | merge_proposal1, merge_proposal2 = self.makeMergeProposals() | 744 | merge_proposal1, merge_proposal2 = self.makeMergeProposals() |
539 | 725 | merge_proposal1_id = merge_proposal1.id | ||
540 | 726 | merge_proposal1.dependent_branch.destroySelf(break_references=True) | 745 | merge_proposal1.dependent_branch.destroySelf(break_references=True) |
541 | 727 | self.assertEqual(None, merge_proposal1.dependent_branch) | 746 | self.assertEqual(None, merge_proposal1.dependent_branch) |
542 | 728 | 747 | ||
543 | @@ -755,7 +774,6 @@ | |||
544 | 755 | def test_branchWithBugDeletion(self): | 774 | def test_branchWithBugDeletion(self): |
545 | 756 | """break_links allows deleting a branch with a bug.""" | 775 | """break_links allows deleting a branch with a bug.""" |
546 | 757 | bug1 = self.factory.makeBug() | 776 | bug1 = self.factory.makeBug() |
547 | 758 | bug2 = self.factory.makeBug() | ||
548 | 759 | bug1.linkBranch(self.branch, self.branch.owner) | 777 | bug1.linkBranch(self.branch, self.branch.owner) |
549 | 760 | bug_branch1 = bug1.linked_branches[0] | 778 | bug_branch1 = bug1.linked_branches[0] |
550 | 761 | bug_branch1_id = bug_branch1.id | 779 | bug_branch1_id = bug_branch1.id |
551 | @@ -951,7 +969,7 @@ | |||
552 | 951 | # some_branch.getStackedBranchesWithIncompleteMirrors does not include | 969 | # some_branch.getStackedBranchesWithIncompleteMirrors does not include |
553 | 952 | # stacked branches that haven't been mirrored at all. | 970 | # stacked branches that haven't been mirrored at all. |
554 | 953 | branch = self.factory.makeAnyBranch() | 971 | branch = self.factory.makeAnyBranch() |
556 | 954 | stacked_a = self.factory.makeAnyBranch(stacked_on=branch) | 972 | self.factory.makeAnyBranch(stacked_on=branch) |
557 | 955 | self.assertEqual( | 973 | self.assertEqual( |
558 | 956 | set(), set(branch.getStackedBranchesWithIncompleteMirrors())) | 974 | set(), set(branch.getStackedBranchesWithIncompleteMirrors())) |
559 | 957 | 975 | ||
560 | @@ -1097,8 +1115,7 @@ | |||
561 | 1097 | branch pair, then another landing target specifying the same pair | 1115 | branch pair, then another landing target specifying the same pair |
562 | 1098 | raises. | 1116 | raises. |
563 | 1099 | """ | 1117 | """ |
566 | 1100 | proposal = self.source.addLandingTarget( | 1118 | self.source.addLandingTarget(self.user, self.target, self.dependent) |
565 | 1101 | self.user, self.target, self.dependent) | ||
567 | 1102 | 1119 | ||
568 | 1103 | self.assertRaises( | 1120 | self.assertRaises( |
569 | 1104 | InvalidBranchMergeProposal, self.source.addLandingTarget, | 1121 | InvalidBranchMergeProposal, self.source.addLandingTarget, |
570 | @@ -1113,8 +1130,7 @@ | |||
571 | 1113 | self.user, self.target, self.dependent) | 1130 | self.user, self.target, self.dependent) |
572 | 1114 | proposal.rejectBranch(self.user, 'some_revision') | 1131 | proposal.rejectBranch(self.user, 'some_revision') |
573 | 1115 | syncUpdate(proposal) | 1132 | syncUpdate(proposal) |
576 | 1116 | new_proposal = self.source.addLandingTarget( | 1133 | self.source.addLandingTarget(self.user, self.target, self.dependent) |
575 | 1117 | self.user, self.target, self.dependent) | ||
577 | 1118 | 1134 | ||
578 | 1119 | def test_attributeAssignment(self): | 1135 | def test_attributeAssignment(self): |
579 | 1120 | """Smoke test to make sure the assignments are there.""" | 1136 | """Smoke test to make sure the assignments are there.""" |
580 | @@ -1614,7 +1630,6 @@ | |||
581 | 1614 | 1630 | ||
582 | 1615 | def test_spec_unlink(self): | 1631 | def test_spec_unlink(self): |
583 | 1616 | # Branches can be unlinked from the spec as well. | 1632 | # Branches can be unlinked from the spec as well. |
584 | 1617 | user = getUtility(IPersonSet).getByEmail('test@canonical.com') | ||
585 | 1618 | branch = self.factory.makeAnyBranch() | 1633 | branch = self.factory.makeAnyBranch() |
586 | 1619 | spec = self.factory.makeSpecification() | 1634 | spec = self.factory.makeSpecification() |
587 | 1620 | branch.linkSpecification(spec, self.user) | 1635 | branch.linkSpecification(spec, self.user) |
588 | 1621 | 1636 | ||
589 | === modified file 'lib/lp/code/model/tests/test_branchlookup.py' | |||
590 | --- lib/lp/code/model/tests/test_branchlookup.py 2009-06-02 13:52:48 +0000 | |||
591 | +++ lib/lp/code/model/tests/test_branchlookup.py 2009-07-16 05:52:44 +0000 | |||
592 | @@ -18,7 +18,7 @@ | |||
593 | 18 | from lp.code.interfaces.branchnamespace import ( | 18 | from lp.code.interfaces.branchnamespace import ( |
594 | 19 | get_branch_namespace, InvalidNamespace) | 19 | get_branch_namespace, InvalidNamespace) |
595 | 20 | from lp.code.interfaces.linkedbranch import ( | 20 | from lp.code.interfaces.linkedbranch import ( |
597 | 21 | CannotHaveLinkedBranch, NoLinkedBranch) | 21 | CannotHaveLinkedBranch, ICanHasLinkedBranch, NoLinkedBranch) |
598 | 22 | from lp.registry.interfaces.distroseries import NoSuchDistroSeries | 22 | from lp.registry.interfaces.distroseries import NoSuchDistroSeries |
599 | 23 | from canonical.launchpad.interfaces.launchpad import ILaunchpadCelebrities | 23 | from canonical.launchpad.interfaces.launchpad import ILaunchpadCelebrities |
600 | 24 | from lp.registry.interfaces.person import NoSuchPerson | 24 | from lp.registry.interfaces.person import NoSuchPerson |
601 | @@ -230,7 +230,7 @@ | |||
602 | 230 | 230 | ||
603 | 231 | This is because Launchpad doesn't currently support ftp. | 231 | This is because Launchpad doesn't currently support ftp. |
604 | 232 | """ | 232 | """ |
606 | 233 | branch = self.makeProductBranch() | 233 | self.makeProductBranch() |
607 | 234 | branch_set = getUtility(IBranchLookup) | 234 | branch_set = getUtility(IBranchLookup) |
608 | 235 | branch2 = branch_set.getByUrl('ftp://bazaar.launchpad.dev/~aa/b/c') | 235 | branch2 = branch_set.getByUrl('ftp://bazaar.launchpad.dev/~aa/b/c') |
609 | 236 | self.assertIs(None, branch2) | 236 | self.assertIs(None, branch2) |
610 | @@ -311,7 +311,7 @@ | |||
611 | 311 | # a non-existent series. | 311 | # a non-existent series. |
612 | 312 | self.assertRaises( | 312 | self.assertRaises( |
613 | 313 | NoSuchProduct, self.traverser.traverse, 'bb/dd') | 313 | NoSuchProduct, self.traverser.traverse, 'bb/dd') |
615 | 314 | product = self.factory.makeProduct(name='bb') | 314 | self.factory.makeProduct(name='bb') |
616 | 315 | self.assertRaises( | 315 | self.assertRaises( |
617 | 316 | NoSuchProductSeries, self.traverser.traverse, 'bb/dd') | 316 | NoSuchProductSeries, self.traverser.traverse, 'bb/dd') |
618 | 317 | 317 | ||
619 | @@ -345,6 +345,13 @@ | |||
620 | 345 | ssp = package.getSuiteSourcePackage(PackagePublishingPocket.RELEASE) | 345 | ssp = package.getSuiteSourcePackage(PackagePublishingPocket.RELEASE) |
621 | 346 | self.assertTraverses(package.path, ssp) | 346 | self.assertTraverses(package.path, ssp) |
622 | 347 | 347 | ||
623 | 348 | def test_distribution_source_package(self): | ||
624 | 349 | # `traverse` resolves 'distro/package' to the distribution source | ||
625 | 350 | # package. | ||
626 | 351 | dsp = self.factory.makeDistributionSourcePackage() | ||
627 | 352 | path = '%s/%s' % (dsp.distribution.name, dsp.sourcepackagename.name) | ||
628 | 353 | self.assertTraverses(path, dsp) | ||
629 | 354 | |||
630 | 348 | def test_traverse_source_package_pocket(self): | 355 | def test_traverse_source_package_pocket(self): |
631 | 349 | # `traverse` resolves 'distro/series-pocket/package' to the official | 356 | # `traverse` resolves 'distro/series-pocket/package' to the official |
632 | 350 | # branch for 'pocket' on that package. | 357 | # branch for 'pocket' on that package. |
633 | @@ -366,7 +373,7 @@ | |||
634 | 366 | def test_no_such_distro_series(self): | 373 | def test_no_such_distro_series(self): |
635 | 367 | # `traverse` raises `NoSuchDistroSeries` if the distro series doesn't | 374 | # `traverse` raises `NoSuchDistroSeries` if the distro series doesn't |
636 | 368 | # exist. | 375 | # exist. |
638 | 369 | distro = self.factory.makeDistribution(name='distro') | 376 | self.factory.makeDistribution(name='distro') |
639 | 370 | self.assertRaises( | 377 | self.assertRaises( |
640 | 371 | NoSuchDistroSeries, self.traverser.traverse, | 378 | NoSuchDistroSeries, self.traverser.traverse, |
641 | 372 | 'distro/series/package') | 379 | 'distro/series/package') |
642 | @@ -380,6 +387,14 @@ | |||
643 | 380 | self.assertRaises( | 387 | self.assertRaises( |
644 | 381 | NoSuchSourcePackageName, self.traverser.traverse, path) | 388 | NoSuchSourcePackageName, self.traverser.traverse, path) |
645 | 382 | 389 | ||
646 | 390 | def test_no_such_distribution_sourcepackage(self): | ||
647 | 391 | # `traverse` raises `NoSuchSourcePackageName` if the package in | ||
648 | 392 | # distro/package doesn't exist. | ||
649 | 393 | distribution = self.factory.makeDistribution() | ||
650 | 394 | path = '%s/doesntexist' % distribution.name | ||
651 | 395 | self.assertRaises( | ||
652 | 396 | NoSuchSourcePackageName, self.traverser.traverse, path) | ||
653 | 397 | |||
654 | 383 | 398 | ||
655 | 384 | class TestGetByLPPath(TestCaseWithFactory): | 399 | class TestGetByLPPath(TestCaseWithFactory): |
656 | 385 | """Ensure URLs are correctly expanded.""" | 400 | """Ensure URLs are correctly expanded.""" |
657 | @@ -397,10 +412,10 @@ | |||
658 | 397 | # components are found. | 412 | # components are found. |
659 | 398 | self.assertRaises( | 413 | self.assertRaises( |
660 | 399 | NoSuchPerson, self.branch_lookup.getByLPPath, '~aa/bb/c') | 414 | NoSuchPerson, self.branch_lookup.getByLPPath, '~aa/bb/c') |
662 | 400 | owner = self.factory.makePerson(name='aa') | 415 | self.factory.makePerson(name='aa') |
663 | 401 | self.assertRaises( | 416 | self.assertRaises( |
664 | 402 | NoSuchProduct, self.branch_lookup.getByLPPath, '~aa/bb/c') | 417 | NoSuchProduct, self.branch_lookup.getByLPPath, '~aa/bb/c') |
666 | 403 | product = self.factory.makeProduct(name='bb') | 418 | self.factory.makeProduct(name='bb') |
667 | 404 | self.assertRaises( | 419 | self.assertRaises( |
668 | 405 | NoSuchBranch, self.branch_lookup.getByLPPath, '~aa/bb/c') | 420 | NoSuchBranch, self.branch_lookup.getByLPPath, '~aa/bb/c') |
669 | 406 | 421 | ||
670 | @@ -434,7 +449,7 @@ | |||
671 | 434 | # doesn't match an existing branch. | 449 | # doesn't match an existing branch. |
672 | 435 | self.assertRaises( | 450 | self.assertRaises( |
673 | 436 | NoSuchPerson, self.branch_lookup.getByLPPath, '~aa/+junk/c') | 451 | NoSuchPerson, self.branch_lookup.getByLPPath, '~aa/+junk/c') |
675 | 437 | owner = self.factory.makePerson(name='aa') | 452 | self.factory.makePerson(name='aa') |
676 | 438 | self.assertRaises( | 453 | self.assertRaises( |
677 | 439 | NoSuchBranch, self.branch_lookup.getByLPPath, '~aa/+junk/c') | 454 | NoSuchBranch, self.branch_lookup.getByLPPath, '~aa/+junk/c') |
678 | 440 | 455 | ||
679 | @@ -455,6 +470,24 @@ | |||
680 | 455 | (branch, 'foo/bar/baz'), | 470 | (branch, 'foo/bar/baz'), |
681 | 456 | self.branch_lookup.getByLPPath(path)) | 471 | self.branch_lookup.getByLPPath(path)) |
682 | 457 | 472 | ||
683 | 473 | def test_resolve_distro_package_branch(self): | ||
684 | 474 | # getByLPPath returns the branch associated with the distribution | ||
685 | 475 | # source package referred to by the path. | ||
686 | 476 | sourcepackage = self.factory.makeSourcePackage() | ||
687 | 477 | branch = self.factory.makePackageBranch(sourcepackage=sourcepackage) | ||
688 | 478 | distro_package = sourcepackage.distribution_sourcepackage | ||
689 | 479 | ubuntu_branches = getUtility(ILaunchpadCelebrities).ubuntu_branches | ||
690 | 480 | registrant = ubuntu_branches.teamowner | ||
691 | 481 | run_with_login( | ||
692 | 482 | registrant, | ||
693 | 483 | ICanHasLinkedBranch(distro_package).setBranch, branch, registrant) | ||
694 | 484 | self.assertEqual( | ||
695 | 485 | (branch, None), | ||
696 | 486 | self.branch_lookup.getByLPPath( | ||
697 | 487 | '%s/%s' % ( | ||
698 | 488 | distro_package.distribution.name, | ||
699 | 489 | distro_package.sourcepackagename.name))) | ||
700 | 490 | |||
701 | 458 | def test_no_product_series_branch(self): | 491 | def test_no_product_series_branch(self): |
702 | 459 | # getByLPPath raises `NoLinkedBranch` if there's no branch registered | 492 | # getByLPPath raises `NoLinkedBranch` if there's no branch registered |
703 | 460 | # linked to the requested series. | 493 | # linked to the requested series. |
704 | @@ -501,6 +534,12 @@ | |||
705 | 501 | self.branch_lookup.getByLPPath, distribution.name) | 534 | self.branch_lookup.getByLPPath, distribution.name) |
706 | 502 | self.assertEqual(distribution, exception.component) | 535 | self.assertEqual(distribution, exception.component) |
707 | 503 | 536 | ||
708 | 537 | def test_distribution_with_no_series(self): | ||
709 | 538 | distro_package = self.factory.makeDistributionSourcePackage() | ||
710 | 539 | path = ICanHasLinkedBranch(distro_package).bzr_identity | ||
711 | 540 | self.assertRaises( | ||
712 | 541 | NoLinkedBranch, self.branch_lookup.getByLPPath, path) | ||
713 | 542 | |||
714 | 504 | def test_project_linked_branch(self): | 543 | def test_project_linked_branch(self): |
715 | 505 | # Projects cannot have linked branches, so `getByLPPath` raises a | 544 | # Projects cannot have linked branches, so `getByLPPath` raises a |
716 | 506 | # `CannotHaveLinkedBranch` error if we try to get the linked branch | 545 | # `CannotHaveLinkedBranch` error if we try to get the linked branch |
717 | 507 | 546 | ||
718 | === modified file 'lib/lp/code/model/tests/test_linkedbranch.py' | |||
719 | --- lib/lp/code/model/tests/test_linkedbranch.py 2009-05-24 18:09:51 +0000 | |||
720 | +++ lib/lp/code/model/tests/test_linkedbranch.py 2009-07-16 05:38:41 +0000 | |||
721 | @@ -18,11 +18,11 @@ | |||
722 | 18 | from lp.testing import run_with_login, TestCaseWithFactory | 18 | from lp.testing import run_with_login, TestCaseWithFactory |
723 | 19 | 19 | ||
724 | 20 | 20 | ||
726 | 21 | class TestLinkedBranch(TestCaseWithFactory): | 21 | class TestProductSeriesLinkedBranch(TestCaseWithFactory): |
727 | 22 | 22 | ||
728 | 23 | layer = DatabaseFunctionalLayer | 23 | layer = DatabaseFunctionalLayer |
729 | 24 | 24 | ||
731 | 25 | def test_product_series(self): | 25 | def test_branch(self): |
732 | 26 | # The linked branch of a product series is its branch attribute. | 26 | # The linked branch of a product series is its branch attribute. |
733 | 27 | product_series = self.factory.makeProductSeries() | 27 | product_series = self.factory.makeProductSeries() |
734 | 28 | product_series.branch = self.factory.makeProductBranch( | 28 | product_series.branch = self.factory.makeProductBranch( |
735 | @@ -30,7 +30,29 @@ | |||
736 | 30 | self.assertEqual( | 30 | self.assertEqual( |
737 | 31 | product_series.branch, ICanHasLinkedBranch(product_series).branch) | 31 | product_series.branch, ICanHasLinkedBranch(product_series).branch) |
738 | 32 | 32 | ||
740 | 33 | def test_product(self): | 33 | def test_setBranch(self): |
741 | 34 | # setBranch sets the linked branch of the product series. | ||
742 | 35 | product_series = self.factory.makeProductSeries() | ||
743 | 36 | branch = self.factory.makeProductBranch( | ||
744 | 37 | product=product_series.product) | ||
745 | 38 | ICanHasLinkedBranch(product_series).setBranch(branch) | ||
746 | 39 | self.assertEqual(branch, product_series.branch) | ||
747 | 40 | |||
748 | 41 | def test_bzr_identity(self): | ||
749 | 42 | # The bzr_identity of a product series linked branch is | ||
750 | 43 | # product/product_series. | ||
751 | 44 | product_series = self.factory.makeProductSeries() | ||
752 | 45 | bzr_identity = '%s/%s' % ( | ||
753 | 46 | product_series.product.name, product_series.name) | ||
754 | 47 | self.assertEqual( | ||
755 | 48 | bzr_identity, ICanHasLinkedBranch(product_series).bzr_identity) | ||
756 | 49 | |||
757 | 50 | |||
758 | 51 | class TestProductLinkedBranch(TestCaseWithFactory): | ||
759 | 52 | |||
760 | 53 | layer = DatabaseFunctionalLayer | ||
761 | 54 | |||
762 | 55 | def test_branch(self): | ||
763 | 34 | # The linked branch of a product is the linked branch of its | 56 | # The linked branch of a product is the linked branch of its |
764 | 35 | # development focus product series. | 57 | # development focus product series. |
765 | 36 | branch = self.factory.makeProductBranch() | 58 | branch = self.factory.makeProductBranch() |
766 | @@ -38,22 +60,130 @@ | |||
767 | 38 | removeSecurityProxy(product).development_focus.branch = branch | 60 | removeSecurityProxy(product).development_focus.branch = branch |
768 | 39 | self.assertEqual(branch, ICanHasLinkedBranch(product).branch) | 61 | self.assertEqual(branch, ICanHasLinkedBranch(product).branch) |
769 | 40 | 62 | ||
771 | 41 | def test_suitesourcepackage(self): | 63 | def test_setBranch(self): |
772 | 64 | # setBranch sets the linked branch of the development focus product | ||
773 | 65 | # series. | ||
774 | 66 | branch = self.factory.makeProductBranch() | ||
775 | 67 | product = removeSecurityProxy(branch.product) | ||
776 | 68 | ICanHasLinkedBranch(product).setBranch(branch) | ||
777 | 69 | self.assertEqual(branch, product.development_focus.branch) | ||
778 | 70 | |||
779 | 71 | def test_bzr_identity(self): | ||
780 | 72 | # The bzr_identity of a product linked branch is the product name. | ||
781 | 73 | product = self.factory.makeProduct() | ||
782 | 74 | self.assertEqual( | ||
783 | 75 | product.name, ICanHasLinkedBranch(product).bzr_identity) | ||
784 | 76 | |||
785 | 77 | |||
786 | 78 | class TestSuiteSourcePackageLinkedBranch(TestCaseWithFactory): | ||
787 | 79 | |||
788 | 80 | layer = DatabaseFunctionalLayer | ||
789 | 81 | |||
790 | 82 | def test_branch(self): | ||
791 | 42 | # The linked branch of a suite source package is the official branch | 83 | # The linked branch of a suite source package is the official branch |
792 | 43 | # for the pocket of that source package. | 84 | # for the pocket of that source package. |
793 | 44 | branch = self.factory.makeAnyBranch() | 85 | branch = self.factory.makeAnyBranch() |
794 | 86 | suite_sourcepackage = self.factory.makeSuiteSourcePackage() | ||
795 | 87 | ubuntu_branches = getUtility(ILaunchpadCelebrities).ubuntu_branches | ||
796 | 88 | registrant = ubuntu_branches.teamowner | ||
797 | 89 | run_with_login( | ||
798 | 90 | registrant, | ||
799 | 91 | suite_sourcepackage.sourcepackage.setBranch, | ||
800 | 92 | suite_sourcepackage.pocket, branch, registrant) | ||
801 | 93 | self.assertEqual( | ||
802 | 94 | branch, ICanHasLinkedBranch(suite_sourcepackage).branch) | ||
803 | 95 | |||
804 | 96 | def test_setBranch(self): | ||
805 | 97 | # setBranch sets the official branch for the appropriate pocket of the | ||
806 | 98 | # source package. | ||
807 | 99 | branch = self.factory.makeAnyBranch() | ||
808 | 100 | suite_sourcepackage = self.factory.makeSuiteSourcePackage() | ||
809 | 101 | ubuntu_branches = getUtility(ILaunchpadCelebrities).ubuntu_branches | ||
810 | 102 | registrant = ubuntu_branches.teamowner | ||
811 | 103 | run_with_login( | ||
812 | 104 | registrant, | ||
813 | 105 | ICanHasLinkedBranch(suite_sourcepackage).setBranch, | ||
814 | 106 | branch, registrant) | ||
815 | 107 | self.assertEqual( | ||
816 | 108 | branch, | ||
817 | 109 | suite_sourcepackage.sourcepackage.getBranch( | ||
818 | 110 | suite_sourcepackage.pocket)) | ||
819 | 111 | |||
820 | 112 | def test_bzr_identity(self): | ||
821 | 113 | # The bzr_identity of a suite source package linked branch is the path | ||
822 | 114 | # of that suite source package. | ||
823 | 115 | suite_sourcepackage = self.factory.makeSuiteSourcePackage() | ||
824 | 116 | self.assertEqual( | ||
825 | 117 | suite_sourcepackage.path, | ||
826 | 118 | ICanHasLinkedBranch(suite_sourcepackage).bzr_identity) | ||
827 | 119 | |||
828 | 120 | |||
829 | 121 | class TestDistributionSourcePackageLinkedBranch(TestCaseWithFactory): | ||
830 | 122 | |||
831 | 123 | layer = DatabaseFunctionalLayer | ||
832 | 124 | |||
833 | 125 | def test_branch(self): | ||
834 | 126 | # The linked branch of a distribution source package is the official | ||
835 | 127 | # branch for the release pocket of the development focus series for | ||
836 | 128 | # that package. Phew. | ||
837 | 129 | branch = self.factory.makeAnyBranch() | ||
838 | 45 | sourcepackage = self.factory.makeSourcePackage() | 130 | sourcepackage = self.factory.makeSourcePackage() |
839 | 131 | dev_sourcepackage = sourcepackage.development_version | ||
840 | 46 | pocket = PackagePublishingPocket.RELEASE | 132 | pocket = PackagePublishingPocket.RELEASE |
841 | 133 | |||
842 | 47 | ubuntu_branches = getUtility(ILaunchpadCelebrities).ubuntu_branches | 134 | ubuntu_branches = getUtility(ILaunchpadCelebrities).ubuntu_branches |
843 | 48 | registrant = ubuntu_branches.teamowner | 135 | registrant = ubuntu_branches.teamowner |
844 | 49 | run_with_login( | 136 | run_with_login( |
845 | 50 | ubuntu_branches.teamowner, | 137 | ubuntu_branches.teamowner, |
852 | 51 | sourcepackage.setBranch, pocket, branch, registrant) | 138 | dev_sourcepackage.setBranch, pocket, branch, registrant) |
853 | 52 | suite_sourcepackage = sourcepackage.getSuiteSourcePackage(pocket) | 139 | |
854 | 53 | self.assertEqual( | 140 | distribution_sourcepackage = sourcepackage.distribution_sourcepackage |
855 | 54 | branch, ICanHasLinkedBranch(suite_sourcepackage).branch) | 141 | self.assertEqual( |
856 | 55 | 142 | branch, ICanHasLinkedBranch(distribution_sourcepackage).branch) | |
857 | 56 | def test_project(self): | 143 | |
858 | 144 | def test_branch_when_no_series(self): | ||
859 | 145 | # Our data model allows distributions that have no series. The linked | ||
860 | 146 | # branch for a package in such a distribution is always None. | ||
861 | 147 | distro_package = self.factory.makeDistributionSourcePackage() | ||
862 | 148 | self.assertIs(None, ICanHasLinkedBranch(distro_package).branch) | ||
863 | 149 | |||
864 | 150 | def test_setBranch(self): | ||
865 | 151 | # Setting the linked branch for a distribution source package links | ||
866 | 152 | # the branch to the release pocket of the development focus series for | ||
867 | 153 | # that package. | ||
868 | 154 | branch = self.factory.makeAnyBranch() | ||
869 | 155 | sourcepackage = self.factory.makeSourcePackage() | ||
870 | 156 | distribution_sourcepackage = sourcepackage.distribution_sourcepackage | ||
871 | 157 | |||
872 | 158 | ubuntu_branches = getUtility(ILaunchpadCelebrities).ubuntu_branches | ||
873 | 159 | registrant = ubuntu_branches.teamowner | ||
874 | 160 | run_with_login( | ||
875 | 161 | registrant, | ||
876 | 162 | ICanHasLinkedBranch(distribution_sourcepackage).setBranch, | ||
877 | 163 | branch, registrant) | ||
878 | 164 | |||
879 | 165 | dev_sourcepackage = sourcepackage.development_version | ||
880 | 166 | pocket = PackagePublishingPocket.RELEASE | ||
881 | 167 | self.assertEqual(branch, dev_sourcepackage.getBranch(pocket)) | ||
882 | 168 | |||
883 | 169 | def test_bzr_identity(self): | ||
884 | 170 | # The bzr_identity of a distribution source package linked branch is | ||
885 | 171 | # distro/package. | ||
886 | 172 | distribution_sourcepackage = ( | ||
887 | 173 | self.factory.makeDistributionSourcePackage()) | ||
888 | 174 | self.assertEqual( | ||
889 | 175 | '%s/%s' % ( | ||
890 | 176 | distribution_sourcepackage.distribution.name, | ||
891 | 177 | distribution_sourcepackage.sourcepackagename.name), | ||
892 | 178 | ICanHasLinkedBranch(distribution_sourcepackage).bzr_identity) | ||
893 | 179 | |||
894 | 180 | |||
895 | 181 | class TestProjectLinkedBranch(TestCaseWithFactory): | ||
896 | 182 | |||
897 | 183 | layer = DatabaseFunctionalLayer | ||
898 | 184 | |||
899 | 185 | def test_cannot_have_linked_branch(self): | ||
900 | 186 | # Projects cannot have linked branches. | ||
901 | 57 | project = self.factory.makeProject() | 187 | project = self.factory.makeProject() |
902 | 58 | self.assertRaises( | 188 | self.assertRaises( |
903 | 59 | CannotHaveLinkedBranch, get_linked_branch, project) | 189 | CannotHaveLinkedBranch, get_linked_branch, project) |
904 | 60 | 190 | ||
905 | === modified file 'lib/lp/registry/browser/distributionsourcepackage.py' | |||
906 | --- lib/lp/registry/browser/distributionsourcepackage.py 2009-07-10 12:34:49 +0000 | |||
907 | +++ lib/lp/registry/browser/distributionsourcepackage.py 2009-07-16 03:53:20 +0000 | |||
908 | @@ -31,9 +31,8 @@ | |||
909 | 31 | from lp.answers.browser.questiontarget import ( | 31 | from lp.answers.browser.questiontarget import ( |
910 | 32 | QuestionTargetFacetMixin, QuestionTargetTraversalMixin) | 32 | QuestionTargetFacetMixin, QuestionTargetTraversalMixin) |
911 | 33 | from canonical.launchpad.webapp import ( | 33 | from canonical.launchpad.webapp import ( |
915 | 34 | ApplicationMenu, GetitemNavigation, LaunchpadEditFormView, | 34 | ApplicationMenu, LaunchpadEditFormView, LaunchpadFormView, Link, |
916 | 35 | LaunchpadFormView, Link, StandardLaunchpadFacets, action, canonical_url, | 35 | Navigation, StandardLaunchpadFacets, action, canonical_url, redirection) |
914 | 36 | redirection) | ||
917 | 37 | from canonical.launchpad.webapp.menu import enabled_with_permission | 36 | from canonical.launchpad.webapp.menu import enabled_with_permission |
918 | 38 | from canonical.launchpad.webapp.breadcrumb import BreadcrumbBuilder | 37 | from canonical.launchpad.webapp.breadcrumb import BreadcrumbBuilder |
919 | 39 | 38 | ||
920 | @@ -88,13 +87,16 @@ | |||
921 | 88 | return Link('+filebug', text, icon='bug') | 87 | return Link('+filebug', text, icon='bug') |
922 | 89 | 88 | ||
923 | 90 | 89 | ||
925 | 91 | class DistributionSourcePackageNavigation(GetitemNavigation, | 90 | class DistributionSourcePackageNavigation(Navigation, |
926 | 92 | BugTargetTraversalMixin, QuestionTargetTraversalMixin): | 91 | BugTargetTraversalMixin, QuestionTargetTraversalMixin): |
927 | 93 | 92 | ||
928 | 94 | usedfor = IDistributionSourcePackage | 93 | usedfor = IDistributionSourcePackage |
929 | 95 | 94 | ||
930 | 96 | redirection("+editbugcontact", "+subscribe") | 95 | redirection("+editbugcontact", "+subscribe") |
931 | 97 | 96 | ||
932 | 97 | def traverse(self, name): | ||
933 | 98 | return self.context.getVersion(name) | ||
934 | 99 | |||
935 | 98 | 100 | ||
936 | 99 | class DecoratedDistributionSourcePackageRelease: | 101 | class DecoratedDistributionSourcePackageRelease: |
937 | 100 | """A decorated DistributionSourcePackageRelease. | 102 | """A decorated DistributionSourcePackageRelease. |
938 | 101 | 103 | ||
939 | === modified file 'lib/lp/registry/configure.zcml' | |||
940 | --- lib/lp/registry/configure.zcml 2009-07-13 14:13:07 +0000 | |||
941 | +++ lib/lp/registry/configure.zcml 2009-07-16 04:47:24 +0000 | |||
942 | @@ -357,6 +357,7 @@ | |||
943 | 357 | <allow | 357 | <allow |
944 | 358 | attributes=" | 358 | attributes=" |
945 | 359 | distribution | 359 | distribution |
946 | 360 | development_version | ||
947 | 360 | sourcepackagename | 361 | sourcepackagename |
948 | 361 | name | 362 | name |
949 | 362 | displayname | 363 | displayname |
950 | 363 | 364 | ||
951 | === modified file 'lib/lp/registry/interfaces/distributionsourcepackage.py' | |||
952 | --- lib/lp/registry/interfaces/distributionsourcepackage.py 2009-07-10 15:38:46 +0000 | |||
953 | +++ lib/lp/registry/interfaces/distributionsourcepackage.py 2009-07-16 04:48:10 +0000 | |||
954 | @@ -68,6 +68,10 @@ | |||
955 | 68 | "The list of all releases of this source package " | 68 | "The list of all releases of this source package " |
956 | 69 | "in this distribution.") | 69 | "in this distribution.") |
957 | 70 | 70 | ||
958 | 71 | development_version = Attribute( | ||
959 | 72 | 'The development version of this source package. None if there is no ' | ||
960 | 73 | 'such package.') | ||
961 | 74 | |||
962 | 71 | def getReleasesAndPublishingHistory(): | 75 | def getReleasesAndPublishingHistory(): |
963 | 72 | """Return a list of all releases of this source package in this | 76 | """Return a list of all releases of this source package in this |
964 | 73 | distribution and their correspodning publishing history. | 77 | distribution and their correspodning publishing history. |
965 | @@ -85,9 +89,6 @@ | |||
966 | 85 | "Return a list of CURRENT publishing records for this source " | 89 | "Return a list of CURRENT publishing records for this source " |
967 | 86 | "package in this distribution.") | 90 | "package in this distribution.") |
968 | 87 | 91 | ||
969 | 88 | def __getitem__(version): | ||
970 | 89 | """Should map to getVersion.""" | ||
971 | 90 | |||
972 | 91 | def getVersion(version): | 92 | def getVersion(version): |
973 | 92 | """Return the a DistributionSourcePackageRelease with the given | 93 | """Return the a DistributionSourcePackageRelease with the given |
974 | 93 | version, or None if there has never been a release with that | 94 | version, or None if there has never been a release with that |
975 | 94 | 95 | ||
976 | === modified file 'lib/lp/registry/interfaces/sourcepackage.py' | |||
977 | --- lib/lp/registry/interfaces/sourcepackage.py 2009-06-12 16:36:02 +0000 | |||
978 | +++ lib/lp/registry/interfaces/sourcepackage.py 2009-07-15 07:25:04 +0000 | |||
979 | @@ -125,6 +125,9 @@ | |||
980 | 125 | development_version = Attribute( | 125 | development_version = Attribute( |
981 | 126 | "This package on the distro's current series.") | 126 | "This package on the distro's current series.") |
982 | 127 | 127 | ||
983 | 128 | distribution_sourcepackage = Attribute( | ||
984 | 129 | "The IDistributionSourcePackage for this source package.") | ||
985 | 130 | |||
986 | 128 | def __getitem__(version): | 131 | def __getitem__(version): |
987 | 129 | """Return the source package release with the given version in this | 132 | """Return the source package release with the given version in this |
988 | 130 | distro series, or None.""" | 133 | distro series, or None.""" |
989 | 131 | 134 | ||
990 | === modified file 'lib/lp/registry/model/distributionsourcepackage.py' | |||
991 | --- lib/lp/registry/model/distributionsourcepackage.py 2009-07-10 15:38:46 +0000 | |||
992 | +++ lib/lp/registry/model/distributionsourcepackage.py 2009-07-16 04:48:10 +0000 | |||
993 | @@ -89,6 +89,14 @@ | |||
994 | 89 | self.sourcepackagename.name, self.distribution.displayname) | 89 | self.sourcepackagename.name, self.distribution.displayname) |
995 | 90 | 90 | ||
996 | 91 | @property | 91 | @property |
997 | 92 | def development_version(self): | ||
998 | 93 | """See `IDistributionSourcePackage`.""" | ||
999 | 94 | series = self.distribution.currentseries | ||
1000 | 95 | if series is None: | ||
1001 | 96 | return None | ||
1002 | 97 | return series.getSourcePackage(self.sourcepackagename) | ||
1003 | 98 | |||
1004 | 99 | @property | ||
1005 | 92 | def _self_in_database(self): | 100 | def _self_in_database(self): |
1006 | 93 | """Return the equivalent database-backed record of self.""" | 101 | """Return the equivalent database-backed record of self.""" |
1007 | 94 | # XXX: allenap 2008-11-13 bug=297736: This is a temporary | 102 | # XXX: allenap 2008-11-13 bug=297736: This is a temporary |
1008 | @@ -124,9 +132,6 @@ | |||
1009 | 124 | _get_bug_reporting_guidelines, | 132 | _get_bug_reporting_guidelines, |
1010 | 125 | _set_bug_reporting_guidelines) | 133 | _set_bug_reporting_guidelines) |
1011 | 126 | 134 | ||
1012 | 127 | def __getitem__(self, version): | ||
1013 | 128 | return self.getVersion(version) | ||
1014 | 129 | |||
1015 | 130 | @property | 135 | @property |
1016 | 131 | def latest_overall_publication(self): | 136 | def latest_overall_publication(self): |
1017 | 132 | """See `IDistributionSourcePackage`.""" | 137 | """See `IDistributionSourcePackage`.""" |
1018 | 133 | 138 | ||
1019 | === modified file 'lib/lp/registry/model/sourcepackage.py' | |||
1020 | --- lib/lp/registry/model/sourcepackage.py 2009-07-11 09:33:12 +0000 | |||
1021 | +++ lib/lp/registry/model/sourcepackage.py 2009-07-15 07:25:04 +0000 | |||
1022 | @@ -424,6 +424,11 @@ | |||
1023 | 424 | self.sourcepackagename, self.distribution.currentseries) | 424 | self.sourcepackagename, self.distribution.currentseries) |
1024 | 425 | 425 | ||
1025 | 426 | @property | 426 | @property |
1026 | 427 | def distribution_sourcepackage(self): | ||
1027 | 428 | """See `ISourcePackage`.""" | ||
1028 | 429 | return self.distribution.getSourcePackage(self.sourcepackagename) | ||
1029 | 430 | |||
1030 | 431 | @property | ||
1031 | 427 | def bug_reporting_guidelines(self): | 432 | def bug_reporting_guidelines(self): |
1032 | 428 | """See `IBugTarget`.""" | 433 | """See `IBugTarget`.""" |
1033 | 429 | return self.distribution.bug_reporting_guidelines | 434 | return self.distribution.bug_reporting_guidelines |
1034 | 430 | 435 | ||
1035 | === modified file 'lib/lp/registry/tests/test_distributionsourcepackage.py' | |||
1036 | --- lib/lp/registry/tests/test_distributionsourcepackage.py 2009-07-10 15:38:46 +0000 | |||
1037 | +++ lib/lp/registry/tests/test_distributionsourcepackage.py 2009-07-15 07:55:56 +0000 | |||
1038 | @@ -109,5 +109,25 @@ | |||
1039 | 109 | 109 | ||
1040 | 110 | self.assertEqual(related_archive_names, ['gedit-nightly']) | 110 | self.assertEqual(related_archive_names, ['gedit-nightly']) |
1041 | 111 | 111 | ||
1042 | 112 | def test_development_version(self): | ||
1043 | 113 | # IDistributionSourcePackage.development_version is the ISourcePackage | ||
1044 | 114 | # for the current series of the distribution. | ||
1045 | 115 | dsp = self.factory.makeDistributionSourcePackage() | ||
1046 | 116 | series = self.factory.makeDistroRelease(distribution=dsp.distribution) | ||
1047 | 117 | self.assertEqual(series, dsp.distribution.currentseries) | ||
1048 | 118 | development_version = dsp.distribution.currentseries.getSourcePackage( | ||
1049 | 119 | dsp.sourcepackagename) | ||
1050 | 120 | self.assertEqual(development_version, dsp.development_version) | ||
1051 | 121 | |||
1052 | 122 | def test_development_version_no_current_series(self): | ||
1053 | 123 | # IDistributionSourcePackage.development_version is the ISourcePackage | ||
1054 | 124 | # for the current series of the distribution. | ||
1055 | 125 | dsp = self.factory.makeDistributionSourcePackage() | ||
1056 | 126 | currentseries = dsp.distribution.currentseries | ||
1057 | 127 | # The current series is None by default. | ||
1058 | 128 | self.assertIs(None, currentseries) | ||
1059 | 129 | self.assertEqual(None, dsp.development_version) | ||
1060 | 130 | |||
1061 | 131 | |||
1062 | 112 | def test_suite(): | 132 | def test_suite(): |
1063 | 113 | return unittest.TestLoader().loadTestsFromName(__name__) | 133 | return unittest.TestLoader().loadTestsFromName(__name__) |
1064 | 114 | 134 | ||
1065 | === modified file 'lib/lp/registry/tests/test_sourcepackage.py' | |||
1066 | --- lib/lp/registry/tests/test_sourcepackage.py 2009-06-02 13:52:48 +0000 | |||
1067 | +++ lib/lp/registry/tests/test_sourcepackage.py 2009-07-15 07:25:04 +0000 | |||
1068 | @@ -154,6 +154,17 @@ | |||
1069 | 154 | self.assertEqual( | 154 | self.assertEqual( |
1070 | 155 | dev_sourcepackage, dev_sourcepackage.development_version) | 155 | dev_sourcepackage, dev_sourcepackage.development_version) |
1071 | 156 | 156 | ||
1072 | 157 | def test_distribution_sourcepackage(self): | ||
1073 | 158 | # ISourcePackage.distribution_sourcepackage is the distribution source | ||
1074 | 159 | # package for the ISourcePackage. | ||
1075 | 160 | sourcepackage = self.factory.makeSourcePackage() | ||
1076 | 161 | distribution = sourcepackage.distribution | ||
1077 | 162 | distribution_sourcepackage = distribution.getSourcePackage( | ||
1078 | 163 | sourcepackage.sourcepackagename) | ||
1079 | 164 | self.assertEqual( | ||
1080 | 165 | distribution_sourcepackage, | ||
1081 | 166 | sourcepackage.distribution_sourcepackage) | ||
1082 | 167 | |||
1083 | 157 | 168 | ||
1084 | 158 | class TestSourcePackageSecurity(TestCaseWithFactory): | 169 | class TestSourcePackageSecurity(TestCaseWithFactory): |
1085 | 159 | """Tests for source package branch linking security.""" | 170 | """Tests for source package branch linking security.""" |
1086 | 160 | 171 | ||
1087 | === modified file 'lib/lp/soyuz/doc/buildd-queuebuilder-lookup.txt' | |||
1088 | --- lib/lp/soyuz/doc/buildd-queuebuilder-lookup.txt 2009-04-29 20:27:44 +0000 | |||
1089 | +++ lib/lp/soyuz/doc/buildd-queuebuilder-lookup.txt 2009-07-16 03:31:45 +0000 | |||
1090 | @@ -31,7 +31,7 @@ | |||
1091 | 31 | >>> partner_archive = getUtility(IArchiveSet).getByDistroPurpose( | 31 | >>> partner_archive = getUtility(IArchiveSet).getByDistroPurpose( |
1092 | 32 | ... distribution=ubuntu, purpose=ArchivePurpose.PARTNER) | 32 | ... distribution=ubuntu, purpose=ArchivePurpose.PARTNER) |
1093 | 33 | 33 | ||
1095 | 34 | >>> dspr = ubuntu.getSourcePackage('mozilla-firefox')['0.9'] | 34 | >>> dspr = ubuntu.getSourcePackage('mozilla-firefox').getVersion('0.9') |
1096 | 35 | >>> the_spr = dspr.sourcepackagerelease | 35 | >>> the_spr = dspr.sourcepackagerelease |
1097 | 36 | 36 | ||
1098 | 37 | >>> initial_candidates = [] | 37 | >>> initial_candidates = [] |
1099 | 38 | 38 | ||
1100 | === modified file 'lib/lp/soyuz/doc/distroseriesqueue.txt' | |||
1101 | --- lib/lp/soyuz/doc/distroseriesqueue.txt 2009-07-03 09:49:13 +0000 | |||
1102 | +++ lib/lp/soyuz/doc/distroseriesqueue.txt 2009-07-16 03:31:45 +0000 | |||
1103 | @@ -712,7 +712,8 @@ | |||
1104 | 712 | have in the sampledata. | 712 | have in the sampledata. |
1105 | 713 | 713 | ||
1106 | 714 | >>> [binary_queue] = queue_items | 714 | >>> [binary_queue] = queue_items |
1108 | 715 | >>> non_matching_pmount = ubuntu.getSourcePackage('pmount')['0.1-2'] | 715 | >>> pmount = ubuntu.getSourcePackage('pmount') |
1109 | 716 | >>> non_matching_pmount = pmount.getVersion('0.1-2') | ||
1110 | 716 | >>> unused = binary_queue.addSource( | 717 | >>> unused = binary_queue.addSource( |
1111 | 717 | ... non_matching_pmount.sourcepackagerelease) | 718 | ... non_matching_pmount.sourcepackagerelease) |
1112 | 718 | 719 | ||
1113 | @@ -732,7 +733,7 @@ | |||
1114 | 732 | ... PackagePublishingPocket.RELEASE, | 733 | ... PackagePublishingPocket.RELEASE, |
1115 | 733 | ... 'pmount_0.1-1_source.changes', 'some content', | 734 | ... 'pmount_0.1-1_source.changes', 'some content', |
1116 | 734 | ... breezy_autotest.main_archive) | 735 | ... breezy_autotest.main_archive) |
1118 | 735 | >>> matching_pmount = ubuntu.getSourcePackage('pmount')['0.1-1'] | 736 | >>> matching_pmount = pmount.getVersion('0.1-1') |
1119 | 736 | >>> unused = candidate_queue.addSource( | 737 | >>> unused = candidate_queue.addSource( |
1120 | 737 | ... matching_pmount.sourcepackagerelease) | 738 | ... matching_pmount.sourcepackagerelease) |
1121 | 738 | 739 | ||
1122 | @@ -1087,7 +1088,7 @@ | |||
1123 | 1087 | are. Contents can be attached to it. | 1088 | are. Contents can be attached to it. |
1124 | 1088 | 1089 | ||
1125 | 1089 | # Retrieve a SourcePackageRelease from the sampledata. | 1090 | # Retrieve a SourcePackageRelease from the sampledata. |
1127 | 1090 | >>> a_source_package = ubuntu.getSourcePackage('pmount')['0.1-2'] | 1091 | >>> a_source_package = pmount.getVersion('0.1-2') |
1128 | 1091 | >>> a_source_release = a_source_package.sourcepackagerelease | 1092 | >>> a_source_release = a_source_package.sourcepackagerelease |
1129 | 1092 | 1093 | ||
1130 | 1093 | >>> unused = delayed_copy.addSource(a_source_release) | 1094 | >>> unused = delayed_copy.addSource(a_source_release) |
1131 | 1094 | 1095 | ||
1132 | === modified file 'lib/lp/soyuz/doc/package-diff.txt' | |||
1133 | --- lib/lp/soyuz/doc/package-diff.txt 2009-05-06 20:53:05 +0000 | |||
1134 | +++ lib/lp/soyuz/doc/package-diff.txt 2009-07-16 03:31:45 +0000 | |||
1135 | @@ -18,8 +18,8 @@ | |||
1136 | 18 | >>> ubuntu = getUtility(IDistributionSet).getByName('ubuntu') | 18 | >>> ubuntu = getUtility(IDistributionSet).getByName('ubuntu') |
1137 | 19 | >>> pmount = ubuntu.getSourcePackage('pmount') | 19 | >>> pmount = ubuntu.getSourcePackage('pmount') |
1138 | 20 | 20 | ||
1141 | 21 | >>> pmount_from = pmount['0.1-1'].sourcepackagerelease | 21 | >>> pmount_from = pmount.getVersion('0.1-1').sourcepackagerelease |
1142 | 22 | >>> pmount_to = pmount['0.1-2'].sourcepackagerelease | 22 | >>> pmount_to = pmount.getVersion('0.1-2').sourcepackagerelease |
1143 | 23 | 23 | ||
1144 | 24 | A packageDiff can be created from the two packages by calling | 24 | A packageDiff can be created from the two packages by calling |
1145 | 25 | requestDiffTo(). It takes two arguments: the user requesting the | 25 | requestDiffTo(). It takes two arguments: the user requesting the |
This branch makes lp:ubuntu/foo resolve to the branch associated with the development version of the 'foo' package on Ubuntu, defined as the branch linked to the Release pocket of that package of the current series of that distribution.
To do this, I've registered a new adapter from DistributionSou rcePackage to ICanHasLinkedBr anch. While I was there, I also added a bzr_identity attribute to ICanHasLinkedBr anch, as well as a setBranch method. Neither of these are deeply associated with the change, but they are both things that are related & I've wanted to do for a while.
bazaar_identity has been changed to return 'lp:ubuntu/package' for source package branches associated to the development focus package.
I had to change the linked branch traversal code to pass segments into the traversable adapters. This was so I could raise context-aware errors in the case of 'ubuntu/ doesntexist' and 'ubuntu/ doesntexist/ openssh' .
I've also increased the interlinking of distro-related registry objects. In particular, SourcePackage now links to DistributionSou rcePackage (the latter being conceptually a parent of the former), and DistributionSou rcePackage links to the development source package.
There are a lot of pyflakes & pep8 cleanups along the way, inflating the size of the diff.
I've also based this branch on lp:~jml/launchpad/remove-getitem. This was necessary in order to get the adapt() method behaving correctly with DistributionSou rcePackage.