commit 9873fad412081d35e508de5c8dd095a17fd921cb Author: Isis Lovecruft isis@torproject.org Date: Thu Aug 7 07:47:05 2014 +0000
Split unittests in test_bridgedb.py; refactor to use already running server.
The unittests in `lib/bridgedb/test/test_bridgedb.py` now expect a BridgeDB instance to have already been started *before* the tests run. This change is necessary because it is non-trivial to have two instances of BridgeDB running simultaneously (at least not without changing every port in the config file) and the tests in `lib/bridgedb/test/test_https.py` also require a running server. These refactored tests should raise SkipTest if they believe that there is no running BridgeDB instance to test against.
For Travis CI, the BridgeDB instance is started automatically in the 'before_script' section of `travis.yml` (in the top directory of this repo).
* FIXES part of #9874. --- lib/bridgedb/test/test_bridgedb.py | 167 +++++++++++++++--------------------- lib/bridgedb/test/util.py | 22 +++++ 2 files changed, 89 insertions(+), 100 deletions(-)
diff --git a/lib/bridgedb/test/test_bridgedb.py b/lib/bridgedb/test/test_bridgedb.py index c7aefff..e54d324 100644 --- a/lib/bridgedb/test/test_bridgedb.py +++ b/lib/bridgedb/test/test_bridgedb.py @@ -14,121 +14,88 @@ from __future__ import print_function
import os -import shutil import signal import time
-from os.path import join as pjoin -from subprocess import Popen, PIPE - -from twisted.python import log -from twisted.python.procutils import which from twisted.trial import unittest +from twisted.trial.unittest import SkipTest
-from bridgedb.test.util import fileCheckDecorator +from bridgedb.test.util import pidExists
class BridgeDBCliTest(unittest.TestCase): """Test the `bridgedb` command."""
- @fileCheckDecorator - def doCopyFile(self, src, dst, description=None): - shutil.copy(src, dst) - - @fileCheckDecorator - def doMoveFile(self, src, dst, description=None): - shutil.move(src, dst) - - def test_bridgedb_commands(self): - print('') - here = os.getcwd() - runDir = pjoin(here, 'rundir') - topDir = here.rstrip('_trial_temp') - scriptsDir = pjoin(topDir, 'scripts') - - # Create the lowest directory we need, and all its parents: - os.makedirs(os.path.join(runDir, 'gnupghome')) - - conf = pjoin(topDir, 'bridgedb.conf') - confMoved = pjoin(runDir, 'bridgedb.conf') - gpgFile = pjoin(topDir, 'gnupghome', 'TESTING.subkeys.sec') - gpgMoved = pjoin(runDir, 'gnupghome', 'TESTING.subkeys.sec') - certFile = pjoin(topDir, 'cert') - certMoved = pjoin(runDir, 'cert') - keyFile = pjoin(topDir, 'privkey.pem') - keyMoved = pjoin(runDir, 'privkey.pem') - - makeSSLCertScript = os.path.join(scriptsDir, 'make-ssl-cert') - bridgedbScript = which('bridgedb') # this returns a list - - self.doCopyFile(conf, confMoved, 'config') - self.doCopyFile(gpgFile, gpgMoved, 'GPG test key') - print("Running subcommands from directory:\n %r" % runDir) - print("Running %r..." % makeSSLCertScript) - makeSSLCertProcess = Popen(makeSSLCertScript) - makeSSLCertProcess.wait() - self.doMoveFile(certFile, certMoved, 'certificate') - self.doMoveFile(keyFile, keyMoved, 'SSL private key') - - self.assertTrue(os.path.isfile(bridgedbScript[0]), - "Couldn't find bridgedb script %r" % bridgedbScript[0]) - bridgedbScript = bridgedbScript[0] - print("Running bridgedb script %r..." % bridgedbScript) - - os.chdir(runDir) # we have to do this to get files to end up there - print("Running `bridgedb mock' to generate mock bridge descriptors...") - mockProc = Popen([bridgedbScript, 'mock', '-n', '50']) - mockProcCode = mockProc.wait() - print("`bridgedb mock' exited with status code %d" % int(mockProcCode)) - os.chdir(here) - - # See ticket #11216, cached-extrainfo* files should not be parsed - # cumulatively. - eidesc = pjoin(runDir, 'cached-extrainfo') - eindesc = pjoin(runDir, 'cached-extrainfo.new') - self.doCopyFile(eindesc, eidesc, 'duplicated cached-extrainfo(.new)') - self.assertTrue(os.path.isfile(eidesc)) - self.assertTrue(os.path.isfile(eindesc)) - - - print("Running `bridgedb' to test server startups...") - # Sorry Windows users - devnull = open('/dev/null', 'w') - bridgedbProc = Popen([bridgedbScript, '-r', runDir], stdout=devnull) - print("Waiting 10 seconds while bridgedb loads...") - time.sleep(10) - assignments = pjoin(runDir, 'assignments.log') - self.assertTrue(os.path.isfile(assignments)) - os.unlink(assignments) - print("Sending SIGHUP, checking for assignments.log ...") - bridgedbProc.send_signal(signal.SIGHUP) - time.sleep(5) + def setUp(self): + here = os.getcwd() + topdir = here.rstrip('_trial_temp') + self.rundir = os.path.join(topdir, 'run') + self.pidfile = os.path.join(self.rundir, 'bridgedb.pid') + self.pid = self.getBridgeDBPID(self.pidfile) + self.assignmentsFile = os.path.join(self.rundir, 'assignments.log') + + def getBridgeDBPID(self, pidfile="bridgedb.pid"): + """Read the ``bridgedb.pid`` file in **rundir**, if it exists, to get + the PID. + + :param str pidfile: The path to the BridgeDB pidfile. + :rtype: int + :returns: The process ID, if available, otherwise ``0``. + """ + fh = None try: - self.assertTrue(os.path.isfile(assignments)) - except self.failureException as e: - bridgedbProc.send_signal(signal.SIGKILL) - bridgedbProcCode = bridgedbProc.wait() - print("`bridgedb' exited with status code %d" % int(bridgedbProcCode)) - raise e - print("Sending SIGUSR1, checking for bucket files...") - bridgedbProc.send_signal(signal.SIGUSR1) + fh = open(pidfile) + except (IOError, OSError) as err: + print(err) + pid = 0 + else: + pid = int(fh.read()) + + if fh: + fh.close() + + return pid + + def test_bridgedb_assignments_log(self): + """This test should only be run if a BridgeDB server has already been + started in another process. + + To see how this is done for the Travis CI tests, see the + 'before_script' section of the ``.travis.yml`` file in the top + directory of this repository. + + This test ensures that an ``assignments.log`` file is created after a + BridgeDB process was started. + """ + if not self.pid: + raise SkipTest("Can't run test: no BridgeDB process running.") + + self.assertTrue(os.path.isfile(self.assignmentsFile)) + + def test_bridgedb_SIGHUP_assignments_log(self): + """Test that BridgeDB creates a new ``assignments.log`` file after + receiving a SIGHUP. + """ + if not self.pid: + raise SkipTest("Can't run test: no BridgeDB process running.") + + os.unlink(self.assignmentsFile) + os.kill(self.pid, signal.SIGHUP) + time.sleep(5) + self.assertTrue(os.path.isfile(self.assignmentsFile)) + + def test_bridgedb_SIGUSR1_buckets(self): + """Test that BridgeDB dumps buckets appropriately after a SIGUSR1.""" + if not self.pid: + raise SkipTest("Can't run test: no BridgeDB process running.") + + os.kill(self.pid, signal.SIGUSR1) time.sleep(5) buckets = [['email', False], ['https', False], ['unallocated', False]] - for rundirfile in os.listdir(runDir): + for rundirfile in os.listdir(self.rundir): for bucket in buckets: if rundirfile.startswith(bucket[0]): bucket[1] = True break for bucket in buckets: - try: - self.assertTrue(bucket[1], "%s bucket was not dumped!" % bucket[0]) - except self.failureException as e: - bridgedbProc.send_signal(signal.SIGKILL) - bridgedbProcCode = bridgedbProc.wait() - print("`bridgedb' exited with status code %d" % int(bridgedbProcCode)) - raise e - print("Done. Killing processes.") - bridgedbProc.send_signal(signal.SIGINT) - bridgedbProcCode = bridgedbProc.wait() - print("`bridgedb' exited with status code %d" % int(bridgedbProcCode)) - self.assertEqual(bridgedbProcCode, 0) + self.assertTrue(bucket[1], "%s bucket was not dumped!" % bucket[0]) diff --git a/lib/bridgedb/test/util.py b/lib/bridgedb/test/util.py index 5538a29..1067526 100644 --- a/lib/bridgedb/test/util.py +++ b/lib/bridgedb/test/util.py @@ -65,6 +65,28 @@ def fileCheckDecorator(func): % (str(description), dst, src)) return wrapper
+def pidExists(pid): + """Test if **pid** exists. + + :raises: OSError, if ``OSError.errno`` wasn't an expected errno (according + to the "ERRORS" section from ``man 2 kill``). + :rtype: bool + :returns: ``True`` if a process with **pid** exists, ``False`` otherwise. + """ + try: + os.kill(pid, 0) + except OSError as err: + if err.errno == errno.ESRCH: # ESRCH: No such process + return False + if err.errno == errno.EPERM: # EPERM: Operation not permitted + # If we're not allowed to signal the process, then there exists a + # process that we don't have permissions to access. + return True + else: + raise + else: + return True +
#: Mixin class for use with :api:`~twisted.trial.unittest.TestCase`. A #: ``TestCaseMixin`` can be used to add additional methods, which should be
tor-commits@lists.torproject.org