← Back to team overview

launchpad-reviewers team mailing list archive

[Merge] lp:~lifeless/launchpad/test into lp:launchpad/devel

 

Robert Collins has proposed merging lp:~lifeless/launchpad/test into lp:launchpad/devel.

Requested reviews:
  Launchpad code reviewers (launchpad-reviewers)


Start to consolidate and rationalise the Librarian test helper support code. Functionally similar, but with less duplicated code and somewhat clearer handling of both existing services and services that don't shut down.
-- 
https://code.launchpad.net/~lifeless/launchpad/test/+merge/36674
Your team Launchpad code reviewers is requested to review the proposed merge of lp:~lifeless/launchpad/test into lp:launchpad/devel.
=== modified file 'lib/canonical/buildd/tests/harness.py'
--- lib/canonical/buildd/tests/harness.py	2010-02-19 20:17:08 +0000
+++ lib/canonical/buildd/tests/harness.py	2010-09-27 01:17:48 +0000
@@ -109,11 +109,7 @@
         # When we are about running it seriously we need :
         # * install sbuild package
         # * to copy the scripts for sbuild
-
-    def tearDown(self):
-        """Tear down the system normally and additionaly remove the root."""
-        TacTestSetup.tearDown(self)
-        remove_tree(self.root)
+        self.addCleanup(remove_tree, self.root)
 
     @property
     def root(self):

=== modified file 'lib/canonical/launchpad/daemons/tachandler.py'
--- lib/canonical/launchpad/daemons/tachandler.py	2010-08-20 20:31:18 +0000
+++ lib/canonical/launchpad/daemons/tachandler.py	2010-09-27 01:17:48 +0000
@@ -27,12 +27,24 @@
 import sys
 import time
 
+from fixtures import Fixture
 from twisted.application import service
 from twisted.python import log
 
 # This file is used by launchpad-buildd, so it cannot import any
 # Launchpad code!
 
+def _kill_may_race(pid, signal_number):
+    """Kill a pid accepting that it may not exist."""
+    try:
+        os.kill(pid, signal_number)
+    except OSError, e:
+        if e.errno in (errno.ESRCH, errno.ECHILD):
+            # Process has already been killed.
+            return
+        # Some other issue (e.g. different user owns it)
+        raise
+
 def two_stage_kill(pid, poll_interval=0.1, num_polls=50):
     """Kill process 'pid' with SIGTERM. If it doesn't die, SIGKILL it.
 
@@ -42,12 +54,7 @@
     :param num_polls: The number of polls to do before doing a SIGKILL.
     """
     # Kill the process.
-    try:
-        os.kill(pid, SIGTERM)
-    except OSError, e:
-        if e.errno in (errno.ESRCH, errno.ECHILD):
-            # Process has already been killed.
-            return
+    _kill_may_race(pid, SIGTERM)
 
     # Poll until the process has ended.
     for i in range(num_polls):
@@ -65,11 +72,7 @@
                 return
 
     # The process is still around, so terminate it violently.
-    try:
-        os.kill(pid, SIGKILL)
-    except OSError:
-        # Already terminated
-        pass
+    _kill_may_race(pid, SIGKILL)
 
 
 def get_pid_from_file(pidfile_path):
@@ -120,18 +123,26 @@
     """Error raised by TacTestSetup."""
 
 
-class TacTestSetup:
+class TacTestSetup(Fixture):
     """Setup an TAC file as daemon for use by functional tests.
 
-    You can override setUpRoot to set up a root directory for the daemon.
+    You must override setUpRoot to set up a root directory for the daemon.
     """
 
     def setUp(self, spew=False, umask=None):
-        # Before we run, we want to make sure that we have cleaned up any
-        # previous runs. Although tearDown() should have been called already,
-        # we can't guarantee it.
-        self.tearDown()
+        Fixture.setUp(self)
+        if get_pid_from_file(self.pidfile):
+            # An attempt to run while there was an existing live helper
+            # was made.
+            pid = get_pid_from_file(self.pidfile)
+            warnings.warn("Attempt to start Tachandler with an existing "
+                "instance (%d) running in %s." % (pid, self.pidfile),
+                DeprecationWarning, stacklevel=2)
+            two_stage_kill(pid)
+            if get_pid_from_file(self.pidfile):
+                raise Exception("Could not kill stale process.")
 
+        Fixture.setUp(self)
         # setUp() watches the logfile to determine when the daemon has fully
         # started. If it sees an old logfile, then it will find the LOG_MAGIC
         # string and return immediately, provoking hard-to-diagnose race
@@ -156,6 +167,7 @@
         # stdout/stderr are written to.
         proc = subprocess.Popen(args, stdout=subprocess.PIPE,
                                 stderr=subprocess.STDOUT)
+        self.addCleanup(self.killTac)
         # XXX: JonathanLange 2008-03-19: This can raise EINTR. We should
         # really catch it and try again if that happens.
         stdout = proc.stdout.read()
@@ -201,7 +213,8 @@
                 self.tacfile, self.logfile, open(self.logfile).read()))
 
     def tearDown(self):
-        self.killTac()
+        # For compatibility - migrate to cleanUp.
+        self.cleanUp()
 
     def killTac(self):
         """Kill the TAC file if it is running."""

=== modified file 'lib/canonical/launchpad/doc/old-testing.txt'
--- lib/canonical/launchpad/doc/old-testing.txt	2009-08-13 15:12:16 +0000
+++ lib/canonical/launchpad/doc/old-testing.txt	2010-09-27 01:17:48 +0000
@@ -253,7 +253,7 @@
 You probably really want LaunchpadFunctionalTestSetup so you can access
 the Librarian as a Utility.
 
->>> from canonical.librarian.ftests.harness import LibrarianTestSetup
+>>> from canonical.librarian.testing.server import LibrarianTestSetup
 >>> from canonical.launchpad.ftests import login, ANONYMOUS
 >>> from zope.app import zapi
 >>> from canonical.librarian.interfaces import ILibrarianClient

=== modified file 'lib/canonical/launchpad/scripts/tests/test_hwdb_submission_processing.py'
--- lib/canonical/launchpad/scripts/tests/test_hwdb_submission_processing.py	2010-08-20 20:31:18 +0000
+++ lib/canonical/launchpad/scripts/tests/test_hwdb_submission_processing.py	2010-09-27 01:17:48 +0000
@@ -18,7 +18,7 @@
 from canonical.config import config
 from canonical.launchpad.interfaces.launchpad import ILaunchpadCelebrities
 from canonical.launchpad.webapp.errorlog import ErrorReportingUtility
-from canonical.librarian.ftests.harness import fillLibrarianFile
+from canonical.librarian.testing.server import fillLibrarianFile
 from canonical.librarian.interfaces import LibrarianServerError
 from canonical.testing import (
     BaseLayer,

=== removed file 'lib/canonical/librarian/ftests/test_harness.py'
--- lib/canonical/librarian/ftests/test_harness.py	2010-07-14 14:11:15 +0000
+++ lib/canonical/librarian/ftests/test_harness.py	1970-01-01 00:00:00 +0000
@@ -1,13 +0,0 @@
-# Copyright 2009 Canonical Ltd.  This software is licensed under the
-# GNU Affero General Public License version 3 (see the file LICENSE).
-
-__metaclass__ = type
-
-import doctest
-
-def test_suite():
-    return doctest.DocTestSuite(
-            'canonical.librarian.ftests.harness',
-            optionflags=doctest.NORMALIZE_WHITESPACE | doctest.ELLIPSIS
-            )
-

=== modified file 'lib/canonical/librarian/librariangc.py'
--- lib/canonical/librarian/librariangc.py	2010-04-23 01:48:11 +0000
+++ lib/canonical/librarian/librariangc.py	2010-09-27 01:17:48 +0000
@@ -632,8 +632,9 @@
             dirnames.remove('incoming')
         if 'lost+found' in dirnames:
             dirnames.remove('lost+found')
-        if 'librarian.pid' in filenames:
-            filenames.remove('librarian.pid')
+        filenames = set(filenames)
+        filenames.discard('librarian.pid')
+        filenames.discard('librarian.log')
 
         for dirname in dirnames[:]:
             if len(dirname) != 2:
@@ -650,7 +651,7 @@
         # We need everything in order to ensure we visit files in the
         # same order we retrieve wanted files from the database.
         dirnames.sort()
-        filenames.sort()
+        filenames = sorted(filenames)
 
         # Noise in the storage area, or maybe we are looking at the wrong
         # path?

=== added directory 'lib/canonical/librarian/testing'
=== added file 'lib/canonical/librarian/testing/__init__.py'
--- lib/canonical/librarian/testing/__init__.py	1970-01-01 00:00:00 +0000
+++ lib/canonical/librarian/testing/__init__.py	2010-09-27 01:17:48 +0000
@@ -0,0 +1,6 @@
+# Copyright 2010 Canonical Ltd.  This software is licensed under the
+# GNU Affero General Public License version 3 (see the file LICENSE).
+
+"""Test support for working with the librarian."""
+
+__metaclass__ = type

=== renamed file 'lib/lp/testing/fakelibrarian.py' => 'lib/canonical/librarian/testing/fake.py'
=== renamed file 'lib/canonical/librarian/ftests/harness.py' => 'lib/canonical/librarian/testing/server.py'
--- lib/canonical/librarian/ftests/harness.py	2010-02-02 16:52:46 +0000
+++ lib/canonical/librarian/testing/server.py	2010-09-27 01:17:48 +0000
@@ -7,21 +7,30 @@
 __all__ = [
     'cleanupLibrarianFiles',
     'fillLibrarianFile',
+    'LibrarianServerFixture',
     'LibrarianTestSetup',
     ]
 
 import os
 import shutil
 import tempfile
+import warnings
+
+from fixtures import Fixture
 
 import canonical
 from canonical.config import config
-from canonical.launchpad.daemons.tachandler import TacException, TacTestSetup
+from canonical.launchpad.daemons.tachandler import (
+    get_pid_from_file,
+    TacException,
+    TacTestSetup,
+    two_stage_kill,
+    )
 from canonical.librarian.storage import _relFileLocation
 
 
-class LibrarianTestSetup:
-    """Set up librarian servers for use by functional tests.
+class LibrarianServerFixture(TacTestSetup):
+    """Librarian server fixture.
 
     >>> from urllib import urlopen
     >>> from canonical.config import config
@@ -33,7 +42,8 @@
     ...     config.librarian.restricted_download_host,
     ...     config.librarian.restricted_download_port)
 
-    >>> LibrarianTestSetup().setUp()
+    >>> fixture = LibrarianServerFixture()
+    >>> fixture.setUp()
 
     Set a socket timeout, so that this test cannot hang indefinitely.
 
@@ -42,8 +52,8 @@
     None
     >>> socket.setdefaulttimeout(1)
 
-    After setUp() is called, two librarian instances are started. The
-    regular one:
+    After setUp() is called, two librarian ports are available:
+    The regular one:
 
     >>> 'Copyright' in urlopen(librarian_url).read()
     True
@@ -59,9 +69,9 @@
     >>> os.path.isdir(config.librarian_server.root)
     True
 
-    After tearDown() is called, both instances are shut down:
+    After tearDown() is called, both ports are closed:
 
-    >>> LibrarianTestSetup().tearDown()
+    >>> fixture.tearDown()
 
     >>> urlopen(librarian_url).read()
     Traceback (most recent call last):
@@ -80,39 +90,72 @@
 
     That fixture can be started and stopped multiple time in succession:
 
-    >>> LibrarianTestSetup().setUp()
+    >>> fixture.setUp()
     >>> 'Copyright' in urlopen(librarian_url).read()
     True
 
     Tidy up.
 
-    >>> LibrarianTestSetup().tearDown()
+    >>> fixture.tearDown()
     >>> socket.setdefaulttimeout(None)
+
+    :ivar: pid pid of the external process.
     """
 
+    def __init__(self):
+        Fixture.__init__(self)
+        self._pid = None
+        self._setup = False
+        """Track whether the fixture has been setup or not."""
+
     def setUp(self):
         """Start both librarian instances."""
-        if (os.environ.get('LP_PERSISTENT_TEST_SERVICES') is not None and
-            os.path.exists(TacLibrarianTestSetup().pidfile)):
+        if (self._persistent_servers() and self.pid):
             return
-        self.setUpRoot()
+        else:
+            # self.pid may have been evaluated - nuke it.
+            self._pid = None
+        # The try:except here can be removed if someone audits the callers.
         try:
-            TacLibrarianTestSetup().setUp()
+            TacTestSetup.setUp(self)
         except TacException:
-            # Remove the directory usually removed in tearDown.
-            self.tearDownRoot()
+            self.cleanUp()
             raise
+        else:
+            self._pid = self._read_pid()
+        self._setup = True
+        self.addCleanup(setattr, self, '_setup', False)
 
-    def tearDown(self):
+    def cleanUp(self):
         """Shut downs both librarian instances."""
-        if os.environ.get('LP_PERSISTENT_TEST_SERVICES') is not None:
-            return
-        TacLibrarianTestSetup().tearDown()
-        self.tearDownRoot()
+        if self._persistent_servers():
+            return
+        if not self._setup:
+            warnings.warn("Attempt to tearDown inactive fixture.",
+                DeprecationWarning, stacklevel=3)
+            return
+        TacTestSetup.cleanUp(self)
 
     def clear(self):
         """Clear all files from the Librarian"""
-        cleanupLibrarianFiles()
+        # Make this smarter if our tests create huge numbers of files
+        root = config.librarian_server.root
+        if os.path.isdir(os.path.join(root, '00')):
+            shutil.rmtree(os.path.join(root, '00'))
+
+    @property
+    def pid(self):
+        if self._pid:
+            return self._pid
+        if self._persistent_servers():
+            self._pid = self._read_pid()
+        return self._pid
+
+    def _read_pid(self):
+        return get_pid_from_file(self.pidfile)
+
+    def _persistent_servers(self):
+        return os.environ.get('LP_PERSISTENT_TEST_SERVICES') is not None
 
     @property
     def root(self):
@@ -126,26 +169,13 @@
         if os.path.exists(self.root):
             self.tearDownRoot()
         os.makedirs(self.root, 0700)
+        self.addCleanup(self.tearDownRoot)
 
     def tearDownRoot(self):
         """Remove the librarian root archive."""
         if os.path.isdir(self.root):
             shutil.rmtree(self.root)
 
-
-class TacLibrarianTestSetup(TacTestSetup):
-    """Start the regular librarian instance."""
-
-    def setUpRoot(self):
-        """Taken care by LibrarianTestSetup."""
-
-    def tearDownRoot(self):
-        """Taken care by LibrarianTestSetup."""
-
-    @property
-    def root(self):
-        return config.librarian_server.root
-
     @property
     def tacfile(self):
         return os.path.abspath(os.path.join(
@@ -158,20 +188,18 @@
         return os.path.join(self.root, 'librarian.pid')
 
     @property
-    def _log_directory(self):
-        # Since the root gets deleted after the tests complete, and since we
-        # may still want to access the log file for post-mortem debugging, put
-        # the log file in the parent directory of root, or the temporary
-        # directory if that doesn't exist.
-        log_directory = os.path.dirname(self.root)
-        if os.path.isdir(log_directory):
-            return log_directory
-        else:
-            return tempfile.tempdir
-
-    @property
     def logfile(self):
-        return os.path.join(self._log_directory, 'librarian.log')
+        # Store the log in the server root; if its wanted after a test, that
+        # test can use addDetail to grab the log and include it in its 
+        # error.
+        return os.path.join(self.root, 'librarian.log')
+
+
+_global_fixture = LibrarianServerFixture()
+
+def LibrarianTestSetup():
+    """Support the stateless lie."""
+    return _global_fixture
 
 
 def fillLibrarianFile(fileid, content='Fake Content'):
@@ -186,9 +214,7 @@
     libfile.write(content)
     libfile.close()
 
+
 def cleanupLibrarianFiles():
     """Remove all librarian files present in disk."""
-    # Make this smarter if our tests create huge numbers of files
-    root = config.librarian_server.root
-    if os.path.isdir(os.path.join(root, '00')):
-        shutil.rmtree(os.path.join(root, '00'))
+    _global_fixture.clear()

=== added directory 'lib/canonical/librarian/testing/tests'
=== added file 'lib/canonical/librarian/testing/tests/__init__.py'
--- lib/canonical/librarian/testing/tests/__init__.py	1970-01-01 00:00:00 +0000
+++ lib/canonical/librarian/testing/tests/__init__.py	2010-09-27 01:17:48 +0000
@@ -0,0 +1,6 @@
+# Copyright 2010 Canonical Ltd.  This software is licensed under the
+# GNU Affero General Public License version 3 (see the file LICENSE).
+
+"""Tests of the librarian test support code."""
+
+__metaclass__ = type

=== renamed file 'lib/lp/testing/tests/test_fakelibrarian.py' => 'lib/canonical/librarian/testing/tests/test_fakelibrarian.py'
--- lib/lp/testing/tests/test_fakelibrarian.py	2010-09-18 09:14:59 +0000
+++ lib/canonical/librarian/testing/tests/test_fakelibrarian.py	2010-09-27 01:17:48 +0000
@@ -17,6 +17,7 @@
     LibraryFileAliasSet,
     )
 from canonical.launchpad.interfaces.librarian import ILibraryFileAliasSet
+from canonical.librarian.testing.fake import FakeLibrarian
 from canonical.launchpad.webapp.testing import verifyObject
 from canonical.librarian.client import LibrarianClient
 from canonical.librarian.interfaces import ILibrarianClient
@@ -25,7 +26,6 @@
     LaunchpadFunctionalLayer,
     )
 from lp.testing import TestCaseWithFactory
-from lp.testing.fakelibrarian import FakeLibrarian
 
 
 class LibraryAccessScenarioMixin:

=== added file 'lib/canonical/librarian/testing/tests/test_server_fixture.py'
--- lib/canonical/librarian/testing/tests/test_server_fixture.py	1970-01-01 00:00:00 +0000
+++ lib/canonical/librarian/testing/tests/test_server_fixture.py	2010-09-27 01:17:48 +0000
@@ -0,0 +1,29 @@
+# Copyright 2010 Canonical Ltd.  This software is licensed under the
+# GNU Affero General Public License version 3 (see the file LICENSE).
+
+"""Test the LibrarianServerFixture."""
+
+__metaclass__ = type
+
+import doctest
+import unittest
+
+from canonical.librarian.testing.server import LibrarianServerFixture
+from lp.testing import TestCase
+
+def test_suite():
+    result = unittest.TestLoader().loadTestsFromName(__name__)
+    result.addTest(doctest.DocTestSuite(
+            'canonical.librarian.testing.server',
+            optionflags=doctest.NORMALIZE_WHITESPACE | doctest.ELLIPSIS
+            ))
+    return result
+
+
+class TestLibrarianServerFixture(TestCase):
+
+    def test_on_init_no_pid(self):
+        fixture = LibrarianServerFixture()
+        if fixture._persistent_servers():
+            self.skip('persistent server running.')
+        self.assertEqual(None, fixture.pid)

=== modified file 'lib/canonical/librarian/tests/test_sigdumpmem.py'
--- lib/canonical/librarian/tests/test_sigdumpmem.py	2010-04-20 14:27:26 +0000
+++ lib/canonical/librarian/tests/test_sigdumpmem.py	2010-09-27 01:17:48 +0000
@@ -9,7 +9,7 @@
 import time
 
 from canonical.librarian.interfaces import DUMP_FILE, SIGDUMPMEM
-from canonical.librarian.ftests.harness import TacLibrarianTestSetup
+from canonical.librarian.testing.server import LibrarianTestSetup
 from canonical.testing.layers import LibrarianLayer
 from lp.testing import TestCase
 
@@ -23,9 +23,10 @@
             os.unlink(DUMP_FILE)
         self.assertFalse(os.path.exists(DUMP_FILE))
 
-        # We rely on the fact that the librarian was started by the test
-        # runner here as we use the LibrarianLayer.
-        pid = int(open(TacLibrarianTestSetup().pidfile).read())
+        # Use the global instance used by the Layer machinery; it would
+        # be nice to be able to access those without globals / magical
+        # 'constructors'.
+        pid = LibrarianTestSetup().pid
 
         # Send the signal and ensure the dump file is created.
         os.kill(pid, SIGDUMPMEM)

=== modified file 'lib/canonical/testing/layers.py'
--- lib/canonical/testing/layers.py	2010-09-12 11:43:36 +0000
+++ lib/canonical/testing/layers.py	2010-09-27 01:17:48 +0000
@@ -116,7 +116,7 @@
 from canonical.lazr.timeout import (
     get_default_timeout_function, set_default_timeout_function)
 from canonical.lp import initZopeless
-from canonical.librarian.ftests.harness import LibrarianTestSetup
+from canonical.librarian.testing.server import LibrarianTestSetup
 from canonical.testing import reset_logging
 from canonical.testing.profiled import profiled
 from canonical.testing.smtpd import SMTPController
@@ -262,7 +262,6 @@
         # about killing memcached - just do it quickly.
         if not BaseLayer.persist_test_services:
             kill_by_pidfile(MemcachedLayer.getPidFile(), num_polls=0)
-            LibrarianTestSetup().tearDown()
         # Kill any database left lying around from a previous test run.
         try:
             DatabaseLayer.connect().close()

=== modified file 'lib/lp/archiveuploader/tests/__init__.py'
--- lib/lp/archiveuploader/tests/__init__.py	2010-09-16 12:27:46 +0000
+++ lib/lp/archiveuploader/tests/__init__.py	2010-09-27 01:17:48 +0000
@@ -22,7 +22,7 @@
 
 from zope.component import getGlobalSiteManager
 
-from canonical.librarian.ftests.harness import fillLibrarianFile
+from canonical.librarian.testing.server import fillLibrarianFile
 from lp.archiveuploader.uploadpolicy import (
     AbstractUploadPolicy,
     findPolicyByName,

=== modified file 'lib/lp/hardwaredb/doc/hwdb-submission.txt'
--- lib/lp/hardwaredb/doc/hwdb-submission.txt	2010-04-01 04:05:10 +0000
+++ lib/lp/hardwaredb/doc/hwdb-submission.txt	2010-09-27 01:17:48 +0000
@@ -253,7 +253,7 @@
 let's add one. Let's add invalid data in order to see how invalid
 submissions are processed.
 
-    >>> from canonical.librarian.ftests.harness import fillLibrarianFile
+    >>> from canonical.librarian.testing.server import fillLibrarianFile
     >>> submission = submission_set.getBySubmissionKey('test_submission_id_1')
     >>> fillLibrarianFile(
     ...     submission.raw_submission.id, 'nonsense')

=== modified file 'lib/lp/soyuz/browser/tests/distroseriesqueue-views.txt'
--- lib/lp/soyuz/browser/tests/distroseriesqueue-views.txt	2010-08-26 15:28:34 +0000
+++ lib/lp/soyuz/browser/tests/distroseriesqueue-views.txt	2010-09-27 01:17:48 +0000
@@ -294,5 +294,5 @@
 
 We created librarian files that need cleaning up before leaving the test.
 
-  >>> from canonical.librarian.ftests.harness import cleanupLibrarianFiles
+  >>> from canonical.librarian.testing.server import cleanupLibrarianFiles
   >>> cleanupLibrarianFiles()

=== modified file 'lib/lp/soyuz/doc/distroseriesqueue-notify.txt'
--- lib/lp/soyuz/doc/distroseriesqueue-notify.txt	2009-05-13 14:05:27 +0000
+++ lib/lp/soyuz/doc/distroseriesqueue-notify.txt	2010-09-27 01:17:48 +0000
@@ -179,7 +179,7 @@
 provided that everything is already committed to the database (which is not
 the case when nascent upload runs).  This example demonstrates this usage:
 
-  >>> from canonical.librarian.ftests.harness import fillLibrarianFile
+  >>> from canonical.librarian.testing.server import fillLibrarianFile
   >>> changes_file = open(changes_file_path,"r")
   >>> fillLibrarianFile(1, content=changes_file.read())
   >>> changes_file.close()
@@ -259,5 +259,5 @@
 
 Clean up, otherwise stuff is left lying around in /var/tmp.
 
-  >>> from canonical.librarian.ftests.harness import cleanupLibrarianFiles
+  >>> from canonical.librarian.testing.server import cleanupLibrarianFiles
   >>> cleanupLibrarianFiles()

=== modified file 'lib/lp/soyuz/doc/distroseriesqueue.txt'
--- lib/lp/soyuz/doc/distroseriesqueue.txt	2010-08-25 11:01:59 +0000
+++ lib/lp/soyuz/doc/distroseriesqueue.txt	2010-09-27 01:17:48 +0000
@@ -1026,7 +1026,7 @@
 
 Clean up the librarian files:
 
-   >>> from canonical.librarian.ftests.harness import cleanupLibrarianFiles
+   >>> from canonical.librarian.testing.server import cleanupLibrarianFiles
    >>> cleanupLibrarianFiles()
 
 

=== modified file 'lib/lp/soyuz/doc/package-diff.txt'
--- lib/lp/soyuz/doc/package-diff.txt	2010-08-25 11:01:59 +0000
+++ lib/lp/soyuz/doc/package-diff.txt	2010-09-27 01:17:48 +0000
@@ -643,7 +643,7 @@
     ...     '1.0-2', archive=foobar.archive)
     >>> transaction.commit()
 
-    >>> from canonical.librarian.ftests.harness import fillLibrarianFile
+    >>> from canonical.librarian.testing.server import fillLibrarianFile
     >>> [orig, upload_diff, dsc] = pub.sourcepackagerelease.files
     >>> fillLibrarianFile(dsc.libraryfile.id)
 

=== modified file 'lib/lp/soyuz/doc/soyuz-set-of-uploads.txt'
--- lib/lp/soyuz/doc/soyuz-set-of-uploads.txt	2010-09-16 12:27:46 +0000
+++ lib/lp/soyuz/doc/soyuz-set-of-uploads.txt	2010-09-27 01:17:48 +0000
@@ -90,7 +90,7 @@
 
 Add disk content for file inherited from ubuntu/breezy-autotest:
 
-  >>> from canonical.librarian.ftests.harness import fillLibrarianFile
+  >>> from canonical.librarian.testing.server import fillLibrarianFile
   >>> fillLibrarianFile(54)
 
 Now that the infrastructure is ready, we prepare a set of useful methods.

=== modified file 'lib/lp/soyuz/scripts/tests/test_copypackage.py'
--- lib/lp/soyuz/scripts/tests/test_copypackage.py	2010-08-27 11:19:54 +0000
+++ lib/lp/soyuz/scripts/tests/test_copypackage.py	2010-09-27 01:17:48 +0000
@@ -17,7 +17,7 @@
 from canonical.config import config
 from canonical.launchpad.interfaces.librarian import ILibraryFileAliasSet
 from canonical.launchpad.scripts import BufferLogger
-from canonical.librarian.ftests.harness import fillLibrarianFile
+from canonical.librarian.testing.server import fillLibrarianFile
 from canonical.testing import (
     DatabaseLayer,
     LaunchpadFunctionalLayer,

=== modified file 'lib/lp/soyuz/scripts/tests/test_queue.py'
--- lib/lp/soyuz/scripts/tests/test_queue.py	2010-09-16 09:19:15 +0000
+++ lib/lp/soyuz/scripts/tests/test_queue.py	2010-09-27 01:17:48 +0000
@@ -32,7 +32,7 @@
     PackageUploadBuild,
     )
 from canonical.launchpad.interfaces.librarian import ILibraryFileAliasSet
-from canonical.librarian.ftests.harness import (
+from canonical.librarian.testing.server import (
     cleanupLibrarianFiles,
     fillLibrarianFile,
     )

=== modified file 'lib/lp/soyuz/scripts/tests/test_sync_source.py'
--- lib/lp/soyuz/scripts/tests/test_sync_source.py	2010-09-16 19:00:35 +0000
+++ lib/lp/soyuz/scripts/tests/test_sync_source.py	2010-09-27 01:17:48 +0000
@@ -24,7 +24,7 @@
 
 from canonical.config import config
 from canonical.launchpad.scripts import BufferLogger
-from canonical.librarian.ftests.harness import (
+from canonical.librarian.testing.server import (
     cleanupLibrarianFiles,
     fillLibrarianFile,
     )

=== modified file 'lib/lp/soyuz/stories/soyuz/xx-queue-pages.txt'
--- lib/lp/soyuz/stories/soyuz/xx-queue-pages.txt	2010-08-24 12:05:25 +0000
+++ lib/lp/soyuz/stories/soyuz/xx-queue-pages.txt	2010-09-27 01:17:48 +0000
@@ -574,6 +574,6 @@
 
 == Clean up ==
 
-  >>> from canonical.librarian.ftests.harness import cleanupLibrarianFiles
+  >>> from canonical.librarian.testing.server import cleanupLibrarianFiles
   >>> cleanupLibrarianFiles()
 

=== added file 'lib/lp/testing/fakelibrarian.py'
--- lib/lp/testing/fakelibrarian.py	1970-01-01 00:00:00 +0000
+++ lib/lp/testing/fakelibrarian.py	2010-09-27 01:17:48 +0000
@@ -0,0 +1,16 @@
+# Copyright 2010 Canonical Ltd.  This software is licensed under the
+# GNU Affero General Public License version 3 (see the file LICENSE).
+
+"""See canonical.librarian.testing.fake."""
+
+import warnings
+
+from canonical.librarian.testing.fake import FakeLibrarian as _FakeLibrarian
+
+
+def FakeLibrarian(*args, **kwargs):
+    """Forward to the new home with a deprecation warning."""
+    warnings.warn("Stale import: please import FakeLibrarian from "
+        "canonical.librarian.testing.fake instead.", DeprecationWarning,
+        stacklevel=2)
+    return _FakeLibrarian(*args, **kwargs)

=== modified file 'lib/lp/translations/utilities/tests/test_file_importer.py'
--- lib/lp/translations/utilities/tests/test_file_importer.py	2010-09-18 09:14:59 +0000
+++ lib/lp/translations/utilities/tests/test_file_importer.py	2010-09-27 01:17:48 +0000
@@ -8,10 +8,10 @@
 from zope.component import getUtility
 from zope.security.proxy import removeSecurityProxy
 
+from canonical.librarian.testing.fake import FakeLibrarian
 from canonical.testing import ZopelessDatabaseLayer
 from lp.registry.interfaces.person import IPersonSet
 from lp.testing import TestCaseWithFactory
-from lp.testing.fakelibrarian import FakeLibrarian
 from lp.translations.interfaces.translationgroup import TranslationPermission
 from lp.translations.interfaces.translationimporter import (
     OutdatedTranslationError,