Merge lp:~lifeless/launchpad/databasefixture into lp:launchpad
- databasefixture
- Merge into devel
Proposed by
Robert Collins
on 2010-10-17
| Status: | Merged |
|---|---|
| Approved by: | Robert Collins on 2010-10-17 |
| Approved revision: | no longer in the source branch. |
| Merged at revision: | 11734 |
| Proposed branch: | lp:~lifeless/launchpad/databasefixture |
| Merge into: | lp:launchpad |
| Diff against target: |
1478 lines (+247/-460) 21 files modified
lib/canonical/config/tests/test_database_config.py (+10/-6) lib/canonical/database/ftests/test_postgresql.py (+5/-3) lib/canonical/database/ftests/test_sqlbaseconnect.txt (+2/-2) lib/canonical/ftests/pgsql.py (+11/-30) lib/canonical/ftests/test_pgsql.py (+60/-63) lib/canonical/launchpad/doc/canonical-config.txt (+6/-4) lib/canonical/launchpad/doc/old-testing.txt (+19/-127) lib/canonical/launchpad/doc/security-proxies.txt (+0/-8) lib/canonical/launchpad/ftests/harness.py (+0/-84) lib/canonical/launchpad/pagetests/standalone/xx-dbpolicy.txt (+5/-3) lib/canonical/launchpad/tests/test_sampledata.py (+2/-6) lib/canonical/launchpad/webapp/ftests/test_adapter.txt (+6/-2) lib/canonical/lp/ftests/test_zopeless.py (+14/-9) lib/canonical/testing/ftests/test_layers.py (+26/-21) lib/canonical/testing/layers.py (+53/-55) lib/lp/codehosting/tests/test_acceptance.py (+20/-21) lib/lp/soyuz/doc/sampledata-setup.txt (+2/-2) lib/lp/soyuz/scripts/tests/test_buildd_cronscripts.py (+2/-3) lib/lp/translations/doc/fix_translation_credits.txt (+2/-2) lib/lp/translations/doc/message-sharing-merge-script.txt (+2/-2) lib/lp/translations/doc/request_country.txt (+0/-7) |
| To merge this branch: | bzr merge lp:~lifeless/launchpad/databasefixture |
| Related bugs: |
| Reviewer | Review Type | Date Requested | Status |
|---|---|---|---|
| Jonathan Lange (community) | 2010-10-17 | Approve on 2010-10-17 | |
|
Review via email:
|
|||
Commit Message
Make database test support capable of running with unique db test names.
Description of the Change
This makes the database test support layer ready for parallel testing - we can run with unique database names. Not supported yet is having the config system unique for a test process (I'd make it more granular than that, but zcml is so global that I don't think thats feasible until we reengineer that entire stack.
Some things were no longer used, so I deleted them. Most things were trivial.
We won't see any test DB leaks at this point, but I'm worried about the possibility when we turn this on... still thats a problem for another day.
To post a comment you must log in.
| Jonathan Lange (jml) wrote : | # |
review:
Approve
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
| 1 | === modified file 'lib/canonical/config/tests/test_database_config.py' |
| 2 | --- lib/canonical/config/tests/test_database_config.py 2010-01-13 20:06:09 +0000 |
| 3 | +++ lib/canonical/config/tests/test_database_config.py 2010-10-17 19:12:52 +0000 |
| 4 | @@ -3,17 +3,20 @@ |
| 5 | |
| 6 | __metaclass__ = type |
| 7 | |
| 8 | -from lp.testing import TestCase |
| 9 | - |
| 10 | from canonical.config import config, dbconfig |
| 11 | - |
| 12 | from canonical.launchpad.readonly import read_only_file_exists |
| 13 | from canonical.launchpad.tests.readonly import ( |
| 14 | - remove_read_only_file, touch_read_only_file) |
| 15 | + remove_read_only_file, |
| 16 | + touch_read_only_file, |
| 17 | + ) |
| 18 | +from canonical.testing.layers import DatabaseLayer |
| 19 | +from lp.testing import TestCase |
| 20 | |
| 21 | |
| 22 | class TestDatabaseConfig(TestCase): |
| 23 | |
| 24 | + layer = DatabaseLayer |
| 25 | + |
| 26 | def test_overlay(self): |
| 27 | # The dbconfig option overlays the database configurations of a |
| 28 | # chosen config section over the base section. |
| 29 | @@ -25,11 +28,12 @@ |
| 30 | self.assertEquals('librarian', config.librarian.dbuser) |
| 31 | |
| 32 | dbconfig.setConfigSection('librarian') |
| 33 | - self.assertEquals('dbname=launchpad_ftest', dbconfig.rw_main_master) |
| 34 | + expected_db = 'dbname=%s' % DatabaseLayer._db_fixture.dbname |
| 35 | + self.assertEquals(expected_db, dbconfig.rw_main_master) |
| 36 | self.assertEquals('librarian', dbconfig.dbuser) |
| 37 | |
| 38 | dbconfig.setConfigSection('launchpad') |
| 39 | - self.assertEquals('dbname=launchpad_ftest', dbconfig.rw_main_master) |
| 40 | + self.assertEquals(expected_db, dbconfig.rw_main_master) |
| 41 | self.assertEquals('launchpad_main', dbconfig.dbuser) |
| 42 | |
| 43 | def test_required_values(self): |
| 44 | |
| 45 | === modified file 'lib/canonical/database/ftests/test_postgresql.py' |
| 46 | --- lib/canonical/database/ftests/test_postgresql.py 2010-07-14 14:11:15 +0000 |
| 47 | +++ lib/canonical/database/ftests/test_postgresql.py 2010-10-17 19:12:52 +0000 |
| 48 | @@ -10,8 +10,9 @@ |
| 49 | def setUp(test): |
| 50 | |
| 51 | # Build a fresh, empty database and connect |
| 52 | - PgTestSetup().setUp() |
| 53 | - con = PgTestSetup().connect() |
| 54 | + test._db_fixture = PgTestSetup() |
| 55 | + test._db_fixture.setUp() |
| 56 | + con = test._db_fixture.connect() |
| 57 | |
| 58 | # Create a test schema demonstrating the edge cases |
| 59 | cur = con.cursor() |
| 60 | @@ -53,8 +54,9 @@ |
| 61 | test.globs['cur'] = cur |
| 62 | |
| 63 | def tearDown(test): |
| 64 | - PgTestSetup().tearDown() |
| 65 | test.globs['con'].close() |
| 66 | + test._db_fixture.tearDown() |
| 67 | + del test._db_fixture |
| 68 | |
| 69 | def test_suite(): |
| 70 | suite = DocTestSuite( |
| 71 | |
| 72 | === modified file 'lib/canonical/database/ftests/test_sqlbaseconnect.txt' |
| 73 | --- lib/canonical/database/ftests/test_sqlbaseconnect.txt 2009-04-17 10:32:16 +0000 |
| 74 | +++ lib/canonical/database/ftests/test_sqlbaseconnect.txt 2010-10-17 19:12:52 +0000 |
| 75 | @@ -19,7 +19,7 @@ |
| 76 | Specifying the user connects as that user. |
| 77 | |
| 78 | >>> do_connect(user=config.launchpad_session.dbuser) |
| 79 | - Connected as session to launchpad_ftest in read committed isolation. |
| 80 | + Connected as session to ... in read committed isolation. |
| 81 | |
| 82 | Specifying the database name connects to that database. |
| 83 | |
| 84 | @@ -31,5 +31,5 @@ |
| 85 | >>> do_connect( |
| 86 | ... user=config.launchpad.dbuser, |
| 87 | ... isolation=ISOLATION_LEVEL_SERIALIZABLE) |
| 88 | - Connected as launchpad_main to launchpad_ftest in serializable isolation. |
| 89 | + Connected as launchpad_main to ... in serializable isolation. |
| 90 | |
| 91 | |
| 92 | === modified file 'lib/canonical/ftests/pgsql.py' |
| 93 | --- lib/canonical/ftests/pgsql.py 2009-10-09 04:05:34 +0000 |
| 94 | +++ lib/canonical/ftests/pgsql.py 2010-10-17 19:12:52 +0000 |
| 95 | @@ -7,7 +7,7 @@ |
| 96 | |
| 97 | __metaclass__ = type |
| 98 | |
| 99 | -import unittest |
| 100 | +import os |
| 101 | import time |
| 102 | |
| 103 | import psycopg2 |
| 104 | @@ -119,7 +119,6 @@ |
| 105 | |
| 106 | _org_connect = None |
| 107 | def fake_connect(*args, **kw): |
| 108 | - global _org_connect |
| 109 | return ConnectionWrapper(_org_connect(*args, **kw)) |
| 110 | |
| 111 | def installFakeConnect(): |
| 112 | @@ -136,9 +135,13 @@ |
| 113 | |
| 114 | |
| 115 | class PgTestSetup: |
| 116 | + |
| 117 | connections = [] # Shared |
| 118 | + # Use a dynamically generated dbname: |
| 119 | + dynamic = object() |
| 120 | |
| 121 | template = 'template1' |
| 122 | + # Needs to match configs/testrunner*/*: |
| 123 | dbname = 'launchpad_ftest' |
| 124 | dbuser = None |
| 125 | host = None |
| 126 | @@ -165,8 +168,13 @@ |
| 127 | ''' |
| 128 | if template is not None: |
| 129 | self.template = template |
| 130 | - if dbname is not None: |
| 131 | + if dbname is PgTestSetup.dynamic: |
| 132 | + self.dbname = self.__class__.dbname + "_" + str(os.getpid()) |
| 133 | + elif dbname is not None: |
| 134 | self.dbname = dbname |
| 135 | + else: |
| 136 | + # Fallback to the class name. |
| 137 | + self.dbname = self.__class__.dbname |
| 138 | if dbuser is not None: |
| 139 | self.dbuser = dbuser |
| 140 | if host is not None: |
| 141 | @@ -331,30 +339,3 @@ |
| 142 | as database changes made from a subprocess. |
| 143 | """ |
| 144 | PgTestSetup._reset_db = True |
| 145 | - |
| 146 | - |
| 147 | -class PgTestCase(unittest.TestCase): |
| 148 | - dbname = None |
| 149 | - dbuser = None |
| 150 | - host = None |
| 151 | - port = None |
| 152 | - template = None |
| 153 | - def setUp(self): |
| 154 | - pg_test_setup = PgTestSetup( |
| 155 | - self.template, self.dbname, self.dbuser, self.host, self.port |
| 156 | - ) |
| 157 | - pg_test_setup.setUp() |
| 158 | - self.dbname = pg_test_setup.dbname |
| 159 | - self.dbuser = pg_test_setup.dbuser |
| 160 | - assert self.dbname, 'self.dbname is not set.' |
| 161 | - |
| 162 | - def tearDown(self): |
| 163 | - PgTestSetup( |
| 164 | - self.template, self.dbname, self.dbuser, self.host, self.port |
| 165 | - ).tearDown() |
| 166 | - |
| 167 | - def connect(self): |
| 168 | - return PgTestSetup( |
| 169 | - self.template, self.dbname, self.dbuser, self.host, self.port |
| 170 | - ).connect() |
| 171 | - |
| 172 | |
| 173 | === modified file 'lib/canonical/ftests/test_pgsql.py' |
| 174 | --- lib/canonical/ftests/test_pgsql.py 2009-06-25 05:30:52 +0000 |
| 175 | +++ lib/canonical/ftests/test_pgsql.py 2010-10-17 19:12:52 +0000 |
| 176 | @@ -1,77 +1,84 @@ |
| 177 | -# Copyright 2009 Canonical Ltd. This software is licensed under the |
| 178 | +# Copyright 2009-2010 Canonical Ltd. This software is licensed under the |
| 179 | # GNU Affero General Public License version 3 (see the file LICENSE). |
| 180 | |
| 181 | -import unittest |
| 182 | -from canonical.ftests.pgsql import PgTestCase, PgTestSetup, ConnectionWrapper |
| 183 | - |
| 184 | - |
| 185 | -class TestPgTestCase(PgTestCase): |
| 186 | - |
| 187 | - def testRollback(self): |
| 188 | - # This test creates a table. We run the same test twice, |
| 189 | - # which will fail if database changes are not rolled back |
| 190 | - con = self.connect() |
| 191 | - cur = con.cursor() |
| 192 | - cur.execute('CREATE TABLE foo (x int)') |
| 193 | - cur.execute('INSERT INTO foo VALUES (1)') |
| 194 | - cur.execute('SELECT x FROM foo') |
| 195 | - res = list(cur.fetchall()) |
| 196 | - self.failUnless(len(res) == 1) |
| 197 | - self.failUnless(res[0][0] == 1) |
| 198 | - con.commit() |
| 199 | - |
| 200 | - testRollback2 = testRollback |
| 201 | - |
| 202 | -class TestOptimization(unittest.TestCase): |
| 203 | +import os |
| 204 | + |
| 205 | +import testtools |
| 206 | + |
| 207 | +from canonical.ftests.pgsql import ( |
| 208 | + ConnectionWrapper, |
| 209 | + PgTestSetup, |
| 210 | + ) |
| 211 | + |
| 212 | + |
| 213 | +class TestPgTestSetup(testtools.TestCase): |
| 214 | + |
| 215 | + def test_db_naming(self): |
| 216 | + fixture = PgTestSetup(dbname=PgTestSetup.dynamic) |
| 217 | + expected_name = "%s_%s" % (PgTestSetup.dbname, os.getpid()) |
| 218 | + self.assertEqual(expected_name, fixture.dbname) |
| 219 | + fixture.setUp() |
| 220 | + self.addCleanup(fixture.dropDb) |
| 221 | + self.addCleanup(fixture.tearDown) |
| 222 | + cur = fixture.connect().cursor() |
| 223 | + cur.execute('SELECT current_database()') |
| 224 | + where = cur.fetchone()[0] |
| 225 | + self.assertEqual(expected_name, where) |
| 226 | + |
| 227 | def testOptimization(self): |
| 228 | # Test to ensure that the database is destroyed only when necessary |
| 229 | |
| 230 | # Make a change to a database |
| 231 | - PgTestSetup().setUp() |
| 232 | + fixture = PgTestSetup() |
| 233 | + fixture.setUp() |
| 234 | try: |
| 235 | - con = PgTestSetup().connect() |
| 236 | + con = fixture.connect() |
| 237 | cur = con.cursor() |
| 238 | cur.execute('CREATE TABLE foo (x int)') |
| 239 | con.commit() |
| 240 | # Fake it so the harness doesn't know a change has been made |
| 241 | ConnectionWrapper.committed = False |
| 242 | finally: |
| 243 | - PgTestSetup().tearDown() |
| 244 | + fixture.tearDown() |
| 245 | |
| 246 | - # Now check to ensure that the table we just created is still there |
| 247 | - PgTestSetup().setUp() |
| 248 | + # Now check to ensure that the table we just created is still there if |
| 249 | + # we reuse the fixture. |
| 250 | + fixture.setUp() |
| 251 | try: |
| 252 | - con = PgTestSetup().connect() |
| 253 | + con = fixture.connect() |
| 254 | cur = con.cursor() |
| 255 | # This tests that the table still exists, as well as modifying the |
| 256 | # db |
| 257 | cur.execute('INSERT INTO foo VALUES (1)') |
| 258 | con.commit() |
| 259 | finally: |
| 260 | - PgTestSetup().tearDown() |
| 261 | + fixture.tearDown() |
| 262 | |
| 263 | - # Now ensure that the table is gone |
| 264 | - PgTestSetup().setUp() |
| 265 | + # Now ensure that the table is gone - the commit must have been rolled |
| 266 | + # back. |
| 267 | + fixture.setUp() |
| 268 | try: |
| 269 | - con = PgTestSetup().connect() |
| 270 | + con = fixture.connect() |
| 271 | cur = con.cursor() |
| 272 | cur.execute('CREATE TABLE foo (x int)') |
| 273 | con.commit() |
| 274 | ConnectionWrapper.committed = False # Leave the table |
| 275 | finally: |
| 276 | - PgTestSetup().tearDown() |
| 277 | + fixture.tearDown() |
| 278 | |
| 279 | - # The database should *always* be recreated if the template |
| 280 | - # changes. |
| 281 | - PgTestSetup._last_db = ('whatever', 'launchpad_ftest') |
| 282 | - PgTestSetup().setUp() |
| 283 | + # The database should *always* be recreated if a new template had been |
| 284 | + # chosen. |
| 285 | + PgTestSetup._last_db = ('different-template', fixture.dbname) |
| 286 | + fixture.setUp() |
| 287 | try: |
| 288 | - con = PgTestSetup().connect() |
| 289 | + con = fixture.connect() |
| 290 | cur = con.cursor() |
| 291 | + # If this fails, TABLE foo still existed and the DB wasn't rebuilt |
| 292 | + # correctly. |
| 293 | cur.execute('CREATE TABLE foo (x int)') |
| 294 | con.commit() |
| 295 | finally: |
| 296 | - PgTestSetup().tearDown() |
| 297 | + fixture.tearDown() |
| 298 | |
| 299 | def test_sequences(self): |
| 300 | # Sequences may be affected by connections even if the connection |
| 301 | @@ -80,9 +87,10 @@ |
| 302 | # the sequences. |
| 303 | |
| 304 | # Setup a table that uses a sequence |
| 305 | - PgTestSetup().setUp() |
| 306 | + fixture = PgTestSetup() |
| 307 | + fixture.setUp() |
| 308 | try: |
| 309 | - con = PgTestSetup().connect() |
| 310 | + con = fixture.connect() |
| 311 | cur = con.cursor() |
| 312 | cur.execute('CREATE TABLE foo (x serial, y integer)') |
| 313 | con.commit() |
| 314 | @@ -90,15 +98,15 @@ |
| 315 | # Fake it so the harness doesn't know a change has been made |
| 316 | ConnectionWrapper.committed = False |
| 317 | finally: |
| 318 | - PgTestSetup().tearDown() |
| 319 | + fixture.tearDown() |
| 320 | |
| 321 | sequence_values = [] |
| 322 | # Insert a row into it and roll back the changes. Each time, we |
| 323 | # should end up with the same sequence value |
| 324 | for i in range(3): |
| 325 | - PgTestSetup().setUp() |
| 326 | + fixture.setUp() |
| 327 | try: |
| 328 | - con = PgTestSetup().connect() |
| 329 | + con = fixture.connect() |
| 330 | cur = con.cursor() |
| 331 | cur.execute('INSERT INTO foo (y) VALUES (1)') |
| 332 | cur.execute("SELECT currval('foo_x_seq')") |
| 333 | @@ -106,7 +114,7 @@ |
| 334 | con.rollback() |
| 335 | con.close() |
| 336 | finally: |
| 337 | - PgTestSetup().tearDown() |
| 338 | + fixture.tearDown() |
| 339 | |
| 340 | # Fail if we got a diffent sequence value at some point |
| 341 | for v in sequence_values: |
| 342 | @@ -114,9 +122,9 @@ |
| 343 | |
| 344 | # Repeat the test, but this time with some data already in the |
| 345 | # table |
| 346 | - PgTestSetup().setUp() |
| 347 | + fixture.setUp() |
| 348 | try: |
| 349 | - con = PgTestSetup().connect() |
| 350 | + con = fixture.connect() |
| 351 | cur = con.cursor() |
| 352 | cur.execute('INSERT INTO foo (y) VALUES (1)') |
| 353 | con.commit() |
| 354 | @@ -124,15 +132,15 @@ |
| 355 | # Fake it so the harness doesn't know a change has been made |
| 356 | ConnectionWrapper.committed = False |
| 357 | finally: |
| 358 | - PgTestSetup().tearDown() |
| 359 | + fixture.tearDown() |
| 360 | |
| 361 | sequence_values = [] |
| 362 | # Insert a row into it and roll back the changes. Each time, we |
| 363 | # should end up with the same sequence value |
| 364 | for i in range(1,3): |
| 365 | - PgTestSetup().setUp() |
| 366 | + fixture.setUp() |
| 367 | try: |
| 368 | - con = PgTestSetup().connect() |
| 369 | + con = fixture.connect() |
| 370 | cur = con.cursor() |
| 371 | cur.execute('INSERT INTO foo (y) VALUES (1)') |
| 372 | cur.execute("SELECT currval('foo_x_seq')") |
| 373 | @@ -140,19 +148,8 @@ |
| 374 | con.rollback() |
| 375 | con.close() |
| 376 | finally: |
| 377 | - PgTestSetup().tearDown() |
| 378 | + fixture.tearDown() |
| 379 | |
| 380 | # Fail if we got a diffent sequence value at some point |
| 381 | for v in sequence_values: |
| 382 | self.failUnlessEqual(v, sequence_values[0]) |
| 383 | - |
| 384 | - |
| 385 | -def test_suite(): |
| 386 | - suite = unittest.TestSuite() |
| 387 | - suite.addTest(unittest.makeSuite(TestPgTestCase)) |
| 388 | - suite.addTest(unittest.makeSuite(TestOptimization)) |
| 389 | - return suite |
| 390 | - |
| 391 | -if __name__ == '__main__': |
| 392 | - unittest.main() |
| 393 | - |
| 394 | |
| 395 | === modified file 'lib/canonical/launchpad/doc/canonical-config.txt' |
| 396 | --- lib/canonical/launchpad/doc/canonical-config.txt 2010-01-05 19:09:58 +0000 |
| 397 | +++ lib/canonical/launchpad/doc/canonical-config.txt 2010-10-17 19:12:52 +0000 |
| 398 | @@ -14,8 +14,10 @@ |
| 399 | simple configuration). |
| 400 | |
| 401 | >>> from canonical.config import config |
| 402 | - >>> print config.database.rw_main_master |
| 403 | - dbname=launchpad_ftest |
| 404 | + >>> from canonical.testing.layers import DatabaseLayer |
| 405 | + >>> expected = 'dbname=%s' % DatabaseLayer._db_fixture.dbname |
| 406 | + >>> expected == config.database.rw_main_master |
| 407 | + True |
| 408 | >>> config.database.db_statement_timeout is None |
| 409 | True |
| 410 | >>> config.launchpad.dbuser |
| 411 | @@ -226,7 +228,7 @@ |
| 412 | # >>> canonical.config.config = config |
| 413 | # >>> config.filename |
| 414 | # '.../configs/testrunner/launchpad-lazr.conf' |
| 415 | -# >>> config.dbname |
| 416 | -# 'launchpad_ftest' |
| 417 | +# >>> config.dbname == DatabaseLayer._db_fixture.dbname |
| 418 | +# True |
| 419 | # >>> config._cache.testrunner |
| 420 | # <SectionValue for canonical 'testrunner'> |
| 421 | |
| 422 | === modified file 'lib/canonical/launchpad/doc/old-testing.txt' |
| 423 | --- lib/canonical/launchpad/doc/old-testing.txt 2010-10-03 20:23:37 +0000 |
| 424 | +++ lib/canonical/launchpad/doc/old-testing.txt 2010-10-17 19:12:52 +0000 |
| 425 | @@ -18,11 +18,6 @@ |
| 426 | zope, we should not be testing it with the full Z3 functional test |
| 427 | harness). |
| 428 | |
| 429 | -If you are wondering why we use `PgTestSetup().setUp()` and |
| 430 | -`PgTestSetup.tearDown()` instead of `pgtestsetup.setUp()` or |
| 431 | -`pgtestsetup.tearDown()`, it is because I'm mirroring the design used in |
| 432 | -Zope3's `FunctionalTestSetup`. |
| 433 | - |
| 434 | canonical.functional.FunctionalTestCase |
| 435 | --------------------------------------- |
| 436 | |
| 437 | @@ -42,11 +37,12 @@ |
| 438 | |
| 439 | The setup procedure builds us a fresh, empty database |
| 440 | |
| 441 | ->>> PgTestSetup().setUp() |
| 442 | +>>> fixture = PgTestSetup() |
| 443 | +>>> fixture.setUp() |
| 444 | |
| 445 | We can get connections to this database |
| 446 | |
| 447 | ->>> connection = PgTestSetup().connect() |
| 448 | +>>> connection = fixture.connect() |
| 449 | >>> cursor = connection.cursor() |
| 450 | >>> cursor.execute("""CREATE TABLE Beer ( |
| 451 | ... id serial PRIMARY KEY, name text, stamp timestamp without time zone |
| 452 | @@ -68,28 +64,29 @@ |
| 453 | When we have finished, we need to call the tearDown method which closes |
| 454 | all outstanding connections and destroys the database |
| 455 | |
| 456 | ->>> PgTestSetup().tearDown() |
| 457 | +>>> fixture.tearDown() |
| 458 | |
| 459 | Because the database has been destroyed, further tests will not be |
| 460 | affected. |
| 461 | |
| 462 | ->>> PgTestSetup().setUp() |
| 463 | ->>> connection = PgTestSetup().connect() |
| 464 | +>>> fixture.setUp() |
| 465 | +>>> connection = fixture.connect() |
| 466 | >>> cursor = connection.cursor() |
| 467 | >>> cursor.execute("CREATE TABLE Beer (id serial PRIMARY KEY, name text)") |
| 468 | ->>> PgTestSetup().tearDown() |
| 469 | +>>> fixture.tearDown() |
| 470 | |
| 471 | We can also specify a different template to duplicate than the default |
| 472 | clean one (template1). For example, if you need a launchpad database |
| 473 | containing no data, you can use `launchpad_empty` as the template. |
| 474 | |
| 475 | ->>> PgTestSetup('launchpad_empty').setUp() |
| 476 | ->>> connection = PgTestSetup().connect() |
| 477 | +>>> fixture = PgTestSetup('launchpad_empty') |
| 478 | +>>> fixture.setUp() |
| 479 | +>>> connection = fixture.connect() |
| 480 | >>> cursor = connection.cursor() |
| 481 | >>> cursor.execute("SELECT COUNT(*) FROM Person") |
| 482 | >>> int(cursor.fetchone()[0]) |
| 483 | 0 |
| 484 | ->>> PgTestSetup().tearDown() |
| 485 | +>>> fixture.tearDown() |
| 486 | |
| 487 | We can also specify the user that we connect as to avoid connecting as the |
| 488 | PostgreSQL default user. |
| 489 | @@ -108,14 +105,12 @@ |
| 490 | ------------------ |
| 491 | |
| 492 | LaunchpadTestSetup is identical to PgTestSetup, except that it creates a |
| 493 | -fresh copy of the Launchpad database filled with our sample data. This |
| 494 | -class is defined in canonical.launchpad.ftests.harness. |
| 495 | - |
| 496 | -Note that at this level, you cannot access any of the SQLBase objects |
| 497 | - |
| 498 | ->>> from canonical.launchpad.ftests.harness import LaunchpadTestSetup |
| 499 | ->>> LaunchpadTestSetup().setUp() |
| 500 | ->>> connection = LaunchpadTestSetup().connect() |
| 501 | +fresh copy of the Launchpad database filled with our sample data. |
| 502 | + |
| 503 | +>>> from canonical.testing.layers import LaunchpadTestSetup |
| 504 | +>>> fixture = LaunchpadTestSetup() |
| 505 | +>>> fixture.setUp() |
| 506 | +>>> connection = fixture.connect() |
| 507 | >>> cursor = connection.cursor() |
| 508 | >>> cursor.execute("SELECT displayname FROM person WHERE name='carlos'") |
| 509 | >>> cursor.fetchone()[0] |
| 510 | @@ -127,7 +122,7 @@ |
| 511 | >>> cursor.fetchone()[0] |
| 512 | u'launchpad' |
| 513 | |
| 514 | ->>> LaunchpadTestSetup().tearDown() |
| 515 | +>>> fixture.tearDown() |
| 516 | |
| 517 | You can connect as a different database user using the same mechanism |
| 518 | described above for PgTestSetup |
| 519 | @@ -143,114 +138,13 @@ |
| 520 | >>> lpsetup.tearDown() |
| 521 | |
| 522 | |
| 523 | -LaunchpadZopelessTestSetup |
| 524 | --------------------------- |
| 525 | - |
| 526 | -LaunchpadZopelessTestSetup builds on LaunchpadTestSetup, calling |
| 527 | -initZopeless for you so you can access the SQLBase objects without needing |
| 528 | -the Zope3 infrastructure. |
| 529 | - |
| 530 | ->>> from canonical.launchpad.ftests.harness import LaunchpadZopelessTestSetup |
| 531 | ->>> LaunchpadZopelessTestSetup().setUp() |
| 532 | ->>> from lp.registry.model.person import Person |
| 533 | ->>> stub = Person.byName('stub') |
| 534 | ->>> stub.displayname |
| 535 | -u'Stuart Bishop' |
| 536 | ->>> stub.displayname = u'The Walrus' |
| 537 | ->>> stub.displayname |
| 538 | -u'The Walrus' |
| 539 | - |
| 540 | -You have access to the zopeless transaction |
| 541 | - |
| 542 | ->>> LaunchpadZopelessTestSetup().txn.abort() |
| 543 | ->>> stub.displayname |
| 544 | -u'Stuart Bishop' |
| 545 | - |
| 546 | -And always remember to tearDown or you will victimize other tests! |
| 547 | - |
| 548 | ->>> LaunchpadZopelessTestSetup().tearDown() |
| 549 | - |
| 550 | - |
| 551 | -In general, Zopeless tests should never be running as the launchpad user. |
| 552 | -You can select the user you connect as: |
| 553 | - |
| 554 | ->>> setup = LaunchpadZopelessTestSetup(dbuser=config.librarian.dbuser) |
| 555 | ->>> setup.setUp() |
| 556 | ->>> from lp.registry.model.sourcepackagename import SourcePackageName |
| 557 | ->>> SourcePackageName.get(1).name |
| 558 | -Traceback (most recent call last): |
| 559 | -... |
| 560 | -ProgrammingError: permission denied for relation sourcepackagename |
| 561 | -<BLANKLINE> |
| 562 | ->>> setup.tearDown() |
| 563 | - |
| 564 | - |
| 565 | -LaunchpadFunctionalTestSetup |
| 566 | ----------------------------- |
| 567 | - |
| 568 | -One with the lot. A LaunchpadTestSetup which also loads in the Zope3 |
| 569 | -environment. |
| 570 | - |
| 571 | ->>> from canonical.launchpad.ftests.harness import LaunchpadFunctionalTestSetup |
| 572 | ->>> LaunchpadFunctionalTestSetup().setUp() |
| 573 | - |
| 574 | -You have full access to the SQLBase objects |
| 575 | - |
| 576 | ->>> mark = Person.byName('mark') |
| 577 | ->>> mark.displayname |
| 578 | -u'Mark Shuttleworth' |
| 579 | - |
| 580 | -You also have access to the Zope3 component architecture, as registered |
| 581 | -by ftesting.zcml |
| 582 | - |
| 583 | ->>> from zope.app import zapi |
| 584 | ->>> from zope.sendmail.interfaces import IMailer |
| 585 | ->>> zapi.getUtility(IMailer, 'smtp') is not None |
| 586 | -True |
| 587 | - |
| 588 | ->>> LaunchpadFunctionalTestSetup().tearDown() |
| 589 | - |
| 590 | -You can change the user that the tests connect as: |
| 591 | - |
| 592 | - XXX 2008-05-29 jamesh: |
| 593 | - Using LaunchpadFunctionalLayer for non-webapp db users is generally |
| 594 | - a sign of a bug. These bits of code should generally be using |
| 595 | - LaunchpadZopelessLayer. |
| 596 | - |
| 597 | -##>>> setup = LaunchpadFunctionalTestSetup(dbuser=config.librarian.dbuser) |
| 598 | -##>>> setup.setUp() |
| 599 | -##>>> connection = setup.connect() |
| 600 | -##>>> cursor = connection.cursor() |
| 601 | -##>>> cursor.execute('SELECT current_user') |
| 602 | -##>>> cursor.fetchone()[0] |
| 603 | -##u'librarian' |
| 604 | -##>>> SourcePackageName.get(1).name |
| 605 | -##Traceback (most recent call last): |
| 606 | -##... |
| 607 | -##ProgrammingError: permission denied ... |
| 608 | -##>>> setup.tearDown() |
| 609 | - |
| 610 | -And the next test will be unaffected: |
| 611 | - |
| 612 | ->>> setup = LaunchpadFunctionalTestSetup() |
| 613 | ->>> setup.setUp() |
| 614 | ->>> connection = setup.connect() |
| 615 | ->>> cursor = connection.cursor() |
| 616 | ->>> cursor.execute('SELECT current_user') |
| 617 | ->>> cursor.fetchone()[0] |
| 618 | -u'launchpad' |
| 619 | ->>> SourcePackageName.get(1).name |
| 620 | -u'mozilla-firefox' |
| 621 | ->>> setup.tearDown() |
| 622 | - |
| 623 | - |
| 624 | LibrarianTestSetup |
| 625 | ------------------ |
| 626 | |
| 627 | Code that needs to access the Librarian can do so easily. Note that |
| 628 | LibrarianTestSetup requires the Launchpad database to be available, and |
| 629 | thus requires LaunchpadTestSetup or similar to be used in tandam. |
| 630 | -You probably really want LaunchpadFunctionalTestSetup so you can access |
| 631 | +You probably really want LaunchpadFunctionalLayer so you can access |
| 632 | the Librarian as a Utility. |
| 633 | |
| 634 | >>> from canonical.librarian.testing.server import LibrarianTestSetup |
| 635 | @@ -259,7 +153,6 @@ |
| 636 | >>> from canonical.librarian.interfaces import ILibrarianClient |
| 637 | >>> from StringIO import StringIO |
| 638 | |
| 639 | ->>> LaunchpadFunctionalTestSetup().setUp() |
| 640 | >>> librarian = LibrarianTestSetup() |
| 641 | >>> librarian.setUp() |
| 642 | >>> login(ANONYMOUS) |
| 643 | @@ -285,7 +178,6 @@ |
| 644 | True |
| 645 | |
| 646 | >>> librarian.tearDown() |
| 647 | ->>> LaunchpadFunctionalTestSetup().tearDown() |
| 648 | |
| 649 | >>> from canonical.testing import reset_logging |
| 650 | >>> reset_logging() |
| 651 | |
| 652 | === modified file 'lib/canonical/launchpad/doc/security-proxies.txt' |
| 653 | --- lib/canonical/launchpad/doc/security-proxies.txt 2010-10-09 16:36:22 +0000 |
| 654 | +++ lib/canonical/launchpad/doc/security-proxies.txt 2010-10-17 19:12:52 +0000 |
| 655 | @@ -6,13 +6,10 @@ |
| 656 | |
| 657 | First, some imports and set up:: |
| 658 | |
| 659 | - >>> from canonical.launchpad.ftests.harness import LaunchpadFunctionalTestSetup |
| 660 | >>> from zope.component import getUtility |
| 661 | >>> from lp.registry.interfaces.person import IPersonSet |
| 662 | >>> from lp.registry.model.person import Person |
| 663 | |
| 664 | - >>> LaunchpadFunctionalTestSetup().setUp() |
| 665 | - |
| 666 | Get a proxied and unproxied person object for the same person, and demonstrate |
| 667 | working comparisons:: |
| 668 | |
| 669 | @@ -57,8 +54,3 @@ |
| 670 | True |
| 671 | >>> hoary.status is SeriesStatus.DEVELOPMENT |
| 672 | False |
| 673 | - |
| 674 | -Finally, tear down the test: |
| 675 | - |
| 676 | - >>> LaunchpadFunctionalTestSetup().tearDown() |
| 677 | - |
| 678 | |
| 679 | === removed file 'lib/canonical/launchpad/ftests/harness.py' |
| 680 | --- lib/canonical/launchpad/ftests/harness.py 2010-10-04 19:50:45 +0000 |
| 681 | +++ lib/canonical/launchpad/ftests/harness.py 1970-01-01 00:00:00 +0000 |
| 682 | @@ -1,84 +0,0 @@ |
| 683 | -# Copyright 2009 Canonical Ltd. This software is licensed under the |
| 684 | -# GNU Affero General Public License version 3 (see the file LICENSE). |
| 685 | - |
| 686 | -""" |
| 687 | -Launchpad functional test helpers. |
| 688 | - |
| 689 | -This file needs to be refactored, moving its functionality into |
| 690 | -canonical.testing |
| 691 | -""" |
| 692 | - |
| 693 | -__metaclass__ = type |
| 694 | - |
| 695 | - |
| 696 | -from zope.app.testing.functional import FunctionalTestSetup |
| 697 | - |
| 698 | -from canonical.database.sqlbase import ZopelessTransactionManager |
| 699 | -from canonical.ftests.pgsql import PgTestSetup |
| 700 | -from canonical.lp import initZopeless |
| 701 | -from canonical.testing.layers import ( |
| 702 | - FunctionalLayer, |
| 703 | - ZopelessLayer, |
| 704 | - ) |
| 705 | -from canonical.testing.layers import ( |
| 706 | - disconnect_stores, |
| 707 | - reconnect_stores, |
| 708 | - ) |
| 709 | - |
| 710 | - |
| 711 | -__all__ = [ |
| 712 | - 'LaunchpadTestSetup', 'LaunchpadZopelessTestSetup', |
| 713 | - 'LaunchpadFunctionalTestSetup', |
| 714 | - ] |
| 715 | - |
| 716 | - |
| 717 | -class LaunchpadTestSetup(PgTestSetup): |
| 718 | - template = 'launchpad_ftest_template' |
| 719 | - dbname = 'launchpad_ftest' # Needs to match ftesting.zcml |
| 720 | - dbuser = 'launchpad' |
| 721 | - |
| 722 | - |
| 723 | -class LaunchpadZopelessTestSetup(LaunchpadTestSetup): |
| 724 | - txn = ZopelessTransactionManager |
| 725 | - def setUp(self, dbuser=None): |
| 726 | - assert ZopelessTransactionManager._installed is None, \ |
| 727 | - 'Last test using Zopeless failed to tearDown correctly' |
| 728 | - super(LaunchpadZopelessTestSetup, self).setUp() |
| 729 | - if self.host is not None: |
| 730 | - raise NotImplementedError('host not supported yet') |
| 731 | - if self.port is not None: |
| 732 | - raise NotImplementedError('port not supported yet') |
| 733 | - if dbuser is not None: |
| 734 | - self.dbuser = dbuser |
| 735 | - initZopeless(dbname=self.dbname, dbuser=self.dbuser) |
| 736 | - |
| 737 | - def tearDown(self): |
| 738 | - LaunchpadZopelessTestSetup.txn.uninstall() |
| 739 | - assert ZopelessTransactionManager._installed is None, \ |
| 740 | - 'Failed to tearDown Zopeless correctly' |
| 741 | - |
| 742 | - |
| 743 | -class LaunchpadFunctionalTestSetup(LaunchpadTestSetup): |
| 744 | - def _checkLayerInvariants(self): |
| 745 | - assert FunctionalLayer.isSetUp or ZopelessLayer.isSetUp, """ |
| 746 | - FunctionalTestSetup invoked at an inappropriate time. |
| 747 | - May only be invoked in the FunctionalLayer or ZopelessLayer |
| 748 | - """ |
| 749 | - |
| 750 | - def setUp(self, dbuser=None): |
| 751 | - self._checkLayerInvariants() |
| 752 | - if dbuser is not None: |
| 753 | - self.dbuser = dbuser |
| 754 | - assert self.dbuser == 'launchpad', ( |
| 755 | - "Non-default user names should probably be using " |
| 756 | - "script layer or zopeless layer.") |
| 757 | - disconnect_stores() |
| 758 | - super(LaunchpadFunctionalTestSetup, self).setUp() |
| 759 | - FunctionalTestSetup().setUp() |
| 760 | - reconnect_stores() |
| 761 | - |
| 762 | - def tearDown(self): |
| 763 | - self._checkLayerInvariants() |
| 764 | - FunctionalTestSetup().tearDown() |
| 765 | - disconnect_stores() |
| 766 | - super(LaunchpadFunctionalTestSetup, self).tearDown() |
| 767 | |
| 768 | === modified file 'lib/canonical/launchpad/pagetests/standalone/xx-dbpolicy.txt' |
| 769 | --- lib/canonical/launchpad/pagetests/standalone/xx-dbpolicy.txt 2010-01-13 13:50:39 +0000 |
| 770 | +++ lib/canonical/launchpad/pagetests/standalone/xx-dbpolicy.txt 2010-10-17 19:12:52 +0000 |
| 771 | @@ -20,9 +20,11 @@ |
| 772 | >>> from zope.component import getUtility |
| 773 | >>> from canonical.launchpad.webapp.interfaces import ( |
| 774 | ... IStoreSelector, MAIN_STORE, MASTER_FLAVOR, SLAVE_FLAVOR) |
| 775 | + >>> from canonical.testing.layers import DatabaseLayer |
| 776 | >>> master = getUtility(IStoreSelector).get(MAIN_STORE, MASTER_FLAVOR) |
| 777 | - >>> master.execute("SELECT current_database()").get_one()[0] |
| 778 | - u'launchpad_ftest' |
| 779 | + >>> dbname = DatabaseLayer._db_fixture.dbname |
| 780 | + >>> dbname == master.execute("SELECT current_database()").get_one()[0] |
| 781 | + True |
| 782 | >>> slave = getUtility(IStoreSelector).get(MAIN_STORE, SLAVE_FLAVOR) |
| 783 | >>> slave.execute("SELECT current_database()").get_one()[0] |
| 784 | u'launchpad_empty' |
| 785 | @@ -47,7 +49,7 @@ |
| 786 | |
| 787 | >>> def whichdb(browser): |
| 788 | ... dbname = extract_text(find_tag_by_id(browser.contents, 'dbname')) |
| 789 | - ... if dbname == 'launchpad_ftest': |
| 790 | + ... if dbname == DatabaseLayer._db_fixture.dbname: |
| 791 | ... return 'MASTER' |
| 792 | ... elif dbname == 'launchpad_empty': |
| 793 | ... return 'SLAVE' |
| 794 | |
| 795 | === modified file 'lib/canonical/launchpad/tests/test_sampledata.py' |
| 796 | --- lib/canonical/launchpad/tests/test_sampledata.py 2010-09-22 13:26:50 +0000 |
| 797 | +++ lib/canonical/launchpad/tests/test_sampledata.py 2010-10-17 19:12:52 +0000 |
| 798 | @@ -12,7 +12,6 @@ |
| 799 | __all__ = [] |
| 800 | |
| 801 | import subprocess |
| 802 | -import unittest |
| 803 | |
| 804 | from canonical.testing.layers import DatabaseLayer |
| 805 | from lp.testing import TestCase |
| 806 | @@ -37,14 +36,11 @@ |
| 807 | cmd = ( |
| 808 | "pg_dump --format=c --compress=0 --no-privileges --no-owner" |
| 809 | " --schema=public %s | pg_restore --clean" |
| 810 | - " --exit-on-error --dbname=launchpad_ftest" % source_dbname) |
| 811 | + " --exit-on-error --dbname=%s" % ( |
| 812 | + source_dbname, DatabaseLayer._db_fixture.dbname)) |
| 813 | proc = subprocess.Popen( |
| 814 | cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, |
| 815 | stdin=subprocess.PIPE) |
| 816 | (stdout, stderr) = proc.communicate() |
| 817 | rv = proc.wait() |
| 818 | self.failUnlessEqual(rv, 0, "Dump/Restore failed: %s" % stdout) |
| 819 | - |
| 820 | - |
| 821 | -def test_suite(): |
| 822 | - return unittest.TestLoader().loadTestsFromName(__name__) |
| 823 | |
| 824 | === modified file 'lib/canonical/launchpad/webapp/ftests/test_adapter.txt' |
| 825 | --- lib/canonical/launchpad/webapp/ftests/test_adapter.txt 2010-09-17 00:53:33 +0000 |
| 826 | +++ lib/canonical/launchpad/webapp/ftests/test_adapter.txt 2010-10-17 19:12:52 +0000 |
| 827 | @@ -18,14 +18,18 @@ |
| 828 | >>> from canonical.launchpad.webapp.adapter import ( |
| 829 | ... clear_request_started, get_request_statements, |
| 830 | ... set_request_started) |
| 831 | + >>> from canonical.testing.layers import DatabaseLayer |
| 832 | >>> from lp.services.timeline.requesttimeline import get_request_timeline |
| 833 | |
| 834 | There are several possible database connections available via the |
| 835 | IStoreSelector utility. |
| 836 | |
| 837 | >>> store = getUtility(IStoreSelector).get(MAIN_STORE, MASTER_FLAVOR) |
| 838 | - >>> print store.execute("SELECT current_database()").get_one()[0] |
| 839 | - launchpad_ftest |
| 840 | + >>> dbname = DatabaseLayer._db_fixture.dbname |
| 841 | + >>> active_name = store.execute("SELECT current_database()").get_one()[0] |
| 842 | + >>> if active_name != dbname: print '%s != %s' % (active_name, dbname) |
| 843 | + >>> active_name == dbname |
| 844 | + True |
| 845 | |
| 846 | |
| 847 | Statement Logging |
| 848 | |
| 849 | === modified file 'lib/canonical/lp/ftests/test_zopeless.py' |
| 850 | --- lib/canonical/lp/ftests/test_zopeless.py 2010-10-04 19:50:45 +0000 |
| 851 | +++ lib/canonical/lp/ftests/test_zopeless.py 2010-10-17 19:12:52 +0000 |
| 852 | @@ -14,9 +14,11 @@ |
| 853 | from sqlobject import StringCol, IntCol |
| 854 | |
| 855 | from canonical.database.sqlbase import SQLBase, alreadyInstalledMsg, cursor |
| 856 | -from canonical.ftests.pgsql import PgTestSetup |
| 857 | from canonical.lp import initZopeless |
| 858 | -from canonical.testing.layers import LaunchpadScriptLayer |
| 859 | +from canonical.testing.layers import ( |
| 860 | + DatabaseLayer, |
| 861 | + LaunchpadScriptLayer, |
| 862 | + ) |
| 863 | |
| 864 | |
| 865 | class MoreBeer(SQLBase): |
| 866 | @@ -28,6 +30,7 @@ |
| 867 | |
| 868 | |
| 869 | class TestInitZopeless(unittest.TestCase): |
| 870 | + |
| 871 | layer = LaunchpadScriptLayer |
| 872 | |
| 873 | def test_initZopelessTwice(self): |
| 874 | @@ -47,10 +50,11 @@ |
| 875 | # Calling initZopeless with the same arguments twice should return |
| 876 | # the exact same object twice, but also emit a warning. |
| 877 | try: |
| 878 | - tm1 = initZopeless(dbname=PgTestSetup().dbname, dbhost='', |
| 879 | - dbuser='launchpad') |
| 880 | - tm2 = initZopeless(dbname=PgTestSetup().dbname, dbhost='', |
| 881 | - dbuser='launchpad') |
| 882 | + dbname = DatabaseLayer._db_fixture.dbname |
| 883 | + tm1 = initZopeless( |
| 884 | + dbname=dbname, dbhost='', dbuser='launchpad') |
| 885 | + tm2 = initZopeless( |
| 886 | + dbname=dbname, dbhost='', dbuser='launchpad') |
| 887 | self.failUnless(tm1 is tm2) |
| 888 | self.failUnless(self.warned) |
| 889 | finally: |
| 890 | @@ -65,10 +69,11 @@ |
| 891 | |
| 892 | |
| 893 | class TestZopeless(unittest.TestCase): |
| 894 | + |
| 895 | layer = LaunchpadScriptLayer |
| 896 | |
| 897 | def setUp(self): |
| 898 | - self.tm = initZopeless(dbname=PgTestSetup().dbname, |
| 899 | + self.tm = initZopeless(dbname=DatabaseLayer._db_fixture.dbname, |
| 900 | dbuser='launchpad') |
| 901 | |
| 902 | c = cursor() |
| 903 | @@ -182,7 +187,7 @@ |
| 904 | self.tm.commit() |
| 905 | |
| 906 | # Make another change from a non-SQLObject connection, and commit that |
| 907 | - conn = psycopg2.connect('dbname=' + PgTestSetup().dbname) |
| 908 | + conn = psycopg2.connect('dbname=' + DatabaseLayer._db_fixture.dbname) |
| 909 | cur = conn.cursor() |
| 910 | cur.execute("BEGIN TRANSACTION;") |
| 911 | cur.execute("UPDATE MoreBeer SET rating=4 " |
| 912 | @@ -202,7 +207,7 @@ |
| 913 | >>> isZopeless() |
| 914 | False |
| 915 | |
| 916 | - >>> tm = initZopeless(dbname=PgTestSetup().dbname, |
| 917 | + >>> tm = initZopeless(dbname=DatabaseLayer._db_fixture.dbname, |
| 918 | ... dbhost='', dbuser='launchpad') |
| 919 | >>> isZopeless() |
| 920 | True |
| 921 | |
| 922 | === modified file 'lib/canonical/testing/ftests/test_layers.py' |
| 923 | --- lib/canonical/testing/ftests/test_layers.py 2010-07-26 13:18:18 +0000 |
| 924 | +++ lib/canonical/testing/ftests/test_layers.py 2010-10-17 19:12:52 +0000 |
| 925 | @@ -20,16 +20,27 @@ |
| 926 | from zope.component import getUtility, ComponentLookupError |
| 927 | |
| 928 | from canonical.config import config, dbconfig |
| 929 | -from canonical.launchpad.ftests.harness import LaunchpadTestSetup |
| 930 | from lazr.config import as_host_port |
| 931 | from canonical.librarian.client import LibrarianClient, UploadFailed |
| 932 | from canonical.librarian.interfaces import ILibrarianClient |
| 933 | from canonical.lazr.pidfile import pidfile_path |
| 934 | from canonical.testing.layers import ( |
| 935 | - AppServerLayer, BaseLayer, DatabaseLayer, FunctionalLayer, |
| 936 | - LaunchpadFunctionalLayer, LaunchpadLayer, LaunchpadScriptLayer, |
| 937 | - LaunchpadZopelessLayer, LayerInvariantError, LayerIsolationError, |
| 938 | - LayerProcessController, LibrarianLayer, MemcachedLayer, ZopelessLayer) |
| 939 | + AppServerLayer, |
| 940 | + BaseLayer, |
| 941 | + DatabaseLayer, |
| 942 | + FunctionalLayer, |
| 943 | + LaunchpadFunctionalLayer, |
| 944 | + LaunchpadLayer, |
| 945 | + LaunchpadScriptLayer, |
| 946 | + LaunchpadTestSetup, |
| 947 | + LaunchpadZopelessLayer, |
| 948 | + LayerInvariantError, |
| 949 | + LayerIsolationError, |
| 950 | + LayerProcessController, |
| 951 | + LibrarianLayer, |
| 952 | + MemcachedLayer, |
| 953 | + ZopelessLayer, |
| 954 | + ) |
| 955 | from lp.services.memcache.client import memcache_client_factory |
| 956 | |
| 957 | class BaseTestCase(unittest.TestCase): |
| 958 | @@ -123,22 +134,13 @@ |
| 959 | ) |
| 960 | |
| 961 | def testLaunchpadDbAvailable(self): |
| 962 | - try: |
| 963 | - con = DatabaseLayer.connect() |
| 964 | - cur = con.cursor() |
| 965 | - cur.execute("SELECT id FROM Person LIMIT 1") |
| 966 | - if cur.fetchone() is not None: |
| 967 | - self.failUnless( |
| 968 | - self.want_launchpad_database, |
| 969 | - 'Launchpad database should not be available.' |
| 970 | - ) |
| 971 | - return |
| 972 | - except psycopg2.Error: |
| 973 | - pass |
| 974 | - self.failIf( |
| 975 | - self.want_launchpad_database, |
| 976 | - 'Launchpad database should be available but is not.' |
| 977 | - ) |
| 978 | + if not self.want_launchpad_database: |
| 979 | + self.assertEqual(None, DatabaseLayer._db_fixture) |
| 980 | + return |
| 981 | + con = DatabaseLayer.connect() |
| 982 | + cur = con.cursor() |
| 983 | + cur.execute("SELECT id FROM Person LIMIT 1") |
| 984 | + self.assertNotEqual(None, cur.fetchone()) |
| 985 | |
| 986 | def testMemcachedWorking(self): |
| 987 | client = MemcachedLayer.client or memcache_client_factory() |
| 988 | @@ -424,6 +426,9 @@ |
| 989 | # The database should be reset by the test invariants. |
| 990 | LayerProcessController.startAppServer() |
| 991 | LayerProcessController.postTestInvariants() |
| 992 | + # XXX: Robert Collins 2010-10-17 bug=661967 - this isn't a reset, its |
| 993 | + # a flag that it *needs* a reset, which is actually quite different; |
| 994 | + # the lack of a teardown will leak daabases. |
| 995 | self.assertEquals(True, LaunchpadTestSetup()._reset_db) |
| 996 | |
| 997 | |
| 998 | |
| 999 | === modified file 'lib/canonical/testing/layers.py' |
| 1000 | --- lib/canonical/testing/layers.py 2010-10-05 13:25:01 +0000 |
| 1001 | +++ lib/canonical/testing/layers.py 2010-10-17 19:12:52 +0000 |
| 1002 | @@ -35,6 +35,7 @@ |
| 1003 | 'LaunchpadFunctionalLayer', |
| 1004 | 'LaunchpadLayer', |
| 1005 | 'LaunchpadScriptLayer', |
| 1006 | + 'LaunchpadTestSetup', |
| 1007 | 'LaunchpadZopelessLayer', |
| 1008 | 'LayerInvariantError', |
| 1009 | 'LayerIsolationError', |
| 1010 | @@ -92,6 +93,7 @@ |
| 1011 | from zope.server.logger.pythonlogger import PythonLogger |
| 1012 | from zope.testing.testrunner.runner import FakeInputContinueGenerator |
| 1013 | |
| 1014 | +from canonical.ftests.pgsql import PgTestSetup |
| 1015 | from canonical.launchpad.webapp.vhosts import allvhosts |
| 1016 | from canonical.lazr import pidfile |
| 1017 | from canonical.config import CanonicalConfig, config, dbconfig |
| 1018 | @@ -264,12 +266,14 @@ |
| 1019 | if not BaseLayer.persist_test_services: |
| 1020 | kill_by_pidfile(MemcachedLayer.getPidFile(), num_polls=0) |
| 1021 | # Kill any database left lying around from a previous test run. |
| 1022 | + db_fixture = LaunchpadTestSetup() |
| 1023 | try: |
| 1024 | - DatabaseLayer.connect().close() |
| 1025 | + db_fixture.connect().close() |
| 1026 | except psycopg2.Error: |
| 1027 | + # We assume this means 'no test database exists.' |
| 1028 | pass |
| 1029 | else: |
| 1030 | - DatabaseLayer._dropDb() |
| 1031 | + db_fixture.dropDb() |
| 1032 | |
| 1033 | @classmethod |
| 1034 | @profiled |
| 1035 | @@ -693,19 +697,19 @@ |
| 1036 | _reset_between_tests = True |
| 1037 | |
| 1038 | _is_setup = False |
| 1039 | + _db_fixture = None |
| 1040 | |
| 1041 | @classmethod |
| 1042 | @profiled |
| 1043 | def setUp(cls): |
| 1044 | cls._is_setup = True |
| 1045 | - DatabaseLayer.force_dirty_database() |
| 1046 | - # Imported here to avoid circular import issues. This |
| 1047 | - # functionality should be migrated into this module at some |
| 1048 | - # point. -- StuartBishop 20060712 |
| 1049 | - from canonical.launchpad.ftests.harness import LaunchpadTestSetup |
| 1050 | - LaunchpadTestSetup().tearDown() |
| 1051 | - DatabaseLayer._reset_sequences_sql = LaunchpadTestSetup( |
| 1052 | + # Read the sequences we'll need from the test template database. |
| 1053 | + reset_sequences_sql = LaunchpadTestSetup( |
| 1054 | dbname='launchpad_ftest_template').generateResetSequencesSQL() |
| 1055 | + cls._db_fixture = LaunchpadTestSetup( |
| 1056 | + reset_sequences_sql=reset_sequences_sql) |
| 1057 | + cls.force_dirty_database() |
| 1058 | + cls._db_fixture.tearDown() |
| 1059 | |
| 1060 | @classmethod |
| 1061 | @profiled |
| 1062 | @@ -716,32 +720,22 @@ |
| 1063 | # Don't leave the DB lying around or it might break tests |
| 1064 | # that depend on it not being there on startup, such as found |
| 1065 | # in test_layers.py |
| 1066 | - DatabaseLayer.force_dirty_database() |
| 1067 | - # Imported here to avoid circular import issues. This |
| 1068 | - # functionality should be migrated into this module at some |
| 1069 | - # point. -- StuartBishop 20060712 |
| 1070 | - from canonical.launchpad.ftests.harness import LaunchpadTestSetup |
| 1071 | - LaunchpadTestSetup().tearDown() |
| 1072 | - DatabaseLayer._reset_sequences_sql = None |
| 1073 | + cls.force_dirty_database() |
| 1074 | + cls._db_fixture.tearDown() |
| 1075 | + cls._db_fixture = None |
| 1076 | |
| 1077 | @classmethod |
| 1078 | @profiled |
| 1079 | def testSetUp(cls): |
| 1080 | - # Imported here to avoid circular import issues. This |
| 1081 | - # functionality should be migrated into this module at some |
| 1082 | - # point. -- StuartBishop 20060712 |
| 1083 | - from canonical.launchpad.ftests.harness import LaunchpadTestSetup |
| 1084 | - if DatabaseLayer._reset_between_tests: |
| 1085 | - LaunchpadTestSetup( |
| 1086 | - reset_sequences_sql=DatabaseLayer._reset_sequences_sql |
| 1087 | - ).setUp() |
| 1088 | + if cls._reset_between_tests: |
| 1089 | + cls._db_fixture.setUp() |
| 1090 | # Ensure that the database is connectable. Because we might have |
| 1091 | # just created it, keep trying for a few seconds incase PostgreSQL |
| 1092 | # is taking its time getting its house in order. |
| 1093 | attempts = 60 |
| 1094 | for count in range(0, attempts): |
| 1095 | try: |
| 1096 | - DatabaseLayer.connect().close() |
| 1097 | + cls.connect().close() |
| 1098 | except psycopg2.Error: |
| 1099 | if count == attempts - 1: |
| 1100 | raise |
| 1101 | @@ -749,24 +743,20 @@ |
| 1102 | else: |
| 1103 | break |
| 1104 | |
| 1105 | - if DatabaseLayer.use_mockdb is True: |
| 1106 | - DatabaseLayer.installMockDb() |
| 1107 | + if cls.use_mockdb is True: |
| 1108 | + cls.installMockDb() |
| 1109 | |
| 1110 | @classmethod |
| 1111 | @profiled |
| 1112 | def testTearDown(cls): |
| 1113 | - if DatabaseLayer.use_mockdb is True: |
| 1114 | - DatabaseLayer.uninstallMockDb() |
| 1115 | + if cls.use_mockdb is True: |
| 1116 | + cls.uninstallMockDb() |
| 1117 | |
| 1118 | # Ensure that the database is connectable |
| 1119 | - DatabaseLayer.connect().close() |
| 1120 | + cls.connect().close() |
| 1121 | |
| 1122 | - # Imported here to avoid circular import issues. This |
| 1123 | - # functionality should be migrated into this module at some |
| 1124 | - # point. -- StuartBishop 20060712 |
| 1125 | - from canonical.launchpad.ftests.harness import LaunchpadTestSetup |
| 1126 | - if DatabaseLayer._reset_between_tests: |
| 1127 | - LaunchpadTestSetup().tearDown() |
| 1128 | + if cls._reset_between_tests: |
| 1129 | + cls._db_fixture.tearDown() |
| 1130 | |
| 1131 | # Fail tests that forget to uninstall their database policies. |
| 1132 | from canonical.launchpad.webapp.adapter import StoreSelector |
| 1133 | @@ -781,7 +771,7 @@ |
| 1134 | @classmethod |
| 1135 | @profiled |
| 1136 | def installMockDb(cls): |
| 1137 | - assert DatabaseLayer.mockdb_mode is None, 'mock db already installed' |
| 1138 | + assert cls.mockdb_mode is None, 'mock db already installed' |
| 1139 | |
| 1140 | from canonical.testing.mockdb import ( |
| 1141 | script_filename, ScriptRecorder, ScriptPlayer, |
| 1142 | @@ -795,32 +785,32 @@ |
| 1143 | # mock db script. |
| 1144 | filename = script_filename(test_key) |
| 1145 | if os.path.exists(filename): |
| 1146 | - DatabaseLayer.mockdb_mode = 'replay' |
| 1147 | - DatabaseLayer.script = ScriptPlayer(test_key) |
| 1148 | + cls.mockdb_mode = 'replay' |
| 1149 | + cls.script = ScriptPlayer(test_key) |
| 1150 | else: |
| 1151 | - DatabaseLayer.mockdb_mode = 'record' |
| 1152 | - DatabaseLayer.script = ScriptRecorder(test_key) |
| 1153 | + cls.mockdb_mode = 'record' |
| 1154 | + cls.script = ScriptRecorder(test_key) |
| 1155 | |
| 1156 | global _org_connect |
| 1157 | _org_connect = psycopg2.connect |
| 1158 | # Proxy real connections with our mockdb. |
| 1159 | def fake_connect(*args, **kw): |
| 1160 | - return DatabaseLayer.script.connect(_org_connect, *args, **kw) |
| 1161 | + return cls.script.connect(_org_connect, *args, **kw) |
| 1162 | psycopg2.connect = fake_connect |
| 1163 | |
| 1164 | @classmethod |
| 1165 | @profiled |
| 1166 | def uninstallMockDb(cls): |
| 1167 | - if DatabaseLayer.mockdb_mode is None: |
| 1168 | + if cls.mockdb_mode is None: |
| 1169 | return # Already uninstalled |
| 1170 | |
| 1171 | # Store results if we are recording |
| 1172 | - if DatabaseLayer.mockdb_mode == 'record': |
| 1173 | - DatabaseLayer.script.store() |
| 1174 | - assert os.path.exists(DatabaseLayer.script.script_filename), ( |
| 1175 | + if cls.mockdb_mode == 'record': |
| 1176 | + cls.script.store() |
| 1177 | + assert os.path.exists(cls.script.script_filename), ( |
| 1178 | "Stored results but no script on disk.") |
| 1179 | |
| 1180 | - DatabaseLayer.mockdb_mode = None |
| 1181 | + cls.mockdb_mode = None |
| 1182 | global _org_connect |
| 1183 | psycopg2.connect = _org_connect |
| 1184 | _org_connect = None |
| 1185 | @@ -828,20 +818,17 @@ |
| 1186 | @classmethod |
| 1187 | @profiled |
| 1188 | def force_dirty_database(cls): |
| 1189 | - from canonical.launchpad.ftests.harness import LaunchpadTestSetup |
| 1190 | - LaunchpadTestSetup().force_dirty_database() |
| 1191 | + cls._db_fixture.force_dirty_database() |
| 1192 | |
| 1193 | @classmethod |
| 1194 | @profiled |
| 1195 | def connect(cls): |
| 1196 | - from canonical.launchpad.ftests.harness import LaunchpadTestSetup |
| 1197 | - return LaunchpadTestSetup().connect() |
| 1198 | + return cls._db_fixture.connect() |
| 1199 | |
| 1200 | @classmethod |
| 1201 | @profiled |
| 1202 | def _dropDb(cls): |
| 1203 | - from canonical.launchpad.ftests.harness import LaunchpadTestSetup |
| 1204 | - return LaunchpadTestSetup().dropDb() |
| 1205 | + return cls._db_fixture.dropDb() |
| 1206 | |
| 1207 | |
| 1208 | def test_default_timeout(): |
| 1209 | @@ -1378,6 +1365,11 @@ |
| 1210 | reconnect_stores(database_config_section=database_config_section) |
| 1211 | |
| 1212 | |
| 1213 | +class LaunchpadTestSetup(PgTestSetup): |
| 1214 | + template = 'launchpad_ftest_template' |
| 1215 | + dbuser = 'launchpad' |
| 1216 | + |
| 1217 | + |
| 1218 | class LaunchpadZopelessLayer(LaunchpadScriptLayer): |
| 1219 | """Full Zopeless environment including Component Architecture and |
| 1220 | database connections initialized. |
| 1221 | @@ -1643,6 +1635,9 @@ |
| 1222 | # configs/testrunner-appserver/mail-configure.zcml |
| 1223 | smtp_controller = None |
| 1224 | |
| 1225 | + # The DB fixture in use |
| 1226 | + _db_fixture = None |
| 1227 | + |
| 1228 | @classmethod |
| 1229 | @profiled |
| 1230 | def startSMTPServer(cls): |
| 1231 | @@ -1770,9 +1765,12 @@ |
| 1232 | @classmethod |
| 1233 | def _runAppServer(cls): |
| 1234 | """Start the app server using runlaunchpad.py""" |
| 1235 | - from canonical.launchpad.ftests.harness import LaunchpadTestSetup |
| 1236 | # The database must be available for the app server to start. |
| 1237 | - LaunchpadTestSetup().setUp() |
| 1238 | + cls._db_fixture = LaunchpadTestSetup() |
| 1239 | + # This is not torn down properly: rather the singleton nature is abused |
| 1240 | + # and the fixture is simply marked as being dirty. |
| 1241 | + # XXX: Robert Collins 2010-10-17 bug=661967 |
| 1242 | + cls._db_fixture.setUp() |
| 1243 | # The app server will not start at all if the database hasn't been |
| 1244 | # correctly patched. The app server will make exactly this check, |
| 1245 | # doing it here makes the error more obvious. |
| 1246 | |
| 1247 | === modified file 'lib/lp/codehosting/tests/test_acceptance.py' |
| 1248 | --- lib/lp/codehosting/tests/test_acceptance.py 2010-10-04 19:50:45 +0000 |
| 1249 | +++ lib/lp/codehosting/tests/test_acceptance.py 2010-10-17 19:12:52 +0000 |
| 1250 | @@ -21,7 +21,6 @@ |
| 1251 | from zope.component import getUtility |
| 1252 | |
| 1253 | from canonical.config import config |
| 1254 | -from canonical.launchpad.ftests.harness import LaunchpadZopelessTestSetup |
| 1255 | from canonical.testing.layers import ZopelessAppServerLayer |
| 1256 | from canonical.testing.profiled import profiled |
| 1257 | from lp.code.bzr import ( |
| 1258 | @@ -334,7 +333,7 @@ |
| 1259 | remote_url = self.getTransportURL('~testuser/+junk/test-branch') |
| 1260 | self.push(self.local_branch_path, remote_url) |
| 1261 | self.assertBranchesMatch(self.local_branch_path, remote_url) |
| 1262 | - LaunchpadZopelessTestSetup().txn.begin() |
| 1263 | + ZopelessAppServerLayer.txn.begin() |
| 1264 | db_branch = getUtility(IBranchSet).getByUniqueName( |
| 1265 | '~testuser/+junk/test-branch') |
| 1266 | self.assertEqual( |
| 1267 | @@ -343,7 +342,7 @@ |
| 1268 | BranchFormat.BZR_BRANCH_7, db_branch.branch_format) |
| 1269 | self.assertEqual( |
| 1270 | ControlFormat.BZR_METADIR_1, db_branch.control_format) |
| 1271 | - LaunchpadZopelessTestSetup().txn.commit() |
| 1272 | + ZopelessAppServerLayer.txn.commit() |
| 1273 | |
| 1274 | def test_push_to_existing_branch(self): |
| 1275 | """Pushing to an existing branch must work.""" |
| 1276 | @@ -374,12 +373,12 @@ |
| 1277 | self.push(self.local_branch_path, remote_url) |
| 1278 | |
| 1279 | # Rename owner, product and branch in the database |
| 1280 | - LaunchpadZopelessTestSetup().txn.begin() |
| 1281 | + ZopelessAppServerLayer.txn.begin() |
| 1282 | branch = self.getDatabaseBranch('testuser', None, 'test-branch') |
| 1283 | branch.owner.name = 'renamed-user' |
| 1284 | branch.setTarget(user=branch.owner, project=Product.byName('firefox')) |
| 1285 | branch.name = 'renamed-branch' |
| 1286 | - LaunchpadZopelessTestSetup().txn.commit() |
| 1287 | + ZopelessAppServerLayer.txn.commit() |
| 1288 | |
| 1289 | # Check that it's not at the old location. |
| 1290 | self.assertNotBranch( |
| 1291 | @@ -405,23 +404,23 @@ |
| 1292 | '~testuser/+junk/totally-new-branch') |
| 1293 | self.push(self.local_branch_path, remote_url) |
| 1294 | |
| 1295 | - LaunchpadZopelessTestSetup().txn.begin() |
| 1296 | + ZopelessAppServerLayer.txn.begin() |
| 1297 | branch = self.getDatabaseBranch( |
| 1298 | 'testuser', None, 'totally-new-branch') |
| 1299 | |
| 1300 | self.assertEqual( |
| 1301 | ['~testuser/+junk/totally-new-branch', self.revid], |
| 1302 | [branch.unique_name, branch.last_mirrored_id]) |
| 1303 | - LaunchpadZopelessTestSetup().txn.abort() |
| 1304 | + ZopelessAppServerLayer.txn.abort() |
| 1305 | |
| 1306 | def test_record_default_stacking(self): |
| 1307 | # If the location being pushed to has a default stacked-on branch, |
| 1308 | # then branches pushed to that location end up stacked on it by |
| 1309 | # default. |
| 1310 | product = self.factory.makeProduct() |
| 1311 | - LaunchpadZopelessTestSetup().txn.commit() |
| 1312 | + ZopelessAppServerLayer.txn.commit() |
| 1313 | |
| 1314 | - LaunchpadZopelessTestSetup().txn.begin() |
| 1315 | + ZopelessAppServerLayer.txn.begin() |
| 1316 | |
| 1317 | self.make_branch_and_tree('stacked-on') |
| 1318 | trunk_unique_name = '~testuser/%s/trunk' % product.name |
| 1319 | @@ -431,7 +430,7 @@ |
| 1320 | self.factory.enableDefaultStackingForProduct( |
| 1321 | db_trunk.product, db_trunk) |
| 1322 | |
| 1323 | - LaunchpadZopelessTestSetup().txn.commit() |
| 1324 | + ZopelessAppServerLayer.txn.commit() |
| 1325 | |
| 1326 | stacked_unique_name = '~testuser/%s/stacked' % product.name |
| 1327 | self.push( |
| 1328 | @@ -447,7 +446,7 @@ |
| 1329 | # attribute of the database branch, and stacked on location of the new |
| 1330 | # branch is normalized to be a relative path. |
| 1331 | product = self.factory.makeProduct() |
| 1332 | - LaunchpadZopelessTestSetup().txn.commit() |
| 1333 | + ZopelessAppServerLayer.txn.commit() |
| 1334 | |
| 1335 | self.make_branch_and_tree('stacked-on') |
| 1336 | trunk_unique_name = '~testuser/%s/trunk' % product.name |
| 1337 | @@ -507,11 +506,11 @@ |
| 1338 | def test_push_to_new_short_branch_alias(self): |
| 1339 | # We can also push branches to URLs like /+branch/firefox |
| 1340 | # Hack 'firefox' so we have permission to do this. |
| 1341 | - LaunchpadZopelessTestSetup().txn.begin() |
| 1342 | + ZopelessAppServerLayer.txn.begin() |
| 1343 | firefox = Product.selectOneBy(name='firefox') |
| 1344 | testuser = Person.selectOneBy(name='testuser') |
| 1345 | firefox.development_focus.owner = testuser |
| 1346 | - LaunchpadZopelessTestSetup().txn.commit() |
| 1347 | + ZopelessAppServerLayer.txn.commit() |
| 1348 | remote_url = self.getTransportURL('+branch/firefox') |
| 1349 | self.push(self.local_branch_path, remote_url) |
| 1350 | self.assertBranchesMatch(self.local_branch_path, remote_url) |
| 1351 | @@ -520,10 +519,10 @@ |
| 1352 | # If a hosted branch exists in the database, but not on the |
| 1353 | # filesystem, and is writable by the user, then the user is able to |
| 1354 | # push to it. |
| 1355 | - LaunchpadZopelessTestSetup().txn.begin() |
| 1356 | + ZopelessAppServerLayer.txn.begin() |
| 1357 | branch = self.makeDatabaseBranch('testuser', 'firefox', 'some-branch') |
| 1358 | remote_url = self.getTransportURL(branch.unique_name) |
| 1359 | - LaunchpadZopelessTestSetup().txn.commit() |
| 1360 | + ZopelessAppServerLayer.txn.commit() |
| 1361 | self.push( |
| 1362 | self.local_branch_path, remote_url, |
| 1363 | extra_args=['--use-existing-dir']) |
| 1364 | @@ -531,21 +530,21 @@ |
| 1365 | |
| 1366 | def test_cant_push_to_existing_mirrored_branch(self): |
| 1367 | # Users cannot push to mirrored branches. |
| 1368 | - LaunchpadZopelessTestSetup().txn.begin() |
| 1369 | + ZopelessAppServerLayer.txn.begin() |
| 1370 | branch = self.makeDatabaseBranch( |
| 1371 | 'testuser', 'firefox', 'some-branch', BranchType.MIRRORED) |
| 1372 | remote_url = self.getTransportURL(branch.unique_name) |
| 1373 | - LaunchpadZopelessTestSetup().txn.commit() |
| 1374 | + ZopelessAppServerLayer.txn.commit() |
| 1375 | self.assertCantPush( |
| 1376 | self.local_branch_path, remote_url, |
| 1377 | ['Permission denied:', 'Transport operation not possible:']) |
| 1378 | |
| 1379 | def test_cant_push_to_existing_unowned_hosted_branch(self): |
| 1380 | # Users can only push to hosted branches that they own. |
| 1381 | - LaunchpadZopelessTestSetup().txn.begin() |
| 1382 | + ZopelessAppServerLayer.txn.begin() |
| 1383 | branch = self.makeDatabaseBranch('mark', 'firefox', 'some-branch') |
| 1384 | remote_url = self.getTransportURL(branch.unique_name) |
| 1385 | - LaunchpadZopelessTestSetup().txn.commit() |
| 1386 | + ZopelessAppServerLayer.txn.commit() |
| 1387 | self.assertCantPush( |
| 1388 | self.local_branch_path, remote_url, |
| 1389 | ['Permission denied:', 'Transport operation not possible:']) |
| 1390 | @@ -566,12 +565,12 @@ |
| 1391 | person_name, product_name, branch_name) |
| 1392 | |
| 1393 | # Mark as mirrored. |
| 1394 | - LaunchpadZopelessTestSetup().txn.begin() |
| 1395 | + ZopelessAppServerLayer.txn.begin() |
| 1396 | branch = self.getDatabaseBranch( |
| 1397 | person_name, product_name, branch_name) |
| 1398 | branch.branch_type = BranchType.MIRRORED |
| 1399 | branch.url = "http://example.com/smartservertest/branch" |
| 1400 | - LaunchpadZopelessTestSetup().txn.commit() |
| 1401 | + ZopelessAppServerLayer.txn.commit() |
| 1402 | return ro_branch_url |
| 1403 | |
| 1404 | def test_can_read_readonly_branch(self): |
| 1405 | |
| 1406 | === modified file 'lib/lp/soyuz/doc/sampledata-setup.txt' |
| 1407 | --- lib/lp/soyuz/doc/sampledata-setup.txt 2010-08-13 02:59:14 +0000 |
| 1408 | +++ lib/lp/soyuz/doc/sampledata-setup.txt 2010-10-17 19:12:52 +0000 |
| 1409 | @@ -21,5 +21,5 @@ |
| 1410 | INFO ... |
| 1411 | INFO Done. |
| 1412 | |
| 1413 | - >>> from canonical.launchpad.ftests.harness import LaunchpadTestSetup |
| 1414 | - >>> LaunchpadTestSetup().force_dirty_database() |
| 1415 | + >>> from canonical.testing.layers import DatabaseLayer |
| 1416 | + >>> DatabaseLayer.force_dirty_database() |
| 1417 | |
| 1418 | === modified file 'lib/lp/soyuz/scripts/tests/test_buildd_cronscripts.py' |
| 1419 | --- lib/lp/soyuz/scripts/tests/test_buildd_cronscripts.py 2010-10-04 19:50:45 +0000 |
| 1420 | +++ lib/lp/soyuz/scripts/tests/test_buildd_cronscripts.py 2010-10-17 19:12:52 +0000 |
| 1421 | @@ -74,9 +74,8 @@ |
| 1422 | rc, out, err = runner() |
| 1423 | self.assertEqual(0, rc, "Err:\n%s" % err) |
| 1424 | |
| 1425 | - # 'runners' commit to the launchpad_ftest database in |
| 1426 | - # subprocesses, so we need to tell the layer to fully |
| 1427 | - # tear down and restore the database. |
| 1428 | + # 'runners' commit to the test database in subprocesses, so we need to |
| 1429 | + # tell the layer to fully tear down and restore the database. |
| 1430 | DatabaseLayer.force_dirty_database() |
| 1431 | |
| 1432 | return rc, out, err |
| 1433 | |
| 1434 | === modified file 'lib/lp/translations/doc/fix_translation_credits.txt' |
| 1435 | --- lib/lp/translations/doc/fix_translation_credits.txt 2010-04-01 04:05:10 +0000 |
| 1436 | +++ lib/lp/translations/doc/fix_translation_credits.txt 2010-10-17 19:12:52 +0000 |
| 1437 | @@ -19,5 +19,5 @@ |
| 1438 | After altering the database from a separate process, we must tell the |
| 1439 | test setup that the database is dirty in spite of appearances. |
| 1440 | |
| 1441 | - >>> from canonical.launchpad.ftests.harness import LaunchpadTestSetup |
| 1442 | - >>> LaunchpadTestSetup().force_dirty_database() |
| 1443 | + >>> from canonical.testing.layers import DatabaseLayer |
| 1444 | + >>> DatabaseLayer.force_dirty_database() |
| 1445 | |
| 1446 | === modified file 'lib/lp/translations/doc/message-sharing-merge-script.txt' |
| 1447 | --- lib/lp/translations/doc/message-sharing-merge-script.txt 2009-08-04 13:37:57 +0000 |
| 1448 | +++ lib/lp/translations/doc/message-sharing-merge-script.txt 2010-10-17 19:12:52 +0000 |
| 1449 | @@ -20,5 +20,5 @@ |
| 1450 | # The script modified the database, even though the database layer may |
| 1451 | # not have noticed it. |
| 1452 | |
| 1453 | - >>> from canonical.launchpad.ftests.harness import LaunchpadTestSetup |
| 1454 | - >>> LaunchpadTestSetup().force_dirty_database() |
| 1455 | + >>> from canonical.testing.layers import DatabaseLayer |
| 1456 | + >>> DatabaseLayer.force_dirty_database() |
| 1457 | |
| 1458 | === modified file 'lib/lp/translations/doc/request_country.txt' |
| 1459 | --- lib/lp/translations/doc/request_country.txt 2010-02-26 21:58:15 +0000 |
| 1460 | +++ lib/lp/translations/doc/request_country.txt 2010-10-17 19:12:52 +0000 |
| 1461 | @@ -4,10 +4,6 @@ |
| 1462 | |
| 1463 | Adapting a request to a country allows you to see where the request came from. |
| 1464 | |
| 1465 | - >>> from canonical.launchpad.ftests.harness import ( |
| 1466 | - ... LaunchpadFunctionalTestSetup) |
| 1467 | - >>> LaunchpadFunctionalTestSetup().setUp() |
| 1468 | - |
| 1469 | Here's a dummy request. Zope adds the REMOTE_ADDR CGI environment variable |
| 1470 | for us. Upstream proxy servers (and tinkering users!) may also add |
| 1471 | X-Forwarded-For: headers. The X-Forwarded-For: header takes precidence |
| 1472 | @@ -34,6 +30,3 @@ |
| 1473 | Traceback (most recent call last): |
| 1474 | ... |
| 1475 | TypeError: ('Could not adapt', ... |
| 1476 | - |
| 1477 | - >>> LaunchpadFunctionalTestSetup().tearDown() |
| 1478 | - |

On Sun, Oct 17, 2010 at 7:59 AM, Robert Collins
<email address hidden> wrote:
> Robert Collins has proposed merging lp:~lifeless/launchpad/databasefixture into lp:launchpad/devel.
>
> Requested reviews:
> Jonathan Lange (jml)
>
>
> This makes the database test support layer ready for parallel testing - we can run with unique database names. Not supported yet is having the config system unique for a test process (I'd make it more granular than that, but zcml is so global that I don't think thats feasible until we reengineer that entire stack.
>
> Some things were no longer used, so I deleted them. Most things were trivial.
>
> We won't see any test DB leaks at this point, but I'm worried about the possibility when we turn this on... still thats a problem for another day.
As you said on IRC, it's a mostly mechanical branch. The changes all
look good to me, and definitely in the right direction.
Land when ready.
jml