Merge lp:~adeuring/launchpad/security-guarded-test-object-factory-1 into lp:launchpad

Proposed by Abel Deuring
Status: Merged
Approved by: Jeroen T. Vermeulen
Approved revision: no longer in the source branch.
Merged at revision: 11208
Proposed branch: lp:~adeuring/launchpad/security-guarded-test-object-factory-1
Merge into: lp:launchpad
Diff against target: 852 lines (+251/-48)
20 files modified
lib/lp/code/browser/tests/test_branchlisting.py (+4/-2)
lib/lp/code/browser/tests/test_sourcepackagerecipe.py (+12/-5)
lib/lp/code/browser/tests/test_sourcepackagerecipebuild.py (+4/-1)
lib/lp/code/model/tests/test_branch.py (+4/-1)
lib/lp/code/model/tests/test_linkedbranch.py (+7/-2)
lib/lp/code/model/tests/test_sourcepackagerecipebuild.py (+5/-2)
lib/lp/registry/browser/tests/distroseries-views.txt (+1/-1)
lib/lp/registry/browser/tests/milestone-views.txt (+5/-1)
lib/lp/registry/browser/tests/productseries-views.txt (+6/-2)
lib/lp/registry/stories/webservice/xx-project-registry.txt (+4/-2)
lib/lp/registry/tests/test_distroseries.py (+15/-6)
lib/lp/registry/tests/test_sourcepackage.py (+9/-2)
lib/lp/soyuz/browser/tests/test_distrosourcepackagerelease.py (+7/-1)
lib/lp/soyuz/browser/tests/test_sourcepackagerelease.py (+11/-5)
lib/lp/soyuz/tests/test_publishing.py (+9/-3)
lib/lp/testing/factory.py (+82/-7)
lib/lp/testing/tests/test_factory.py (+50/-0)
lib/lp/translations/browser/tests/test_breadcrumbs.py (+3/-1)
lib/lp/translations/doc/translations-export-to-branch.txt (+5/-1)
lib/lp/translations/stories/buildfarm/xx-build-summary.txt (+8/-3)
To merge this branch: bzr merge lp:~adeuring/launchpad/security-guarded-test-object-factory-1
Reviewer Review Type Date Requested Status
Jeroen T. Vermeulen (community) code Approve
Review via email: mp+30497@code.launchpad.net

Description of the change

While working together with Brian on another branch during the LP epic last week, I was bitten by the fact that we have still a number of methods in LaunchpadObjectFactory which return unproxied objects. While we fixed this problem for the one or two methods we needed in that branch, a number of other methods still remained that return unproxied objects.

Some time ago, Gavin noticed the same problem, IIRC for makePerson(); he also filed a bug that other methods still returned unproxied objects, and Salgado finally fixed that bug.

However, new methods obviously returning unproxied objects creeped into LaunchpadObjectFactory in the meantime, thus reintroducing the old problem. So I thought that it might be reasonable to ensure that all objects returned by LPObjectFactory methods are either of a simple Python type or are security proxied.

The implementation is quite simple: I renamed LaunchpadObjectFactory to _LaunchpadObjectFactory and wrote a new class LaunchpadObjectFactory with basically one method, __getattr__(). This method returns a the "real" factory method, wrapped in a function guarded_method() which ensures that the object returned by the _LaunchpadObjectFactory.makeWhatever() matches the conditions described above.

We have at present ca 20 methods which return, at least sometimes, unproxied objects. Fixing all these methods in one branch would result in a huge diff, so I fixed only two methods in this branch, makeDistroRelease() and makeProductSeries().

My plan is to let guarded_method() raise an exception if it detects an unproxied object, once all existing factory methods return properly proxied objects. For now, it just print a warning to stderr.

The largest part of the diff is test fixes. I took the simple route: Just to remove the security proxy so that the affected tests pass again. I did not bother to check if this is reasonable in a given test -- doing so would simply have needed far too much time. Instead, I added a function remove_security_proxy_and_shout_at_engineer() which just calls removeSecurityProxy() and print a warning to stderr. This will make the tests quite noisy (as do the warnings issued guarded_method() about unproxied objects)...

Pre-/mid-imp discussions with Jeroen and Gary.

affected tests: Many, see the diff.

I also removed some lint in the files I touched, but some messages remain:

= Launchpad lint =

Checking for conflicts and issues in changed files.

Linting changed files:
  lib/lp/code/browser/tests/test_branchlisting.py
  lib/lp/code/browser/tests/test_sourcepackagerecipe.py
  lib/lp/code/browser/tests/test_sourcepackagerecipebuild.py
  lib/lp/code/model/tests/test_branch.py
  lib/lp/code/model/tests/test_linkedbranch.py
  lib/lp/code/model/tests/test_sourcepackagerecipe.py
  lib/lp/code/model/tests/test_sourcepackagerecipebuild.py
  lib/lp/registry/browser/tests/distroseries-views.txt
  lib/lp/registry/browser/tests/milestone-views.txt
  lib/lp/registry/browser/tests/productseries-views.txt
  lib/lp/registry/stories/webservice/xx-project-registry.txt
  lib/lp/registry/tests/test_distroseries.py
  lib/lp/registry/tests/test_sourcepackage.py
  lib/lp/soyuz/browser/tests/test_distrosourcepackagerelease.py
  lib/lp/soyuz/browser/tests/test_sourcepackagerelease.py
  lib/lp/soyuz/tests/test_publishing.py
  lib/lp/testing/factory.py
  lib/lp/translations/browser/tests/test_breadcrumbs.py
  lib/lp/translations/doc/translations-export-to-branch.txt
  lib/lp/translations/stories/buildfarm/xx-build-summary.txt

./lib/lp/registry/browser/tests/productseries-views.txt
      83: want exceeds 78 characters.
./lib/lp/registry/stories/webservice/xx-project-registry.txt
     570: source exceeds 78 characters.
    1254: 'IRepresentationCache' imported but unused
./lib/lp/soyuz/browser/tests/test_sourcepackagerelease.py
     119: E302 expected 2 blank lines, found 1
./lib/lp/translations/browser/tests/test_breadcrumbs.py
      27: E501 line too long (81 characters)
      37: E501 line too long (86 characters)
      45: E501 line too long (81 characters)
      56: E501 line too long (86 characters)
      64: E501 line too long (81 characters)
      72: E501 line too long (82 characters)
      81: E501 line too long (82 characters)
      88: E501 line too long (81 characters)
     138: E301 expected 1 blank line, found 0
     152: E501 line too long (85 characters)
      27: Line exceeds 78 characters.
      37: Line exceeds 78 characters.
      45: Line exceeds 78 characters.
      56: Line exceeds 78 characters.
      64: Line exceeds 78 characters.
      72: Line exceeds 78 characters.
      81: Line exceeds 78 characters.
      88: Line exceeds 78 characters.
     116: Line exceeds 78 characters.
     133: Line exceeds 78 characters.
     152: Line exceeds 78 characters.
./lib/lp/translations/doc/translations-export-to-branch.txt
       0: narrative uses a moin header.
      15: want exceeds 78 characters.
     273: narrative uses a moin header.
     301: narrative uses a moin header.
     242: Could not compile:
             finally:
./lib/lp/translations/stories/buildfarm/xx-build-summary.txt
       0: narrative uses a moin header.
       5: narrative uses a moin header.
      64: narrative uses a moin header.

To post a comment you must log in.
Revision history for this message
Jeroen T. Vermeulen (jtv) wrote :

Thanks for doing this. We discussed this over IRL chat and came up with many little improvements: chiefly renaming _LaunchpadObjectFactory to BareLaunchpadObjectFactory, moving some code out of a nested function, fixing some corner cases, and adding a test for the proxy-checking function since it turned out to be a lot less trivial than originally thought.

This is often the case when engineers say a change will be trivial.

Jeroen

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/tests/test_branchlisting.py'
--- lib/lp/code/browser/tests/test_branchlisting.py 2010-05-28 09:54:45 +0000
+++ lib/lp/code/browser/tests/test_branchlisting.py 2010-07-22 13:01:10 +0000
@@ -32,12 +32,14 @@
32from lp.testing import (32from lp.testing import (
33 BrowserTestCase, TestCase, TestCaseWithFactory, login_person,33 BrowserTestCase, TestCase, TestCaseWithFactory, login_person,
34 person_logged_in, time_counter)34 person_logged_in, time_counter)
35from lp.testing.factory import remove_security_proxy_and_shout_at_engineer
35from lp.testing.views import create_initialized_view36from lp.testing.views import create_initialized_view
36from canonical.launchpad.testing.pages import extract_text, find_tag_by_id37from canonical.launchpad.testing.pages import extract_text, find_tag_by_id
37from canonical.launchpad.webapp import canonical_url38from canonical.launchpad.webapp import canonical_url
38from canonical.launchpad.webapp.servers import LaunchpadTestRequest39from canonical.launchpad.webapp.servers import LaunchpadTestRequest
39from canonical.testing.layers import DatabaseFunctionalLayer40from canonical.testing.layers import DatabaseFunctionalLayer
4041
42
41class TestListingToSortOrder(TestCase):43class TestListingToSortOrder(TestCase):
42 """Tests for the BranchSet._listingSortToOrderBy static method.44 """Tests for the BranchSet._listingSortToOrderBy static method.
4345
@@ -60,6 +62,7 @@
6062
61 def assertSortsEqual(self, sort_one, sort_two):63 def assertSortsEqual(self, sort_one, sort_two):
62 """Assert that one list of sort specs is equal to another."""64 """Assert that one list of sort specs is equal to another."""
65
63 def sort_data(sort):66 def sort_data(sort):
64 return sort.suffix, sort.expr67 return sort.suffix, sort.expr
65 self.assertEqual(map(sort_data, sort_one), map(sort_data, sort_two))68 self.assertEqual(map(sort_data, sort_one), map(sort_data, sort_two))
@@ -352,7 +355,7 @@
352 # series on the main site, not the code site.355 # series on the main site, not the code site.
353 branch = self.factory.makeProductBranch()356 branch = self.factory.makeProductBranch()
354 series = self.factory.makeProductSeries(product=branch.product)357 series = self.factory.makeProductSeries(product=branch.product)
355 series.branch = branch358 remove_security_proxy_and_shout_at_engineer(series).branch = branch
356 browser = self.getUserBrowser(359 browser = self.getUserBrowser(
357 canonical_url(branch.product, rootsite='code'))360 canonical_url(branch.product, rootsite='code'))
358 link = browser.getLink(re.compile('^' + series.name + '$'))361 link = browser.getLink(re.compile('^' + series.name + '$'))
@@ -402,4 +405,3 @@
402405
403def test_suite():406def test_suite():
404 return unittest.TestLoader().loadTestsFromName(__name__)407 return unittest.TestLoader().loadTestsFromName(__name__)
405
406408
=== modified file 'lib/lp/code/browser/tests/test_sourcepackagerecipe.py'
--- lib/lp/code/browser/tests/test_sourcepackagerecipe.py 2010-07-21 21:16:54 +0000
+++ lib/lp/code/browser/tests/test_sourcepackagerecipe.py 2010-07-22 13:01:10 +0000
@@ -29,6 +29,7 @@
29from lp.registry.interfaces.pocket import PackagePublishingPocket29from lp.registry.interfaces.pocket import PackagePublishingPocket
30from lp.soyuz.model.processor import ProcessorFamily30from lp.soyuz.model.processor import ProcessorFamily
31from lp.testing import ANONYMOUS, BrowserTestCase, login, logout31from lp.testing import ANONYMOUS, BrowserTestCase, login, logout
32from lp.testing.factory import remove_security_proxy_and_shout_at_engineer
3233
3334
34class TestCaseForRecipe(BrowserTestCase):35class TestCaseForRecipe(BrowserTestCase):
@@ -45,7 +46,9 @@
45 self.squirrel = self.factory.makeDistroSeries(46 self.squirrel = self.factory.makeDistroSeries(
46 displayname='Secret Squirrel', name='secret', version='100.04',47 displayname='Secret Squirrel', name='secret', version='100.04',
47 distribution=self.ppa.distribution)48 distribution=self.ppa.distribution)
48 self.squirrel.nominatedarchindep = self.squirrel.newArch(49 naked_squirrel = remove_security_proxy_and_shout_at_engineer(
50 self.squirrel)
51 naked_squirrel.nominatedarchindep = self.squirrel.newArch(
49 'i386', ProcessorFamily.get(1), False, self.chef,52 'i386', ProcessorFamily.get(1), False, self.chef,
50 supports_virtualized=True)53 supports_virtualized=True)
5154
@@ -494,7 +497,7 @@
494 def makeBuildJob(self, recipe):497 def makeBuildJob(self, recipe):
495 """Return a build associated with a buildjob."""498 """Return a build associated with a buildjob."""
496 build = self.factory.makeSourcePackageRecipeBuild(499 build = self.factory.makeSourcePackageRecipeBuild(
497 recipe=recipe, distroseries=self.squirrel, archive=self.ppa )500 recipe=recipe, distroseries=self.squirrel, archive=self.ppa)
498 self.factory.makeSourcePackageRecipeBuildJob(recipe_build=build)501 self.factory.makeSourcePackageRecipeBuildJob(recipe_build=build)
499 return build502 return build
500503
@@ -527,6 +530,7 @@
527 self.assertEqual(530 self.assertEqual(
528 set([build1, build2, build3, build4, build5, build6]),531 set([build1, build2, build3, build4, build5, build6]),
529 set(view.builds))532 set(view.builds))
533
530 def set_day(build, day):534 def set_day(build, day):
531 removeSecurityProxy(build).datebuilt = datetime(535 removeSecurityProxy(build).datebuilt = datetime(
532 2010, 03, day, tzinfo=utc)536 2010, 03, day, tzinfo=utc)
@@ -569,7 +573,8 @@
569 woody = self.factory.makeDistroSeries(573 woody = self.factory.makeDistroSeries(
570 name='woody', displayname='Woody',574 name='woody', displayname='Woody',
571 distribution=self.ppa.distribution)575 distribution=self.ppa.distribution)
572 woody.nominatedarchindep = woody.newArch(576 naked_woody = remove_security_proxy_and_shout_at_engineer(woody)
577 naked_woody.nominatedarchindep = woody.newArch(
573 'i386', ProcessorFamily.get(1), False, self.factory.makePerson(),578 'i386', ProcessorFamily.get(1), False, self.factory.makePerson(),
574 supports_virtualized=True)579 supports_virtualized=True)
575580
@@ -603,7 +608,8 @@
603 woody = self.factory.makeDistroSeries(608 woody = self.factory.makeDistroSeries(
604 name='woody', displayname='Woody',609 name='woody', displayname='Woody',
605 distribution=self.ppa.distribution)610 distribution=self.ppa.distribution)
606 woody.nominatedarchindep = woody.newArch(611 naked_woody = remove_security_proxy_and_shout_at_engineer(woody)
612 naked_woody.nominatedarchindep = woody.newArch(
607 'i386', ProcessorFamily.get(1), False, self.factory.makePerson(),613 'i386', ProcessorFamily.get(1), False, self.factory.makePerson(),
608 supports_virtualized=True)614 supports_virtualized=True)
609615
@@ -624,7 +630,8 @@
624 woody = self.factory.makeDistroSeries(630 woody = self.factory.makeDistroSeries(
625 name='woody', displayname='Woody',631 name='woody', displayname='Woody',
626 distribution=self.ppa.distribution)632 distribution=self.ppa.distribution)
627 woody.nominatedarchindep = woody.newArch(633 naked_woody = remove_security_proxy_and_shout_at_engineer(woody)
634 naked_woody.nominatedarchindep = woody.newArch(
628 'i386', ProcessorFamily.get(1), False, self.factory.makePerson(),635 'i386', ProcessorFamily.get(1), False, self.factory.makePerson(),
629 supports_virtualized=True)636 supports_virtualized=True)
630637
631638
=== modified file 'lib/lp/code/browser/tests/test_sourcepackagerecipebuild.py'
--- lib/lp/code/browser/tests/test_sourcepackagerecipebuild.py 2010-07-19 09:22:06 +0000
+++ lib/lp/code/browser/tests/test_sourcepackagerecipebuild.py 2010-07-22 13:01:10 +0000
@@ -18,6 +18,7 @@
18from lp.buildmaster.interfaces.buildbase import BuildStatus18from lp.buildmaster.interfaces.buildbase import BuildStatus
19from lp.soyuz.model.processor import ProcessorFamily19from lp.soyuz.model.processor import ProcessorFamily
20from lp.testing import ANONYMOUS, BrowserTestCase, login, logout20from lp.testing import ANONYMOUS, BrowserTestCase, login, logout
21from lp.testing.factory import remove_security_proxy_and_shout_at_engineer
2122
2223
23class TestSourcePackageRecipeBuild(BrowserTestCase):24class TestSourcePackageRecipeBuild(BrowserTestCase):
@@ -36,7 +37,9 @@
36 self.squirrel = self.factory.makeDistroSeries(37 self.squirrel = self.factory.makeDistroSeries(
37 displayname='Secret Squirrel', name='secret', version='100.04',38 displayname='Secret Squirrel', name='secret', version='100.04',
38 distribution=self.ppa.distribution)39 distribution=self.ppa.distribution)
39 self.squirrel.nominatedarchindep = self.squirrel.newArch(40 naked_squirrel = remove_security_proxy_and_shout_at_engineer(
41 self.squirrel)
42 naked_squirrel.nominatedarchindep = self.squirrel.newArch(
40 'i386', ProcessorFamily.get(1), False, self.chef,43 'i386', ProcessorFamily.get(1), False, self.chef,
41 supports_virtualized=True)44 supports_virtualized=True)
4245
4346
=== modified file 'lib/lp/code/model/tests/test_branch.py'
--- lib/lp/code/model/tests/test_branch.py 2010-07-17 23:15:30 +0000
+++ lib/lp/code/model/tests/test_branch.py 2010-07-22 13:01:10 +0000
@@ -537,7 +537,7 @@
537 jobs = list(getUtility(IBranchUpgradeJobSource).iterReady())537 jobs = list(getUtility(IBranchUpgradeJobSource).iterReady())
538 self.assertEqual(538 self.assertEqual(
539 jobs,539 jobs,
540 [job,])540 [job, ])
541541
542 def test_requestUpgrade_no_upgrade_needed(self):542 def test_requestUpgrade_no_upgrade_needed(self):
543 # If a branch doesn't need to be upgraded, requestUpgrade raises an543 # If a branch doesn't need to be upgraded, requestUpgrade raises an
@@ -635,6 +635,7 @@
635 branch = self.factory.makeProductBranch(635 branch = self.factory.makeProductBranch(
636 product=fooix, owner=eric, name='trunk')636 product=fooix, owner=eric, name='trunk')
637 linked_branch = ICanHasLinkedBranch(future)637 linked_branch = ICanHasLinkedBranch(future)
638 login_person(fooix.owner)
638 linked_branch.setBranch(branch)639 linked_branch.setBranch(branch)
639 self.assertEqual(640 self.assertEqual(
640 [linked_branch],641 [linked_branch],
@@ -827,6 +828,7 @@
827 product = branch.product828 product = branch.product
828 series = self.factory.makeProductSeries(product=product)829 series = self.factory.makeProductSeries(product=product)
829 linked_branch = ICanHasLinkedBranch(series)830 linked_branch = ICanHasLinkedBranch(series)
831 login_person(series.owner)
830 linked_branch.setBranch(branch)832 linked_branch.setBranch(branch)
831 self.assertBzrIdentity(branch, linked_branch.bzr_path)833 self.assertBzrIdentity(branch, linked_branch.bzr_path)
832834
@@ -852,6 +854,7 @@
852 removeSecurityProxy(branch.product))854 removeSecurityProxy(branch.product))
853 series_link = ICanHasLinkedBranch(series)855 series_link = ICanHasLinkedBranch(series)
854 product_link.setBranch(branch)856 product_link.setBranch(branch)
857 login_person(series.owner)
855 series_link.setBranch(branch)858 series_link.setBranch(branch)
856 self.assertBzrIdentity(branch, product_link.bzr_path)859 self.assertBzrIdentity(branch, product_link.bzr_path)
857860
858861
=== modified file 'lib/lp/code/model/tests/test_linkedbranch.py'
--- lib/lp/code/model/tests/test_linkedbranch.py 2010-04-16 15:06:55 +0000
+++ lib/lp/code/model/tests/test_linkedbranch.py 2010-07-22 13:01:10 +0000
@@ -18,6 +18,7 @@
18from lp.registry.interfaces.distroseries import NoSuchDistroSeries18from lp.registry.interfaces.distroseries import NoSuchDistroSeries
19from lp.registry.interfaces.pocket import PackagePublishingPocket19from lp.registry.interfaces.pocket import PackagePublishingPocket
20from lp.testing import run_with_login, TestCaseWithFactory20from lp.testing import run_with_login, TestCaseWithFactory
21from lp.testing.factory import remove_security_proxy_and_shout_at_engineer
2122
2223
23class TestProductSeriesLinkedBranch(TestCaseWithFactory):24class TestProductSeriesLinkedBranch(TestCaseWithFactory):
@@ -27,7 +28,9 @@
27 def test_branch(self):28 def test_branch(self):
28 # The linked branch of a product series is its branch attribute.29 # The linked branch of a product series is its branch attribute.
29 product_series = self.factory.makeProductSeries()30 product_series = self.factory.makeProductSeries()
30 product_series.branch = self.factory.makeProductBranch(31 naked_product_series = remove_security_proxy_and_shout_at_engineer(
32 product_series)
33 naked_product_series.branch = self.factory.makeProductBranch(
31 product=product_series.product)34 product=product_series.product)
32 self.assertEqual(35 self.assertEqual(
33 product_series.branch, ICanHasLinkedBranch(product_series).branch)36 product_series.branch, ICanHasLinkedBranch(product_series).branch)
@@ -35,9 +38,11 @@
35 def test_setBranch(self):38 def test_setBranch(self):
36 # setBranch sets the linked branch of the product series.39 # setBranch sets the linked branch of the product series.
37 product_series = self.factory.makeProductSeries()40 product_series = self.factory.makeProductSeries()
41 naked_product_series = remove_security_proxy_and_shout_at_engineer(
42 product_series)
38 branch = self.factory.makeProductBranch(43 branch = self.factory.makeProductBranch(
39 product=product_series.product)44 product=product_series.product)
40 ICanHasLinkedBranch(product_series).setBranch(branch)45 ICanHasLinkedBranch(naked_product_series).setBranch(branch)
41 self.assertEqual(branch, product_series.branch)46 self.assertEqual(branch, product_series.branch)
4247
43 def test_bzr_path(self):48 def test_bzr_path(self):
4449
=== modified file 'lib/lp/code/model/tests/test_sourcepackagerecipebuild.py'
--- lib/lp/code/model/tests/test_sourcepackagerecipebuild.py 2010-07-20 12:12:46 +0000
+++ lib/lp/code/model/tests/test_sourcepackagerecipebuild.py 2010-07-22 13:01:10 +0000
@@ -37,6 +37,7 @@
37from lp.soyuz.model.processor import ProcessorFamily37from lp.soyuz.model.processor import ProcessorFamily
38from lp.soyuz.tests.soyuzbuilddhelpers import WaitingSlave38from lp.soyuz.tests.soyuzbuilddhelpers import WaitingSlave
39from lp.testing import ANONYMOUS, login, person_logged_in, TestCaseWithFactory39from lp.testing import ANONYMOUS, login, person_logged_in, TestCaseWithFactory
40from lp.testing.factory import remove_security_proxy_and_shout_at_engineer
40from lp.testing.fakemethod import FakeMethod41from lp.testing.fakemethod import FakeMethod
41from lp.testing.mail_helpers import pop_notifications42from lp.testing.mail_helpers import pop_notifications
4243
@@ -53,7 +54,9 @@
53 distroseries_i386 = distroseries.newArch(54 distroseries_i386 = distroseries.newArch(
54 'i386', ProcessorFamily.get(1), False, person,55 'i386', ProcessorFamily.get(1), False, person,
55 supports_virtualized=True)56 supports_virtualized=True)
56 distroseries.nominatedarchindep = distroseries_i38657 naked_distroseries = remove_security_proxy_and_shout_at_engineer(
58 distroseries)
59 naked_distroseries.nominatedarchindep = distroseries_i386
5760
58 return getUtility(ISourcePackageRecipeBuildSource).new(61 return getUtility(ISourcePackageRecipeBuildSource).new(
59 distroseries=distroseries,62 distroseries=distroseries,
@@ -320,7 +323,7 @@
320 removeSecurityProxy(build).buildstate = BuildStatus.FULLYBUILT323 removeSecurityProxy(build).buildstate = BuildStatus.FULLYBUILT
321 IStore(build).flush()324 IStore(build).flush()
322 build.notify()325 build.notify()
323 (message,) = pop_notifications()326 (message, ) = pop_notifications()
324 requester = build.requester327 requester = build.requester
325 requester_address = format_address(328 requester_address = format_address(
326 requester.displayname, requester.preferredemail.email)329 requester.displayname, requester.preferredemail.email)
327330
=== modified file 'lib/lp/registry/browser/tests/distroseries-views.txt'
--- lib/lp/registry/browser/tests/distroseries-views.txt 2010-05-24 22:04:19 +0000
+++ lib/lp/registry/browser/tests/distroseries-views.txt 2010-07-22 13:01:10 +0000
@@ -196,7 +196,7 @@
196 >>> [field.__name__ for field in view.form_fields]196 >>> [field.__name__ for field in view.form_fields]
197 ['displayname', 'title', 'summary', 'description', 'status']197 ['displayname', 'title', 'summary', 'description', 'status']
198198
199 >>> print view.widgets.get('status')._getFormValue()199 >>> print view.widgets.get('status')._getFormValue().title
200 Active Development200 Active Development
201201
202202
203203
=== modified file 'lib/lp/registry/browser/tests/milestone-views.txt'
--- lib/lp/registry/browser/tests/milestone-views.txt 2010-06-30 19:37:09 +0000
+++ lib/lp/registry/browser/tests/milestone-views.txt 2010-07-22 13:01:10 +0000
@@ -579,9 +579,13 @@
579The driver of a series that belongs to an `IDerivativeDistribution` is a579The driver of a series that belongs to an `IDerivativeDistribution` is a
580release manager and can create milestones.580release manager and can create milestones.
581581
582 >>> from lp.testing.factory import (
583 ... remove_security_proxy_and_shout_at_engineer)
582 >>> distroseries = factory.makeDistroRelease(name='pumpkin')584 >>> distroseries = factory.makeDistroRelease(name='pumpkin')
583 >>> driver = factory.makePerson(name='a-driver')585 >>> driver = factory.makePerson(name='a-driver')
584 >>> distroseries.driver = driver586 >>> naked_distroseries = remove_security_proxy_and_shout_at_engineer(
587 ... distroseries)
588 >>> naked_distroseries.driver = driver
585 >>> login_person(driver)589 >>> login_person(driver)
586590
587 >>> form = {591 >>> form = {
588592
=== modified file 'lib/lp/registry/browser/tests/productseries-views.txt'
--- lib/lp/registry/browser/tests/productseries-views.txt 2010-06-13 02:01:25 +0000
+++ lib/lp/registry/browser/tests/productseries-views.txt 2010-07-22 13:01:10 +0000
@@ -255,6 +255,8 @@
255255
256 >>> from datetime import datetime256 >>> from datetime import datetime
257 >>> from pytz import UTC257 >>> from pytz import UTC
258 >>> from lp.testing.factory import (
259 ... remove_security_proxy_and_shout_at_engineer)
258260
259 >>> product = factory.makeProduct(name="field", displayname='Field')261 >>> product = factory.makeProduct(name="field", displayname='Field')
260 >>> productseries = factory.makeProductSeries(262 >>> productseries = factory.makeProductSeries(
@@ -263,7 +265,9 @@
263265
264 # Hack the creation date for testing purposes.266 # Hack the creation date for testing purposes.
265 >>> test_date = datetime(2009, 05, 01, 19, 34, 24, tzinfo=UTC)267 >>> test_date = datetime(2009, 05, 01, 19, 34, 24, tzinfo=UTC)
266 >>> productseries.datecreated = test_date268 >>> naked_productseries = remove_security_proxy_and_shout_at_engineer(
269 ... productseries)
270 >>> naked_productseries.datecreated = test_date
267271
268Users without edit permission cannot access the view.272Users without edit permission cannot access the view.
269273
@@ -470,7 +474,7 @@
470474
471The series status is set to obsolete and the releasefileglob was set to None.475The series status is set to obsolete and the releasefileglob was set to None.
472476
473 >>> print productseries.status477 >>> print productseries.status.title
474 Obsolete478 Obsolete
475 >>> print productseries.releasefileglob479 >>> print productseries.releasefileglob
476 None480 None
477481
=== modified file 'lib/lp/registry/stories/webservice/xx-project-registry.txt'
--- lib/lp/registry/stories/webservice/xx-project-registry.txt 2010-06-14 18:32:58 +0000
+++ lib/lp/registry/stories/webservice/xx-project-registry.txt 2010-07-22 13:01:10 +0000
@@ -847,15 +847,17 @@
847The entry for a project series is available at its canonical URL on the847The entry for a project series is available at its canonical URL on the
848virtual host.848virtual host.
849849
850 >>> from zope.security.proxy import removeSecurityProxy
850 >>> login('test@canonical.com')851 >>> login('test@canonical.com')
851 >>> babadoo_owner = factory.makePerson(name='babadoo-owner')852 >>> babadoo_owner = factory.makePerson(name='babadoo-owner')
852 >>> babadoo = factory.makeProduct(name='babadoo', owner=babadoo_owner)853 >>> babadoo = factory.makeProduct(name='babadoo', owner=babadoo_owner)
853 >>> foobadoo = factory.makeProductSeries(854 >>> foobadoo = factory.makeProductSeries(
854 ... product=babadoo, name='foobadoo', owner=babadoo_owner)855 ... product=babadoo, name='foobadoo', owner=babadoo_owner)
855 >>> foobadoo.summary = u'Foobadoo support for Babadoo'856 >>> removeSecurityProxy(foobadoo).summary = (
857 ... u'Foobadoo support for Babadoo')
856 >>> fooey = factory.makeAnyBranch(858 >>> fooey = factory.makeAnyBranch(
857 ... product=babadoo, name='fooey', owner=babadoo_owner)859 ... product=babadoo, name='fooey', owner=babadoo_owner)
858 >>> foobadoo.branch = fooey860 >>> removeSecurityProxy(foobadoo).branch = fooey
859 >>> logout()861 >>> logout()
860862
861 >>> babadoo_foobadoo = webservice.get('/babadoo/foobadoo').jsonBody()863 >>> babadoo_foobadoo = webservice.get('/babadoo/foobadoo').jsonBody()
862864
=== modified file 'lib/lp/registry/tests/test_distroseries.py'
--- lib/lp/registry/tests/test_distroseries.py 2010-06-23 23:23:28 +0000
+++ lib/lp/registry/tests/test_distroseries.py 2010-07-22 13:01:10 +0000
@@ -24,6 +24,7 @@
24 active_publishing_status, PackagePublishingStatus)24 active_publishing_status, PackagePublishingStatus)
25from lp.soyuz.model.processor import ProcessorFamilySet25from lp.soyuz.model.processor import ProcessorFamilySet
26from lp.testing import TestCase, TestCaseWithFactory26from lp.testing import TestCase, TestCaseWithFactory
27from lp.testing.factory import remove_security_proxy_and_shout_at_engineer
27from lp.soyuz.tests.test_publishing import SoyuzTestPublisher28from lp.soyuz.tests.test_publishing import SoyuzTestPublisher
28from lp.translations.interfaces.translations import (29from lp.translations.interfaces.translations import (
29 TranslationsBranchImportMode)30 TranslationsBranchImportMode)
@@ -285,7 +286,9 @@
285 self.linkPackage('hot')286 self.linkPackage('hot')
286 self.makeSeriesPackage('cold')287 self.makeSeriesPackage('cold')
287 product_series = self.linkPackage('cold')288 product_series = self.linkPackage('cold')
288 product_series.product.bugtraker = self.factory.makeBugTracker()289 naked_product_series = remove_security_proxy_and_shout_at_engineer(
290 product_series)
291 naked_product_series.product.bugtraker = self.factory.makeBugTracker()
289 packagings = self.series.getPrioritizedlPackagings()292 packagings = self.series.getPrioritizedlPackagings()
290 names = [packaging.sourcepackagename.name for packaging in packagings]293 names = [packaging.sourcepackagename.name for packaging in packagings]
291 expected = [u'hot', u'cold', u'linked']294 expected = [u'hot', u'cold', u'linked']
@@ -296,7 +299,9 @@
296 self.linkPackage('translatable')299 self.linkPackage('translatable')
297 self.makeSeriesPackage('withbranch')300 self.makeSeriesPackage('withbranch')
298 product_series = self.linkPackage('withbranch')301 product_series = self.linkPackage('withbranch')
299 product_series.branch = self.factory.makeBranch()302 naked_product_series = remove_security_proxy_and_shout_at_engineer(
303 product_series)
304 naked_product_series.branch = self.factory.makeBranch()
300 packagings = self.series.getPrioritizedlPackagings()305 packagings = self.series.getPrioritizedlPackagings()
301 names = [packaging.sourcepackagename.name for packaging in packagings]306 names = [packaging.sourcepackagename.name for packaging in packagings]
302 expected = [u'translatable', u'linked', u'withbranch']307 expected = [u'translatable', u'linked', u'withbranch']
@@ -308,8 +313,10 @@
308 self.linkPackage('translatable')313 self.linkPackage('translatable')
309 self.makeSeriesPackage('importabletranslatable')314 self.makeSeriesPackage('importabletranslatable')
310 product_series = self.linkPackage('importabletranslatable')315 product_series = self.linkPackage('importabletranslatable')
311 product_series.branch = self.factory.makeBranch()316 naked_product_series = remove_security_proxy_and_shout_at_engineer(
312 product_series.translations_autoimport_mode = (317 product_series)
318 naked_product_series.branch = self.factory.makeBranch()
319 naked_product_series.translations_autoimport_mode = (
313 TranslationsBranchImportMode.IMPORT_TEMPLATES)320 TranslationsBranchImportMode.IMPORT_TEMPLATES)
314 packagings = self.series.getPrioritizedlPackagings()321 packagings = self.series.getPrioritizedlPackagings()
315 names = [packaging.sourcepackagename.name for packaging in packagings]322 names = [packaging.sourcepackagename.name for packaging in packagings]
@@ -343,7 +350,9 @@
343350
344 new_distroseries = (351 new_distroseries = (
345 self.factory.makeDistroRelease(name=u"sampleseries"))352 self.factory.makeDistroRelease(name=u"sampleseries"))
346 new_distroseries.hide_all_translations = False353 naked_new_distroseries = remove_security_proxy_and_shout_at_engineer(
354 new_distroseries)
355 naked_new_distroseries.hide_all_translations = False
347 transaction.commit()356 transaction.commit()
348 translatables = self._get_translatables()357 translatables = self._get_translatables()
349 self.failUnlessEqual(358 self.failUnlessEqual(
@@ -365,7 +374,7 @@
365 translatables,374 translatables,
366 self._ref_translatables(u"sampleseries")))375 self._ref_translatables(u"sampleseries")))
367376
368 new_distroseries.hide_all_translations = True377 naked_new_distroseries.hide_all_translations = True
369 transaction.commit()378 transaction.commit()
370 translatables = self._get_translatables()379 translatables = self._get_translatables()
371 self.failUnlessEqual(380 self.failUnlessEqual(
372381
=== modified file 'lib/lp/registry/tests/test_sourcepackage.py'
--- lib/lp/registry/tests/test_sourcepackage.py 2010-02-24 03:06:54 +0000
+++ lib/lp/registry/tests/test_sourcepackage.py 2010-07-22 13:01:10 +0000
@@ -22,6 +22,7 @@
22from lp.code.interfaces.seriessourcepackagebranch import (22from lp.code.interfaces.seriessourcepackagebranch import (
23 IMakeOfficialBranchLinks)23 IMakeOfficialBranchLinks)
24from lp.testing import TestCaseWithFactory24from lp.testing import TestCaseWithFactory
25from lp.testing.factory import remove_security_proxy_and_shout_at_engineer
25from lp.testing.views import create_initialized_view26from lp.testing.views import create_initialized_view
26from canonical.testing.layers import DatabaseFunctionalLayer27from canonical.testing.layers import DatabaseFunctionalLayer
2728
@@ -252,11 +253,17 @@
252253
253 self.obsolete_productseries = self.factory.makeProductSeries(254 self.obsolete_productseries = self.factory.makeProductSeries(
254 name='obsolete', product=self.product)255 name='obsolete', product=self.product)
255 self.obsolete_productseries.status = SeriesStatus.OBSOLETE256 naked_obsolete_productseries = (
257 remove_security_proxy_and_shout_at_engineer(
258 self.obsolete_productseries))
259 naked_obsolete_productseries.status = SeriesStatus.OBSOLETE
256260
257 self.dev_productseries = self.factory.makeProductSeries(261 self.dev_productseries = self.factory.makeProductSeries(
258 name='current', product=self.product)262 name='current', product=self.product)
259 self.dev_productseries.status = SeriesStatus.DEVELOPMENT263 naked_dev_productseries = (
264 remove_security_proxy_and_shout_at_engineer(
265 self.dev_productseries))
266 naked_dev_productseries.status = SeriesStatus.DEVELOPMENT
260267
261 self.distribution = self.factory.makeDistribution(268 self.distribution = self.factory.makeDistribution(
262 name='youbuntu', displayname='Youbuntu', owner=self.owner)269 name='youbuntu', displayname='Youbuntu', owner=self.owner)
263270
=== modified file 'lib/lp/soyuz/browser/tests/test_distrosourcepackagerelease.py'
--- lib/lp/soyuz/browser/tests/test_distrosourcepackagerelease.py 2010-07-20 12:06:36 +0000
+++ lib/lp/soyuz/browser/tests/test_distrosourcepackagerelease.py 2010-07-22 13:01:10 +0000
@@ -14,6 +14,7 @@
14 DistributionSourcePackageRelease)14 DistributionSourcePackageRelease)
15from lp.soyuz.tests.test_publishing import SoyuzTestPublisher15from lp.soyuz.tests.test_publishing import SoyuzTestPublisher
16from lp.testing import TestCaseWithFactory16from lp.testing import TestCaseWithFactory
17from lp.testing.factory import remove_security_proxy_and_shout_at_engineer
17from lp.testing.views import create_initialized_view18from lp.testing.views import create_initialized_view
1819
1920
@@ -27,7 +28,12 @@
27 # The package must be published for the page to render.28 # The package must be published for the page to render.
28 stp = SoyuzTestPublisher()29 stp = SoyuzTestPublisher()
29 distroseries = stp.setUpDefaultDistroSeries()30 distroseries = stp.setUpDefaultDistroSeries()
30 distro = distroseries.distribution31 naked_distroseries = remove_security_proxy_and_shout_at_engineer(
32 distroseries)
33 # XXX Abel Deuring, 2010-07-21, bug 608240. This is scary. But
34 # if we use distroseries.distribution instead,
35 # test_spr_files_deleted() and test_spr_files_one() fail.
36 distro = naked_distroseries.distribution
31 source_package_release = stp.getPubSource().sourcepackagerelease37 source_package_release = stp.getPubSource().sourcepackagerelease
32 self.dspr = DistributionSourcePackageRelease(38 self.dspr = DistributionSourcePackageRelease(
33 distro, source_package_release)39 distro, source_package_release)
3440
=== modified file 'lib/lp/soyuz/browser/tests/test_sourcepackagerelease.py'
--- lib/lp/soyuz/browser/tests/test_sourcepackagerelease.py 2010-07-20 12:06:36 +0000
+++ lib/lp/soyuz/browser/tests/test_sourcepackagerelease.py 2010-07-22 13:01:10 +0000
@@ -16,6 +16,7 @@
16from canonical.testing import (16from canonical.testing import (
17 DatabaseFunctionalLayer, LaunchpadFunctionalLayer)17 DatabaseFunctionalLayer, LaunchpadFunctionalLayer)
18from lp.testing import TestCaseWithFactory18from lp.testing import TestCaseWithFactory
19from lp.testing.factory import remove_security_proxy_and_shout_at_engineer
19from lp.testing.views import create_initialized_view20from lp.testing.views import create_initialized_view
2021
2122
@@ -64,20 +65,23 @@
6465
65 def test_highlighted_copyright_is_None(self):66 def test_highlighted_copyright_is_None(self):
66 expected = ''67 expected = ''
67 self.source_package_release.copyright = None68 remove_security_proxy_and_shout_at_engineer(
69 self.source_package_release).copyright = None
68 view = create_initialized_view(70 view = create_initialized_view(
69 self.source_package_release, '+copyright')71 self.source_package_release, '+copyright')
70 self.assertEqual(expected, view.highlighted_copyright)72 self.assertEqual(expected, view.highlighted_copyright)
7173
72 def test_highlighted_copyright_no_matches(self):74 def test_highlighted_copyright_no_matches(self):
73 expected = 'nothing to see and/or do.'75 expected = 'nothing to see and/or do.'
74 self.source_package_release.copyright = expected76 remove_security_proxy_and_shout_at_engineer(
77 self.source_package_release).copyright = expected
75 view = create_initialized_view(78 view = create_initialized_view(
76 self.source_package_release, '+copyright')79 self.source_package_release, '+copyright')
77 self.assertEqual(expected, view.highlighted_copyright)80 self.assertEqual(expected, view.highlighted_copyright)
7881
79 def test_highlighted_copyright_match_url(self):82 def test_highlighted_copyright_match_url(self):
80 self.source_package_release.copyright = (83 remove_security_proxy_and_shout_at_engineer(
84 self.source_package_release).copyright = (
81 'Downloaded from https://upstream.dom/fnord/no/ and')85 'Downloaded from https://upstream.dom/fnord/no/ and')
82 expected = (86 expected = (
83 'Downloaded from '87 'Downloaded from '
@@ -88,7 +92,8 @@
88 self.assertEqual(expected, view.highlighted_copyright)92 self.assertEqual(expected, view.highlighted_copyright)
8993
90 def test_highlighted_copyright_match_path(self):94 def test_highlighted_copyright_match_path(self):
91 self.source_package_release.copyright = (95 remove_security_proxy_and_shout_at_engineer(
96 self.source_package_release).copyright = (
92 'See /usr/share/common-licenses/GPL')97 'See /usr/share/common-licenses/GPL')
93 expected = (98 expected = (
94 'See '99 'See '
@@ -98,7 +103,8 @@
98 self.assertEqual(expected, view.highlighted_copyright)103 self.assertEqual(expected, view.highlighted_copyright)
99104
100 def test_highlighted_copyright_match_multiple(self):105 def test_highlighted_copyright_match_multiple(self):
101 self.source_package_release.copyright = (106 remove_security_proxy_and_shout_at_engineer(
107 self.source_package_release).copyright = (
102 'See /usr/share/common-licenses/GPL or https://osi.org/mit')108 'See /usr/share/common-licenses/GPL or https://osi.org/mit')
103 expected = (109 expected = (
104 'See '110 'See '
105111
=== modified file 'lib/lp/soyuz/tests/test_publishing.py'
--- lib/lp/soyuz/tests/test_publishing.py 2010-07-21 14:14:54 +0000
+++ lib/lp/soyuz/tests/test_publishing.py 2010-07-22 13:01:10 +0000
@@ -44,7 +44,8 @@
44from lp.soyuz.interfaces.queue import PackageUploadStatus44from lp.soyuz.interfaces.queue import PackageUploadStatus
45from canonical.launchpad.scripts import FakeLogger45from canonical.launchpad.scripts import FakeLogger
46from lp.testing import TestCaseWithFactory46from lp.testing import TestCaseWithFactory
47from lp.testing.factory import LaunchpadObjectFactory47from lp.testing.factory import (
48 LaunchpadObjectFactory, remove_security_proxy_and_shout_at_engineer)
48from lp.testing.fakemethod import FakeMethod49from lp.testing.fakemethod import FakeMethod
4950
5051
@@ -146,7 +147,10 @@
146 PackageUploadStatus.DONE: 'setDone',147 PackageUploadStatus.DONE: 'setDone',
147 PackageUploadStatus.ACCEPTED: 'setAccepted',148 PackageUploadStatus.ACCEPTED: 'setAccepted',
148 }149 }
149 method = getattr(package_upload, status_to_method[upload_status])150 naked_package_upload = remove_security_proxy_and_shout_at_engineer(
151 package_upload)
152 method = getattr(
153 naked_package_upload, status_to_method[upload_status])
150 method()154 method()
151155
152 return package_upload156 return package_upload
@@ -222,7 +226,9 @@
222 changes_file_name=changes_file_name,226 changes_file_name=changes_file_name,
223 changes_file_content=changes_file_content,227 changes_file_content=changes_file_content,
224 upload_status=upload_status)228 upload_status=upload_status)
225 package_upload.addSource(spr)229 naked_package_upload = remove_security_proxy_and_shout_at_engineer(
230 package_upload)
231 naked_package_upload.addSource(spr)
226232
227 if filename is None:233 if filename is None:
228 filename = "%s_%s.dsc" % (sourcename, version)234 filename = "%s_%s.dsc" % (sourcename, version)
229235
=== modified file 'lib/lp/testing/factory.py'
--- lib/lp/testing/factory.py 2010-07-21 14:41:26 +0000
+++ lib/lp/testing/factory.py 2010-07-22 13:01:10 +0000
@@ -13,8 +13,10 @@
13__metaclass__ = type13__metaclass__ = type
14__all__ = [14__all__ = [
15 'GPGSigningContext',15 'GPGSigningContext',
16 'is_security_proxied_or_harmless',
16 'LaunchpadObjectFactory',17 'LaunchpadObjectFactory',
17 'ObjectFactory',18 'ObjectFactory',
19 'remove_security_proxy_and_shout_at_engineer',
18 ]20 ]
1921
20from contextlib import nested22from contextlib import nested
@@ -25,18 +27,22 @@
25from email.mime.text import MIMEText27from email.mime.text import MIMEText
26from email.mime.multipart import MIMEMultipart28from email.mime.multipart import MIMEMultipart
27from itertools import count29from itertools import count
30from operator import isSequenceType
28import os.path31import os.path
29from random import randint32from random import randint
30from StringIO import StringIO33from StringIO import StringIO
34import sys
31from textwrap import dedent35from textwrap import dedent
32from threading import local36from threading import local
37from types import InstanceType
3338
34import pytz39import pytz
3540
36from twisted.python.util import mergeFunctionMetadata41from twisted.python.util import mergeFunctionMetadata
3742
38from zope.component import ComponentLookupError, getUtility43from zope.component import ComponentLookupError, getUtility
39from zope.security.proxy import removeSecurityProxy44from zope.security.proxy import (
45 builtin_isinstance, Proxy, ProxyFactory, removeSecurityProxy)
4046
41from canonical.autodecorate import AutoDecorate47from canonical.autodecorate import AutoDecorate
42from canonical.config import config48from canonical.config import config
@@ -325,7 +331,7 @@
325 branch_id, rcstype, url, cvs_root, cvs_module)331 branch_id, rcstype, url, cvs_root, cvs_module)
326332
327333
328class LaunchpadObjectFactory(ObjectFactory):334class BareLaunchpadObjectFactory(ObjectFactory):
329 """Factory methods for creating Launchpad objects.335 """Factory methods for creating Launchpad objects.
330336
331 All the factory methods should be callable with no parameters.337 All the factory methods should be callable with no parameters.
@@ -744,8 +750,8 @@
744 # We don't want to login() as the person used to create the product,750 # We don't want to login() as the person used to create the product,
745 # so we remove the security proxy before creating the series.751 # so we remove the security proxy before creating the series.
746 naked_product = removeSecurityProxy(product)752 naked_product = removeSecurityProxy(product)
747 return naked_product.newSeries(owner=owner, name=name,753 return ProxyFactory(
748 summary=summary)754 naked_product.newSeries(owner=owner, name=name, summary=summary))
749755
750 def makeProject(self, name=None, displayname=None, title=None,756 def makeProject(self, name=None, displayname=None, title=None,
751 homepageurl=None, summary=None, owner=None,757 homepageurl=None, summary=None, owner=None,
@@ -1671,7 +1677,7 @@
1671 description=self.getUniqueString(),1677 description=self.getUniqueString(),
1672 parent_series=parent_series, owner=distribution.owner)1678 parent_series=parent_series, owner=distribution.owner)
1673 series.status = status1679 series.status = status
1674 return series1680 return ProxyFactory(series)
16751681
1676 # Most people think of distro releases as distro series.1682 # Most people think of distro releases as distro series.
1677 makeDistroSeries = makeDistroRelease1683 makeDistroSeries = makeDistroRelease
@@ -1780,7 +1786,7 @@
17801786
1781 def makeRecipeText(self, *branches):1787 def makeRecipeText(self, *branches):
1782 if len(branches) == 0:1788 if len(branches) == 0:
1783 branches = (self.makeAnyBranch(),)1789 branches = (self.makeAnyBranch(), )
1784 base_branch = branches[0]1790 base_branch = branches[0]
1785 other_branches = branches[1:]1791 other_branches = branches[1:]
1786 text = MINIMAL_RECIPE_TEXT % base_branch.bzr_identity1792 text = MINIMAL_RECIPE_TEXT % base_branch.bzr_identity
@@ -1943,7 +1949,7 @@
1943 productseries = self.makeProductSeries(owner=owner)1949 productseries = self.makeProductSeries(owner=owner)
1944 # Make it use Translations, otherwise there's little point1950 # Make it use Translations, otherwise there's little point
1945 # to us creating a template for it.1951 # to us creating a template for it.
1946 productseries.product.official_rosetta = True1952 removeSecurityProxy(productseries).product.official_rosetta = True
1947 templateset = getUtility(IPOTemplateSet)1953 templateset = getUtility(IPOTemplateSet)
1948 subset = templateset.getSubset(1954 subset = templateset.getSubset(
1949 distroseries, sourcepackagename, productseries)1955 distroseries, sourcepackagename, productseries)
@@ -2718,3 +2724,72 @@
2718 new_uuid = getUtility(ITemporaryStorageManager).new(blob, expires)2724 new_uuid = getUtility(ITemporaryStorageManager).new(blob, expires)
27192725
2720 return getUtility(ITemporaryStorageManager).fetch(new_uuid)2726 return getUtility(ITemporaryStorageManager).fetch(new_uuid)
2727
2728
2729# Some factory methods return simple Python types. We don't add
2730# security wrappers for them.
2731unwrapped_types = (
2732 DSCFile, InstanceType, Message, datetime, int, str, unicode)
2733
2734def is_security_proxied_or_harmless(obj):
2735 """Check that the given object is security wrapped or a harmless object."""
2736 if obj is None:
2737 return True
2738 if builtin_isinstance(obj, Proxy):
2739 return True
2740 if type(obj) in unwrapped_types:
2741 return True
2742 if isSequenceType(obj):
2743 for element in obj:
2744 if not is_security_proxied_or_harmless(element):
2745 return False
2746 return True
2747 return False
2748
2749
2750class LaunchpadObjectFactory:
2751 """A wrapper around `BareLaunchpadObjectFactory`.
2752
2753 Ensure that each object created by a `BareLaunchpadObjectFactory` method
2754 is either of a simple Python type or is security proxied.
2755
2756 A warning message is printed to stderr if a factory method creates
2757 an object without a security proxy.
2758
2759 Whereever you see such a warning: fix it!
2760 """
2761 def __init__(self):
2762 self._factory = BareLaunchpadObjectFactory()
2763
2764 def __getattr__(self, name):
2765 attr = getattr(self._factory, name)
2766 if callable(attr):
2767
2768 def guarded_method(*args, **kw):
2769 result = attr(*args, **kw)
2770 if not is_security_proxied_or_harmless(result):
2771 message = (
2772 "PLEASE FIX: LaunchpadObjectFactory.%s returns an "
2773 "unproxied object." % name)
2774 print >>sys.stderr, message
2775 return result
2776 return guarded_method
2777 else:
2778 return attr
2779
2780
2781def remove_security_proxy_and_shout_at_engineer(obj):
2782 """Remove an object's security proxy and print a warning.
2783
2784 A number of LaunchpadObjectFactory methods returned objects without
2785 a security proxy. This is now no longer possible, but a number of
2786 tests rely on unrestricted access to object attributes.
2787
2788 This function should only be used in legacy tests which fail because
2789 they expect unproxied objects.
2790 """
2791 print >>sys.stderr, (
2792 "\nWarning: called removeSecurityProxy() for %r without a check if "
2793 "this reasonable. Look for a call of "
2794 "remove_security_proxy_and_shout_at_engineer(some_object)." % obj)
2795 return removeSecurityProxy(obj)
27212796
=== modified file 'lib/lp/testing/tests/test_factory.py'
--- lib/lp/testing/tests/test_factory.py 2010-07-17 18:40:02 +0000
+++ lib/lp/testing/tests/test_factory.py 2010-07-22 13:01:10 +0000
@@ -8,11 +8,13 @@
8import unittest8import unittest
99
10from zope.component import getUtility10from zope.component import getUtility
11from zope.security.proxy import removeSecurityProxy
1112
12from canonical.launchpad.webapp.interfaces import ILaunchBag13from canonical.launchpad.webapp.interfaces import ILaunchBag
13from canonical.testing.layers import DatabaseFunctionalLayer14from canonical.testing.layers import DatabaseFunctionalLayer
14from lp.code.enums import CodeImportReviewStatus15from lp.code.enums import CodeImportReviewStatus
15from lp.testing import TestCaseWithFactory16from lp.testing import TestCaseWithFactory
17from lp.testing.factory import is_security_proxied_or_harmless
1618
1719
18class TestFactory(TestCaseWithFactory):20class TestFactory(TestCaseWithFactory):
@@ -39,6 +41,54 @@
39 self.assertIsNot(None, person)41 self.assertIsNot(None, person)
40 self.assertEqual(person, current_person)42 self.assertEqual(person, current_person)
4143
44 def test_is_security_proxied_or_harmless__none(self):
45 # is_security_proxied_or_harmless() considers the None object
46 # to be a harmless object.
47 self.assertTrue(is_security_proxied_or_harmless(None))
48
49 def test_is_security_proxied_or_harmless__int(self):
50 # is_security_proxied_or_harmless() considers integers
51 # to be harmless.
52 self.assertTrue(is_security_proxied_or_harmless(1))
53
54 def test_is_security_proxied_or_harmless__string(self):
55 # is_security_proxied_or_harmless() considers strings
56 # to be harmless.
57 self.assertTrue(is_security_proxied_or_harmless('abc'))
58
59 def test_is_security_proxied_or_harmless__unicode(self):
60 # is_security_proxied_or_harmless() considers unicode objects
61 # to be harmless.
62 self.assertTrue(is_security_proxied_or_harmless(u'abc'))
63
64 def test_is_security_proxied_or_harmless__proxied_object(self):
65 # is_security_proxied_or_harmless() treats security proxied
66 # objects as harmless.
67 proxied_person = self.factory.makePerson()
68 self.assertTrue(is_security_proxied_or_harmless(proxied_person))
69
70 def test_is_security_proxied_or_harmless__unproxied_object(self):
71 # is_security_proxied_or_harmless() treats security proxied
72 # objects as harmless.
73 unproxied_person = removeSecurityProxy(self.factory.makePerson())
74 self.assertFalse(is_security_proxied_or_harmless(unproxied_person))
75
76 def test_is_security_proxied_or_harmless__sequence_harmless_content(self):
77 # is_security_proxied_or_harmless() checks all elements
78 # of a sequence. If all elements are harmless, so is the
79 # sequence.
80 proxied_person = self.factory.makePerson()
81 self.assertTrue(
82 is_security_proxied_or_harmless([1, '2', proxied_person]))
83
84 def test_is_security_proxied_or_harmless__sequence_harmful_content(self):
85 # is_security_proxied_or_harmless() checks all elements
86 # of a sequence. If at least elements are harmful, so is the
87 # sequence.
88 unproxied_person = removeSecurityProxy(self.factory.makePerson())
89 self.assertFalse(
90 is_security_proxied_or_harmless([1, '2', unproxied_person]))
91
4292
43def test_suite():93def test_suite():
44 return unittest.TestLoader().loadTestsFromName(__name__)94 return unittest.TestLoader().loadTestsFromName(__name__)
4595
=== modified file 'lib/lp/translations/browser/tests/test_breadcrumbs.py'
--- lib/lp/translations/browser/tests/test_breadcrumbs.py 2010-07-19 15:31:57 +0000
+++ lib/lp/translations/browser/tests/test_breadcrumbs.py 2010-07-22 13:01:10 +0000
@@ -9,6 +9,7 @@
99
10from lp.services.worlddata.interfaces.language import ILanguageSet10from lp.services.worlddata.interfaces.language import ILanguageSet
11from lp.testing.breadcrumbs import BaseBreadcrumbTestCase11from lp.testing.breadcrumbs import BaseBreadcrumbTestCase
12from lp.testing.factory import remove_security_proxy_and_shout_at_engineer
12from lp.translations.interfaces.distroserieslanguage import (13from lp.translations.interfaces.distroserieslanguage import (
13 IDistroSeriesLanguageSet)14 IDistroSeriesLanguageSet)
14from lp.translations.interfaces.productserieslanguage import (15from lp.translations.interfaces.productserieslanguage import (
@@ -109,7 +110,8 @@
109 name='crumb-tester', displayname="Crumb Tester")110 name='crumb-tester', displayname="Crumb Tester")
110 series = self.factory.makeDistroRelease(111 series = self.factory.makeDistroRelease(
111 name="test", version="1.0", distribution=distribution)112 name="test", version="1.0", distribution=distribution)
112 series.hide_all_translations = False113 naked_series = remove_security_proxy_and_shout_at_engineer(series)
114 naked_series.hide_all_translations = False
113 serieslanguage = getUtility(IDistroSeriesLanguageSet).getDummy(115 serieslanguage = getUtility(IDistroSeriesLanguageSet).getDummy(
114 series, self.language)116 series, self.language)
115117
116118
=== modified file 'lib/lp/translations/doc/translations-export-to-branch.txt'
--- lib/lp/translations/doc/translations-export-to-branch.txt 2010-06-16 07:22:57 +0000
+++ lib/lp/translations/doc/translations-export-to-branch.txt 2010-07-22 13:01:10 +0000
@@ -220,8 +220,12 @@
220 >>> from email import message_from_string220 >>> from email import message_from_string
221 >>> from lp.services.mail import stub221 >>> from lp.services.mail import stub
222 >>> from lp.codehosting.vfs import get_rw_server222 >>> from lp.codehosting.vfs import get_rw_server
223 >>> from lp.testing.factory import (
224 ... remove_security_proxy_and_shout_at_engineer)
223 >>> productseries = factory.makeProductSeries()225 >>> productseries = factory.makeProductSeries()
224 >>> productseries.translations_branch = factory.makeBranch()226 >>> naked_productseries = remove_security_proxy_and_shout_at_engineer(
227 ... productseries)
228 >>> naked_productseries.translations_branch = factory.makeBranch()
225 >>> template = factory.makePOTemplate(productseries=productseries)229 >>> template = factory.makePOTemplate(productseries=productseries)
226 >>> potmsgset = factory.makePOTMsgSet(template)230 >>> potmsgset = factory.makePOTMsgSet(template)
227 >>> pofile = removeSecurityProxy(231 >>> pofile = removeSecurityProxy(
228232
=== modified file 'lib/lp/translations/stories/buildfarm/xx-build-summary.txt'
--- lib/lp/translations/stories/buildfarm/xx-build-summary.txt 2010-03-16 11:09:46 +0000
+++ lib/lp/translations/stories/buildfarm/xx-build-summary.txt 2010-07-22 13:01:10 +0000
@@ -14,6 +14,8 @@
14 ... ILibraryFileAliasSet)14 ... ILibraryFileAliasSet)
15 >>> from canonical.launchpad.scripts.logger import QuietFakeLogger15 >>> from canonical.launchpad.scripts.logger import QuietFakeLogger
16 >>> from lp.buildmaster.interfaces.buildqueue import IBuildQueueSet16 >>> from lp.buildmaster.interfaces.buildqueue import IBuildQueueSet
17 >>> from lp.testing.factory import (
18 ... remove_security_proxy_and_shout_at_engineer)
17 >>> from lp.testing.fakemethod import FakeMethod19 >>> from lp.testing.fakemethod import FakeMethod
18 >>> from lp.translations.interfaces.translations import (20 >>> from lp.translations.interfaces.translations import (
19 ... TranslationsBranchImportMode)21 ... TranslationsBranchImportMode)
@@ -29,12 +31,15 @@
2931
30 >>> productseries = factory.makeProductSeries(owner=owner)32 >>> productseries = factory.makeProductSeries(owner=owner)
31 >>> product = productseries.product33 >>> product = productseries.product
32 >>> product.official_rosetta = True34 >>> naked_product = remove_security_proxy_and_shout_at_engineer(product)
35 >>> naked_product.official_rosetta = True
33 >>> branch = factory.makeProductBranch(product=product, owner=owner)36 >>> branch = factory.makeProductBranch(product=product, owner=owner)
34 >>> branch_url = branch.unique_name37 >>> branch_url = branch.unique_name
3538
36 >>> productseries.branch = factory.makeBranch()39 >>> naked_productseries = remove_security_proxy_and_shout_at_engineer(
37 >>> productseries.translations_autoimport_mode = (40 ... productseries)
41 >>> naked_productseries.branch = factory.makeBranch()
42 >>> naked_productseries.translations_autoimport_mode = (
38 ... TranslationsBranchImportMode.IMPORT_TEMPLATES)43 ... TranslationsBranchImportMode.IMPORT_TEMPLATES)
39 >>> specific_job = factory.makeTranslationTemplatesBuildJob(branch=branch)44 >>> specific_job = factory.makeTranslationTemplatesBuildJob(branch=branch)
40 >>> buildqueue = getUtility(IBuildQueueSet).getByJob(specific_job.job)45 >>> buildqueue = getUtility(IBuildQueueSet).getByJob(specific_job.job)