Merge lp:~jml/launchpad/branch-sample-data-unit-tests into lp:launchpad

Proposed by Jonathan Lange
Status: Merged
Approved by: Robert Collins
Approved revision: no longer in the source branch.
Merged at revision: 11124
Proposed branch: lp:~jml/launchpad/branch-sample-data-unit-tests
Merge into: lp:launchpad
Prerequisite: lp:~jml/launchpad/more-login-helpers
Diff against target: 696 lines (+159/-132)
9 files modified
database/schema/Makefile (+2/-2)
lib/canonical/launchpad/scripts/tests/test_garbo.py (+8/-1)
lib/canonical/launchpad/tests/test_launchpadlib.py (+13/-10)
lib/lp/code/browser/tests/test_branch.py (+9/-16)
lib/lp/code/interfaces/codeimportjob.py (+0/-2)
lib/lp/code/model/tests/test_codeimportjob.py (+67/-96)
lib/lp/testing/factory.py (+8/-5)
lib/lp/testing/sampledata.py (+18/-0)
lib/lp/testing/tests/test_factory.py (+34/-0)
To merge this branch: bzr merge lp:~jml/launchpad/branch-sample-data-unit-tests
Reviewer Review Type Date Requested Status
Robert Collins (community) Approve
Review via email: mp+29652@code.launchpad.net

Commit message

Fix some unit tests to not need sample data. Add "sampledata" module and tests for Launchpad object factory.

Description of the change

This branch fixes up a bunch of unit tests to not use sample data.

The tests that I've changed are tests that would fail if all branch-related sample data were removed.

There's also a new "lp.testing.sampledata" module. The idea is that rather than using magic literal strings or numbers, we define constants in the file. Doing so also makes it much easier to statically analyze the code-base to find tests that use sampledata.

I have also used this branch as an opportunity to start testing the Launchpad object factory. I haven't written complete tests, but I have written tests to cover the changes I made. Now that tests exists, I suggest that we insist on tests for any changes made to the factory.

There are some incidental cleanups:
  * Change the sampledata header to have the correct copyright date, and change the Makefile to generate the correct copyright date.
  * Many tests changed to use DatabaseFunctionalLayer instead of LaunchpadFunctionalLayer
  * Removed "verifyObject" from the lp.testing namespace. I have no idea why it was there in the first place.

To post a comment you must log in.
Revision history for this message
Robert Collins (lifeless) wrote :

I think this is useful; thanks for doing it.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'database/schema/Makefile'
--- database/schema/Makefile 2010-03-26 07:37:50 +0000
+++ database/schema/Makefile 2010-07-13 10:02:50 +0000
@@ -1,4 +1,4 @@
1# Copyright 2009 Canonical Ltd. This software is licensed under the1# Copyright 2010 Canonical Ltd. This software is licensed under the
2# GNU Affero General Public License version 3 (see the file LICENSE).2# GNU Affero General Public License version 3 (see the file LICENSE).
3#3#
4# Quick hack makefile to (re)create the Launchpad database.4# Quick hack makefile to (re)create the Launchpad database.
@@ -42,7 +42,7 @@
42# The command we use to drop (if exists) and recreate a database.42# The command we use to drop (if exists) and recreate a database.
43CREATEDB=${PYTHON} ../../utilities/pgmassacre.py -t43CREATEDB=${PYTHON} ../../utilities/pgmassacre.py -t
4444
45HEADER="-- Copyright 2009 Canonical Ltd. This software is licensed under \45HEADER="-- Copyright 2010 Canonical Ltd. This software is licensed under \
46 the\n-- GNU Affero General Public License version 3 (see the file \46 the\n-- GNU Affero General Public License version 3 (see the file \
47 LICENSE)."47 LICENSE)."
4848
4949
=== modified file 'lib/canonical/launchpad/scripts/tests/test_garbo.py'
--- lib/canonical/launchpad/scripts/tests/test_garbo.py 2010-05-26 01:48:12 +0000
+++ lib/canonical/launchpad/scripts/tests/test_garbo.py 2010-07-13 10:02:50 +0000
@@ -175,11 +175,18 @@
175 results_to_keep_count = (175 results_to_keep_count = (
176 config.codeimport.consecutive_failure_limit - 1)176 config.codeimport.consecutive_failure_limit - 1)
177177
178 LaunchpadZopelessLayer.switchDbUser('testadmin')
179 code_import_id = self.factory.makeCodeImport().id
180 machine_id = self.factory.makeCodeImportMachine().id
181 requester_id = self.factory.makePerson().id
182 transaction.commit()
183
178 def new_code_import_result(timestamp):184 def new_code_import_result(timestamp):
179 LaunchpadZopelessLayer.switchDbUser('testadmin')185 LaunchpadZopelessLayer.switchDbUser('testadmin')
180 CodeImportResult(186 CodeImportResult(
181 date_created=timestamp,187 date_created=timestamp,
182 code_importID=1, machineID=1, requesting_userID=1,188 code_importID=code_import_id, machineID=machine_id,
189 requesting_userID=requester_id,
183 status=CodeImportResultStatus.FAILURE,190 status=CodeImportResultStatus.FAILURE,
184 date_job_started=timestamp)191 date_job_started=timestamp)
185 transaction.commit()192 transaction.commit()
186193
=== modified file 'lib/canonical/launchpad/tests/test_launchpadlib.py'
--- lib/canonical/launchpad/tests/test_launchpadlib.py 2009-06-25 05:30:52 +0000
+++ lib/canonical/launchpad/tests/test_launchpadlib.py 2010-07-13 10:02:50 +0000
@@ -5,22 +5,22 @@
55
6import unittest6import unittest
77
8import transaction
9
8from launchpadlib.testing.helpers import salgado_with_full_permissions10from launchpadlib.testing.helpers import salgado_with_full_permissions
9from canonical.testing import AppServerLayer11from canonical.testing import AppServerLayer
1012from lp.testing import TestCaseWithFactory
1113
12class TestLaunchpadLib(unittest.TestCase):14
15class TestLaunchpadLib(TestCaseWithFactory):
13 """Tests for the launchpadlib client for the REST API."""16 """Tests for the launchpadlib client for the REST API."""
1417
15 layer = AppServerLayer18 layer = AppServerLayer
16 launchpad = None
17 project = None
1819
19 def setUp(self):20 def setUp(self):
20 if self.launchpad is None:21 super(TestLaunchpadLib, self).setUp()
21 self.launchpad = salgado_with_full_permissions.login()22 self.launchpad = salgado_with_full_permissions.login()
22 if self.project is None:23 self.project = self.launchpad.projects['firefox']
23 self.project = self.launchpad.projects['firefox']
2424
25 def verifyAttributes(self, element):25 def verifyAttributes(self, element):
26 """Verify that launchpadlib can parse the element's attributes."""26 """Verify that launchpadlib can parse the element's attributes."""
@@ -43,7 +43,10 @@
4343
44 def test_branch(self):44 def test_branch(self):
45 """Test branch attributes."""45 """Test branch attributes."""
46 branch = self.project.getBranches()[0]46 branch_name = self.factory.makeBranch().unique_name
47 transaction.commit()
48 branch = self.launchpad.branches.getByUniqueName(
49 unique_name=branch_name)
47 self.verifyAttributes(branch)50 self.verifyAttributes(branch)
4851
49 def test_milestone(self):52 def test_milestone(self):
5053
=== modified file 'lib/lp/code/browser/tests/test_branch.py'
--- lib/lp/code/browser/tests/test_branch.py 2010-06-14 02:37:06 +0000
+++ lib/lp/code/browser/tests/test_branch.py 2010-07-13 10:02:50 +0000
@@ -6,7 +6,6 @@
6from __future__ import with_statement6from __future__ import with_statement
77
8__metaclass__ = type8__metaclass__ = type
9__all__ = ['TestBranchView', 'test_suite']
109
11from datetime import datetime, timedelta10from datetime import datetime, timedelta
12from textwrap import dedent11from textwrap import dedent
@@ -16,7 +15,6 @@
16import simplejson15import simplejson
1716
1817
19from zope.component import getUtility
20from zope.security.proxy import removeSecurityProxy18from zope.security.proxy import removeSecurityProxy
2119
22from canonical.config import config20from canonical.config import config
@@ -32,9 +30,6 @@
32from lp.code.interfaces.branchtarget import IBranchTarget30from lp.code.interfaces.branchtarget import IBranchTarget
33from canonical.launchpad.helpers import truncate_text31from canonical.launchpad.helpers import truncate_text
34from lp.code.enums import BranchLifecycleStatus, BranchType32from lp.code.enums import BranchLifecycleStatus, BranchType
35from lp.registry.interfaces.person import IPersonSet
36from lp.registry.interfaces.product import IProductSet
37from lp.code.interfaces.branchlookup import IBranchLookup
38from lp.testing import (33from lp.testing import (
39 login, login_person, logout, person_logged_in, ANONYMOUS,34 login, login_person, logout, person_logged_in, ANONYMOUS,
40 TestCaseWithFactory)35 TestCaseWithFactory)
@@ -122,20 +117,18 @@
122117
123class TestBranchView(TestCaseWithFactory):118class TestBranchView(TestCaseWithFactory):
124119
125 layer = LaunchpadFunctionalLayer120 layer = DatabaseFunctionalLayer
126121
127 def setUp(self):122 def setUp(self):
128 super(TestBranchView, self).setUp()123 super(TestBranchView, self).setUp()
129 login(ANONYMOUS)
130 self.request = LaunchpadTestRequest()124 self.request = LaunchpadTestRequest()
131125
132 def tearDown(self):
133 logout()
134 super(TestBranchView, self).tearDown()
135
136 def testMirrorStatusMessageIsTruncated(self):126 def testMirrorStatusMessageIsTruncated(self):
137 """mirror_status_message is truncated if the text is overly long."""127 """mirror_status_message is truncated if the text is overly long."""
138 branch = getUtility(IBranchLookup).get(28)128 branch = self.factory.makeBranch()
129 branch.mirrorFailed(
130 "on quick brown fox the dog jumps to" *
131 BranchMirrorStatusView.MAXIMUM_STATUS_MESSAGE_LENGTH)
139 branch_view = BranchMirrorStatusView(branch, self.request)132 branch_view = BranchMirrorStatusView(branch, self.request)
140 self.assertEqual(133 self.assertEqual(
141 truncate_text(branch.mirror_status_message,134 truncate_text(branch.mirror_status_message,
@@ -144,7 +137,7 @@
144137
145 def testMirrorStatusMessage(self):138 def testMirrorStatusMessage(self):
146 """mirror_status_message on the view is the same as on the branch."""139 """mirror_status_message on the view is the same as on the branch."""
147 branch = getUtility(IBranchLookup).get(5)140 branch = self.factory.makeBranch()
148 branch.mirrorFailed("This is a short error message.")141 branch.mirrorFailed("This is a short error message.")
149 branch_view = BranchMirrorStatusView(branch, self.request)142 branch_view = BranchMirrorStatusView(branch, self.request)
150 self.assertTrue(143 self.assertTrue(
@@ -160,8 +153,8 @@
160153
161 def testBranchAddRequestsMirror(self):154 def testBranchAddRequestsMirror(self):
162 """Registering a mirrored branch requests a mirror."""155 """Registering a mirrored branch requests a mirror."""
163 arbitrary_person = getUtility(IPersonSet).get(1)156 arbitrary_person = self.factory.makePerson()
164 arbitrary_product = getUtility(IProductSet).get(1)157 arbitrary_product = self.factory.makeProduct()
165 login(arbitrary_person.preferredemail.email)158 login(arbitrary_person.preferredemail.email)
166 try:159 try:
167 add_view = BranchAddView(arbitrary_person, self.request)160 add_view = BranchAddView(arbitrary_person, self.request)
@@ -195,7 +188,7 @@
195 # The merge links are shown on projects that have multiple branches.188 # The merge links are shown on projects that have multiple branches.
196 product = self.factory.makeProduct(name='super-awesome-project')189 product = self.factory.makeProduct(name='super-awesome-project')
197 branch1 = self.factory.makeAnyBranch(product=product)190 branch1 = self.factory.makeAnyBranch(product=product)
198 branch2 = self.factory.makeAnyBranch(product=product)191 self.factory.makeAnyBranch(product=product)
199 view = BranchView(branch1, self.request)192 view = BranchView(branch1, self.request)
200 view.initialize()193 view.initialize()
201 self.assertTrue(view.show_merge_links)194 self.assertTrue(view.show_merge_links)
202195
=== modified file 'lib/lp/code/interfaces/codeimportjob.py'
--- lib/lp/code/interfaces/codeimportjob.py 2010-03-16 01:34:38 +0000
+++ lib/lp/code/interfaces/codeimportjob.py 2010-07-13 10:02:50 +0000
@@ -16,8 +16,6 @@
16 'ICodeImportJobWorkflow',16 'ICodeImportJobWorkflow',
17 ]17 ]
1818
19import datetime
20
21from zope.interface import Interface19from zope.interface import Interface
22from zope.schema import Choice, Datetime, Int, Object, Text20from zope.schema import Choice, Datetime, Int, Object, Text
2321
2422
=== modified file 'lib/lp/code/model/tests/test_codeimportjob.py'
--- lib/lp/code/model/tests/test_codeimportjob.py 2010-07-13 10:02:48 +0000
+++ lib/lp/code/model/tests/test_codeimportjob.py 2010-07-13 10:02:50 +0000
@@ -5,7 +5,9 @@
55
6__metaclass__ = type6__metaclass__ = type
77
8__all__ = ['NewEvents', 'test_suite']8__all__ = [
9 'NewEvents',
10 ]
911
10from datetime import datetime12from datetime import datetime
11from pytz import UTC13from pytz import UTC
@@ -24,7 +26,6 @@
24from canonical.config import config26from canonical.config import config
25from canonical.database.constants import UTC_NOW27from canonical.database.constants import UTC_NOW
26from lp.code.model.codeimportjob import CodeImportJob28from lp.code.model.codeimportjob import CodeImportJob
27from lp.code.model.codeimportmachine import CodeImportMachine
28from lp.code.model.codeimportresult import CodeImportResult29from lp.code.model.codeimportresult import CodeImportResult
29from canonical.launchpad.interfaces.librarian import ILibraryFileAliasSet30from canonical.launchpad.interfaces.librarian import ILibraryFileAliasSet
30from lp.code.enums import (31from lp.code.enums import (
@@ -38,12 +39,14 @@
38from lp.registry.interfaces.person import IPersonSet39from lp.registry.interfaces.person import IPersonSet
39from lp.testing import (40from lp.testing import (
40 ANONYMOUS, login, login_celebrity, logout, TestCaseWithFactory)41 ANONYMOUS, login, login_celebrity, logout, TestCaseWithFactory)
42from lp.testing.sampledata import NO_PRIVILEGE_EMAIL, VCS_IMPORTS_MEMBER_EMAIL
41from canonical.launchpad.testing.codeimporthelpers import (43from canonical.launchpad.testing.codeimporthelpers import (
42 make_finished_import, make_running_import)44 make_finished_import, make_running_import)
43from canonical.launchpad.testing.pages import get_feedback_messages45from canonical.launchpad.testing.pages import get_feedback_messages
44from canonical.launchpad.webapp import canonical_url46from canonical.launchpad.webapp import canonical_url
45from canonical.librarian.interfaces import ILibrarianClient47from canonical.librarian.interfaces import ILibrarianClient
46from canonical.testing import LaunchpadFunctionalLayer48from canonical.testing import (
49 DatabaseFunctionalLayer, LaunchpadFunctionalLayer)
4750
4851
49def login_for_code_imports():52def login_for_code_imports():
@@ -52,28 +55,29 @@
52 CodeImports are currently hidden from regular users currently. Members of55 CodeImports are currently hidden from regular users currently. Members of
53 the vcs-imports team and can access the objects freely.56 the vcs-imports team and can access the objects freely.
54 """57 """
55 login_celebrity('vcs_imports')58 return login_celebrity('vcs_imports')
5659
5760
58class TestCodeImportJobSet(unittest.TestCase):61class TestCodeImportJobSet(TestCaseWithFactory):
59 """Unit tests for the CodeImportJobSet utility."""62 """Unit tests for the CodeImportJobSet utility."""
6063
61 layer = LaunchpadFunctionalLayer64 layer = DatabaseFunctionalLayer
6265
63 def setUp(self):66 def setUp(self):
67 super(TestCodeImportJobSet, self).setUp()
64 login_for_code_imports()68 login_for_code_imports()
6569
66 def test_getByIdExisting(self):70 def test_getByIdExisting(self):
67 # CodeImportJobSet.getById retrieves a CodeImportJob by database id.71 # CodeImportJobSet.getById retrieves a CodeImportJob by database id.
68 job = getUtility(ICodeImportJobSet).getById(1)72 made_job = self.factory.makeCodeImportJob()
69 self.assertNotEqual(job, None)73 found_job = getUtility(ICodeImportJobSet).getById(made_job.id)
70 self.assertEqual(job.id, 1)74 self.assertEqual(made_job, found_job)
7175
72 def test_getByIdNotExisting(self):76 def test_getByIdNotExisting(self):
73 # CodeImportJobSet.getById returns None if there is not CodeImportJob77 # CodeImportJobSet.getById returns None if there is not CodeImportJob
74 # with the specified id.78 # with the specified id.
75 no_job = getUtility(ICodeImportJobSet).getById(-1)79 no_job = getUtility(ICodeImportJobSet).getById(-1)
76 self.assertEqual(no_job, None)80 self.assertIs(None, no_job)
7781
7882
79class TestCodeImportJobSetGetJobForMachine(TestCaseWithFactory):83class TestCodeImportJobSetGetJobForMachine(TestCaseWithFactory):
@@ -88,7 +92,7 @@
88 method makeJob() creates actual CodeImportJob objects from these specs.92 method makeJob() creates actual CodeImportJob objects from these specs.
89 """93 """
9094
91 layer = LaunchpadFunctionalLayer95 layer = DatabaseFunctionalLayer
9296
93 def setUp(self):97 def setUp(self):
94 # Login so we can access the code import system, delete all jobs in98 # Login so we can access the code import system, delete all jobs in
@@ -115,7 +119,7 @@
115 def assertJobIsSelected(self, desired_job):119 def assertJobIsSelected(self, desired_job):
116 """Assert that the expected job is chosen by getJobForMachine."""120 """Assert that the expected job is chosen by getJobForMachine."""
117 observed_job = getUtility(ICodeImportJobSet).getJobForMachine(121 observed_job = getUtility(ICodeImportJobSet).getJobForMachine(
118 self.machine.hostname, 10)122 self.machine.hostname, worker_limit=10)
119 self.assert_(observed_job is not None, "No job was selected.")123 self.assert_(observed_job is not None, "No job was selected.")
120 self.assertEqual(desired_job, observed_job,124 self.assertEqual(desired_job, observed_job,
121 "Expected job not selected.")125 "Expected job not selected.")
@@ -123,7 +127,7 @@
123 def assertNoJobSelected(self):127 def assertNoJobSelected(self):
124 """Assert that no job is selected."""128 """Assert that no job is selected."""
125 observed_job = getUtility(ICodeImportJobSet).getJobForMachine(129 observed_job = getUtility(ICodeImportJobSet).getJobForMachine(
126 'machine', 10)130 'machine', worker_limit=10)
127 self.assert_(observed_job is None, "Job unexpectedly selected.")131 self.assert_(observed_job is None, "Job unexpectedly selected.")
128132
129 def test_nothingSelectedIfNothingCreated(self):133 def test_nothingSelectedIfNothingCreated(self):
@@ -231,7 +235,7 @@
231class TestCodeImportJobSetGetReclaimableJobs(ReclaimableJobTests):235class TestCodeImportJobSetGetReclaimableJobs(ReclaimableJobTests):
232 """Tests for the CodeImportJobSet.getReclaimableJobs method."""236 """Tests for the CodeImportJobSet.getReclaimableJobs method."""
233237
234 layer = LaunchpadFunctionalLayer238 layer = DatabaseFunctionalLayer
235239
236 def test_upToDateJob(self):240 def test_upToDateJob(self):
237 # A job that was updated recently is not considered reclaimable.241 # A job that was updated recently is not considered reclaimable.
@@ -264,7 +268,7 @@
264class TestCodeImportJobSetGetJobForMachineGardening(ReclaimableJobTests):268class TestCodeImportJobSetGetJobForMachineGardening(ReclaimableJobTests):
265 """Test that getJobForMachine gardens stale code import jobs."""269 """Test that getJobForMachine gardens stale code import jobs."""
266270
267 layer = LaunchpadFunctionalLayer271 layer = DatabaseFunctionalLayer
268272
269 def test_getJobForMachineGardens(self):273 def test_getJobForMachineGardens(self):
270 # getJobForMachine reclaims all reclaimable jobs each time it is274 # getJobForMachine reclaims all reclaimable jobs each time it is
@@ -275,7 +279,7 @@
275 machine = self.factory.makeCodeImportMachine(set_online=True)279 machine = self.factory.makeCodeImportMachine(set_online=True)
276 login(ANONYMOUS)280 login(ANONYMOUS)
277 getUtility(ICodeImportJobSet).getJobForMachine(281 getUtility(ICodeImportJobSet).getJobForMachine(
278 machine.hostname, 10)282 machine.hostname, worker_limit=10)
279 login_for_code_imports()283 login_for_code_imports()
280 # Now there are no reclaimable jobs.284 # Now there are no reclaimable jobs.
281 self.assertReclaimableJobs([])285 self.assertReclaimableJobs([])
@@ -364,7 +368,7 @@
364 AssertFailureMixin):368 AssertFailureMixin):
365 """Unit tests for the CodeImportJobWorkflow.newJob method."""369 """Unit tests for the CodeImportJobWorkflow.newJob method."""
366370
367 layer = LaunchpadFunctionalLayer371 layer = DatabaseFunctionalLayer
368372
369 def setUp(self):373 def setUp(self):
370 super(TestCodeImportJobWorkflowNewJob, self).setUp()374 super(TestCodeImportJobWorkflowNewJob, self).setUp()
@@ -373,50 +377,31 @@
373 def test_wrongReviewStatus(self):377 def test_wrongReviewStatus(self):
374 # CodeImportJobWorkflow.newJob fails if the CodeImport review_status378 # CodeImportJobWorkflow.newJob fails if the CodeImport review_status
375 # is different from REVIEWED.379 # is different from REVIEWED.
376 new_import = getUtility(ICodeImportSet).get(2)380 new_import = self.factory.makeCodeImport()
377 # Checking sampledata expectations.381 branch_name = new_import.branch.unique_name
378 self.assertEqual(new_import.branch.unique_name,
379 '~vcs-imports/evolution/import')
380 NEW = CodeImportReviewStatus.NEW
381 self.assertEqual(new_import.review_status, NEW)
382 # Testing newJob failure.382 # Testing newJob failure.
383 self.assertFailure(383 self.assertFailure(
384 "Review status of ~vcs-imports/evolution/import "384 "Review status of %s is not REVIEWED: NEW" % (branch_name,),
385 "is not REVIEWED: NEW",
386 getUtility(ICodeImportJobWorkflow).newJob, new_import)385 getUtility(ICodeImportJobWorkflow).newJob, new_import)
387386
388 def test_existingJob(self):387 def test_existingJob(self):
389 # CodeImportJobWorkflow.newJob fails if the CodeImport is already388 # CodeImportJobWorkflow.newJob fails if the CodeImport is already
390 # associated to a CodeImportJob.389 # associated to a CodeImportJob.
391 reviewed_import = getUtility(ICodeImportSet).get(1)390 job = self.factory.makeCodeImportJob()
392 # Checking sampledata expectations.391 reviewed_import = job.code_import
393 self.assertEqual(reviewed_import.branch.unique_name,392 branch_name = reviewed_import.branch.unique_name
394 '~vcs-imports/gnome-terminal/import')
395 REVIEWED = CodeImportReviewStatus.REVIEWED
396 self.assertEqual(reviewed_import.review_status, REVIEWED)
397 self.assertNotEqual(reviewed_import.import_job, None)
398 # Testing newJob failure.
399 self.assertFailure(393 self.assertFailure(
400 "Already associated to a CodeImportJob: "394 "Already associated to a CodeImportJob: %s" % (branch_name,),
401 "~vcs-imports/gnome-terminal/import",
402 getUtility(ICodeImportJobWorkflow).newJob, reviewed_import)395 getUtility(ICodeImportJobWorkflow).newJob, reviewed_import)
403396
404 def getCodeImportForDateDueTest(self):397 def getCodeImportForDateDueTest(self):
405 """Return a `CodeImport` object for testing how date_due is set.398 """Return a `CodeImport` object for testing how date_due is set.
406399
407 We check that it is not associated to any `CodeImportJob` or400 It is not associated to any `CodeImportJob` or `CodeImportResult`, and
408 `CodeImportResult`, and we ensure its review_status is REVIEWED.401 its review_status is REVIEWED.
409 """402 """
410 new_import = getUtility(ICodeImportSet).get(2)403 return self.factory.makeCodeImport(
411 # Checking sampledata expectations.404 review_status=CodeImportReviewStatus.REVIEWED)
412 self.assertEqual(new_import.import_job, None)
413 self.assertEqual(
414 CodeImportResult.selectBy(code_importID=new_import.id).count(), 0)
415 # We need to set review_status to REVIEWED before calling newJob, and
416 # the interface marks review_status as read-only.
417 REVIEWED = CodeImportReviewStatus.REVIEWED
418 removeSecurityProxy(new_import).review_status = REVIEWED
419 return new_import
420405
421 def test_dateDueNoPreviousResult(self):406 def test_dateDueNoPreviousResult(self):
422 # If there is no CodeImportResult for the CodeImport, then the new407 # If there is no CodeImportResult for the CodeImport, then the new
@@ -432,7 +417,7 @@
432 code_import = self.getCodeImportForDateDueTest()417 code_import = self.getCodeImportForDateDueTest()
433 # Create a CodeImportResult that started a long time ago. This one418 # Create a CodeImportResult that started a long time ago. This one
434 # must be superseded by the more recent one created below.419 # must be superseded by the more recent one created below.
435 machine = CodeImportMachine.get(1)420 machine = self.factory.makeCodeImportMachine()
436 FAILURE = CodeImportResultStatus.FAILURE421 FAILURE = CodeImportResultStatus.FAILURE
437 CodeImportResult(422 CodeImportResult(
438 code_import=code_import, machine=machine, status=FAILURE,423 code_import=code_import, machine=machine, status=FAILURE,
@@ -467,7 +452,7 @@
467 # set to UTC_NOW.452 # set to UTC_NOW.
468 code_import = self.getCodeImportForDateDueTest()453 code_import = self.getCodeImportForDateDueTest()
469 # Create a CodeImportResult that started a long time ago.454 # Create a CodeImportResult that started a long time ago.
470 machine = CodeImportMachine.get(1)455 machine = self.factory.makeCodeImportMachine()
471 FAILURE = CodeImportResultStatus.FAILURE456 FAILURE = CodeImportResultStatus.FAILURE
472 CodeImportResult(457 CodeImportResult(
473 code_import=code_import, machine=machine, status=FAILURE,458 code_import=code_import, machine=machine, status=FAILURE,
@@ -478,78 +463,66 @@
478 self.assertSqlAttributeEqualsDate(job, 'date_due', UTC_NOW)463 self.assertSqlAttributeEqualsDate(job, 'date_due', UTC_NOW)
479464
480465
481class TestCodeImportJobWorkflowDeletePendingJob(unittest.TestCase,466class TestCodeImportJobWorkflowDeletePendingJob(TestCaseWithFactory,
482 AssertFailureMixin):467 AssertFailureMixin):
483 """Unit tests for CodeImportJobWorkflow.deletePendingJob."""468 """Unit tests for CodeImportJobWorkflow.deletePendingJob."""
484469
485 layer = LaunchpadFunctionalLayer470 layer = DatabaseFunctionalLayer
486471
487 def setUp(self):472 def setUp(self):
488 super(TestCodeImportJobWorkflowDeletePendingJob, self).setUp()473 super(TestCodeImportJobWorkflowDeletePendingJob, self).setUp()
489 login_for_code_imports()474 self.import_admin = login_for_code_imports()
490475
491 def test_wrongReviewStatus(self):476 def test_wrongReviewStatus(self):
492 # CodeImportJobWorkflow.deletePendingJob fails if the477 # CodeImportJobWorkflow.deletePendingJob fails if the
493 # CodeImport review_status is equal to REVIEWED.478 # CodeImport review_status is equal to REVIEWED.
494 reviewed_import = getUtility(ICodeImportSet).get(1)479 reviewed_import = self.factory.makeCodeImport()
495 # Checking sampledata expectations.480 reviewed_import.updateFromData(
496 self.assertEqual(reviewed_import.branch.unique_name,481 {'review_status': CodeImportReviewStatus.REVIEWED},
497 '~vcs-imports/gnome-terminal/import')482 self.import_admin)
498 REVIEWED = CodeImportReviewStatus.REVIEWED483 branch_name = reviewed_import.branch.unique_name
499 self.assertEqual(reviewed_import.review_status, REVIEWED)
500 # Testing deletePendingJob failure.484 # Testing deletePendingJob failure.
501 self.assertFailure(485 self.assertFailure(
502 "The review status of ~vcs-imports/gnome-terminal/import "486 "The review status of %s is REVIEWED." % (branch_name,),
503 "is REVIEWED.",
504 getUtility(ICodeImportJobWorkflow).deletePendingJob,487 getUtility(ICodeImportJobWorkflow).deletePendingJob,
505 reviewed_import)488 reviewed_import)
506489
507 def test_noJob(self):490 def test_noJob(self):
508 # CodeImportJobWorkflow.deletePendingJob fails if the491 # CodeImportJobWorkflow.deletePendingJob fails if the
509 # CodeImport is not associated to a CodeImportJob.492 # CodeImport is not associated to a CodeImportJob.
510 new_import = getUtility(ICodeImportSet).get(2)493 new_import = self.factory.makeCodeImport()
511 # Checking sampledata expectations.494 branch_name = new_import.branch.unique_name
512 self.assertEqual(new_import.branch.unique_name,
513 '~vcs-imports/evolution/import')
514 NEW = CodeImportReviewStatus.NEW
515 self.assertEqual(new_import.review_status, NEW)
516 self.assertEqual(new_import.import_job, None)
517 # Testing deletePendingJob failure.495 # Testing deletePendingJob failure.
518 self.assertFailure(496 self.assertFailure(
519 "Not associated to a CodeImportJob: "497 "Not associated to a CodeImportJob: %s" % (branch_name,),
520 "~vcs-imports/evolution/import",
521 getUtility(ICodeImportJobWorkflow).deletePendingJob,498 getUtility(ICodeImportJobWorkflow).deletePendingJob,
522 new_import)499 new_import)
523500
524 def test_wrongJobState(self):501 def test_wrongJobState(self):
525 # CodeImportJobWorkflow.deletePendingJob fails if the state of502 # CodeImportJobWorkflow.deletePendingJob fails if the state of
526 # the CodeImportJob is different from PENDING.503 # the CodeImportJob is different from PENDING.
527 reviewed_import = getUtility(ICodeImportSet).get(1)504 job = self.factory.makeCodeImportJob()
528 # Checking sampledata expectations.505 code_import = job.code_import
529 self.assertEqual(reviewed_import.branch.unique_name,506 branch_name = job.code_import.branch.unique_name
530 '~vcs-imports/gnome-terminal/import')507 # ICodeImport does not allow setting 'review_status', so we must use
531 # ICodeImport does not allow setting any attribute, so we need to use508 # removeSecurityProxy.
532 # removeSecurityProxy to set the review_status attribute.
533 INVALID = CodeImportReviewStatus.INVALID509 INVALID = CodeImportReviewStatus.INVALID
534 removeSecurityProxy(reviewed_import).review_status = INVALID510 removeSecurityProxy(code_import).review_status = INVALID
535 self.assertNotEqual(reviewed_import.import_job, None)
536 # ICodeImportJob does not allow setting 'state', so we must511 # ICodeImportJob does not allow setting 'state', so we must
537 # use removeSecurityProxy.512 # use removeSecurityProxy.
538 RUNNING = CodeImportJobState.RUNNING513 RUNNING = CodeImportJobState.RUNNING
539 removeSecurityProxy(reviewed_import.import_job).state = RUNNING514 removeSecurityProxy(code_import.import_job).state = RUNNING
540 # Testing deletePendingJob failure.515 # Testing deletePendingJob failure.
541 self.assertFailure(516 self.assertFailure(
542 "The CodeImportJob associated to "517 "The CodeImportJob associated to %s is RUNNING." % (branch_name,),
543 "~vcs-imports/gnome-terminal/import is RUNNING.",518 getUtility(ICodeImportJobWorkflow).deletePendingJob, code_import)
544 getUtility(ICodeImportJobWorkflow).deletePendingJob,
545 reviewed_import)
546519
547520
548class TestCodeImportJobWorkflowRequestJob(TestCaseWithFactory,521class TestCodeImportJobWorkflowRequestJob(TestCaseWithFactory,
549 AssertFailureMixin, AssertEventMixin):522 AssertFailureMixin, AssertEventMixin):
550 """Unit tests for CodeImportJobWorkflow.requestJob."""523 """Unit tests for CodeImportJobWorkflow.requestJob."""
551524
552 layer = LaunchpadFunctionalLayer525 layer = DatabaseFunctionalLayer
553526
554 def setUp(self):527 def setUp(self):
555 super(TestCodeImportJobWorkflowRequestJob, self).setUp()528 super(TestCodeImportJobWorkflowRequestJob, self).setUp()
@@ -641,7 +614,7 @@
641 AssertFailureMixin, AssertEventMixin):614 AssertFailureMixin, AssertEventMixin):
642 """Unit tests for CodeImportJobWorkflow.startJob."""615 """Unit tests for CodeImportJobWorkflow.startJob."""
643616
644 layer = LaunchpadFunctionalLayer617 layer = DatabaseFunctionalLayer
645618
646 def setUp(self):619 def setUp(self):
647 super(TestCodeImportJobWorkflowStartJob, self).setUp()620 super(TestCodeImportJobWorkflowStartJob, self).setUp()
@@ -679,7 +652,7 @@
679 AssertFailureMixin, AssertEventMixin):652 AssertFailureMixin, AssertEventMixin):
680 """Unit tests for CodeImportJobWorkflow.updateHeartbeat."""653 """Unit tests for CodeImportJobWorkflow.updateHeartbeat."""
681654
682 layer = LaunchpadFunctionalLayer655 layer = DatabaseFunctionalLayer
683656
684 def setUp(self):657 def setUp(self):
685 super(TestCodeImportJobWorkflowUpdateHeartbeat, self).setUp()658 super(TestCodeImportJobWorkflowUpdateHeartbeat, self).setUp()
@@ -705,7 +678,7 @@
705678
706 def setUp(self):679 def setUp(self):
707 super(TestCodeImportJobWorkflowFinishJob, self).setUp()680 super(TestCodeImportJobWorkflowFinishJob, self).setUp()
708 login_for_code_imports()681 self.vcs_imports = login_for_code_imports()
709 self.machine = self.factory.makeCodeImportMachine(set_online=True)682 self.machine = self.factory.makeCodeImportMachine(set_online=True)
710683
711 def makeRunningJob(self, code_import=None):684 def makeRunningJob(self, code_import=None):
@@ -804,10 +777,9 @@
804 # unless the CodeImport has been suspended or marked invalid.777 # unless the CodeImport has been suspended or marked invalid.
805 running_job = self.makeRunningJob()778 running_job = self.makeRunningJob()
806 code_import = running_job.code_import779 code_import = running_job.code_import
807 ddaa = getUtility(IPersonSet).getByEmail(
808 'david.allouche@canonical.com')
809 code_import.updateFromData(780 code_import.updateFromData(
810 {'review_status': CodeImportReviewStatus.SUSPENDED}, ddaa)781 {'review_status': CodeImportReviewStatus.SUSPENDED},
782 self.vcs_imports)
811 getUtility(ICodeImportJobWorkflow).finishJob(783 getUtility(ICodeImportJobWorkflow).finishJob(
812 running_job, CodeImportResultStatus.SUCCESS, None)784 running_job, CodeImportResultStatus.SUCCESS, None)
813 self.assertTrue(code_import.import_job is None)785 self.assertTrue(code_import.import_job is None)
@@ -920,8 +892,7 @@
920 log_alias = getUtility(ILibraryFileAliasSet)[log_alias_id]892 log_alias = getUtility(ILibraryFileAliasSet)[log_alias_id]
921 result = self.getResultForJob(job, log_alias=log_alias)893 result = self.getResultForJob(job, log_alias=log_alias)
922894
923 self.assertEqual(895 self.assertEqual(result.log_file.read(), log_data)
924 result.log_file.read(), log_data)
925896
926 def test_createsFinishCodeImportEvent(self):897 def test_createsFinishCodeImportEvent(self):
927 # finishJob() creates a FINISH CodeImportEvent.898 # finishJob() creates a FINISH CodeImportEvent.
@@ -1005,7 +976,7 @@
1005976
1006# This is a dependence on the sample data: David Allouche is a member of the977# This is a dependence on the sample data: David Allouche is a member of the
1007# ~vcs-imports celebrity team.978# ~vcs-imports celebrity team.
1008logged_in_for_code_imports = logged_in_as('david.allouche@canonical.com')979logged_in_for_code_imports = logged_in_as(VCS_IMPORTS_MEMBER_EMAIL)
1009980
1010981
1011class TestRequestJobUIRaces(TestCaseWithFactory):982class TestRequestJobUIRaces(TestCaseWithFactory):
@@ -1016,7 +987,7 @@
1016 the button and check that appropriate notifications are displayed.987 the button and check that appropriate notifications are displayed.
1017 """988 """
1018989
1019 layer = LaunchpadFunctionalLayer990 layer = DatabaseFunctionalLayer
1020991
1021 @logged_in_for_code_imports992 @logged_in_for_code_imports
1022 def getNewCodeImportIDAndBranchURL(self):993 def getNewCodeImportIDAndBranchURL(self):
@@ -1027,7 +998,7 @@
1027 code_import_id = code_import.id998 code_import_id = code_import.id
1028 return code_import_id, branch_url999 return code_import_id, branch_url
10291000
1030 @logged_in_as('no-priv@canonical.com')1001 @logged_in_as(NO_PRIVILEGE_EMAIL)
1031 def requestJobByUserWithDisplayName(self, code_import_id, displayname):1002 def requestJobByUserWithDisplayName(self, code_import_id, displayname):
1032 """Record a request for the job by a user with the given name."""1003 """Record a request for the job by a user with the given name."""
1033 getUtility(ICodeImportJobWorkflow).requestJob(1004 getUtility(ICodeImportJobWorkflow).requestJob(
10341005
=== modified file 'lib/lp/testing/factory.py'
--- lib/lp/testing/factory.py 2010-07-08 11:54:57 +0000
+++ lib/lp/testing/factory.py 2010-07-13 10:02:50 +0000
@@ -1436,7 +1436,7 @@
1436 def makeCodeImport(self, svn_branch_url=None, cvs_root=None,1436 def makeCodeImport(self, svn_branch_url=None, cvs_root=None,
1437 cvs_module=None, target=None, branch_name=None,1437 cvs_module=None, target=None, branch_name=None,
1438 git_repo_url=None, hg_repo_url=None, registrant=None,1438 git_repo_url=None, hg_repo_url=None, registrant=None,
1439 rcs_type=None):1439 rcs_type=None, review_status=None):
1440 """Create and return a new, arbitrary code import.1440 """Create and return a new, arbitrary code import.
14411441
1442 The type of code import will be inferred from the source details1442 The type of code import will be inferred from the source details
@@ -1461,26 +1461,29 @@
1461 else:1461 else:
1462 assert rcs_type in (RevisionControlSystems.SVN,1462 assert rcs_type in (RevisionControlSystems.SVN,
1463 RevisionControlSystems.BZR_SVN)1463 RevisionControlSystems.BZR_SVN)
1464 return code_import_set.new(1464 code_import = code_import_set.new(
1465 registrant, target, branch_name, rcs_type=rcs_type,1465 registrant, target, branch_name, rcs_type=rcs_type,
1466 url=svn_branch_url)1466 url=svn_branch_url)
1467 elif git_repo_url is not None:1467 elif git_repo_url is not None:
1468 assert rcs_type in (None, RevisionControlSystems.GIT)1468 assert rcs_type in (None, RevisionControlSystems.GIT)
1469 return code_import_set.new(1469 code_import = code_import_set.new(
1470 registrant, target, branch_name,1470 registrant, target, branch_name,
1471 rcs_type=RevisionControlSystems.GIT,1471 rcs_type=RevisionControlSystems.GIT,
1472 url=git_repo_url)1472 url=git_repo_url)
1473 elif hg_repo_url is not None:1473 elif hg_repo_url is not None:
1474 return code_import_set.new(1474 code_import = code_import_set.new(
1475 registrant, target, branch_name,1475 registrant, target, branch_name,
1476 rcs_type=RevisionControlSystems.HG,1476 rcs_type=RevisionControlSystems.HG,
1477 url=hg_repo_url)1477 url=hg_repo_url)
1478 else:1478 else:
1479 assert rcs_type in (None, RevisionControlSystems.CVS)1479 assert rcs_type in (None, RevisionControlSystems.CVS)
1480 return code_import_set.new(1480 code_import = code_import_set.new(
1481 registrant, target, branch_name,1481 registrant, target, branch_name,
1482 rcs_type=RevisionControlSystems.CVS,1482 rcs_type=RevisionControlSystems.CVS,
1483 cvs_root=cvs_root, cvs_module=cvs_module)1483 cvs_root=cvs_root, cvs_module=cvs_module)
1484 if review_status:
1485 removeSecurityProxy(code_import).review_status = review_status
1486 return code_import
14841487
1485 def makeCodeImportEvent(self):1488 def makeCodeImportEvent(self):
1486 """Create and return a CodeImportEvent."""1489 """Create and return a CodeImportEvent."""
14871490
=== added file 'lib/lp/testing/sampledata.py'
--- lib/lp/testing/sampledata.py 1970-01-01 00:00:00 +0000
+++ lib/lp/testing/sampledata.py 2010-07-13 10:02:50 +0000
@@ -0,0 +1,18 @@
1# Copyright 2010 Canonical Ltd. This software is licensed under the
2# GNU Affero General Public License version 3 (see the file LICENSE).
3
4"""Constants that refer to values in sampledata.
5
6If ever you use a literal in a test that refers to sample data, even if it's
7just a small number, then you should define it as a constant here.
8"""
9
10__metaclass__ = type
11__all__ = [
12 'NO_PRIVILEGE_EMAIL',
13 'VCS_IMPORTS_MEMBER_EMAIL',
14 ]
15
16
17NO_PRIVILEGE_EMAIL = 'no-priv@canonical.com'
18VCS_IMPORTS_MEMBER_EMAIL = 'david.allouche@canonical.com'
019
=== added file 'lib/lp/testing/tests/test_factory.py'
--- lib/lp/testing/tests/test_factory.py 1970-01-01 00:00:00 +0000
+++ lib/lp/testing/tests/test_factory.py 2010-07-13 10:02:50 +0000
@@ -0,0 +1,34 @@
1# Copyright 2010 Canonical Ltd. This software is licensed under the
2# GNU Affero General Public License version 3 (see the file LICENSE).
3
4"""Tests for the Launchpad object factory."""
5
6__metaclass__ = type
7
8import unittest
9
10from canonical.testing.layers import DatabaseFunctionalLayer
11from lp.code.enums import CodeImportReviewStatus
12from lp.testing import TestCaseWithFactory
13
14
15class TestFactory(TestCaseWithFactory):
16
17 layer = DatabaseFunctionalLayer
18
19 def test_makeCodeImportNoStatus(self):
20 # If makeCodeImport is not given a review status, it defaults to NEW.
21 code_import = self.factory.makeCodeImport()
22 self.assertEqual(
23 CodeImportReviewStatus.NEW, code_import.review_status)
24
25 def test_makeCodeImportReviewStatus(self):
26 # If makeCodeImport is given a review status, then that is the status
27 # of the created import.
28 status = CodeImportReviewStatus.REVIEWED
29 code_import = self.factory.makeCodeImport(review_status=status)
30 self.assertEqual(status, code_import.review_status)
31
32
33def test_suite():
34 return unittest.TestLoader().loadTestsFromName(__name__)