Tarek Ziade | 1231a4e | 2011-05-19 13:07:25 +0200 | [diff] [blame] | 1 | """Tests for packaging.config.""" |
| 2 | import os |
| 3 | import sys |
| 4 | import logging |
| 5 | from io import StringIO |
| 6 | |
| 7 | from packaging import command |
| 8 | from packaging.dist import Distribution |
| 9 | from packaging.errors import PackagingFileError |
| 10 | from packaging.compiler import new_compiler, _COMPILERS |
| 11 | from packaging.command.sdist import sdist |
| 12 | |
| 13 | from packaging.tests import unittest, support |
Ezio Melotti | cad648c | 2011-05-19 21:25:10 +0300 | [diff] [blame] | 14 | from packaging.tests.support import requires_zlib |
Tarek Ziade | 1231a4e | 2011-05-19 13:07:25 +0200 | [diff] [blame] | 15 | |
| 16 | |
| 17 | SETUP_CFG = """ |
| 18 | [metadata] |
| 19 | name = RestingParrot |
| 20 | version = 0.6.4 |
| 21 | author = Carl Meyer |
| 22 | author_email = carl@oddbird.net |
| 23 | maintainer = Éric Araujo |
| 24 | maintainer_email = merwok@netwok.org |
| 25 | summary = A sample project demonstrating packaging |
| 26 | description-file = %(description-file)s |
| 27 | keywords = packaging, sample project |
| 28 | |
| 29 | classifier = |
| 30 | Development Status :: 4 - Beta |
| 31 | Environment :: Console (Text Based) |
| 32 | Environment :: X11 Applications :: GTK; python_version < '3' |
| 33 | License :: OSI Approved :: MIT License |
| 34 | Programming Language :: Python |
| 35 | Programming Language :: Python :: 2 |
| 36 | Programming Language :: Python :: 3 |
| 37 | |
| 38 | requires_python = >=2.4, <3.2 |
| 39 | |
| 40 | requires_dist = |
| 41 | PetShoppe |
| 42 | MichaelPalin (> 1.1) |
| 43 | pywin32; sys.platform == 'win32' |
| 44 | pysqlite2; python_version < '2.5' |
| 45 | inotify (0.0.1); sys.platform == 'linux2' |
| 46 | |
| 47 | requires_external = libxml2 |
| 48 | |
| 49 | provides_dist = packaging-sample-project (0.2) |
| 50 | unittest2-sample-project |
| 51 | |
| 52 | project_url = |
| 53 | Main repository, http://bitbucket.org/carljm/sample-distutils2-project |
| 54 | Fork in progress, http://bitbucket.org/Merwok/sample-distutils2-project |
| 55 | |
| 56 | [files] |
| 57 | packages_root = src |
| 58 | |
| 59 | packages = one |
| 60 | two |
| 61 | three |
| 62 | |
| 63 | modules = haven |
| 64 | |
| 65 | scripts = |
| 66 | script1.py |
| 67 | scripts/find-coconuts |
| 68 | bin/taunt |
| 69 | |
| 70 | package_data = |
| 71 | cheese = data/templates/* |
| 72 | |
| 73 | extra_files = %(extra-files)s |
| 74 | |
| 75 | # Replaces MANIFEST.in |
| 76 | sdist_extra = |
| 77 | include THANKS HACKING |
| 78 | recursive-include examples *.txt *.py |
| 79 | prune examples/sample?/build |
| 80 | |
| 81 | resources= |
| 82 | bm/ {b1,b2}.gif = {icon} |
| 83 | Cf*/ *.CFG = {config}/baBar/ |
| 84 | init_script = {script}/JunGle/ |
| 85 | |
| 86 | [global] |
| 87 | commands = |
| 88 | packaging.tests.test_config.FooBarBazTest |
| 89 | |
| 90 | compilers = |
| 91 | packaging.tests.test_config.DCompiler |
| 92 | |
Éric Araujo | 643cb73 | 2011-06-11 00:33:38 +0200 | [diff] [blame] | 93 | setup_hooks = %(setup-hooks)s |
Tarek Ziade | 1231a4e | 2011-05-19 13:07:25 +0200 | [diff] [blame] | 94 | |
| 95 | |
| 96 | |
| 97 | [install_dist] |
| 98 | sub_commands = foo |
| 99 | """ |
| 100 | |
| 101 | # Can not be merged with SETUP_CFG else install_dist |
| 102 | # command will fail when trying to compile C sources |
| 103 | EXT_SETUP_CFG = """ |
| 104 | [files] |
| 105 | packages = one |
| 106 | two |
| 107 | |
| 108 | [extension=speed_coconuts] |
| 109 | name = one.speed_coconuts |
| 110 | sources = c_src/speed_coconuts.c |
| 111 | extra_link_args = "`gcc -print-file-name=libgcc.a`" -shared |
| 112 | define_macros = HAVE_CAIRO HAVE_GTK2 |
| 113 | libraries = gecodeint gecodekernel -- sys.platform != 'win32' |
| 114 | GecodeInt GecodeKernel -- sys.platform == 'win32' |
| 115 | |
| 116 | [extension=fast_taunt] |
Éric Araujo | 0a975f9 | 2011-06-09 07:47:25 +0200 | [diff] [blame] | 117 | name = two.fast_taunt |
Tarek Ziade | 1231a4e | 2011-05-19 13:07:25 +0200 | [diff] [blame] | 118 | sources = cxx_src/utils_taunt.cxx |
| 119 | cxx_src/python_module.cxx |
| 120 | include_dirs = /usr/include/gecode |
| 121 | /usr/include/blitz |
| 122 | extra_compile_args = -fPIC -O2 |
| 123 | -DGECODE_VERSION=$(./gecode_version) -- sys.platform != 'win32' |
| 124 | /DGECODE_VERSION='win32' -- sys.platform == 'win32' |
| 125 | language = cxx |
| 126 | |
| 127 | """ |
| 128 | |
Éric Araujo | 3e425ac | 2011-06-19 21:23:43 +0200 | [diff] [blame] | 129 | HOOKS_MODULE = """ |
| 130 | import logging |
| 131 | |
| 132 | logger = logging.getLogger('packaging') |
| 133 | |
| 134 | def logging_hook(config): |
| 135 | logger.warning('logging_hook called') |
| 136 | """ |
| 137 | |
Tarek Ziade | 1231a4e | 2011-05-19 13:07:25 +0200 | [diff] [blame] | 138 | |
| 139 | class DCompiler: |
| 140 | name = 'd' |
| 141 | description = 'D Compiler' |
| 142 | |
| 143 | def __init__(self, *args): |
| 144 | pass |
| 145 | |
| 146 | |
Éric Araujo | 643cb73 | 2011-06-11 00:33:38 +0200 | [diff] [blame] | 147 | def version_hook(config): |
| 148 | config['metadata']['version'] += '.dev1' |
| 149 | |
| 150 | |
| 151 | def first_hook(config): |
| 152 | config['files']['modules'] += '\n first' |
| 153 | |
| 154 | |
| 155 | def third_hook(config): |
| 156 | config['files']['modules'] += '\n third' |
Tarek Ziade | 1231a4e | 2011-05-19 13:07:25 +0200 | [diff] [blame] | 157 | |
| 158 | |
| 159 | class FooBarBazTest: |
| 160 | |
| 161 | def __init__(self, dist): |
| 162 | self.distribution = dist |
| 163 | |
| 164 | @classmethod |
| 165 | def get_command_name(cls): |
| 166 | return 'foo' |
| 167 | |
| 168 | def run(self): |
| 169 | self.distribution.foo_was_here = True |
| 170 | |
| 171 | def nothing(self): |
| 172 | pass |
| 173 | |
| 174 | def get_source_files(self): |
| 175 | return [] |
| 176 | |
| 177 | ensure_finalized = finalize_options = initialize_options = nothing |
| 178 | |
| 179 | |
| 180 | class ConfigTestCase(support.TempdirManager, |
| 181 | support.EnvironRestorer, |
| 182 | support.LoggingCatcher, |
| 183 | unittest.TestCase): |
| 184 | |
| 185 | restore_environ = ['PLAT'] |
| 186 | |
| 187 | def setUp(self): |
| 188 | super(ConfigTestCase, self).setUp() |
| 189 | self.addCleanup(setattr, sys, 'stdout', sys.stdout) |
| 190 | self.addCleanup(setattr, sys, 'stderr', sys.stderr) |
| 191 | sys.stdout = StringIO() |
| 192 | sys.stderr = StringIO() |
| 193 | |
| 194 | self.addCleanup(os.chdir, os.getcwd()) |
| 195 | tempdir = self.mkdtemp() |
Tarek Ziade | eb64b61 | 2011-05-23 18:31:20 +0200 | [diff] [blame] | 196 | self.working_dir = os.getcwd() |
Tarek Ziade | 1231a4e | 2011-05-19 13:07:25 +0200 | [diff] [blame] | 197 | os.chdir(tempdir) |
| 198 | self.tempdir = tempdir |
| 199 | |
Tarek Ziade | eb64b61 | 2011-05-23 18:31:20 +0200 | [diff] [blame] | 200 | def tearDown(self): |
| 201 | os.chdir(self.working_dir) |
| 202 | super(ConfigTestCase, self).tearDown() |
| 203 | |
Tarek Ziade | 1231a4e | 2011-05-19 13:07:25 +0200 | [diff] [blame] | 204 | def write_setup(self, kwargs=None): |
| 205 | opts = {'description-file': 'README', 'extra-files': '', |
Éric Araujo | 643cb73 | 2011-06-11 00:33:38 +0200 | [diff] [blame] | 206 | 'setup-hooks': 'packaging.tests.test_config.version_hook'} |
Tarek Ziade | 1231a4e | 2011-05-19 13:07:25 +0200 | [diff] [blame] | 207 | if kwargs: |
| 208 | opts.update(kwargs) |
Victor Stinner | dd13dd4 | 2011-05-19 18:45:32 +0200 | [diff] [blame] | 209 | self.write_file('setup.cfg', SETUP_CFG % opts, encoding='utf-8') |
Tarek Ziade | 1231a4e | 2011-05-19 13:07:25 +0200 | [diff] [blame] | 210 | |
| 211 | def get_dist(self): |
| 212 | dist = Distribution() |
| 213 | dist.parse_config_files() |
| 214 | return dist |
| 215 | |
| 216 | def test_config(self): |
| 217 | self.write_setup() |
| 218 | self.write_file('README', 'yeah') |
| 219 | os.mkdir('bm') |
| 220 | self.write_file(('bm', 'b1.gif'), '') |
| 221 | self.write_file(('bm', 'b2.gif'), '') |
| 222 | os.mkdir('Cfg') |
| 223 | self.write_file(('Cfg', 'data.CFG'), '') |
| 224 | self.write_file('init_script', '') |
| 225 | |
| 226 | # try to load the metadata now |
| 227 | dist = self.get_dist() |
| 228 | |
| 229 | # check what was done |
| 230 | self.assertEqual(dist.metadata['Author'], 'Carl Meyer') |
| 231 | self.assertEqual(dist.metadata['Author-Email'], 'carl@oddbird.net') |
| 232 | |
| 233 | # the hook adds .dev1 |
| 234 | self.assertEqual(dist.metadata['Version'], '0.6.4.dev1') |
| 235 | |
| 236 | wanted = [ |
| 237 | 'Development Status :: 4 - Beta', |
| 238 | 'Environment :: Console (Text Based)', |
| 239 | "Environment :: X11 Applications :: GTK; python_version < '3'", |
| 240 | 'License :: OSI Approved :: MIT License', |
| 241 | 'Programming Language :: Python', |
| 242 | 'Programming Language :: Python :: 2', |
| 243 | 'Programming Language :: Python :: 3'] |
| 244 | self.assertEqual(dist.metadata['Classifier'], wanted) |
| 245 | |
| 246 | wanted = ['packaging', 'sample project'] |
| 247 | self.assertEqual(dist.metadata['Keywords'], wanted) |
| 248 | |
| 249 | self.assertEqual(dist.metadata['Requires-Python'], '>=2.4, <3.2') |
| 250 | |
| 251 | wanted = ['PetShoppe', |
| 252 | 'MichaelPalin (> 1.1)', |
| 253 | "pywin32; sys.platform == 'win32'", |
| 254 | "pysqlite2; python_version < '2.5'", |
| 255 | "inotify (0.0.1); sys.platform == 'linux2'"] |
| 256 | |
| 257 | self.assertEqual(dist.metadata['Requires-Dist'], wanted) |
| 258 | urls = [('Main repository', |
| 259 | 'http://bitbucket.org/carljm/sample-distutils2-project'), |
| 260 | ('Fork in progress', |
| 261 | 'http://bitbucket.org/Merwok/sample-distutils2-project')] |
| 262 | self.assertEqual(dist.metadata['Project-Url'], urls) |
| 263 | |
| 264 | self.assertEqual(dist.packages, ['one', 'two', 'three']) |
| 265 | self.assertEqual(dist.py_modules, ['haven']) |
| 266 | self.assertEqual(dist.package_data, {'cheese': 'data/templates/*'}) |
| 267 | self.assertEqual( |
| 268 | {'bm/b1.gif': '{icon}/b1.gif', |
| 269 | 'bm/b2.gif': '{icon}/b2.gif', |
| 270 | 'Cfg/data.CFG': '{config}/baBar/data.CFG', |
| 271 | 'init_script': '{script}/JunGle/init_script'}, |
| 272 | dist.data_files) |
| 273 | |
| 274 | self.assertEqual(dist.package_dir, 'src') |
| 275 | |
| 276 | # Make sure we get the foo command loaded. We use a string comparison |
| 277 | # instead of assertIsInstance because the class is not the same when |
| 278 | # this test is run directly: foo is packaging.tests.test_config.Foo |
| 279 | # because get_command_class uses the full name, but a bare "Foo" in |
| 280 | # this file would be __main__.Foo when run as "python test_config.py". |
| 281 | # The name FooBarBazTest should be unique enough to prevent |
| 282 | # collisions. |
| 283 | self.assertEqual('FooBarBazTest', |
| 284 | dist.get_command_obj('foo').__class__.__name__) |
| 285 | |
| 286 | # did the README got loaded ? |
| 287 | self.assertEqual(dist.metadata['description'], 'yeah') |
| 288 | |
| 289 | # do we have the D Compiler enabled ? |
| 290 | self.assertIn('d', _COMPILERS) |
| 291 | d = new_compiler(compiler='d') |
| 292 | self.assertEqual(d.description, 'D Compiler') |
| 293 | |
| 294 | def test_multiple_description_file(self): |
| 295 | self.write_setup({'description-file': 'README CHANGES'}) |
| 296 | self.write_file('README', 'yeah') |
| 297 | self.write_file('CHANGES', 'changelog2') |
| 298 | dist = self.get_dist() |
| 299 | self.assertEqual(dist.metadata.requires_files, ['README', 'CHANGES']) |
| 300 | |
| 301 | def test_multiline_description_file(self): |
| 302 | self.write_setup({'description-file': 'README\n CHANGES'}) |
| 303 | self.write_file('README', 'yeah') |
| 304 | self.write_file('CHANGES', 'changelog') |
| 305 | dist = self.get_dist() |
| 306 | self.assertEqual(dist.metadata['description'], 'yeah\nchangelog') |
| 307 | self.assertEqual(dist.metadata.requires_files, ['README', 'CHANGES']) |
| 308 | |
| 309 | def test_parse_extensions_in_config(self): |
| 310 | self.write_file('setup.cfg', EXT_SETUP_CFG) |
| 311 | dist = self.get_dist() |
| 312 | |
| 313 | ext_modules = dict((mod.name, mod) for mod in dist.ext_modules) |
| 314 | self.assertEqual(len(ext_modules), 2) |
| 315 | ext = ext_modules.get('one.speed_coconuts') |
| 316 | self.assertEqual(ext.sources, ['c_src/speed_coconuts.c']) |
| 317 | self.assertEqual(ext.define_macros, ['HAVE_CAIRO', 'HAVE_GTK2']) |
| 318 | libs = ['gecodeint', 'gecodekernel'] |
| 319 | if sys.platform == 'win32': |
| 320 | libs = ['GecodeInt', 'GecodeKernel'] |
| 321 | self.assertEqual(ext.libraries, libs) |
| 322 | self.assertEqual(ext.extra_link_args, |
| 323 | ['`gcc -print-file-name=libgcc.a`', '-shared']) |
| 324 | |
Éric Araujo | 0a975f9 | 2011-06-09 07:47:25 +0200 | [diff] [blame] | 325 | ext = ext_modules.get('two.fast_taunt') |
Tarek Ziade | 1231a4e | 2011-05-19 13:07:25 +0200 | [diff] [blame] | 326 | self.assertEqual(ext.sources, |
| 327 | ['cxx_src/utils_taunt.cxx', 'cxx_src/python_module.cxx']) |
| 328 | self.assertEqual(ext.include_dirs, |
| 329 | ['/usr/include/gecode', '/usr/include/blitz']) |
| 330 | cargs = ['-fPIC', '-O2'] |
| 331 | if sys.platform == 'win32': |
Tarek Ziade | 91f0e34 | 2011-05-21 12:00:10 +0200 | [diff] [blame] | 332 | cargs.append("/DGECODE_VERSION=win32") |
Tarek Ziade | 1231a4e | 2011-05-19 13:07:25 +0200 | [diff] [blame] | 333 | else: |
| 334 | cargs.append('-DGECODE_VERSION=$(./gecode_version)') |
| 335 | self.assertEqual(ext.extra_compile_args, cargs) |
| 336 | self.assertEqual(ext.language, 'cxx') |
| 337 | |
Éric Araujo | 3e425ac | 2011-06-19 21:23:43 +0200 | [diff] [blame] | 338 | def test_project_setup_hook_works(self): |
| 339 | # Bug #11637: ensure the project directory is on sys.path to allow |
| 340 | # project-specific hooks |
| 341 | self.write_setup({'setup-hooks': 'hooks.logging_hook'}) |
| 342 | self.write_file('README', 'yeah') |
| 343 | self.write_file('hooks.py', HOOKS_MODULE) |
| 344 | self.get_dist() |
| 345 | logs = self.get_logs(logging.WARNING) |
| 346 | self.assertEqual(['logging_hook called'], logs) |
| 347 | self.assertIn('hooks', sys.modules) |
| 348 | |
Éric Araujo | 643cb73 | 2011-06-11 00:33:38 +0200 | [diff] [blame] | 349 | def test_missing_setup_hook_warns(self): |
| 350 | self.write_setup({'setup-hooks': 'this.does._not.exist'}) |
Tarek Ziade | 1231a4e | 2011-05-19 13:07:25 +0200 | [diff] [blame] | 351 | self.write_file('README', 'yeah') |
Éric Araujo | 3e425ac | 2011-06-19 21:23:43 +0200 | [diff] [blame] | 352 | self.get_dist() |
Tarek Ziade | 1231a4e | 2011-05-19 13:07:25 +0200 | [diff] [blame] | 353 | logs = self.get_logs(logging.WARNING) |
| 354 | self.assertEqual(1, len(logs)) |
Éric Araujo | 643cb73 | 2011-06-11 00:33:38 +0200 | [diff] [blame] | 355 | self.assertIn('cannot find setup hook', logs[0]) |
| 356 | |
| 357 | def test_multiple_setup_hooks(self): |
| 358 | self.write_setup({ |
| 359 | 'setup-hooks': '\n packaging.tests.test_config.first_hook' |
| 360 | '\n packaging.tests.test_config.missing_hook' |
| 361 | '\n packaging.tests.test_config.third_hook' |
| 362 | }) |
| 363 | self.write_file('README', 'yeah') |
| 364 | dist = self.get_dist() |
| 365 | |
| 366 | self.assertEqual(['haven', 'first', 'third'], dist.py_modules) |
| 367 | logs = self.get_logs(logging.WARNING) |
| 368 | self.assertEqual(1, len(logs)) |
| 369 | self.assertIn('cannot find setup hook', logs[0]) |
Tarek Ziade | 1231a4e | 2011-05-19 13:07:25 +0200 | [diff] [blame] | 370 | |
| 371 | def test_metadata_requires_description_files_missing(self): |
Éric Araujo | 8474f29 | 2011-06-11 00:21:18 +0200 | [diff] [blame] | 372 | self.write_setup({'description-file': 'README README2'}) |
Tarek Ziade | 1231a4e | 2011-05-19 13:07:25 +0200 | [diff] [blame] | 373 | self.write_file('README', 'yeah') |
| 374 | self.write_file('README2', 'yeah') |
| 375 | os.mkdir('src') |
| 376 | self.write_file(('src', 'haven.py'), '#') |
| 377 | self.write_file('script1.py', '#') |
| 378 | os.mkdir('scripts') |
| 379 | self.write_file(('scripts', 'find-coconuts'), '#') |
| 380 | os.mkdir('bin') |
| 381 | self.write_file(('bin', 'taunt'), '#') |
| 382 | |
| 383 | for pkg in ('one', 'two', 'three'): |
| 384 | pkg = os.path.join('src', pkg) |
| 385 | os.mkdir(pkg) |
| 386 | self.write_file((pkg, '__init__.py'), '#') |
| 387 | |
| 388 | dist = self.get_dist() |
| 389 | cmd = sdist(dist) |
| 390 | cmd.finalize_options() |
| 391 | cmd.get_file_list() |
| 392 | self.assertRaises(PackagingFileError, cmd.make_distribution) |
| 393 | |
Ezio Melotti | cad648c | 2011-05-19 21:25:10 +0300 | [diff] [blame] | 394 | @requires_zlib |
Tarek Ziade | 1231a4e | 2011-05-19 13:07:25 +0200 | [diff] [blame] | 395 | def test_metadata_requires_description_files(self): |
| 396 | # Create the following file structure: |
| 397 | # README |
| 398 | # README2 |
| 399 | # script1.py |
| 400 | # scripts/ |
| 401 | # find-coconuts |
| 402 | # bin/ |
| 403 | # taunt |
| 404 | # src/ |
| 405 | # haven.py |
| 406 | # one/__init__.py |
| 407 | # two/__init__.py |
| 408 | # three/__init__.py |
| 409 | |
| 410 | self.write_setup({'description-file': 'README\n README2', |
| 411 | 'extra-files': '\n README3'}) |
| 412 | self.write_file('README', 'yeah 1') |
| 413 | self.write_file('README2', 'yeah 2') |
| 414 | self.write_file('README3', 'yeah 3') |
| 415 | os.mkdir('src') |
| 416 | self.write_file(('src', 'haven.py'), '#') |
| 417 | self.write_file('script1.py', '#') |
| 418 | os.mkdir('scripts') |
| 419 | self.write_file(('scripts', 'find-coconuts'), '#') |
| 420 | os.mkdir('bin') |
| 421 | self.write_file(('bin', 'taunt'), '#') |
| 422 | |
| 423 | for pkg in ('one', 'two', 'three'): |
| 424 | pkg = os.path.join('src', pkg) |
| 425 | os.mkdir(pkg) |
| 426 | self.write_file((pkg, '__init__.py'), '#') |
| 427 | |
| 428 | dist = self.get_dist() |
| 429 | self.assertIn('yeah 1\nyeah 2', dist.metadata['description']) |
| 430 | |
| 431 | cmd = sdist(dist) |
| 432 | cmd.finalize_options() |
| 433 | cmd.get_file_list() |
| 434 | self.assertRaises(PackagingFileError, cmd.make_distribution) |
| 435 | |
| 436 | self.write_setup({'description-file': 'README\n README2', |
| 437 | 'extra-files': '\n README2\n README'}) |
| 438 | dist = self.get_dist() |
| 439 | cmd = sdist(dist) |
| 440 | cmd.finalize_options() |
| 441 | cmd.get_file_list() |
| 442 | cmd.make_distribution() |
| 443 | with open('MANIFEST') as fp: |
| 444 | self.assertIn('README\nREADME2\n', fp.read()) |
| 445 | |
| 446 | def test_sub_commands(self): |
| 447 | self.write_setup() |
| 448 | self.write_file('README', 'yeah') |
| 449 | os.mkdir('src') |
| 450 | self.write_file(('src', 'haven.py'), '#') |
| 451 | self.write_file('script1.py', '#') |
| 452 | os.mkdir('scripts') |
| 453 | self.write_file(('scripts', 'find-coconuts'), '#') |
| 454 | os.mkdir('bin') |
| 455 | self.write_file(('bin', 'taunt'), '#') |
| 456 | |
| 457 | for pkg in ('one', 'two', 'three'): |
| 458 | pkg = os.path.join('src', pkg) |
| 459 | os.mkdir(pkg) |
| 460 | self.write_file((pkg, '__init__.py'), '#') |
| 461 | |
| 462 | # try to run the install command to see if foo is called |
| 463 | dist = self.get_dist() |
| 464 | self.assertIn('foo', command.get_command_names()) |
| 465 | self.assertEqual('FooBarBazTest', |
| 466 | dist.get_command_obj('foo').__class__.__name__) |
| 467 | |
| 468 | |
| 469 | def test_suite(): |
| 470 | return unittest.makeSuite(ConfigTestCase) |
| 471 | |
| 472 | if __name__ == '__main__': |
| 473 | unittest.main(defaultTest='test_suite') |