Commit fb76e721 authored by PJ Eby's avatar PJ Eby

Enhanced test loader to scan packages as well as modules, and call

``additional_tests()`` if present to get non-unittest tests.

--HG--
branch : setuptools
extra : convert_revision : svn%3A6015fed2-1504-0410-9fe1-9d1591cc4771/sandbox/trunk/setuptools%4043412
parent c1294e1d
...@@ -37,7 +37,7 @@ setup( ...@@ -37,7 +37,7 @@ setup(
long_description = get_description(), long_description = get_description(),
keywords = "CPAN PyPI distutils eggs package management", keywords = "CPAN PyPI distutils eggs package management",
url = "http://peak.telecommunity.com/DevCenter/setuptools", url = "http://peak.telecommunity.com/DevCenter/setuptools",
test_suite = 'setuptools.tests.test_suite', test_suite = 'setuptools.tests',
packages = find_packages(), packages = find_packages(),
package_data = {'setuptools':['*.exe']}, package_data = {'setuptools':['*.exe']},
......
...@@ -332,9 +332,14 @@ unless you need the associated ``setuptools`` feature. ...@@ -332,9 +332,14 @@ unless you need the associated ``setuptools`` feature.
for more information. for more information.
``test_suite`` ``test_suite``
A string naming a ``unittest.TestCase`` subclass (or a module containing A string naming a ``unittest.TestCase`` subclass (or a package or module
one or more of them, or a method of such a subclass), or naming a function containing one or more of them, or a method of such a subclass), or naming
that can be called with no arguments and returns a ``unittest.TestSuite``. a function that can be called with no arguments and returns a
``unittest.TestSuite``. If the named suite is a module, and the module
has an ``additional_tests()`` function, it is called and the results are
added to the tests to be run. If the named suite is a package, any
submodules and subpackages are recursively added to the overall test suite.
Specifying this argument enables use of the `test`_ command to run the Specifying this argument enables use of the `test`_ command to run the
specified test suite, e.g. via ``setup.py test``. See the section on the specified test suite, e.g. via ``setup.py test``. See the section on the
`test`_ command below for more details. `test`_ command below for more details.
...@@ -2070,12 +2075,17 @@ up-to-date. ...@@ -2070,12 +2075,17 @@ up-to-date.
To use this command, your project's tests must be wrapped in a ``unittest`` To use this command, your project's tests must be wrapped in a ``unittest``
test suite by either a function, a ``TestCase`` class or method, or a module test suite by either a function, a ``TestCase`` class or method, or a module
containing ``TestCase`` classes. Note that many test systems including or package containing ``TestCase`` classes. If the named suite is a module,
``doctest`` support wrapping their non-``unittest`` tests in ``TestSuite`` and the module has an ``additional_tests()`` function, it is called and the
objects. So, if you are using a test package that does not support this, we result (which must be a ``unittest.TestSuite``) is added to the tests to be
suggest you encourage its developers to implement test suite support, as this run. If the named suite is a package, any submodules and subpackages are
is a convenient and standard way to aggregate a collection of tests to be run recursively added to the overall test suite.
under a common test harness.
Note that many test systems including ``doctest`` support wrapping their
non-``unittest`` tests in ``TestSuite`` objects. So, if you are using a test
package that does not support this, we suggest you encourage its developers to
implement test suite support, as this is a convenient and standard way to
aggregate a collection of tests to be run under a common test harness.
By default, tests will be run in the "verbose" mode of the ``unittest`` By default, tests will be run in the "verbose" mode of the ``unittest``
package's text test runner, but you can get the "quiet" mode (just dots) if package's text test runner, but you can get the "quiet" mode (just dots) if
...@@ -2349,6 +2359,9 @@ Release Notes/Change History ...@@ -2349,6 +2359,9 @@ Release Notes/Change History
---------------------------- ----------------------------
0.6a11 0.6a11
* Enhanced test loader to scan packages as well as modules, and call
``additional_tests()`` if present to get non-unittest tests.
* Support namespace packages in conjunction with system packagers, by omitting * Support namespace packages in conjunction with system packagers, by omitting
the installation of any ``__init__.py`` files for namespace packages, and the installation of any ``__init__.py`` files for namespace packages, and
adding a special ``.pth`` file to create a working package in adding a special ``.pth`` file to create a working package in
......
...@@ -2,6 +2,42 @@ from setuptools import Command ...@@ -2,6 +2,42 @@ from setuptools import Command
from distutils.errors import DistutilsOptionError from distutils.errors import DistutilsOptionError
import sys import sys
from pkg_resources import * from pkg_resources import *
from unittest import TestLoader, main
class ScanningLoader(TestLoader):
def loadTestsFromModule(self, module):
"""Return a suite of all tests cases contained in the given module
If the module is a package, load tests from all the modules in it.
If the module has an ``additional_tests`` function, call it and add
the return value to the tests.
"""
tests = [TestLoader.loadTestsFromModule(self,module)]
if hasattr(module, "additional_tests"):
tests.append(module.additional_tests())
if hasattr(module, '__path__'):
for file in resource_listdir(module.__name__, ''):
if file.endswith('.py') and file!='__init__.py':
submodule = module.__name__+'.'+file[:-3]
else:
if resource_exists(
module.__name__, file+'/__init__.py'
):
submodule = module.__name__+'.'+file
else:
continue
tests.append(self.loadTestsFromName(submodule))
if len(tests)>1:
return self.suiteClass(tests)
else:
return tests[0] # don't create a nested suite for only one return
class test(Command): class test(Command):
...@@ -39,6 +75,11 @@ class test(Command): ...@@ -39,6 +75,11 @@ class test(Command):
if self.verbose: if self.verbose:
self.test_args.insert(0,'--verbose') self.test_args.insert(0,'--verbose')
def run(self): def run(self):
# Ensure metadata is up-to-date # Ensure metadata is up-to-date
self.run_command('egg_info') self.run_command('egg_info')
...@@ -70,10 +111,10 @@ class test(Command): ...@@ -70,10 +111,10 @@ class test(Command):
dist = Distribution(path_item, metadata, project_name=ei_cmd.egg_name) dist = Distribution(path_item, metadata, project_name=ei_cmd.egg_name)
working_set.add(dist) working_set.add(dist)
require(str(dist.as_requirement())) require(str(dist.as_requirement()))
unittest.main(None, None, [unittest.__file__]+self.test_args) unittest.main(
None, None, [unittest.__file__]+self.test_args,
testLoader = ScanningLoader()
)
......
...@@ -13,6 +13,12 @@ from distutils.version import StrictVersion, LooseVersion ...@@ -13,6 +13,12 @@ from distutils.version import StrictVersion, LooseVersion
from distutils.util import convert_path from distutils.util import convert_path
import sys, os.path import sys, os.path
def additional_tests():
import doctest
return doctest.DocFileSuite(
'api_tests.txt', optionflags=doctest.ELLIPSIS, package='pkg_resources',
)
def makeSetup(**args): def makeSetup(**args):
"""Return distribution from 'setup(**args)', without executing commands""" """Return distribution from 'setup(**args)', without executing commands"""
...@@ -33,12 +39,6 @@ def makeSetup(**args): ...@@ -33,12 +39,6 @@ def makeSetup(**args):
class DependsTests(TestCase): class DependsTests(TestCase):
def testExtractConst(self): def testExtractConst(self):
...@@ -362,47 +362,6 @@ class TestCommandTests(TestCase): ...@@ -362,47 +362,6 @@ class TestCommandTests(TestCase):
ts5 = makeSetup().get_command_obj('test') ts5 = makeSetup().get_command_obj('test')
ts5.ensure_finalized() ts5.ensure_finalized()
self.assertEqual(ts5.test_suite, None) self.assertEqual(ts5.test_suite, None)
def test_api():
import doctest
return doctest.DocFileSuite(
'api_tests.txt', optionflags=doctest.ELLIPSIS, package='pkg_resources',
)
testClasses = (DependsTests, DistroTests, FeatureTests, TestCommandTests)
testNames = ["setuptools.tests.test_resources", "setuptools.tests.test_api"]
def test_suite():
return TestSuite(
[makeSuite(t,'test') for t in testClasses]+
[defaultTestLoader.loadTestsFromName(n) for n in testNames]
)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment