class Tester(object): def __init__(self, filesystem=None): self.finder = TestFinder(filesystem or FileSystem()) self.stream = sys.stderr def add_tree(self, top_directory, starting_subdirectory=None): self.finder.add_tree(top_directory, starting_subdirectory) def _parse_args(self): parser = optparse.OptionParser( usage='usage: %prog [options] [args...]') parser.add_option('-a', '--all', action='store_true', default=False, help='run all the tests'), parser.add_option( '-c', '--coverage', action='store_true', default=False, help= 'generate code coverage info (requires http://pypi.python.org/pypi/coverage)' ), parser.add_option( '-q', '--quiet', action='store_true', default=False, help='run quietly (errors, warnings, and progress only)'), parser.add_option( '-t', '--timing', action='store_true', default=False, help='display per-test execution time (implies --verbose)'), parser.add_option( '-v', '--verbose', action='count', default=0, help= 'verbose output (specify once for individual test results, twice for debug messages)' ) parser.add_option('--skip-integrationtests', action='store_true', default=False, help='do not run the integration tests') parser.epilog = ( '[args...] is an optional list of modules, test_classes, or individual tests. ' 'If no args are given, all the tests will be run.') return parser.parse_args() def _configure(self, options): self._options = options if options.timing: # --timing implies --verbose options.verbose = max(options.verbose, 1) log_level = logging.INFO if options.quiet: log_level = logging.WARNING elif options.verbose == 2: log_level = logging.DEBUG self._configure_logging(log_level) def _configure_logging(self, log_level): """Configure the root logger. Configure the root logger not to log any messages from webkitpy -- except for messages from the autoinstall module. Also set the logging level as described below. """ handler = logging.StreamHandler(self.stream) # We constrain the level on the handler rather than on the root # logger itself. This is probably better because the handler is # configured and known only to this module, whereas the root logger # is an object shared (and potentially modified) by many modules. # Modifying the handler, then, is less intrusive and less likely to # interfere with modifications made by other modules (e.g. in unit # tests). handler.name = __name__ handler.setLevel(log_level) formatter = logging.Formatter("%(message)s") handler.setFormatter(formatter) logger = logging.getLogger() logger.addHandler(handler) logger.setLevel(logging.NOTSET) # Filter out most webkitpy messages. # # Messages can be selectively re-enabled for this script by updating # this method accordingly. def filter(record): """Filter out autoinstall and non-third-party webkitpy messages.""" # FIXME: Figure out a way not to use strings here, for example by # using syntax like webkitpy.test.__name__. We want to be # sure not to import any non-Python 2.4 code, though, until # after the version-checking code has executed. if (record.name.startswith("webkitpy.common.system.autoinstall") or record.name.startswith("webkitpy.test")): return True if record.name.startswith("webkitpy"): return False return True testing_filter = logging.Filter() testing_filter.filter = filter # Display a message so developers are not mystified as to why # logging does not work in the unit tests. _log.info( "Suppressing most webkitpy logging while running unit tests.") handler.addFilter(testing_filter) def run(self): options, args = self._parse_args() self._configure(options) self.finder.clean_trees() names = self.finder.find_names(args, self._options.skip_integrationtests, self._options.all) return self._run_tests(names) def _run_tests(self, names): if self._options.coverage: try: import webkitpy.thirdparty.autoinstalled.coverage as coverage except ImportError, e: _log.error( "Failed to import 'coverage'; can't generate coverage numbers." ) return False cov = coverage.coverage() cov.start() # Make sure PYTHONPATH is set up properly. sys.path = self.finder.additional_paths(sys.path) + sys.path _log.debug("Loading the tests...") loader = unittest.defaultTestLoader suites = [] for name in names: if self.finder.is_module(name): # if we failed to load a name and it looks like a module, # try importing it directly, because loadTestsFromName() # produces lousy error messages for bad modules. try: __import__(name) except ImportError, e: _log.fatal('Failed to import %s:' % name) self._log_exception() return False suites.append(loader.loadTestsFromName(name, None))
class Tester(object): def __init__(self, filesystem=None): self.finder = TestFinder(filesystem or FileSystem()) self.stream = sys.stderr def add_tree(self, top_directory, starting_subdirectory=None): self.finder.add_tree(top_directory, starting_subdirectory) def _parse_args(self): parser = optparse.OptionParser(usage='usage: %prog [options] [args...]') parser.add_option('-a', '--all', action='store_true', default=False, help='run all the tests'), parser.add_option('-c', '--coverage', action='store_true', default=False, help='generate code coverage info (requires http://pypi.python.org/pypi/coverage)'), parser.add_option('-q', '--quiet', action='store_true', default=False, help='run quietly (errors, warnings, and progress only)'), parser.add_option('-t', '--timing', action='store_true', default=False, help='display per-test execution time (implies --verbose)'), parser.add_option('-v', '--verbose', action='count', default=0, help='verbose output (specify once for individual test results, twice for debug messages)') parser.add_option('--skip-integrationtests', action='store_true', default=False, help='do not run the integration tests') parser.epilog = ('[args...] is an optional list of modules, test_classes, or individual tests. ' 'If no args are given, all the tests will be run.') return parser.parse_args() def _configure(self, options): self._options = options if options.timing: # --timing implies --verbose options.verbose = max(options.verbose, 1) log_level = logging.INFO if options.quiet: log_level = logging.WARNING elif options.verbose == 2: log_level = logging.DEBUG self._configure_logging(log_level) def _configure_logging(self, log_level): """Configure the root logger. Configure the root logger not to log any messages from webkitpy -- except for messages from the autoinstall module. Also set the logging level as described below. """ handler = logging.StreamHandler(self.stream) # We constrain the level on the handler rather than on the root # logger itself. This is probably better because the handler is # configured and known only to this module, whereas the root logger # is an object shared (and potentially modified) by many modules. # Modifying the handler, then, is less intrusive and less likely to # interfere with modifications made by other modules (e.g. in unit # tests). handler.name = __name__ handler.setLevel(log_level) formatter = logging.Formatter("%(message)s") handler.setFormatter(formatter) logger = logging.getLogger() logger.addHandler(handler) logger.setLevel(logging.NOTSET) # Filter out most webkitpy messages. # # Messages can be selectively re-enabled for this script by updating # this method accordingly. def filter(record): """Filter out autoinstall and non-third-party webkitpy messages.""" # FIXME: Figure out a way not to use strings here, for example by # using syntax like webkitpy.test.__name__. We want to be # sure not to import any non-Python 2.4 code, though, until # after the version-checking code has executed. if (record.name.startswith("webkitpy.common.system.autoinstall") or record.name.startswith("webkitpy.test")): return True if record.name.startswith("webkitpy"): return False return True testing_filter = logging.Filter() testing_filter.filter = filter # Display a message so developers are not mystified as to why # logging does not work in the unit tests. _log.info("Suppressing most webkitpy logging while running unit tests.") handler.addFilter(testing_filter) def run(self): options, args = self._parse_args() self._configure(options) self.finder.clean_trees() names = self.finder.find_names(args, self._options.skip_integrationtests, self._options.all) if not names: _log.error('No tests to run') return False return self._run_tests(names) def _run_tests(self, names): if self._options.coverage: try: import webkitpy.thirdparty.autoinstalled.coverage as coverage except ImportError, e: _log.error("Failed to import 'coverage'; can't generate coverage numbers.") return False cov = coverage.coverage() cov.start() # Make sure PYTHONPATH is set up properly. sys.path = self.finder.additional_paths(sys.path) + sys.path _log.debug("Loading the tests...") loader = unittest.defaultTestLoader suites = [] for name in names: if self.finder.is_module(name): # if we failed to load a name and it looks like a module, # try importing it directly, because loadTestsFromName() # produces lousy error messages for bad modules. try: __import__(name) except ImportError, e: _log.fatal('Failed to import %s:' % name) self._log_exception() return False suites.append(loader.loadTestsFromName(name, None))
class TestFinderTest(unittest.TestCase): def setUp(self): files = { '/foo/bar/baz.py': '', '/foo/bar/baz_unittest.py': '', '/foo2/bar2/baz2.py': '', '/foo2/bar2/baz2.pyc': '', '/foo2/bar2/baz2_integrationtest.py': '', '/foo2/bar2/missing.pyc': '', '/tmp/another_unittest.py': '', } self.fs = MockFileSystem(files) self.finder = TestFinder(self.fs) self.finder.add_tree('/foo', 'bar') self.finder.add_tree('/foo2') # Here we have to jump through a hoop to make sure test-webkitpy doesn't log # any messages from these tests :(. self.root_logger = logging.getLogger() self.log_handler = None for h in self.root_logger.handlers: if getattr(h, 'name', None) == 'webkitpy.test.main': self.log_handler = h break if self.log_handler: self.log_level = self.log_handler.level self.log_handler.level = logging.CRITICAL def tearDown(self): if self.log_handler: self.log_handler.setLevel(self.log_level) def test_additional_system_paths(self): self.assertEquals(self.finder.additional_paths(['/usr']), ['/foo', '/foo2']) def test_is_module(self): self.assertTrue(self.finder.is_module('bar.baz')) self.assertTrue(self.finder.is_module('bar2.baz2')) self.assertTrue(self.finder.is_module('bar2.baz2_integrationtest')) # Missing the proper namespace. self.assertFalse(self.finder.is_module('baz')) def test_to_module(self): self.assertEquals(self.finder.to_module('/foo/test.py'), 'test') self.assertEquals(self.finder.to_module('/foo/bar/test.py'), 'bar.test') self.assertEquals(self.finder.to_module('/foo/bar/pytest.py'), 'bar.pytest') def test_clean(self): self.assertTrue(self.fs.exists('/foo2/bar2/missing.pyc')) self.finder.clean_trees() self.assertFalse(self.fs.exists('/foo2/bar2/missing.pyc')) def check_names(self, names, expected_names, skip_integrationtests=False, find_all=False): self.assertEquals( self.finder.find_names(names, skip_integrationtests, find_all), expected_names) def test_default_names(self): self.check_names([], ['bar.baz_unittest', 'bar2.baz2_integrationtest']) self.check_names([], ['bar.baz_unittest'], skip_integrationtests=True, find_all=True) self.check_names([], ['bar.baz_unittest'], skip_integrationtests=True, find_all=False) # Should return the names given it, even if they don't exist. self.check_names(['foobar'], ['foobar'], skip_integrationtests=True, find_all=False) def test_paths(self): self.fs.chdir('/foo/bar') self.check_names(['baz_unittest.py'], ['bar.baz_unittest']) self.check_names(['./baz_unittest.py'], ['bar.baz_unittest']) self.check_names(['/foo/bar/baz_unittest.py'], ['bar.baz_unittest']) self.check_names(['.'], ['bar.baz_unittest']) self.check_names(['../../foo2/bar2'], ['bar2.baz2_integrationtest']) self.fs.chdir('/') self.check_names(['bar'], ['bar.baz_unittest']) self.check_names(['/foo/bar/'], ['bar.baz_unittest']) # This works 'by accident' since it maps onto a package. self.check_names(['bar/'], ['bar.baz_unittest']) # This should log an error, since it's outside the trees. oc = OutputCapture() oc.set_log_level(logging.ERROR) oc.capture_output() try: self.check_names(['/tmp/another_unittest.py'], []) finally: _, _, logs = oc.restore_output() self.assertTrue('another_unittest.py' in logs) # Paths that don't exist are errors. oc.capture_output() try: self.check_names(['/foo/bar/notexist_unittest.py'], []) finally: _, _, logs = oc.restore_output() self.assertTrue('notexist_unittest.py' in logs) # Names that don't exist are caught later, at load time. self.check_names(['bar.notexist_unittest'], ['bar.notexist_unittest'])
class TestFinderTest(unittest.TestCase): def setUp(self): files = { '/foo/bar/baz.py': '', '/foo/bar/baz_unittest.py': '', '/foo2/bar2/baz2.py': '', '/foo2/bar2/baz2.pyc': '', '/foo2/bar2/baz2_integrationtest.py': '', '/foo2/bar2/missing.pyc': '', '/tmp/another_unittest.py': '', } self.fs = MockFileSystem(files) self.finder = TestFinder(self.fs) self.finder.add_tree('/foo', 'bar') self.finder.add_tree('/foo2') # Here we have to jump through a hoop to make sure test-webkitpy doesn't log # any messages from these tests :(. self.root_logger = logging.getLogger() self.log_handler = None for h in self.root_logger.handlers: if getattr(h, 'name', None) == 'webkitpy.test.main': self.log_handler = h break if self.log_handler: self.log_level = self.log_handler.level self.log_handler.level = logging.CRITICAL def tearDown(self): if self.log_handler: self.log_handler.setLevel(self.log_level) def test_additional_system_paths(self): self.assertEquals(self.finder.additional_paths(['/usr']), ['/foo', '/foo2']) def test_is_module(self): self.assertTrue(self.finder.is_module('bar.baz')) self.assertTrue(self.finder.is_module('bar2.baz2')) self.assertTrue(self.finder.is_module('bar2.baz2_integrationtest')) # Missing the proper namespace. self.assertFalse(self.finder.is_module('baz')) def test_to_module(self): self.assertEquals(self.finder.to_module('/foo/test.py'), 'test') self.assertEquals(self.finder.to_module('/foo/bar/test.py'), 'bar.test') self.assertEquals(self.finder.to_module('/foo/bar/pytest.py'), 'bar.pytest') def test_clean(self): self.assertTrue(self.fs.exists('/foo2/bar2/missing.pyc')) self.finder.clean_trees() self.assertFalse(self.fs.exists('/foo2/bar2/missing.pyc')) def check_names(self, names, expected_names, skip_integrationtests=False, find_all=False): self.assertEquals(self.finder.find_names(names, skip_integrationtests, find_all), expected_names) def test_default_names(self): self.check_names([], ['bar.baz_unittest', 'bar2.baz2_integrationtest']) self.check_names([], ['bar.baz_unittest'], skip_integrationtests=True, find_all=True) self.check_names([], ['bar.baz_unittest'], skip_integrationtests=True, find_all=False) # Should return the names given it, even if they don't exist. self.check_names(['foobar'], ['foobar'], skip_integrationtests=True, find_all=False) def test_paths(self): self.fs.chdir('/foo/bar') self.check_names(['baz_unittest.py'], ['bar.baz_unittest']) self.check_names(['./baz_unittest.py'], ['bar.baz_unittest']) self.check_names(['/foo/bar/baz_unittest.py'], ['bar.baz_unittest']) self.check_names(['.'], ['bar.baz_unittest']) self.check_names(['../../foo2/bar2'], ['bar2.baz2_integrationtest']) self.fs.chdir('/') self.check_names(['bar'], ['bar.baz_unittest']) self.check_names(['/foo/bar/'], ['bar.baz_unittest']) # This works 'by accident' since it maps onto a package. self.check_names(['bar/'], ['bar.baz_unittest']) # This should log an error, since it's outside the trees. oc = OutputCapture() oc.set_log_level(logging.ERROR) oc.capture_output() try: self.check_names(['/tmp/another_unittest.py'], []) finally: _, _, logs = oc.restore_output() self.assertTrue('another_unittest.py' in logs) # Paths that don't exist are errors. oc.capture_output() try: self.check_names(['/foo/bar/notexist_unittest.py'], []) finally: _, _, logs = oc.restore_output() self.assertTrue('notexist_unittest.py' in logs) # Names that don't exist are caught later, at load time. self.check_names(['bar.notexist_unittest'], ['bar.notexist_unittest'])