PageRenderTime 45ms CodeModel.GetById 13ms app.highlight 26ms RepoModel.GetById 1ms app.codeStats 1ms

/nose/selector.py

https://bitbucket.org/jpellerin/nose/
Python | 251 lines | 173 code | 23 blank | 55 comment | 60 complexity | 14d2c06bd1ad4a7d408d07560be08c7d MD5 | raw file
  1"""
  2Test Selection
  3--------------
  4
  5Test selection is handled by a Selector. The test loader calls the
  6appropriate selector method for each object it encounters that it
  7thinks may be a test.
  8"""
  9import logging
 10import os
 11import unittest
 12from nose.config import Config
 13from nose.util import split_test_name, src, getfilename, getpackage, ispackage
 14
 15log = logging.getLogger(__name__)
 16
 17__all__ = ['Selector', 'defaultSelector', 'TestAddress']
 18
 19
 20# for efficiency and easier mocking
 21op_join = os.path.join
 22op_basename = os.path.basename
 23op_exists = os.path.exists
 24op_splitext = os.path.splitext
 25op_isabs = os.path.isabs
 26op_abspath = os.path.abspath
 27
 28
 29class Selector(object):
 30    """Core test selector. Examines test candidates and determines whether,
 31    given the specified configuration, the test candidate should be selected
 32    as a test.
 33    """
 34    def __init__(self, config):
 35        if config is None:
 36            config = Config()
 37        self.configure(config)
 38
 39    def configure(self, config):
 40        self.config = config
 41        self.exclude = config.exclude
 42        self.ignoreFiles = config.ignoreFiles
 43        self.include = config.include
 44        self.plugins = config.plugins
 45        self.match = config.testMatch
 46        
 47    def matches(self, name):
 48        """Does the name match my requirements?
 49
 50        To match, a name must match config.testMatch OR config.include
 51        and it must not match config.exclude
 52        """
 53        return ((self.match.search(name)
 54                 or (self.include and
 55                     filter(None,
 56                            [inc.search(name) for inc in self.include])))
 57                and ((not self.exclude)
 58                     or not filter(None,
 59                                   [exc.search(name) for exc in self.exclude])
 60                 ))
 61    
 62    def wantClass(self, cls):
 63        """Is the class a wanted test class?
 64
 65        A class must be a unittest.TestCase subclass, or match test name
 66        requirements. Classes that start with _ are always excluded.
 67        """
 68        declared = getattr(cls, '__test__', None)
 69        if declared is not None:
 70            wanted = declared
 71        else:
 72            wanted = (not cls.__name__.startswith('_')
 73                      and (issubclass(cls, unittest.TestCase)
 74                           or self.matches(cls.__name__)))
 75        
 76        plug_wants = self.plugins.wantClass(cls)        
 77        if plug_wants is not None:
 78            log.debug("Plugin setting selection of %s to %s", cls, plug_wants)
 79            wanted = plug_wants
 80        log.debug("wantClass %s? %s", cls, wanted)
 81        return wanted
 82
 83    def wantDirectory(self, dirname):
 84        """Is the directory a wanted test directory?
 85
 86        All package directories match, so long as they do not match exclude. 
 87        All other directories must match test requirements.
 88        """
 89        tail = op_basename(dirname)
 90        if ispackage(dirname):
 91            wanted = (not self.exclude
 92                      or not filter(None,
 93                                    [exc.search(tail) for exc in self.exclude]
 94                                    ))
 95        else:
 96            wanted = (self.matches(tail)
 97                      or (self.config.srcDirs
 98                          and tail in self.config.srcDirs))
 99        plug_wants = self.plugins.wantDirectory(dirname)
100        if plug_wants is not None:
101            log.debug("Plugin setting selection of %s to %s",
102                      dirname, plug_wants)
103            wanted = plug_wants
104        log.debug("wantDirectory %s? %s", dirname, wanted)
105        return wanted
106    
107    def wantFile(self, file):
108        """Is the file a wanted test file?
109
110        The file must be a python source file and match testMatch or
111        include, and not match exclude. Files that match ignore are *never*
112        wanted, regardless of plugin, testMatch, include or exclude settings.
113        """
114        # never, ever load files that match anything in ignore
115        # (.* _* and *setup*.py by default)
116        base = op_basename(file)
117        ignore_matches = [ ignore_this for ignore_this in self.ignoreFiles
118                           if ignore_this.search(base) ]
119        if ignore_matches:
120            log.debug('%s matches ignoreFiles pattern; skipped',
121                      base) 
122            return False
123        if not self.config.includeExe and os.access(file, os.X_OK):
124            log.info('%s is executable; skipped', file)
125            return False
126        dummy, ext = op_splitext(base)
127        pysrc = ext == '.py'
128
129        wanted = pysrc and self.matches(base) 
130        plug_wants = self.plugins.wantFile(file)
131        if plug_wants is not None:
132            log.debug("plugin setting want %s to %s", file, plug_wants)
133            wanted = plug_wants
134        log.debug("wantFile %s? %s", file, wanted)
135        return wanted
136
137    def wantFunction(self, function):
138        """Is the function a test function?
139        """
140        try:
141            if hasattr(function, 'compat_func_name'):
142                funcname = function.compat_func_name
143            else:
144                funcname = function.__name__
145        except AttributeError:
146            # not a function
147            return False
148        declared = getattr(function, '__test__', None)
149        if declared is not None:
150            wanted = declared
151        else:
152            wanted = not funcname.startswith('_') and self.matches(funcname)
153        plug_wants = self.plugins.wantFunction(function)
154        if plug_wants is not None:
155            wanted = plug_wants
156        log.debug("wantFunction %s? %s", function, wanted)
157        return wanted
158
159    def wantMethod(self, method):
160        """Is the method a test method?
161        """
162        try:
163            method_name = method.__name__
164        except AttributeError:
165            # not a method
166            return False
167        if method_name.startswith('_'):
168            # never collect 'private' methods
169            return False
170        declared = getattr(method, '__test__', None)
171        if declared is not None:
172            wanted = declared
173        else:
174            wanted = self.matches(method_name)
175        plug_wants = self.plugins.wantMethod(method)
176        if plug_wants is not None:
177            wanted = plug_wants
178        log.debug("wantMethod %s? %s", method, wanted)
179        return wanted
180    
181    def wantModule(self, module):
182        """Is the module a test module?
183
184        The tail of the module name must match test requirements. One exception:
185        we always want __main__.
186        """
187        declared = getattr(module, '__test__', None)
188        if declared is not None:
189            wanted = declared
190        else:
191            wanted = self.matches(module.__name__.split('.')[-1]) \
192                     or module.__name__ == '__main__'
193        plug_wants = self.plugins.wantModule(module)
194        if plug_wants is not None:
195            wanted = plug_wants
196        log.debug("wantModule %s? %s", module, wanted)
197        return wanted
198        
199defaultSelector = Selector        
200
201
202class TestAddress(object):
203    """A test address represents a user's request to run a particular
204    test. The user may specify a filename or module (or neither),
205    and/or a callable (a class, function, or method). The naming
206    format for test addresses is:
207
208    filename_or_module:callable
209
210    Filenames that are not absolute will be made absolute relative to
211    the working dir.
212
213    The filename or module part will be considered a module name if it
214    doesn't look like a file, that is, if it doesn't exist on the file
215    system and it doesn't contain any directory separators and it
216    doesn't end in .py.
217
218    Callables may be a class name, function name, method name, or
219    class.method specification.
220    """
221    def __init__(self, name, workingDir=None):
222        if workingDir is None:
223            workingDir = os.getcwd()
224        self.name = name
225        self.workingDir = workingDir
226        self.filename, self.module, self.call = split_test_name(name)
227        log.debug('Test name %s resolved to file %s, module %s, call %s',
228                  name, self.filename, self.module, self.call)
229        if self.filename is None:
230            if self.module is not None:
231                self.filename = getfilename(self.module, self.workingDir)
232        if self.filename:
233            self.filename = src(self.filename)
234            if not op_isabs(self.filename):
235                self.filename = op_abspath(op_join(workingDir,
236                                                   self.filename))
237            if self.module is None:
238                self.module = getpackage(self.filename)
239        log.debug(
240            'Final resolution of test name %s: file %s module %s call %s',
241            name, self.filename, self.module, self.call)
242
243    def totuple(self):
244        return (self.filename, self.module, self.call)
245        
246    def __str__(self):
247        return self.name
248
249    def __repr__(self):
250        return "%s: (%s, %s, %s)" % (self.name, self.filename,
251                                     self.module, self.call)