Ejemplo n.º 1
0
    def generate(cls, directory, control_hostname, num_nodes, cluster_id=None):
        """
        Generate certificates in the given directory.

        :param FilePath directory: Directory to use for certificate authority.
        :param bytes control_hostname: The hostname of the control service.
        :param int num_nodes: Number of nodes in the cluster.
        :param UUID cluster_id: The unique identifier of the cluster for which
            the certificates are being generated.  If not given, a random
            identifier will be generated.

        :return: ``Certificates`` instance.
        """
        RootCredential.initialize(
            directory, b"acceptance-cluster", cluster_id=cluster_id,
        )

        def run(*arguments):
            check_call([b"flocker-ca"] + list(arguments), cwd=directory.path)

        run(b"create-control-certificate", control_hostname)
        run(b"create-api-certificate", b"allison")
        # Rename to user.crt/user.key so we can use this folder directly
        # from flocker-deploy and other clients:
        directory.child(b"allison.crt").moveTo(directory.child(b"user.crt"))
        directory.child(b"allison.key").moveTo(directory.child(b"user.key"))
        for i in range(num_nodes):
            run(b"create-node-certificate")
        for i, child in enumerate(
                directory.globChildren(b"????????-????-*.crt")):
            sibling = FilePath(child.path[:-3] + b"key")
            child.moveTo(directory.child(b"node-%d.crt" % (i,)))
            sibling.moveTo(directory.child(b"node-%d.key" % (i,)))
        return cls(directory)
Ejemplo n.º 2
0
    def generate(cls, directory, control_hostname, num_nodes):
        """
        Generate certificates in the given directory.

        :param FilePath directory: Directory to use for ceritificate authority.
        :param bytes control_hostname: The hostname of the control service.
        :param int num_nodes: Number of nodes in the cluster.

        :return: ``Certificates`` instance.
        """
        def run(*arguments):
            check_call([b"flocker-ca"] + list(arguments), cwd=directory.path)
        run(b"initialize", b"acceptance-cluster")
        run(b"create-control-certificate", control_hostname)
        run(b"create-api-certificate", b"allison")
        # Rename to user.crt/user.key so we can use this folder directly
        # from flocker-deploy and other clients:
        directory.child(b"allison.crt").moveTo(directory.child(b"user.crt"))
        directory.child(b"allison.key").moveTo(directory.child(b"user.key"))
        for i in range(num_nodes):
            run(b"create-node-certificate")
        for i, child in enumerate(
                directory.globChildren(b"????????-????-*.crt")):
            sibling = FilePath(child.path[:-3] + b"key")
            child.moveTo(directory.child(b"node-%di.crt" % i))
            sibling.moveTo(directory.child(b"node-%di.key" % i))
        return cls(directory)
Ejemplo n.º 3
0
    def _backup_pcap(self, username, ip_addr):
        """
        Backup existing pcap file. Used when restarting traffic capture for
        ACTIVE accounts and after the account expires.

        :param username (str): account username
        :param ip_addr (IPv4Address): IP address allocated for the account.

        """
        log.debug("ACCOUNTS:: Backing up pcap for {} with IP {}.".format(
            username, str(ip_addr)))

        day_month_str = datetime.now().strftime("%m%d%H%M")
        cur_pcap_file = "{}_{}.pcap".format(username, str(ip_addr))
        new_path = os.path.join(self.path['pcaps'],
                                "{}_{}".format(username, str(ip_addr)))
        new_path_fp = FilePath(new_path)
        if not new_path_fp.isdir():
            log.debug("ACCOUNTS:: Creating directory {}".format(new_path))
            new_path_fp.createDirectory()

        new_pcap_file = "{}_{}_{}.pcap".format(username, str(ip_addr),
                                               day_month_str)
        cur_pcap_file = os.path.join(self.path['pcaps'], cur_pcap_file)
        new_pcap_file = os.path.join(new_path, new_pcap_file)
        log.debug("ACCOUNTS:: Current pcap file {}".format(cur_pcap_file))
        log.debug("ACCOUNTS:: New pcap file {}".format(new_pcap_file))

        fp = FilePath(cur_pcap_file)
        backup_fp = FilePath(new_pcap_file)
        fp.moveTo(backup_fp)
        backup_fp.chmod(0654)
Ejemplo n.º 4
0
    def test_renamedSource(self):
        """
        Warnings emitted by a function defined in a file which has been renamed
        since it was initially compiled can still be flushed.

        This is testing the code which specifically supports working around the
        unfortunate behavior of CPython to write a .py source file name into
        the .pyc files it generates and then trust that it is correct in
        various places.  If source files are renamed, .pyc files may not be
        regenerated, but they will contain incorrect filenames.
        """
        package = FilePath(self.mktemp().encode("utf-8")).child(
            b"twisted_private_helper"
        )
        package.makedirs()
        package.child(b"__init__.py").setContent(b"")
        package.child(b"module.py").setContent(
            b"""
import warnings
def foo():
    warnings.warn("oh no")
"""
        )
        pathEntry = package.parent().path.decode("utf-8")
        sys.path.insert(0, pathEntry)
        self.addCleanup(sys.path.remove, pathEntry)

        # Import it to cause pycs to be generated
        from twisted_private_helper import module

        # Clean up the state resulting from that import; we're not going to use
        # this module, so it should go away.
        del sys.modules["twisted_private_helper"]
        del sys.modules[module.__name__]

        # Some Python versions have extra state related to the just
        # imported/renamed package.  Clean it up too.  See also
        # http://bugs.python.org/issue15912
        try:
            from importlib import invalidate_caches
        except ImportError:
            pass
        else:
            invalidate_caches()

        # Rename the source directory
        package.moveTo(package.sibling(b"twisted_renamed_helper"))

        # Import the newly renamed version
        from twisted_renamed_helper import module  # type: ignore[import]

        self.addCleanup(sys.modules.pop, "twisted_renamed_helper")
        self.addCleanup(sys.modules.pop, module.__name__)

        # Generate the warning
        module.foo()

        # Flush it
        self.assertEqual(len(self.flushWarnings([module.foo])), 1)
    def test_renamedSource(self):
        """
        Warnings emitted by a function defined in a file which has been renamed
        since it was initially compiled can still be flushed.

        This is testing the code which specifically supports working around the
        unfortunate behavior of CPython to write a .py source file name into
        the .pyc files it generates and then trust that it is correct in
        various places.  If source files are renamed, .pyc files may not be
        regenerated, but they will contain incorrect filenames.
        """
        package = FilePath(self.mktemp().encode('utf-8')).child(b'twisted_private_helper')
        package.makedirs()
        package.child(b'__init__.py').setContent(b'')
        package.child(b'module.py').setContent(b'''
import warnings
def foo():
    warnings.warn("oh no")
''')
        pathEntry = package.parent().path.decode('utf-8')
        sys.path.insert(0, pathEntry)
        self.addCleanup(sys.path.remove, pathEntry)

        # Import it to cause pycs to be generated
        from twisted_private_helper import module

        # Clean up the state resulting from that import; we're not going to use
        # this module, so it should go away.
        del sys.modules['twisted_private_helper']
        del sys.modules[module.__name__]

        # Some Python versions have extra state related to the just
        # imported/renamed package.  Clean it up too.  See also
        # http://bugs.python.org/issue15912
        try:
            from importlib import invalidate_caches
        except ImportError:
            pass
        else:
            invalidate_caches()

        # Rename the source directory
        package.moveTo(package.sibling(b'twisted_renamed_helper'))

        # Import the newly renamed version
        from twisted_renamed_helper import module
        self.addCleanup(sys.modules.pop, 'twisted_renamed_helper')
        self.addCleanup(sys.modules.pop, module.__name__)

        # Generate the warning
        module.foo()

        # Flush it
        self.assertEqual(len(self.flushWarnings([module.foo])), 1)
Ejemplo n.º 6
0
    def buildPDF(self, bookPath, inputDirectory, outputPath):
        """
        Build a PDF from the given a LaTeX book document.

        @type bookPath: L{FilePath}
        @param bookPath: The location of a LaTeX document defining a book.

        @type inputDirectory: L{FilePath}
        @param inputDirectory: The directory which the inputs of the book are
            relative to.

        @type outputPath: L{FilePath}
        @param outputPath: The location to which to write the resulting book.
        """
        if not bookPath.basename().endswith(".tex"):
            raise ValueError("Book filename must end with .tex")

        workPath = FilePath(mkdtemp())
        try:
            startDir = os.getcwd()
            try:
                os.chdir(inputDirectory.path)

                texToDVI = (
                    "latex -interaction=nonstopmode "
                    "-output-directory=%s %s") % (
                    workPath.path, bookPath.path)

                # What I tell you three times is true!
                # The first two invocations of latex on the book file allows it
                # correctly create page numbers for in-text references.  Why this is
                # the case, I could not tell you. -exarkun
                for i in range(3):
                    self.run(texToDVI)

                bookBaseWithoutExtension = bookPath.basename()[:-4]
                dviPath = workPath.child(bookBaseWithoutExtension + ".dvi")
                psPath = workPath.child(bookBaseWithoutExtension + ".ps")
                pdfPath = workPath.child(bookBaseWithoutExtension + ".pdf")
                self.run(
                    "dvips -o %(postscript)s -t letter -Ppdf %(dvi)s" % {
                        'postscript': psPath.path,
                        'dvi': dviPath.path})
                self.run("ps2pdf13 %(postscript)s %(pdf)s" % {
                        'postscript': psPath.path,
                        'pdf': pdfPath.path})
                pdfPath.moveTo(outputPath)
                workPath.remove()
            finally:
                os.chdir(startDir)
        except:
            workPath.moveTo(bookPath.parent().child(workPath.basename()))
            raise
Ejemplo n.º 7
0
        def _wrap(path,**template):
            scratchfile=path.dirname()+"."+path.basename()+".tmp"
            fh=path.open('r')

            sfp=FilePath(scratchfile)
            sfh=sfp.open('w')
            seeklast=0
            for buffer in fh.readlines():
                for line in buffer:
                    sfh.write(line.format(**template))
            sfh.flush()
            sfh.close()
            fh.close()

            sfp.moveTo(path.realpath())
Ejemplo n.º 8
0
    def _delete_ipfile(self, username):
        """
        Create configuration file for static IP allocation.

        :param username (str): account username.
        """
        log.info("ACCOUNTS:: Deleting IP file for {}.".format(username))

        filename = os.path.join(self.path['client-ips'], username)
        log.debug("ACCOUNTS:: Moving file to {}.revoked.".format(filename))

        # do not delete it, just rename it to `revoked`
        fp = FilePath(filename)
        revoked_fp = FilePath("{}.revoked".format(filename))
        fp.moveTo(revoked_fp)
Ejemplo n.º 9
0
    def test_renamedSource(self):
        """
        Warnings emitted by a function defined in a file which has been renamed
        since it was initially compiled can still be flushed.

        This is testing the code which specifically supports working around the
        unfortunate behavior of CPython to write a .py source file name into
        the .pyc files it generates and then trust that it is correct in
        various places.  If source files are renamed, .pyc files may not be
        regenerated, but they will contain incorrect filenames.
        """
        package = FilePath(self.mktemp()).child('twisted_private_helper')
        package.makedirs()
        package.child('__init__.py').setContent('')
        package.child('module.py').setContent('''
import warnings
def foo():
    warnings.warn("oh no")
''')
        sys.path.insert(0, package.parent().path)
        self.addCleanup(sys.path.remove, package.parent().path)

        # Import it to cause pycs to be generated
        from twisted_private_helper import module

        # Clean up the state resulting from that import; we're not going to use
        # this module, so it should go away.
        del sys.modules['twisted_private_helper']
        del sys.modules[module.__name__]

        # Rename the source directory
        package.moveTo(package.sibling('twisted_renamed_helper'))

        # Import the newly renamed version
        from twisted_renamed_helper import module
        self.addCleanup(sys.modules.pop, 'twisted_renamed_helper')
        self.addCleanup(sys.modules.pop, module.__name__)

        # Generate the warning
        module.foo()

        # Flush it
        self.assertEqual(len(self.flushWarnings([module.foo])), 1)
Ejemplo n.º 10
0
class DirectoryChangeListenerTestCase(TestCase):
    def test_delete(self):
        """
        Verify directory deletions can be monitored
        """

        self.tmpdir = FilePath(self.mktemp())
        self.tmpdir.makedirs()

        def deleteAction():
            self.tmpdir.remove()

        resource = KQueueReactorTestFixture(self, deleteAction)
        storageService = StubStorageService(resource.reactor)
        delegate = DataStoreMonitor(resource.reactor, storageService)
        dcl = DirectoryChangeListener(resource.reactor, self.tmpdir.path,
                                      delegate)
        dcl.startListening()
        resource.runReactor()
        self.assertTrue(storageService.stopCalled)
        self.assertEquals(delegate.methodCalled, "deleted")

    def test_rename(self):
        """
        Verify directory renames can be monitored
        """

        self.tmpdir = FilePath(self.mktemp())
        self.tmpdir.makedirs()

        def renameAction():
            self.tmpdir.moveTo(FilePath(self.mktemp()))

        resource = KQueueReactorTestFixture(self, renameAction)
        storageService = StubStorageService(resource.reactor)
        delegate = DataStoreMonitor(resource.reactor, storageService)
        dcl = DirectoryChangeListener(resource.reactor, self.tmpdir.path,
                                      delegate)
        dcl.startListening()
        resource.runReactor()
        self.assertTrue(storageService.stopCalled)
        self.assertEquals(delegate.methodCalled, "renamed")
Ejemplo n.º 11
0
    def generate(cls,
                 directory,
                 control_hostname,
                 num_nodes,
                 cluster_name,
                 cluster_id=None):
        """
        Generate certificates in the given directory.

        :param FilePath directory: Directory to use for certificate authority.
        :param bytes control_hostname: The hostname of the control service.
        :param int num_nodes: Number of nodes in the cluster.
        :param UUID cluster_id: The unique identifier of the cluster for which
            the certificates are being generated.  If not given, a random
            identifier will be generated.

        :return: ``Certificates`` instance.
        """
        RootCredential.initialize(
            directory,
            cluster_name,
            cluster_id=cluster_id,
        )

        def run(*arguments):
            check_call([b"flocker-ca"] + list(arguments), cwd=directory.path)

        run(b"create-control-certificate", control_hostname)
        run(b"create-api-certificate", b"allison")
        # Rename to user.crt/user.key so we can use this folder directly
        # from clients:
        directory.child(b"allison.crt").moveTo(directory.child(b"user.crt"))
        directory.child(b"allison.key").moveTo(directory.child(b"user.key"))
        for i in range(num_nodes):
            run(b"create-node-certificate")
        for i, child in enumerate(
                directory.globChildren(b"????????-????-*.crt")):
            sibling = FilePath(child.path[:-3] + b"key")
            child.moveTo(directory.child(b"node-%d.crt" % (i, )))
            sibling.moveTo(directory.child(b"node-%d.key" % (i, )))
        return cls(directory)
Ejemplo n.º 12
0
    def add_node(self, index):
        """
        Generate another node certificate.

        :rtype: CertAndKey
        :return: The newly created node certificate.
        """
        def run(*arguments):
            check_call([b"flocker-ca"] + list(arguments),
                       cwd=self.directory.path)

        run(b"create-node-certificate")
        tmp_crt = self.directory.globChildren(b"????????-????-*.crt")[0]
        tmp_key = FilePath(tmp_crt.path[:-3] + b"key")
        crt = self.directory.child(b"node-%d.crt" % (index, ))
        key = self.directory.child(b"node-%d.key" % (index, ))
        tmp_crt.moveTo(crt)
        tmp_key.moveTo(key)
        cert = CertAndKey(crt, key)
        self.nodes.append(cert)
        return cert
Ejemplo n.º 13
0
    def add_node(self, index):
        """
        Generate another node certificate.

        :rtype: CertAndKey
        :return: The newly created node certificate.
        """

        def run(*arguments):
            check_call([b"flocker-ca"] + list(arguments), cwd=self.directory.path)

        run(b"create-node-certificate")
        tmp_crt = self.directory.globChildren(b"????????-????-*.crt")[0]
        tmp_key = FilePath(tmp_crt.path[:-3] + b"key")
        crt = self.directory.child(b"node-%d.crt" % (index,))
        key = self.directory.child(b"node-%d.key" % (index,))
        tmp_crt.moveTo(crt)
        tmp_key.moveTo(key)
        cert = CertAndKey(crt, key)
        self.nodes.append(cert)
        return cert
Ejemplo n.º 14
0
class WarnAboutFunctionTests(SynchronousTestCase):
    """
    Tests for L{twisted.python.deprecate.warnAboutFunction} which allows the
    callers of a function to issue a C{DeprecationWarning} about that function.
    """
    def setUp(self):
        """
        Create a file that will have known line numbers when emitting warnings.
        """
        self.package = FilePath(self.mktemp().encode("utf-8")
                                ).child(b'twisted_private_helper')
        self.package.makedirs()
        self.package.child(b'__init__.py').setContent(b'')
        self.package.child(b'module.py').setContent(b'''
"A module string"

from twisted.python import deprecate

def testFunction():
    "A doc string"
    a = 1 + 2
    return a

def callTestFunction():
    b = testFunction()
    if b == 3:
        deprecate.warnAboutFunction(testFunction, "A Warning String")
''')
        # Python 3 doesn't accept bytes in sys.path:
        packagePath = self.package.parent().path.decode("utf-8")
        sys.path.insert(0, packagePath)
        self.addCleanup(sys.path.remove, packagePath)

        modules = sys.modules.copy()
        self.addCleanup(
            lambda: (sys.modules.clear(), sys.modules.update(modules)))

        # On Windows on Python 3, most FilePath interactions produce
        # DeprecationWarnings, so flush them here so that they don't interfere
        # with the tests.
        if platform.isWindows() and _PY3:
            self.flushWarnings()


    def test_warning(self):
        """
        L{deprecate.warnAboutFunction} emits a warning the file and line number
        of which point to the beginning of the implementation of the function
        passed to it.
        """
        def aFunc():
            pass
        deprecate.warnAboutFunction(aFunc, 'A Warning Message')
        warningsShown = self.flushWarnings()
        filename = __file__
        if filename.lower().endswith('.pyc'):
            filename = filename[:-1]
        self.assertSamePath(
            FilePath(warningsShown[0]["filename"]), FilePath(filename))
        self.assertEqual(warningsShown[0]["message"], "A Warning Message")


    def test_warningLineNumber(self):
        """
        L{deprecate.warnAboutFunction} emits a C{DeprecationWarning} with the
        number of a line within the implementation of the function passed to it.
        """
        from twisted_private_helper import module
        module.callTestFunction()
        warningsShown = self.flushWarnings()
        self.assertSamePath(
            FilePath(warningsShown[0]["filename"].encode("utf-8")),
            self.package.sibling(b'twisted_private_helper').child(b'module.py'))
        # Line number 9 is the last line in the testFunction in the helper
        # module.
        self.assertEqual(warningsShown[0]["lineno"], 9)
        self.assertEqual(warningsShown[0]["message"], "A Warning String")
        self.assertEqual(len(warningsShown), 1)


    def assertSamePath(self, first, second):
        """
        Assert that the two paths are the same, considering case normalization
        appropriate for the current platform.

        @type first: L{FilePath}
        @type second: L{FilePath}

        @raise C{self.failureType}: If the paths are not the same.
        """
        self.assertTrue(
            normcase(first.path) == normcase(second.path),
            "%r != %r" % (first, second))


    def test_renamedFile(self):
        """
        Even if the implementation of a deprecated function is moved around on
        the filesystem, the line number in the warning emitted by
        L{deprecate.warnAboutFunction} points to a line in the implementation of
        the deprecated function.
        """
        from twisted_private_helper import module
        # Clean up the state resulting from that import; we're not going to use
        # this module, so it should go away.
        del sys.modules['twisted_private_helper']
        del sys.modules[module.__name__]

        # Rename the source directory
        self.package.moveTo(self.package.sibling(b'twisted_renamed_helper'))

        # Make sure importlib notices we've changed importable packages:
        if invalidate_caches:
            invalidate_caches()

        # Import the newly renamed version
        from twisted_renamed_helper import module
        self.addCleanup(sys.modules.pop, 'twisted_renamed_helper')
        self.addCleanup(sys.modules.pop, module.__name__)

        module.callTestFunction()
        warningsShown = self.flushWarnings([module.testFunction])
        warnedPath = FilePath(warningsShown[0]["filename"].encode("utf-8"))
        expectedPath = self.package.sibling(
            b'twisted_renamed_helper').child(b'module.py')
        self.assertSamePath(warnedPath, expectedPath)
        self.assertEqual(warningsShown[0]["lineno"], 9)
        self.assertEqual(warningsShown[0]["message"], "A Warning String")
        self.assertEqual(len(warningsShown), 1)


    def test_filteredWarning(self):
        """
        L{deprecate.warnAboutFunction} emits a warning that will be filtered if
        L{warnings.filterwarning} is called with the module name of the
        deprecated function.
        """
        # Clean up anything *else* that might spuriously filter out the warning,
        # such as the "always" simplefilter set up by unittest._collectWarnings.
        # We'll also rely on trial to restore the original filters afterwards.
        del warnings.filters[:]

        warnings.filterwarnings(
            action="ignore", module="twisted_private_helper")

        from twisted_private_helper import module
        module.callTestFunction()

        warningsShown = self.flushWarnings()
        self.assertEqual(len(warningsShown), 0)


    def test_filteredOnceWarning(self):
        """
        L{deprecate.warnAboutFunction} emits a warning that will be filtered
        once if L{warnings.filterwarning} is called with the module name of the
        deprecated function and an action of once.
        """
        # Clean up anything *else* that might spuriously filter out the warning,
        # such as the "always" simplefilter set up by unittest._collectWarnings.
        # We'll also rely on trial to restore the original filters afterwards.
        del warnings.filters[:]

        warnings.filterwarnings(
            action="module", module="twisted_private_helper")

        from twisted_private_helper import module
        module.callTestFunction()
        module.callTestFunction()

        warningsShown = self.flushWarnings()
        self.assertEqual(len(warningsShown), 1)
        message = warningsShown[0]['message']
        category = warningsShown[0]['category']
        filename = warningsShown[0]['filename']
        lineno = warningsShown[0]['lineno']
        msg = warnings.formatwarning(message, category, filename, lineno)
        self.assertTrue(
            msg.endswith("module.py:9: DeprecationWarning: A Warning String\n"
                         "  return a\n"),
            "Unexpected warning string: %r" % (msg,))
Ejemplo n.º 15
0
 def connectionLost(self, reason):
     if self.persistent:
         self._historyFd.close()
         path = FilePath(self.historyFile + ('.%d' % self._historySession))
         path.moveTo(FilePath(self.historyFile))
     ConsoleManhole.connectionLost(self, reason)
Ejemplo n.º 16
0
 def connectionLost(self, reason):
     if self.persistent:
         self._historyFd.close()
         path = FilePath(self.historyFile + ('.%d' % self._historySession))
         path.moveTo(FilePath(self.historyFile))
     ConsoleManhole.connectionLost(self, reason)
Ejemplo n.º 17
0
class WarnAboutFunctionTests(SynchronousTestCase):
    """
    Tests for L{twisted.python.deprecate.warnAboutFunction} which allows the
    callers of a function to issue a C{DeprecationWarning} about that function.
    """
    def setUp(self):
        """
        Create a file that will have known line numbers when emitting warnings.
        """
        self.package = FilePath(
            self.mktemp().encode("utf-8")).child(b'twisted_private_helper')
        self.package.makedirs()
        self.package.child(b'__init__.py').setContent(b'')
        self.package.child(b'module.py').setContent(b'''
"A module string"

from twisted.python import deprecate

def testFunction():
    "A doc string"
    a = 1 + 2
    return a

def callTestFunction():
    b = testFunction()
    if b == 3:
        deprecate.warnAboutFunction(testFunction, "A Warning String")
''')
        # Python 3 doesn't accept bytes in sys.path:
        packagePath = self.package.parent().path.decode("utf-8")
        sys.path.insert(0, packagePath)
        self.addCleanup(sys.path.remove, packagePath)

        modules = sys.modules.copy()
        self.addCleanup(lambda:
                        (sys.modules.clear(), sys.modules.update(modules)))

    def test_warning(self):
        """
        L{deprecate.warnAboutFunction} emits a warning the file and line number
        of which point to the beginning of the implementation of the function
        passed to it.
        """
        def aFunc():
            pass

        deprecate.warnAboutFunction(aFunc, 'A Warning Message')
        warningsShown = self.flushWarnings()
        filename = __file__
        if filename.lower().endswith('.pyc'):
            filename = filename[:-1]
        self.assertSamePath(FilePath(warningsShown[0]["filename"]),
                            FilePath(filename))
        self.assertEqual(warningsShown[0]["message"], "A Warning Message")

    def test_warningLineNumber(self):
        """
        L{deprecate.warnAboutFunction} emits a C{DeprecationWarning} with the
        number of a line within the implementation of the function passed to it.
        """
        from twisted_private_helper import module
        module.callTestFunction()
        warningsShown = self.flushWarnings()
        self.assertSamePath(
            FilePath(warningsShown[0]["filename"].encode("utf-8")),
            self.package.sibling(b'twisted_private_helper').child(
                b'module.py'))
        # Line number 9 is the last line in the testFunction in the helper
        # module.
        self.assertEqual(warningsShown[0]["lineno"], 9)
        self.assertEqual(warningsShown[0]["message"], "A Warning String")
        self.assertEqual(len(warningsShown), 1)

    def assertSamePath(self, first, second):
        """
        Assert that the two paths are the same, considering case normalization
        appropriate for the current platform.

        @type first: L{FilePath}
        @type second: L{FilePath}

        @raise C{self.failureType}: If the paths are not the same.
        """
        self.assertTrue(
            normcase(first.path) == normcase(second.path),
            "%r != %r" % (first, second))

    def test_renamedFile(self):
        """
        Even if the implementation of a deprecated function is moved around on
        the filesystem, the line number in the warning emitted by
        L{deprecate.warnAboutFunction} points to a line in the implementation of
        the deprecated function.
        """
        from twisted_private_helper import module
        # Clean up the state resulting from that import; we're not going to use
        # this module, so it should go away.
        del sys.modules['twisted_private_helper']
        del sys.modules[module.__name__]

        # Rename the source directory
        self.package.moveTo(self.package.sibling(b'twisted_renamed_helper'))

        # Make sure importlib notices we've changed importable packages:
        if invalidate_caches:
            invalidate_caches()

        # Import the newly renamed version
        from twisted_renamed_helper import module
        self.addCleanup(sys.modules.pop, 'twisted_renamed_helper')
        self.addCleanup(sys.modules.pop, module.__name__)

        module.callTestFunction()
        warningsShown = self.flushWarnings()
        warnedPath = FilePath(warningsShown[0]["filename"].encode("utf-8"))
        expectedPath = self.package.sibling(b'twisted_renamed_helper').child(
            b'module.py')
        self.assertSamePath(warnedPath, expectedPath)
        self.assertEqual(warningsShown[0]["lineno"], 9)
        self.assertEqual(warningsShown[0]["message"], "A Warning String")
        self.assertEqual(len(warningsShown), 1)

    def test_filteredWarning(self):
        """
        L{deprecate.warnAboutFunction} emits a warning that will be filtered if
        L{warnings.filterwarning} is called with the module name of the
        deprecated function.
        """
        # Clean up anything *else* that might spuriously filter out the warning,
        # such as the "always" simplefilter set up by unittest._collectWarnings.
        # We'll also rely on trial to restore the original filters afterwards.
        del warnings.filters[:]

        warnings.filterwarnings(action="ignore",
                                module="twisted_private_helper")

        from twisted_private_helper import module
        module.callTestFunction()

        warningsShown = self.flushWarnings()
        self.assertEqual(len(warningsShown), 0)

    def test_filteredOnceWarning(self):
        """
        L{deprecate.warnAboutFunction} emits a warning that will be filtered
        once if L{warnings.filterwarning} is called with the module name of the
        deprecated function and an action of once.
        """
        # Clean up anything *else* that might spuriously filter out the warning,
        # such as the "always" simplefilter set up by unittest._collectWarnings.
        # We'll also rely on trial to restore the original filters afterwards.
        del warnings.filters[:]

        warnings.filterwarnings(action="module",
                                module="twisted_private_helper")

        from twisted_private_helper import module
        module.callTestFunction()
        module.callTestFunction()

        warningsShown = self.flushWarnings()
        self.assertEqual(len(warningsShown), 1)
        message = warningsShown[0]['message']
        category = warningsShown[0]['category']
        filename = warningsShown[0]['filename']
        lineno = warningsShown[0]['lineno']
        msg = warnings.formatwarning(message, category, filename, lineno)
        self.assertTrue(
            msg.endswith("module.py:9: DeprecationWarning: A Warning String\n"
                         "  return a\n"),
            "Unexpected warning string: %r" % (msg, ))