summaryrefslogtreecommitdiff
path: root/mailman/bin/testall.py
diff options
context:
space:
mode:
Diffstat (limited to 'mailman/bin/testall.py')
-rw-r--r--mailman/bin/testall.py244
1 files changed, 244 insertions, 0 deletions
diff --git a/mailman/bin/testall.py b/mailman/bin/testall.py
new file mode 100644
index 000000000..9d50be111
--- /dev/null
+++ b/mailman/bin/testall.py
@@ -0,0 +1,244 @@
+# Copyright (C) 2001-2008 by the Free Software Foundation, Inc.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+# USA.
+
+"""Mailman unit test driver."""
+
+from __future__ import with_statement
+
+import os
+import re
+import grp
+import pwd
+import sys
+import shutil
+import optparse
+import tempfile
+import unittest
+
+import mailman
+import mailman.tests
+
+from mailman import Version
+from mailman.configuration import config
+from mailman.i18n import _
+from mailman.initialize import initialize_1, initialize_2
+
+basedir = None
+
+
+
+def v_callback(option, opt, value, parser):
+ if opt in ('-q', '--quiet'):
+ delta = -1
+ elif opt in ('-v', '--verbose'):
+ delta = 1
+ else:
+ raise AssertionError('Unexpected option: %s' % opt)
+ dest = getattr(parser.values, option.dest)
+ setattr(parser.values, option.dest, max(0, dest + delta))
+
+
+def parseargs():
+ parser = optparse.OptionParser(version=Version.MAILMAN_VERSION,
+ usage=_("""\
+%prog [options] [tests]
+
+Run the Mailman unit test suite. 'tests' is one or more Python regular
+expressions matching only the tests you want to run. Prefix the regular
+expression with '!' to specify a negative test."""))
+ parser.set_defaults(verbosity=2)
+ parser.add_option('-v', '--verbose',
+ action='callback', callback=v_callback,
+ dest='verbosity', help=_("""\
+Increase verbosity by 1, which defaults to %default. Use -q to reduce
+verbosity. -v and -q options accumulate."""))
+ parser.add_option('-q', '--quiet',
+ action='callback', callback=v_callback,
+ dest='verbosity', help=_("""\
+Reduce verbosity by 1 (but not below 0)."""))
+ parser.add_option('-e', '--stderr',
+ default=False, action='store_true',
+ help=_('Propagate log errors to stderr.'))
+ parser.add_option('-c', '--coverage',
+ default=False, action='store_true',
+ help=_('Enable code coverage.'))
+ opts, args = parser.parse_args()
+ return parser, opts, args
+
+
+
+def search():
+ testnames = []
+ # Walk the entire tree from the current base directory. Look for modules
+ # that start with 'test_'. Calculate the full module path name to this
+ # module, append 'test_suite' and add that to testnames. This way, we run
+ # all the suites defined in the test_suite() function inside all test
+ # modules.
+ for dirpath, dirnames, filenames in os.walk(basedir):
+ for fn in filenames:
+ if fn.startswith('test_') and fn.endswith('.py'):
+ # Start with full path
+ path = os.path.join(dirpath, fn)
+ # Convert the file path to a module path. First, we must make
+ # the file path relative to the root directory. Then strip
+ # off the trailing .py
+ path = path[len(basedir)+1:-3]
+ # Convert slashes to dots
+ modpath = path.replace(os.sep, '.') + '.test_suite'
+ testnames.append('mailman.' + modpath)
+ return testnames
+
+
+def match(pat, name):
+ if not pat:
+ return True
+ if pat.startswith('!'):
+ # Negative test
+ return re.search(pat[1:], name) is None
+ else:
+ # Positive test
+ return re.search(pat, name) is not None
+
+
+def filter_tests(suite, patterns):
+ if '.' in patterns:
+ return suite
+ new = unittest.TestSuite()
+ for test in suite._tests:
+ if isinstance(test, unittest.TestCase):
+ # Get the fill test name: package.module.class.method
+ name = test.id()
+ for pat in patterns:
+ if match(pat, name):
+ new.addTest(test)
+ break
+ else:
+ filtered = filter_tests(test, patterns)
+ if filtered:
+ new.addTest(filtered)
+ return new
+
+
+def suite(patterns=None):
+ if patterns is None:
+ patterns = '.'
+ loader = unittest.TestLoader()
+ # Search for all tests that match the given patterns
+ testnames = search()
+ suite = loader.loadTestsFromNames(testnames)
+ return filter_tests(suite, patterns)
+
+
+
+def main():
+ global basedir
+
+ parser, opts, args = parseargs()
+ if not args:
+ args = ['.']
+
+ # Store the options some place that other code can get to it.
+ config.opts = opts
+
+ # Turn on code coverage if selected.
+ if opts.coverage:
+ try:
+ import coverage
+ except ImportError:
+ opts.coverage = False
+ else:
+ coverage.start()
+
+ # Set up the testing configuration file both for this process, and for all
+ # sub-processes testing will spawn (e.g. the qrunners).
+ #
+ # Calculate a temporary VAR_DIR directory so that run-time artifacts of
+ # the tests won't tread on the installation's data. This also makes it
+ # easier to clean up after the tests are done, and insures isolation of
+ # test suite runs.
+ cfg_in = os.path.join(os.path.dirname(mailman.tests.__file__),
+ 'testing.cfg.in')
+ fd, cfg_out = tempfile.mkstemp(suffix='.cfg')
+ os.close(fd)
+ shutil.copyfile(cfg_in, cfg_out)
+
+ var_dir = tempfile.mkdtemp()
+ if opts.verbosity > 2:
+ print 'VAR_DIR :', var_dir
+ print 'config file:', cfg_out
+
+ user_id = os.getuid()
+ user_name = pwd.getpwuid(user_id).pw_name
+ group_id = os.getgid()
+ group_name = grp.getgrgid(group_id).gr_name
+
+ try:
+ with open(cfg_out, 'a') as fp:
+ print >> fp, 'VAR_DIR = "%s"' % var_dir
+ print >> fp, 'MAILMAN_USER = "%s"' % user_name
+ print >> fp, 'MAILMAN_UID = %d' % user_id
+ print >> fp, 'MAILMAN_GROUP = "%s"' % group_name
+ print >> fp, 'MAILMAN_GID = %d' % group_id
+ print >> fp, "LANGUAGES = 'en'"
+
+ initialize_1(cfg_out, propagate_logs=opts.stderr)
+ mailman_uid = pwd.getpwnam(config.MAILMAN_USER).pw_uid
+ mailman_gid = grp.getgrnam(config.MAILMAN_GROUP).gr_gid
+ os.chmod(cfg_out, 0660)
+ os.chown(cfg_out, mailman_uid, mailman_gid)
+
+ # Create an empty SQLite database file with the proper permissions and
+ # calculate the SQLAlchemy engine url to this database file.
+ fd, config.dbfile = tempfile.mkstemp(dir=config.DATA_DIR, suffix='.db')
+ os.close(fd)
+ os.chmod(config.dbfile, 0660)
+ os.chown(config.dbfile, mailman_uid, mailman_gid)
+
+ # Patch ups
+ test_engine_url = 'sqlite:///' + config.dbfile
+ config.SQLALCHEMY_ENGINE_URL = test_engine_url
+
+ # Write this to the config file so subprocesses share the same testing
+ # database file.
+ with open(cfg_out, 'a') as fp:
+ print >> fp, 'SQLALCHEMY_ENGINE_URL = "%s"' % test_engine_url
+
+ # With -vvv, turn on engine debugging.
+ initialize_2(opts.verbosity > 3)
+
+ # Run the tests
+ basedir = os.path.dirname(mailman.__file__)
+ runner = unittest.TextTestRunner(verbosity=opts.verbosity)
+ results = runner.run(suite(args))
+ finally:
+ os.remove(cfg_out)
+ shutil.rmtree(var_dir)
+
+ # Turn off coverage and print a report
+ if opts.coverage:
+ coverage.stop()
+ modules = [module for name, module in sys.modules.items()
+ if module
+ and name is not None
+ and name.split('.')[0] == 'mailman']
+ coverage.report(modules)
+ sys.exit(bool(results.failures or results.errors))
+
+
+
+if __name__ == '__main__':
+ main()