Merge lp:~jml/launchpad/branch-sample-data-unit-tests into lp:launchpad
- branch-sample-data-unit-tests
- Merge into devel
| Status: | Merged |
|---|---|
| Approved by: | Robert Collins on 2010-07-12 |
| 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 |
| Related bugs: |
| Reviewer | Review Type | Date Requested | Status |
|---|---|---|---|
| Robert Collins (community) | 2010-07-11 | Approve on 2010-07-12 | |
|
Review via email:
|
|||
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.
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 DatabaseFunctio
* Removed "verifyObject" from the lp.testing namespace. I have no idea why it was there in the first place.
Preview Diff
| 1 | === modified file 'database/schema/Makefile' |
| 2 | --- database/schema/Makefile 2010-03-26 07:37:50 +0000 |
| 3 | +++ database/schema/Makefile 2010-07-13 10:02:50 +0000 |
| 4 | @@ -1,4 +1,4 @@ |
| 5 | -# Copyright 2009 Canonical Ltd. This software is licensed under the |
| 6 | +# Copyright 2010 Canonical Ltd. This software is licensed under the |
| 7 | # GNU Affero General Public License version 3 (see the file LICENSE). |
| 8 | # |
| 9 | # Quick hack makefile to (re)create the Launchpad database. |
| 10 | @@ -42,7 +42,7 @@ |
| 11 | # The command we use to drop (if exists) and recreate a database. |
| 12 | CREATEDB=${PYTHON} ../../utilities/pgmassacre.py -t |
| 13 | |
| 14 | -HEADER="-- Copyright 2009 Canonical Ltd. This software is licensed under \ |
| 15 | +HEADER="-- Copyright 2010 Canonical Ltd. This software is licensed under \ |
| 16 | the\n-- GNU Affero General Public License version 3 (see the file \ |
| 17 | LICENSE)." |
| 18 | |
| 19 | |
| 20 | === modified file 'lib/canonical/launchpad/scripts/tests/test_garbo.py' |
| 21 | --- lib/canonical/launchpad/scripts/tests/test_garbo.py 2010-05-26 01:48:12 +0000 |
| 22 | +++ lib/canonical/launchpad/scripts/tests/test_garbo.py 2010-07-13 10:02:50 +0000 |
| 23 | @@ -175,11 +175,18 @@ |
| 24 | results_to_keep_count = ( |
| 25 | config.codeimport.consecutive_failure_limit - 1) |
| 26 | |
| 27 | + LaunchpadZopelessLayer.switchDbUser('testadmin') |
| 28 | + code_import_id = self.factory.makeCodeImport().id |
| 29 | + machine_id = self.factory.makeCodeImportMachine().id |
| 30 | + requester_id = self.factory.makePerson().id |
| 31 | + transaction.commit() |
| 32 | + |
| 33 | def new_code_import_result(timestamp): |
| 34 | LaunchpadZopelessLayer.switchDbUser('testadmin') |
| 35 | CodeImportResult( |
| 36 | date_created=timestamp, |
| 37 | - code_importID=1, machineID=1, requesting_userID=1, |
| 38 | + code_importID=code_import_id, machineID=machine_id, |
| 39 | + requesting_userID=requester_id, |
| 40 | status=CodeImportResultStatus.FAILURE, |
| 41 | date_job_started=timestamp) |
| 42 | transaction.commit() |
| 43 | |
| 44 | === modified file 'lib/canonical/launchpad/tests/test_launchpadlib.py' |
| 45 | --- lib/canonical/launchpad/tests/test_launchpadlib.py 2009-06-25 05:30:52 +0000 |
| 46 | +++ lib/canonical/launchpad/tests/test_launchpadlib.py 2010-07-13 10:02:50 +0000 |
| 47 | @@ -5,22 +5,22 @@ |
| 48 | |
| 49 | import unittest |
| 50 | |
| 51 | +import transaction |
| 52 | + |
| 53 | from launchpadlib.testing.helpers import salgado_with_full_permissions |
| 54 | from canonical.testing import AppServerLayer |
| 55 | - |
| 56 | - |
| 57 | -class TestLaunchpadLib(unittest.TestCase): |
| 58 | +from lp.testing import TestCaseWithFactory |
| 59 | + |
| 60 | + |
| 61 | +class TestLaunchpadLib(TestCaseWithFactory): |
| 62 | """Tests for the launchpadlib client for the REST API.""" |
| 63 | |
| 64 | layer = AppServerLayer |
| 65 | - launchpad = None |
| 66 | - project = None |
| 67 | |
| 68 | def setUp(self): |
| 69 | - if self.launchpad is None: |
| 70 | - self.launchpad = salgado_with_full_permissions.login() |
| 71 | - if self.project is None: |
| 72 | - self.project = self.launchpad.projects['firefox'] |
| 73 | + super(TestLaunchpadLib, self).setUp() |
| 74 | + self.launchpad = salgado_with_full_permissions.login() |
| 75 | + self.project = self.launchpad.projects['firefox'] |
| 76 | |
| 77 | def verifyAttributes(self, element): |
| 78 | """Verify that launchpadlib can parse the element's attributes.""" |
| 79 | @@ -43,7 +43,10 @@ |
| 80 | |
| 81 | def test_branch(self): |
| 82 | """Test branch attributes.""" |
| 83 | - branch = self.project.getBranches()[0] |
| 84 | + branch_name = self.factory.makeBranch().unique_name |
| 85 | + transaction.commit() |
| 86 | + branch = self.launchpad.branches.getByUniqueName( |
| 87 | + unique_name=branch_name) |
| 88 | self.verifyAttributes(branch) |
| 89 | |
| 90 | def test_milestone(self): |
| 91 | |
| 92 | === modified file 'lib/lp/code/browser/tests/test_branch.py' |
| 93 | --- lib/lp/code/browser/tests/test_branch.py 2010-06-14 02:37:06 +0000 |
| 94 | +++ lib/lp/code/browser/tests/test_branch.py 2010-07-13 10:02:50 +0000 |
| 95 | @@ -6,7 +6,6 @@ |
| 96 | from __future__ import with_statement |
| 97 | |
| 98 | __metaclass__ = type |
| 99 | -__all__ = ['TestBranchView', 'test_suite'] |
| 100 | |
| 101 | from datetime import datetime, timedelta |
| 102 | from textwrap import dedent |
| 103 | @@ -16,7 +15,6 @@ |
| 104 | import simplejson |
| 105 | |
| 106 | |
| 107 | -from zope.component import getUtility |
| 108 | from zope.security.proxy import removeSecurityProxy |
| 109 | |
| 110 | from canonical.config import config |
| 111 | @@ -32,9 +30,6 @@ |
| 112 | from lp.code.interfaces.branchtarget import IBranchTarget |
| 113 | from canonical.launchpad.helpers import truncate_text |
| 114 | from lp.code.enums import BranchLifecycleStatus, BranchType |
| 115 | -from lp.registry.interfaces.person import IPersonSet |
| 116 | -from lp.registry.interfaces.product import IProductSet |
| 117 | -from lp.code.interfaces.branchlookup import IBranchLookup |
| 118 | from lp.testing import ( |
| 119 | login, login_person, logout, person_logged_in, ANONYMOUS, |
| 120 | TestCaseWithFactory) |
| 121 | @@ -122,20 +117,18 @@ |
| 122 | |
| 123 | class TestBranchView(TestCaseWithFactory): |
| 124 | |
| 125 | - layer = LaunchpadFunctionalLayer |
| 126 | + layer = DatabaseFunctionalLayer |
| 127 | |
| 128 | def setUp(self): |
| 129 | super(TestBranchView, self).setUp() |
| 130 | - login(ANONYMOUS) |
| 131 | self.request = LaunchpadTestRequest() |
| 132 | |
| 133 | - def tearDown(self): |
| 134 | - logout() |
| 135 | - super(TestBranchView, self).tearDown() |
| 136 | - |
| 137 | def testMirrorStatusMessageIsTruncated(self): |
| 138 | """mirror_status_message is truncated if the text is overly long.""" |
| 139 | - branch = getUtility(IBranchLookup).get(28) |
| 140 | + branch = self.factory.makeBranch() |
| 141 | + branch.mirrorFailed( |
| 142 | + "on quick brown fox the dog jumps to" * |
| 143 | + BranchMirrorStatusView.MAXIMUM_STATUS_MESSAGE_LENGTH) |
| 144 | branch_view = BranchMirrorStatusView(branch, self.request) |
| 145 | self.assertEqual( |
| 146 | truncate_text(branch.mirror_status_message, |
| 147 | @@ -144,7 +137,7 @@ |
| 148 | |
| 149 | def testMirrorStatusMessage(self): |
| 150 | """mirror_status_message on the view is the same as on the branch.""" |
| 151 | - branch = getUtility(IBranchLookup).get(5) |
| 152 | + branch = self.factory.makeBranch() |
| 153 | branch.mirrorFailed("This is a short error message.") |
| 154 | branch_view = BranchMirrorStatusView(branch, self.request) |
| 155 | self.assertTrue( |
| 156 | @@ -160,8 +153,8 @@ |
| 157 | |
| 158 | def testBranchAddRequestsMirror(self): |
| 159 | """Registering a mirrored branch requests a mirror.""" |
| 160 | - arbitrary_person = getUtility(IPersonSet).get(1) |
| 161 | - arbitrary_product = getUtility(IProductSet).get(1) |
| 162 | + arbitrary_person = self.factory.makePerson() |
| 163 | + arbitrary_product = self.factory.makeProduct() |
| 164 | login(arbitrary_person.preferredemail.email) |
| 165 | try: |
| 166 | add_view = BranchAddView(arbitrary_person, self.request) |
| 167 | @@ -195,7 +188,7 @@ |
| 168 | # The merge links are shown on projects that have multiple branches. |
| 169 | product = self.factory.makeProduct(name='super-awesome-project') |
| 170 | branch1 = self.factory.makeAnyBranch(product=product) |
| 171 | - branch2 = self.factory.makeAnyBranch(product=product) |
| 172 | + self.factory.makeAnyBranch(product=product) |
| 173 | view = BranchView(branch1, self.request) |
| 174 | view.initialize() |
| 175 | self.assertTrue(view.show_merge_links) |
| 176 | |
| 177 | === modified file 'lib/lp/code/interfaces/codeimportjob.py' |
| 178 | --- lib/lp/code/interfaces/codeimportjob.py 2010-03-16 01:34:38 +0000 |
| 179 | +++ lib/lp/code/interfaces/codeimportjob.py 2010-07-13 10:02:50 +0000 |
| 180 | @@ -16,8 +16,6 @@ |
| 181 | 'ICodeImportJobWorkflow', |
| 182 | ] |
| 183 | |
| 184 | -import datetime |
| 185 | - |
| 186 | from zope.interface import Interface |
| 187 | from zope.schema import Choice, Datetime, Int, Object, Text |
| 188 | |
| 189 | |
| 190 | === modified file 'lib/lp/code/model/tests/test_codeimportjob.py' |
| 191 | --- lib/lp/code/model/tests/test_codeimportjob.py 2010-07-13 10:02:48 +0000 |
| 192 | +++ lib/lp/code/model/tests/test_codeimportjob.py 2010-07-13 10:02:50 +0000 |
| 193 | @@ -5,7 +5,9 @@ |
| 194 | |
| 195 | __metaclass__ = type |
| 196 | |
| 197 | -__all__ = ['NewEvents', 'test_suite'] |
| 198 | +__all__ = [ |
| 199 | + 'NewEvents', |
| 200 | + ] |
| 201 | |
| 202 | from datetime import datetime |
| 203 | from pytz import UTC |
| 204 | @@ -24,7 +26,6 @@ |
| 205 | from canonical.config import config |
| 206 | from canonical.database.constants import UTC_NOW |
| 207 | from lp.code.model.codeimportjob import CodeImportJob |
| 208 | -from lp.code.model.codeimportmachine import CodeImportMachine |
| 209 | from lp.code.model.codeimportresult import CodeImportResult |
| 210 | from canonical.launchpad.interfaces.librarian import ILibraryFileAliasSet |
| 211 | from lp.code.enums import ( |
| 212 | @@ -38,12 +39,14 @@ |
| 213 | from lp.registry.interfaces.person import IPersonSet |
| 214 | from lp.testing import ( |
| 215 | ANONYMOUS, login, login_celebrity, logout, TestCaseWithFactory) |
| 216 | +from lp.testing.sampledata import NO_PRIVILEGE_EMAIL, VCS_IMPORTS_MEMBER_EMAIL |
| 217 | from canonical.launchpad.testing.codeimporthelpers import ( |
| 218 | make_finished_import, make_running_import) |
| 219 | from canonical.launchpad.testing.pages import get_feedback_messages |
| 220 | from canonical.launchpad.webapp import canonical_url |
| 221 | from canonical.librarian.interfaces import ILibrarianClient |
| 222 | -from canonical.testing import LaunchpadFunctionalLayer |
| 223 | +from canonical.testing import ( |
| 224 | + DatabaseFunctionalLayer, LaunchpadFunctionalLayer) |
| 225 | |
| 226 | |
| 227 | def login_for_code_imports(): |
| 228 | @@ -52,28 +55,29 @@ |
| 229 | CodeImports are currently hidden from regular users currently. Members of |
| 230 | the vcs-imports team and can access the objects freely. |
| 231 | """ |
| 232 | - login_celebrity('vcs_imports') |
| 233 | - |
| 234 | - |
| 235 | -class TestCodeImportJobSet(unittest.TestCase): |
| 236 | + return login_celebrity('vcs_imports') |
| 237 | + |
| 238 | + |
| 239 | +class TestCodeImportJobSet(TestCaseWithFactory): |
| 240 | """Unit tests for the CodeImportJobSet utility.""" |
| 241 | |
| 242 | - layer = LaunchpadFunctionalLayer |
| 243 | + layer = DatabaseFunctionalLayer |
| 244 | |
| 245 | def setUp(self): |
| 246 | + super(TestCodeImportJobSet, self).setUp() |
| 247 | login_for_code_imports() |
| 248 | |
| 249 | def test_getByIdExisting(self): |
| 250 | # CodeImportJobSet.getById retrieves a CodeImportJob by database id. |
| 251 | - job = getUtility(ICodeImportJobSet).getById(1) |
| 252 | - self.assertNotEqual(job, None) |
| 253 | - self.assertEqual(job.id, 1) |
| 254 | + made_job = self.factory.makeCodeImportJob() |
| 255 | + found_job = getUtility(ICodeImportJobSet).getById(made_job.id) |
| 256 | + self.assertEqual(made_job, found_job) |
| 257 | |
| 258 | def test_getByIdNotExisting(self): |
| 259 | # CodeImportJobSet.getById returns None if there is not CodeImportJob |
| 260 | # with the specified id. |
| 261 | no_job = getUtility(ICodeImportJobSet).getById(-1) |
| 262 | - self.assertEqual(no_job, None) |
| 263 | + self.assertIs(None, no_job) |
| 264 | |
| 265 | |
| 266 | class TestCodeImportJobSetGetJobForMachine(TestCaseWithFactory): |
| 267 | @@ -88,7 +92,7 @@ |
| 268 | method makeJob() creates actual CodeImportJob objects from these specs. |
| 269 | """ |
| 270 | |
| 271 | - layer = LaunchpadFunctionalLayer |
| 272 | + layer = DatabaseFunctionalLayer |
| 273 | |
| 274 | def setUp(self): |
| 275 | # Login so we can access the code import system, delete all jobs in |
| 276 | @@ -115,7 +119,7 @@ |
| 277 | def assertJobIsSelected(self, desired_job): |
| 278 | """Assert that the expected job is chosen by getJobForMachine.""" |
| 279 | observed_job = getUtility(ICodeImportJobSet).getJobForMachine( |
| 280 | - self.machine.hostname, 10) |
| 281 | + self.machine.hostname, worker_limit=10) |
| 282 | self.assert_(observed_job is not None, "No job was selected.") |
| 283 | self.assertEqual(desired_job, observed_job, |
| 284 | "Expected job not selected.") |
| 285 | @@ -123,7 +127,7 @@ |
| 286 | def assertNoJobSelected(self): |
| 287 | """Assert that no job is selected.""" |
| 288 | observed_job = getUtility(ICodeImportJobSet).getJobForMachine( |
| 289 | - 'machine', 10) |
| 290 | + 'machine', worker_limit=10) |
| 291 | self.assert_(observed_job is None, "Job unexpectedly selected.") |
| 292 | |
| 293 | def test_nothingSelectedIfNothingCreated(self): |
| 294 | @@ -231,7 +235,7 @@ |
| 295 | class TestCodeImportJobSetGetReclaimableJobs(ReclaimableJobTests): |
| 296 | """Tests for the CodeImportJobSet.getReclaimableJobs method.""" |
| 297 | |
| 298 | - layer = LaunchpadFunctionalLayer |
| 299 | + layer = DatabaseFunctionalLayer |
| 300 | |
| 301 | def test_upToDateJob(self): |
| 302 | # A job that was updated recently is not considered reclaimable. |
| 303 | @@ -264,7 +268,7 @@ |
| 304 | class TestCodeImportJobSetGetJobForMachineGardening(ReclaimableJobTests): |
| 305 | """Test that getJobForMachine gardens stale code import jobs.""" |
| 306 | |
| 307 | - layer = LaunchpadFunctionalLayer |
| 308 | + layer = DatabaseFunctionalLayer |
| 309 | |
| 310 | def test_getJobForMachineGardens(self): |
| 311 | # getJobForMachine reclaims all reclaimable jobs each time it is |
| 312 | @@ -275,7 +279,7 @@ |
| 313 | machine = self.factory.makeCodeImportMachine(set_online=True) |
| 314 | login(ANONYMOUS) |
| 315 | getUtility(ICodeImportJobSet).getJobForMachine( |
| 316 | - machine.hostname, 10) |
| 317 | + machine.hostname, worker_limit=10) |
| 318 | login_for_code_imports() |
| 319 | # Now there are no reclaimable jobs. |
| 320 | self.assertReclaimableJobs([]) |
| 321 | @@ -364,7 +368,7 @@ |
| 322 | AssertFailureMixin): |
| 323 | """Unit tests for the CodeImportJobWorkflow.newJob method.""" |
| 324 | |
| 325 | - layer = LaunchpadFunctionalLayer |
| 326 | + layer = DatabaseFunctionalLayer |
| 327 | |
| 328 | def setUp(self): |
| 329 | super(TestCodeImportJobWorkflowNewJob, self).setUp() |
| 330 | @@ -373,50 +377,31 @@ |
| 331 | def test_wrongReviewStatus(self): |
| 332 | # CodeImportJobWorkflow.newJob fails if the CodeImport review_status |
| 333 | # is different from REVIEWED. |
| 334 | - new_import = getUtility(ICodeImportSet).get(2) |
| 335 | - # Checking sampledata expectations. |
| 336 | - self.assertEqual(new_import.branch.unique_name, |
| 337 | - '~vcs-imports/evolution/import') |
| 338 | - NEW = CodeImportReviewStatus.NEW |
| 339 | - self.assertEqual(new_import.review_status, NEW) |
| 340 | + new_import = self.factory.makeCodeImport() |
| 341 | + branch_name = new_import.branch.unique_name |
| 342 | # Testing newJob failure. |
| 343 | self.assertFailure( |
| 344 | - "Review status of ~vcs-imports/evolution/import " |
| 345 | - "is not REVIEWED: NEW", |
| 346 | + "Review status of %s is not REVIEWED: NEW" % (branch_name,), |
| 347 | getUtility(ICodeImportJobWorkflow).newJob, new_import) |
| 348 | |
| 349 | def test_existingJob(self): |
| 350 | # CodeImportJobWorkflow.newJob fails if the CodeImport is already |
| 351 | # associated to a CodeImportJob. |
| 352 | - reviewed_import = getUtility(ICodeImportSet).get(1) |
| 353 | - # Checking sampledata expectations. |
| 354 | - self.assertEqual(reviewed_import.branch.unique_name, |
| 355 | - '~vcs-imports/gnome-terminal/import') |
| 356 | - REVIEWED = CodeImportReviewStatus.REVIEWED |
| 357 | - self.assertEqual(reviewed_import.review_status, REVIEWED) |
| 358 | - self.assertNotEqual(reviewed_import.import_job, None) |
| 359 | - # Testing newJob failure. |
| 360 | + job = self.factory.makeCodeImportJob() |
| 361 | + reviewed_import = job.code_import |
| 362 | + branch_name = reviewed_import.branch.unique_name |
| 363 | self.assertFailure( |
| 364 | - "Already associated to a CodeImportJob: " |
| 365 | - "~vcs-imports/gnome-terminal/import", |
| 366 | + "Already associated to a CodeImportJob: %s" % (branch_name,), |
| 367 | getUtility(ICodeImportJobWorkflow).newJob, reviewed_import) |
| 368 | |
| 369 | def getCodeImportForDateDueTest(self): |
| 370 | """Return a `CodeImport` object for testing how date_due is set. |
| 371 | |
| 372 | - We check that it is not associated to any `CodeImportJob` or |
| 373 | - `CodeImportResult`, and we ensure its review_status is REVIEWED. |
| 374 | + It is not associated to any `CodeImportJob` or `CodeImportResult`, and |
| 375 | + its review_status is REVIEWED. |
| 376 | """ |
| 377 | - new_import = getUtility(ICodeImportSet).get(2) |
| 378 | - # Checking sampledata expectations. |
| 379 | - self.assertEqual(new_import.import_job, None) |
| 380 | - self.assertEqual( |
| 381 | - CodeImportResult.selectBy(code_importID=new_import.id).count(), 0) |
| 382 | - # We need to set review_status to REVIEWED before calling newJob, and |
| 383 | - # the interface marks review_status as read-only. |
| 384 | - REVIEWED = CodeImportReviewStatus.REVIEWED |
| 385 | - removeSecurityProxy(new_import).review_status = REVIEWED |
| 386 | - return new_import |
| 387 | + return self.factory.makeCodeImport( |
| 388 | + review_status=CodeImportReviewStatus.REVIEWED) |
| 389 | |
| 390 | def test_dateDueNoPreviousResult(self): |
| 391 | # If there is no CodeImportResult for the CodeImport, then the new |
| 392 | @@ -432,7 +417,7 @@ |
| 393 | code_import = self.getCodeImportForDateDueTest() |
| 394 | # Create a CodeImportResult that started a long time ago. This one |
| 395 | # must be superseded by the more recent one created below. |
| 396 | - machine = CodeImportMachine.get(1) |
| 397 | + machine = self.factory.makeCodeImportMachine() |
| 398 | FAILURE = CodeImportResultStatus.FAILURE |
| 399 | CodeImportResult( |
| 400 | code_import=code_import, machine=machine, status=FAILURE, |
| 401 | @@ -467,7 +452,7 @@ |
| 402 | # set to UTC_NOW. |
| 403 | code_import = self.getCodeImportForDateDueTest() |
| 404 | # Create a CodeImportResult that started a long time ago. |
| 405 | - machine = CodeImportMachine.get(1) |
| 406 | + machine = self.factory.makeCodeImportMachine() |
| 407 | FAILURE = CodeImportResultStatus.FAILURE |
| 408 | CodeImportResult( |
| 409 | code_import=code_import, machine=machine, status=FAILURE, |
| 410 | @@ -478,78 +463,66 @@ |
| 411 | self.assertSqlAttributeEqualsDate(job, 'date_due', UTC_NOW) |
| 412 | |
| 413 | |
| 414 | -class TestCodeImportJobWorkflowDeletePendingJob(unittest.TestCase, |
| 415 | +class TestCodeImportJobWorkflowDeletePendingJob(TestCaseWithFactory, |
| 416 | AssertFailureMixin): |
| 417 | """Unit tests for CodeImportJobWorkflow.deletePendingJob.""" |
| 418 | |
| 419 | - layer = LaunchpadFunctionalLayer |
| 420 | + layer = DatabaseFunctionalLayer |
| 421 | |
| 422 | def setUp(self): |
| 423 | super(TestCodeImportJobWorkflowDeletePendingJob, self).setUp() |
| 424 | - login_for_code_imports() |
| 425 | + self.import_admin = login_for_code_imports() |
| 426 | |
| 427 | def test_wrongReviewStatus(self): |
| 428 | # CodeImportJobWorkflow.deletePendingJob fails if the |
| 429 | # CodeImport review_status is equal to REVIEWED. |
| 430 | - reviewed_import = getUtility(ICodeImportSet).get(1) |
| 431 | - # Checking sampledata expectations. |
| 432 | - self.assertEqual(reviewed_import.branch.unique_name, |
| 433 | - '~vcs-imports/gnome-terminal/import') |
| 434 | - REVIEWED = CodeImportReviewStatus.REVIEWED |
| 435 | - self.assertEqual(reviewed_import.review_status, REVIEWED) |
| 436 | + reviewed_import = self.factory.makeCodeImport() |
| 437 | + reviewed_import.updateFromData( |
| 438 | + {'review_status': CodeImportReviewStatus.REVIEWED}, |
| 439 | + self.import_admin) |
| 440 | + branch_name = reviewed_import.branch.unique_name |
| 441 | # Testing deletePendingJob failure. |
| 442 | self.assertFailure( |
| 443 | - "The review status of ~vcs-imports/gnome-terminal/import " |
| 444 | - "is REVIEWED.", |
| 445 | + "The review status of %s is REVIEWED." % (branch_name,), |
| 446 | getUtility(ICodeImportJobWorkflow).deletePendingJob, |
| 447 | reviewed_import) |
| 448 | |
| 449 | def test_noJob(self): |
| 450 | # CodeImportJobWorkflow.deletePendingJob fails if the |
| 451 | # CodeImport is not associated to a CodeImportJob. |
| 452 | - new_import = getUtility(ICodeImportSet).get(2) |
| 453 | - # Checking sampledata expectations. |
| 454 | - self.assertEqual(new_import.branch.unique_name, |
| 455 | - '~vcs-imports/evolution/import') |
| 456 | - NEW = CodeImportReviewStatus.NEW |
| 457 | - self.assertEqual(new_import.review_status, NEW) |
| 458 | - self.assertEqual(new_import.import_job, None) |
| 459 | + new_import = self.factory.makeCodeImport() |
| 460 | + branch_name = new_import.branch.unique_name |
| 461 | # Testing deletePendingJob failure. |
| 462 | self.assertFailure( |
| 463 | - "Not associated to a CodeImportJob: " |
| 464 | - "~vcs-imports/evolution/import", |
| 465 | + "Not associated to a CodeImportJob: %s" % (branch_name,), |
| 466 | getUtility(ICodeImportJobWorkflow).deletePendingJob, |
| 467 | new_import) |
| 468 | |
| 469 | def test_wrongJobState(self): |
| 470 | # CodeImportJobWorkflow.deletePendingJob fails if the state of |
| 471 | # the CodeImportJob is different from PENDING. |
| 472 | - reviewed_import = getUtility(ICodeImportSet).get(1) |
| 473 | - # Checking sampledata expectations. |
| 474 | - self.assertEqual(reviewed_import.branch.unique_name, |
| 475 | - '~vcs-imports/gnome-terminal/import') |
| 476 | - # ICodeImport does not allow setting any attribute, so we need to use |
| 477 | - # removeSecurityProxy to set the review_status attribute. |
| 478 | + job = self.factory.makeCodeImportJob() |
| 479 | + code_import = job.code_import |
| 480 | + branch_name = job.code_import.branch.unique_name |
| 481 | + # ICodeImport does not allow setting 'review_status', so we must use |
| 482 | + # removeSecurityProxy. |
| 483 | INVALID = CodeImportReviewStatus.INVALID |
| 484 | - removeSecurityProxy(reviewed_import).review_status = INVALID |
| 485 | - self.assertNotEqual(reviewed_import.import_job, None) |
| 486 | + removeSecurityProxy(code_import).review_status = INVALID |
| 487 | # ICodeImportJob does not allow setting 'state', so we must |
| 488 | # use removeSecurityProxy. |
| 489 | RUNNING = CodeImportJobState.RUNNING |
| 490 | - removeSecurityProxy(reviewed_import.import_job).state = RUNNING |
| 491 | + removeSecurityProxy(code_import.import_job).state = RUNNING |
| 492 | # Testing deletePendingJob failure. |
| 493 | self.assertFailure( |
| 494 | - "The CodeImportJob associated to " |
| 495 | - "~vcs-imports/gnome-terminal/import is RUNNING.", |
| 496 | - getUtility(ICodeImportJobWorkflow).deletePendingJob, |
| 497 | - reviewed_import) |
| 498 | + "The CodeImportJob associated to %s is RUNNING." % (branch_name,), |
| 499 | + getUtility(ICodeImportJobWorkflow).deletePendingJob, code_import) |
| 500 | |
| 501 | |
| 502 | class TestCodeImportJobWorkflowRequestJob(TestCaseWithFactory, |
| 503 | AssertFailureMixin, AssertEventMixin): |
| 504 | """Unit tests for CodeImportJobWorkflow.requestJob.""" |
| 505 | |
| 506 | - layer = LaunchpadFunctionalLayer |
| 507 | + layer = DatabaseFunctionalLayer |
| 508 | |
| 509 | def setUp(self): |
| 510 | super(TestCodeImportJobWorkflowRequestJob, self).setUp() |
| 511 | @@ -641,7 +614,7 @@ |
| 512 | AssertFailureMixin, AssertEventMixin): |
| 513 | """Unit tests for CodeImportJobWorkflow.startJob.""" |
| 514 | |
| 515 | - layer = LaunchpadFunctionalLayer |
| 516 | + layer = DatabaseFunctionalLayer |
| 517 | |
| 518 | def setUp(self): |
| 519 | super(TestCodeImportJobWorkflowStartJob, self).setUp() |
| 520 | @@ -679,7 +652,7 @@ |
| 521 | AssertFailureMixin, AssertEventMixin): |
| 522 | """Unit tests for CodeImportJobWorkflow.updateHeartbeat.""" |
| 523 | |
| 524 | - layer = LaunchpadFunctionalLayer |
| 525 | + layer = DatabaseFunctionalLayer |
| 526 | |
| 527 | def setUp(self): |
| 528 | super(TestCodeImportJobWorkflowUpdateHeartbeat, self).setUp() |
| 529 | @@ -705,7 +678,7 @@ |
| 530 | |
| 531 | def setUp(self): |
| 532 | super(TestCodeImportJobWorkflowFinishJob, self).setUp() |
| 533 | - login_for_code_imports() |
| 534 | + self.vcs_imports = login_for_code_imports() |
| 535 | self.machine = self.factory.makeCodeImportMachine(set_online=True) |
| 536 | |
| 537 | def makeRunningJob(self, code_import=None): |
| 538 | @@ -804,10 +777,9 @@ |
| 539 | # unless the CodeImport has been suspended or marked invalid. |
| 540 | running_job = self.makeRunningJob() |
| 541 | code_import = running_job.code_import |
| 542 | - ddaa = getUtility(IPersonSet).getByEmail( |
| 543 | - 'david.allouche@canonical.com') |
| 544 | code_import.updateFromData( |
| 545 | - {'review_status': CodeImportReviewStatus.SUSPENDED}, ddaa) |
| 546 | + {'review_status': CodeImportReviewStatus.SUSPENDED}, |
| 547 | + self.vcs_imports) |
| 548 | getUtility(ICodeImportJobWorkflow).finishJob( |
| 549 | running_job, CodeImportResultStatus.SUCCESS, None) |
| 550 | self.assertTrue(code_import.import_job is None) |
| 551 | @@ -920,8 +892,7 @@ |
| 552 | log_alias = getUtility(ILibraryFileAliasSet)[log_alias_id] |
| 553 | result = self.getResultForJob(job, log_alias=log_alias) |
| 554 | |
| 555 | - self.assertEqual( |
| 556 | - result.log_file.read(), log_data) |
| 557 | + self.assertEqual(result.log_file.read(), log_data) |
| 558 | |
| 559 | def test_createsFinishCodeImportEvent(self): |
| 560 | # finishJob() creates a FINISH CodeImportEvent. |
| 561 | @@ -1005,7 +976,7 @@ |
| 562 | |
| 563 | # This is a dependence on the sample data: David Allouche is a member of the |
| 564 | # ~vcs-imports celebrity team. |
| 565 | -logged_in_for_code_imports = logged_in_as('david.allouche@canonical.com') |
| 566 | +logged_in_for_code_imports = logged_in_as(VCS_IMPORTS_MEMBER_EMAIL) |
| 567 | |
| 568 | |
| 569 | class TestRequestJobUIRaces(TestCaseWithFactory): |
| 570 | @@ -1016,7 +987,7 @@ |
| 571 | the button and check that appropriate notifications are displayed. |
| 572 | """ |
| 573 | |
| 574 | - layer = LaunchpadFunctionalLayer |
| 575 | + layer = DatabaseFunctionalLayer |
| 576 | |
| 577 | @logged_in_for_code_imports |
| 578 | def getNewCodeImportIDAndBranchURL(self): |
| 579 | @@ -1027,7 +998,7 @@ |
| 580 | code_import_id = code_import.id |
| 581 | return code_import_id, branch_url |
| 582 | |
| 583 | - @logged_in_as('no-priv@canonical.com') |
| 584 | + @logged_in_as(NO_PRIVILEGE_EMAIL) |
| 585 | def requestJobByUserWithDisplayName(self, code_import_id, displayname): |
| 586 | """Record a request for the job by a user with the given name.""" |
| 587 | getUtility(ICodeImportJobWorkflow).requestJob( |
| 588 | |
| 589 | === modified file 'lib/lp/testing/factory.py' |
| 590 | --- lib/lp/testing/factory.py 2010-07-08 11:54:57 +0000 |
| 591 | +++ lib/lp/testing/factory.py 2010-07-13 10:02:50 +0000 |
| 592 | @@ -1436,7 +1436,7 @@ |
| 593 | def makeCodeImport(self, svn_branch_url=None, cvs_root=None, |
| 594 | cvs_module=None, target=None, branch_name=None, |
| 595 | git_repo_url=None, hg_repo_url=None, registrant=None, |
| 596 | - rcs_type=None): |
| 597 | + rcs_type=None, review_status=None): |
| 598 | """Create and return a new, arbitrary code import. |
| 599 | |
| 600 | The type of code import will be inferred from the source details |
| 601 | @@ -1461,26 +1461,29 @@ |
| 602 | else: |
| 603 | assert rcs_type in (RevisionControlSystems.SVN, |
| 604 | RevisionControlSystems.BZR_SVN) |
| 605 | - return code_import_set.new( |
| 606 | + code_import = code_import_set.new( |
| 607 | registrant, target, branch_name, rcs_type=rcs_type, |
| 608 | url=svn_branch_url) |
| 609 | elif git_repo_url is not None: |
| 610 | assert rcs_type in (None, RevisionControlSystems.GIT) |
| 611 | - return code_import_set.new( |
| 612 | + code_import = code_import_set.new( |
| 613 | registrant, target, branch_name, |
| 614 | rcs_type=RevisionControlSystems.GIT, |
| 615 | url=git_repo_url) |
| 616 | elif hg_repo_url is not None: |
| 617 | - return code_import_set.new( |
| 618 | + code_import = code_import_set.new( |
| 619 | registrant, target, branch_name, |
| 620 | rcs_type=RevisionControlSystems.HG, |
| 621 | url=hg_repo_url) |
| 622 | else: |
| 623 | assert rcs_type in (None, RevisionControlSystems.CVS) |
| 624 | - return code_import_set.new( |
| 625 | + code_import = code_import_set.new( |
| 626 | registrant, target, branch_name, |
| 627 | rcs_type=RevisionControlSystems.CVS, |
| 628 | cvs_root=cvs_root, cvs_module=cvs_module) |
| 629 | + if review_status: |
| 630 | + removeSecurityProxy(code_import).review_status = review_status |
| 631 | + return code_import |
| 632 | |
| 633 | def makeCodeImportEvent(self): |
| 634 | """Create and return a CodeImportEvent.""" |
| 635 | |
| 636 | === added file 'lib/lp/testing/sampledata.py' |
| 637 | --- lib/lp/testing/sampledata.py 1970-01-01 00:00:00 +0000 |
| 638 | +++ lib/lp/testing/sampledata.py 2010-07-13 10:02:50 +0000 |
| 639 | @@ -0,0 +1,18 @@ |
| 640 | +# Copyright 2010 Canonical Ltd. This software is licensed under the |
| 641 | +# GNU Affero General Public License version 3 (see the file LICENSE). |
| 642 | + |
| 643 | +"""Constants that refer to values in sampledata. |
| 644 | + |
| 645 | +If ever you use a literal in a test that refers to sample data, even if it's |
| 646 | +just a small number, then you should define it as a constant here. |
| 647 | +""" |
| 648 | + |
| 649 | +__metaclass__ = type |
| 650 | +__all__ = [ |
| 651 | + 'NO_PRIVILEGE_EMAIL', |
| 652 | + 'VCS_IMPORTS_MEMBER_EMAIL', |
| 653 | + ] |
| 654 | + |
| 655 | + |
| 656 | +NO_PRIVILEGE_EMAIL = 'no-priv@canonical.com' |
| 657 | +VCS_IMPORTS_MEMBER_EMAIL = 'david.allouche@canonical.com' |
| 658 | |
| 659 | === added file 'lib/lp/testing/tests/test_factory.py' |
| 660 | --- lib/lp/testing/tests/test_factory.py 1970-01-01 00:00:00 +0000 |
| 661 | +++ lib/lp/testing/tests/test_factory.py 2010-07-13 10:02:50 +0000 |
| 662 | @@ -0,0 +1,34 @@ |
| 663 | +# Copyright 2010 Canonical Ltd. This software is licensed under the |
| 664 | +# GNU Affero General Public License version 3 (see the file LICENSE). |
| 665 | + |
| 666 | +"""Tests for the Launchpad object factory.""" |
| 667 | + |
| 668 | +__metaclass__ = type |
| 669 | + |
| 670 | +import unittest |
| 671 | + |
| 672 | +from canonical.testing.layers import DatabaseFunctionalLayer |
| 673 | +from lp.code.enums import CodeImportReviewStatus |
| 674 | +from lp.testing import TestCaseWithFactory |
| 675 | + |
| 676 | + |
| 677 | +class TestFactory(TestCaseWithFactory): |
| 678 | + |
| 679 | + layer = DatabaseFunctionalLayer |
| 680 | + |
| 681 | + def test_makeCodeImportNoStatus(self): |
| 682 | + # If makeCodeImport is not given a review status, it defaults to NEW. |
| 683 | + code_import = self.factory.makeCodeImport() |
| 684 | + self.assertEqual( |
| 685 | + CodeImportReviewStatus.NEW, code_import.review_status) |
| 686 | + |
| 687 | + def test_makeCodeImportReviewStatus(self): |
| 688 | + # If makeCodeImport is given a review status, then that is the status |
| 689 | + # of the created import. |
| 690 | + status = CodeImportReviewStatus.REVIEWED |
| 691 | + code_import = self.factory.makeCodeImport(review_status=status) |
| 692 | + self.assertEqual(status, code_import.review_status) |
| 693 | + |
| 694 | + |
| 695 | +def test_suite(): |
| 696 | + return unittest.TestLoader().loadTestsFromName(__name__) |

I think this is useful; thanks for doing it.