1"""
2Test Loader
3-----------
4
5nose's test loader implements the same basic functionality as its
6superclass, unittest.TestLoader, but extends it by more liberal
7interpretations of what may be a test and how a test may be named.
8"""
9
10
11import logging
12import os
13import sys
14import unittest
15import types
16from inspect import isfunction
17from nose.pyversion import unbound_method, ismethod
18from nose.case import FunctionTestCase, MethodTestCase
19from nose.failure import Failure
20from nose.config import Config
21from nose.importer import Importer, add_path, remove_path
22from nose.selector import defaultSelector, TestAddress
23from nose.util import func_lineno, getpackage, isclass, isgenerator, \
24    ispackage, regex_last_key, resolve_name, transplant_func, \
25    transplant_class, test_address
26from nose.suite import ContextSuiteFactory, ContextList, LazySuite
27from nose.pyversion import sort_list, cmp_to_key
28import collections
29
30
31log = logging.getLogger(__name__)
32#log.setLevel(logging.DEBUG)
33
34# for efficiency and easier mocking
35op_normpath = os.path.normpath
36op_abspath = os.path.abspath
37op_join = os.path.join
38op_isdir = os.path.isdir
39op_isfile = os.path.isfile
40
41
42__all__ = ['TestLoader', 'defaultTestLoader']
43
44
45class TestLoader(unittest.TestLoader):
46    """Test loader that extends unittest.TestLoader to:
47
48    * Load tests from test-like functions and classes that are not
49      unittest.TestCase subclasses
50    * Find and load test modules in a directory
51    * Support tests that are generators
52    * Support easy extensions of or changes to that behavior through plugins
53    """
54    config = None
55    importer = None
56    workingDir = None
57    selector = None
58    suiteClass = None
59
60    def __init__(self, config=None, importer=None, workingDir=None,
61                 selector=None):
62        """Initialize a test loader.
63
64        Parameters (all optional):
65
66        * config: provide a `nose.config.Config`_ or other config class
67          instance; if not provided a `nose.config.Config`_ with
68          default values is used.
69        * importer: provide an importer instance that implements
70          `importFromPath`. If not provided, a
71          `nose.importer.Importer`_ is used.
72        * workingDir: the directory to which file and module names are
73          relative. If not provided, assumed to be the current working
74          directory.
75        * selector: a selector class or instance. If a class is
76          provided, it will be instantiated with one argument, the
77          current config. If not provided, a `nose.selector.Selector`_
78          is used.
79        """
80        if config is None:
81            config = Config()
82        if importer is None:
83            importer = Importer(config=config)
84        if workingDir is None:
85            workingDir = config.workingDir
86        if selector is None:
87            selector = defaultSelector(config)
88        elif isclass(selector):
89            selector = selector(config)
90        self.config = config
91        self.importer = importer
92        self.workingDir = op_normpath(op_abspath(workingDir))
93        self.selector = selector
94        if config.addPaths:
95            add_path(workingDir, config)
96        self.suiteClass = ContextSuiteFactory(config=config)
97
98        self._visitedPaths = set([])
99
100        unittest.TestLoader.__init__(self)
101
102    def getTestCaseNames(self, testCaseClass):
103        """Override to select with selector, unless
104        config.getTestCaseNamesCompat is True
105        """
106        if self.config.getTestCaseNamesCompat:
107            return unittest.TestLoader.getTestCaseNames(self, testCaseClass)
108
109        def wanted(attr, cls=testCaseClass, sel=self.selector):
110            item = getattr(cls, attr, None)
111            if isfunction(item):
112                item = unbound_method(cls, item)
113            elif not ismethod(item):
114                return False
115            return sel.wantMethod(item)
116
117        cases = list(filter(wanted, dir(testCaseClass)))
118
119        # add runTest if nothing else picked
120        if not cases and hasattr(testCaseClass, 'runTest'):
121            cases = ['runTest']
122        if self.sortTestMethodsUsing:
123            sort_list(cases, cmp_to_key(self.sortTestMethodsUsing))
124        return cases
125
126    def _haveVisited(self, path):
127        # For cases where path is None, we always pretend we haven't visited
128        # them.
129        if path is None:
130            return False
131
132        return path in self._visitedPaths
133
134    def _addVisitedPath(self, path):
135        if path is not None:
136            self._visitedPaths.add(path)
137
138    def loadTestsFromDir(self, path):
139        """Load tests from the directory at path. This is a generator
140        -- each suite of tests from a module or other file is yielded
141        and is expected to be executed before the next file is
142        examined.
143        """
144        log.debug("load from dir %s", path)
145        plugins = self.config.plugins
146        plugins.beforeDirectory(path)
147        if self.config.addPaths:
148            paths_added = add_path(path, self.config)
149
150        entries = os.listdir(path)
151        sort_list(entries, regex_last_key(self.config.testMatch))
152        for entry in entries:
153            # this hard-coded initial-dot test will be removed:
154            # http://code.google.com/p/python-nose/issues/detail?id=82
155            if entry.startswith('.'):
156                continue
157            entry_path = op_abspath(op_join(path, entry))
158            is_file = op_isfile(entry_path)
159            wanted = False
160            if is_file:
161                is_dir = False
162                wanted = self.selector.wantFile(entry_path)
163            else:
164                is_dir = op_isdir(entry_path)
165                if is_dir:
166                    # this hard-coded initial-underscore test will be removed:
167                    # http://code.google.com/p/python-nose/issues/detail?id=82
168                    if entry.startswith('_'):
169                        continue
170                    wanted = self.selector.wantDirectory(entry_path)
171            is_package = ispackage(entry_path)
172
173            # Python 3.3 now implements PEP 420: Implicit Namespace Packages.
174            # As a result, it's now possible that parent paths that have a
175            # segment with the same basename as our package ends up
176            # in module.__path__.  So we have to keep track of what we've
177            # visited, and not-revisit them again.
178            if wanted and not self._haveVisited(entry_path):
179                self._addVisitedPath(entry_path)
180                if is_file:
181                    plugins.beforeContext()
182                    if entry.endswith('.py'):
183                        yield self.loadTestsFromName(
184                            entry_path, discovered=True)
185                    else:
186                        yield self.loadTestsFromFile(entry_path)
187                    plugins.afterContext()
188                elif is_package:
189                    # Load the entry as a package: given the full path,
190                    # loadTestsFromName() will figure it out
191                    yield self.loadTestsFromName(
192                        entry_path, discovered=True)
193                else:
194                    # Another test dir in this one: recurse lazily
195                    yield self.suiteClass(
196                        lambda: self.loadTestsFromDir(entry_path))
197        tests = []
198        for test in plugins.loadTestsFromDir(path):
199            tests.append(test)
200        # TODO: is this try/except needed?
201        try:
202            if tests:
203                yield self.suiteClass(tests)
204        except (KeyboardInterrupt, SystemExit):
205            raise
206        except:
207            yield self.suiteClass([Failure(*sys.exc_info())])
208
209        # pop paths
210        if self.config.addPaths:
211            for p in paths_added:
212              remove_path(p)
213        plugins.afterDirectory(path)
214
215    def loadTestsFromFile(self, filename):
216        """Load tests from a non-module file. Default is to raise a
217        ValueError; plugins may implement `loadTestsFromFile` to
218        provide a list of tests loaded from the file.
219        """
220        log.debug("Load from non-module file %s", filename)
221        try:
222            tests = [test for test in
223                     self.config.plugins.loadTestsFromFile(filename)]
224            if tests:
225                # Plugins can yield False to indicate that they were
226                # unable to load tests from a file, but it was not an
227                # error -- the file just had no tests to load.
228                tests = [_f for _f in tests if _f]
229                return self.suiteClass(tests)
230            else:
231                # Nothing was able to even try to load from this file
232                open(filename, 'r').close() # trigger os error
233                raise ValueError("Unable to load tests from file %s"
234                                 % filename)
235        except (KeyboardInterrupt, SystemExit):
236            raise
237        except:
238            exc = sys.exc_info()
239            return self.suiteClass(
240                [Failure(exc[0], exc[1], exc[2],
241                         address=(filename, None, None))])
242
243    def loadTestsFromGenerator(self, generator, module):
244        """Lazy-load tests from a generator function. The generator function
245        may yield either:
246
247        * a callable, or
248        * a function name resolvable within the same module
249        """
250        def generate(g=generator, m=module):
251            try:
252                for test in g():
253                    test_func, arg = self.parseGeneratedTest(test)
254                    if not isinstance(test_func, collections.Callable):
255                        test_func = getattr(m, test_func)
256                    yield FunctionTestCase(test_func, arg=arg, descriptor=g)
257            except KeyboardInterrupt:
258                raise
259            except:
260                exc = sys.exc_info()
261                yield Failure(exc[0], exc[1], exc[2],
262                              address=test_address(generator))
263        return self.suiteClass(generate, context=generator, can_split=False)
264
265    def loadTestsFromGeneratorMethod(self, generator, cls):
266        """Lazy-load tests from a generator method.
267
268        This is more complicated than loading from a generator function,
269        since a generator method may yield:
270
271        * a function
272        * a bound or unbound method, or
273        * a method name
274        """
275        # convert the unbound generator method
276        # into a bound method so it can be called below
277        if hasattr(generator, 'im_class'):
278            cls = generator.__self__.__class__
279        inst = cls()
280        method = generator.__name__
281        generator = getattr(inst, method)
282
283        def generate(g=generator, c=cls):
284            try:
285                for test in g():
286                    test_func, arg = self.parseGeneratedTest(test)
287                    if not isinstance(test_func, collections.Callable):
288                        test_func = unbound_method(c, getattr(c, test_func))
289                    if ismethod(test_func):
290                        yield MethodTestCase(test_func, arg=arg, descriptor=g)
291                    elif isinstance(test_func, collections.Callable):
292                        # In this case we're forcing the 'MethodTestCase'
293                        # to run the inline function as its test call,
294                        # but using the generator method as the 'method of
295                        # record' (so no need to pass it as the descriptor)
296                        yield MethodTestCase(g, test=test_func, arg=arg)
297                    else:
298                        yield Failure(
299                            TypeError,
300                            "%s is not a callable or method" % test_func)
301            except KeyboardInterrupt:
302                raise
303            except:
304                exc = sys.exc_info()
305                yield Failure(exc[0], exc[1], exc[2],
306                              address=test_address(generator))
307        return self.suiteClass(generate, context=generator, can_split=False)
308
309    def loadTestsFromModule(self, module, path=None, discovered=False):
310        """Load all tests from module and return a suite containing
311        them. If the module has been discovered and is not test-like,
312        the suite will be empty by default, though plugins may add
313        their own tests.
314        """
315        log.debug("Load from module %s", module)
316        tests = []
317        test_classes = []
318        test_funcs = []
319        # For *discovered* modules, we only load tests when the module looks
320        # testlike. For modules we've been directed to load, we always
321        # look for tests. (discovered is set to True by loadTestsFromDir)
322        if not discovered or self.selector.wantModule(module):
323            for item in dir(module):
324                test = getattr(module, item, None)
325                # print "Check %s (%s) in %s" % (item, test, module.__name__)
326                if isclass(test):
327                    if self.selector.wantClass(test):
328                        test_classes.append(test)
329                elif isfunction(test) and self.selector.wantFunction(test):
330                    test_funcs.append(test)
331            sort_list(test_classes, lambda x: x.__name__)
332            sort_list(test_funcs, func_lineno)
333            tests = [self.makeTest(t, parent=module) for t in test_classes + test_funcs]
334
335        # Now, descend into packages
336        # FIXME can or should this be lazy?
337        # is this syntax 2.2 compatible?
338        module_paths = getattr(module, '__path__', [])
339
340        if path:
341            path = os.path.normcase(os.path.realpath(path))
342
343        for module_path in module_paths:
344            log.debug("Load tests from module path %s?", module_path)
345            log.debug("path: %s os.path.realpath(%s): %s",
346                      path, os.path.normcase(module_path),
347                      os.path.realpath(os.path.normcase(module_path)))
348            if (self.config.traverseNamespace or not path) or \
349                    os.path.realpath(
350                        os.path.normcase(module_path)).startswith(path):
351                # Egg files can be on sys.path, so make sure the path is a
352                # directory before trying to load from it.
353                if os.path.isdir(module_path):
354                    tests.extend(self.loadTestsFromDir(module_path))
355
356        for test in self.config.plugins.loadTestsFromModule(module, path):
357            tests.append(test)
358
359        return self.suiteClass(ContextList(tests, context=module))
360
361    def loadTestsFromName(self, name, module=None, discovered=False):
362        """Load tests from the entity with the given name.
363
364        The name may indicate a file, directory, module, or any object
365        within a module. See `nose.util.split_test_name` for details on
366        test name parsing.
367        """
368        # FIXME refactor this method into little bites?
369        log.debug("load from %s (%s)", name, module)
370
371        suite = self.suiteClass
372
373        # give plugins first crack
374        plug_tests = self.config.plugins.loadTestsFromName(name, module)
375        if plug_tests:
376            return suite(plug_tests)
377
378        addr = TestAddress(name, workingDir=self.workingDir)
379        if module:
380            # Two cases:
381            #  name is class.foo
382            #    The addr will be incorrect, since it thinks class.foo is
383            #    a dotted module name. It's actually a dotted attribute
384            #    name. In this case we want to use the full submitted
385            #    name as the name to load from the module.
386            #  name is module:class.foo
387            #    The addr will be correct. The part we want is the part after
388            #    the :, which is in addr.call.
389            if addr.call:
390                name = addr.call
391            parent, obj = self.resolve(name, module)
392            if (isclass(parent)
393                and getattr(parent, '__module__', None) != module.__name__
394                and not isinstance(obj, Failure)):
395                parent = transplant_class(parent, module.__name__)
396                obj = getattr(parent, obj.__name__)
397            log.debug("parent %s obj %s module %s", parent, obj, module)
398            if isinstance(obj, Failure):
399                return suite([obj])
400            else:
401                return suite(ContextList([self.makeTest(obj, parent)],
402                                         context=parent))
403        else:
404            if addr.module:
405                try:
406                    if addr.filename is None:
407                        module = resolve_name(addr.module)
408                    else:
409                        self.config.plugins.beforeImport(
410                            addr.filename, addr.module)
411                        # FIXME: to support module.name names,
412                        # do what resolve-name does and keep trying to
413                        # import, popping tail of module into addr.call,
414                        # until we either get an import or run out of
415                        # module parts
416                        try:
417                            module = self.importer.importFromPath(
418                                addr.filename, addr.module)
419                        finally:
420                            self.config.plugins.afterImport(
421                                addr.filename, addr.module)
422                except (KeyboardInterrupt, SystemExit):
423                    raise
424                except:
425                    exc = sys.exc_info()
426                    return suite([Failure(exc[0], exc[1], exc[2],
427                                          address=addr.totuple())])
428                if addr.call:
429                    return self.loadTestsFromName(addr.call, module)
430                else:
431                    return self.loadTestsFromModule(
432                        module, addr.filename,
433                        discovered=discovered)
434            elif addr.filename:
435                path = addr.filename
436                if addr.call:
437                    package = getpackage(path)
438                    if package is None:
439                        return suite([
440                            Failure(ValueError,
441                                    "Can't find callable %s in file %s: "
442                                    "file is not a python module" %
443                                    (addr.call, path),
444                                    address=addr.totuple())])
445                    return self.loadTestsFromName(addr.call, module=package)
446                else:
447                    if op_isdir(path):
448                        # In this case we *can* be lazy since we know
449                        # that each module in the dir will be fully
450                        # loaded before its tests are executed; we
451                        # also know that we're not going to be asked
452                        # to load from . and ./some_module.py *as part
453                        # of this named test load*
454                        return LazySuite(
455                            lambda: self.loadTestsFromDir(path))
456                    elif op_isfile(path):
457                        return self.loadTestsFromFile(path)
458                    else:
459                        return suite([
460                                Failure(OSError, "No such file %s" % path,
461                                        address=addr.totuple())])
462            else:
463                # just a function? what to do? I think it can only be
464                # handled when module is not None
465                return suite([
466                    Failure(ValueError, "Unresolvable test name %s" % name,
467                            address=addr.totuple())])
468
469    def loadTestsFromNames(self, names, module=None):
470        """Load tests from all names, returning a suite containing all
471        tests.
472        """
473        plug_res = self.config.plugins.loadTestsFromNames(names, module)
474        if plug_res:
475            suite, names = plug_res
476            if suite:
477                return self.suiteClass([
478                    self.suiteClass(suite),
479                    unittest.TestLoader.loadTestsFromNames(self, names, module)
480                    ])
481        return unittest.TestLoader.loadTestsFromNames(self, names, module)
482
483    def loadTestsFromTestCase(self, testCaseClass):
484        """Load tests from a unittest.TestCase subclass.
485        """
486        cases = []
487        plugins = self.config.plugins
488        for case in plugins.loadTestsFromTestCase(testCaseClass):
489            cases.append(case)
490        # For efficiency in the most common case, just call and return from
491        # super. This avoids having to extract cases and rebuild a context
492        # suite when there are no plugin-contributed cases.
493        if not cases:
494            return super(TestLoader, self).loadTestsFromTestCase(testCaseClass)
495        cases.extend(
496            [case for case in
497             super(TestLoader, self).loadTestsFromTestCase(testCaseClass)])
498        return self.suiteClass(cases)
499
500    def loadTestsFromTestClass(self, cls):
501        """Load tests from a test class that is *not* a unittest.TestCase
502        subclass.
503
504        In this case, we can't depend on the class's `__init__` taking method
505        name arguments, so we have to compose a MethodTestCase for each
506        method in the class that looks testlike.
507        """
508        def wanted(attr, cls=cls, sel=self.selector):
509            item = getattr(cls, attr, None)
510            if isfunction(item):
511                item = unbound_method(cls, item)
512            elif not ismethod(item):
513                return False
514            return sel.wantMethod(item)
515        cases = [self.makeTest(getattr(cls, case), cls)
516                 for case in filter(wanted, dir(cls))]
517        for test in self.config.plugins.loadTestsFromTestClass(cls):
518            cases.append(test)
519        return self.suiteClass(ContextList(cases, context=cls))
520
521    def makeTest(self, obj, parent=None):
522        try:
523            return self._makeTest(obj, parent)
524        except (KeyboardInterrupt, SystemExit):
525            raise
526        except:
527            exc = sys.exc_info()
528            try:
529                addr = test_address(obj)
530            except KeyboardInterrupt:
531                raise
532            except:
533                addr = None
534            return Failure(exc[0], exc[1], exc[2], address=addr)
535
536    def _makeTest(self, obj, parent=None):
537        """Given a test object and its parent, return a test case
538        or test suite.
539        """
540        plug_tests = []
541        try:
542            addr = test_address(obj)
543        except KeyboardInterrupt:
544            raise
545        except:
546            addr = None
547        for test in self.config.plugins.makeTest(obj, parent):
548            plug_tests.append(test)
549        # TODO: is this try/except needed?
550        try:
551            if plug_tests:
552                return self.suiteClass(plug_tests)
553        except (KeyboardInterrupt, SystemExit):
554            raise
555        except:
556            exc = sys.exc_info()
557            return Failure(exc[0], exc[1], exc[2], address=addr)
558
559        if isfunction(obj) and parent and not isinstance(parent, types.ModuleType):
560	    # This is a Python 3.x 'unbound method'.  Wrap it with its
561	    # associated class..
562            obj = unbound_method(parent, obj)
563
564        if isinstance(obj, unittest.TestCase):
565            return obj
566        elif isclass(obj):
567            if parent and obj.__module__ != parent.__name__:
568                obj = transplant_class(obj, parent.__name__)
569            if issubclass(obj, unittest.TestCase):
570                return self.loadTestsFromTestCase(obj)
571            else:
572                return self.loadTestsFromTestClass(obj)
573        elif ismethod(obj):
574            if parent is None:
575                parent = obj.__class__
576            if issubclass(parent, unittest.TestCase):
577                return parent(obj.__name__)
578            else:
579                if isgenerator(obj):
580                    return self.loadTestsFromGeneratorMethod(obj, parent)
581                else:
582                    return MethodTestCase(obj)
583        elif isfunction(obj):
584            if parent and obj.__module__ != parent.__name__:
585                obj = transplant_func(obj, parent.__name__)
586            if isgenerator(obj):
587                return self.loadTestsFromGenerator(obj, parent)
588            else:
589                return FunctionTestCase(obj)
590        else:
591            return Failure(TypeError,
592                           "Can't make a test from %s" % obj,
593                           address=addr)
594
595    def resolve(self, name, module):
596        """Resolve name within module
597        """
598        obj = module
599        parts = name.split('.')
600        for part in parts:
601            parent, obj = obj, getattr(obj, part, None)
602        if obj is None:
603            # no such test
604            obj = Failure(ValueError, "No such test %s" % name)
605        return parent, obj
606
607    def parseGeneratedTest(self, test):
608        """Given the yield value of a test generator, return a func and args.
609
610        This is used in the two loadTestsFromGenerator* methods.
611
612        """
613        if not isinstance(test, tuple):         # yield test
614            test_func, arg = (test, tuple())
615        elif len(test) == 1:                    # yield (test,)
616            test_func, arg = (test[0], tuple())
617        else:                                   # yield test, foo, bar, ...
618            assert len(test) > 1 # sanity check
619            test_func, arg = (test[0], test[1:])
620        return test_func, arg
621
622defaultTestLoader = TestLoader
623
624