209 lines
		
	
	
		
			7.1 KiB
		
	
	
	
		
			Python
		
	
	
	
			
		
		
	
	
			209 lines
		
	
	
		
			7.1 KiB
		
	
	
	
		
			Python
		
	
	
	
| """ generic mechanism for marking and selecting python functions. """
 | |
| import pytest, py
 | |
| 
 | |
| def pytest_namespace():
 | |
|     return {'mark': MarkGenerator()}
 | |
| 
 | |
| def pytest_addoption(parser):
 | |
|     group = parser.getgroup("general")
 | |
|     group._addoption('-k',
 | |
|         action="store", dest="keyword", default='', metavar="EXPRESSION",
 | |
|         help="only run tests which match the given substring expression. "
 | |
|              "An expression is a python evaluatable expression "
 | |
|              "where all names are substring-matched against test names "
 | |
|              "and keywords.  Example: -k 'test_method or test_other' "
 | |
|              "matches all test functions whose name contains "
 | |
|              "'test_method' or 'test_other'.")
 | |
| 
 | |
|     group._addoption("-m",
 | |
|         action="store", dest="markexpr", default="", metavar="MARKEXPR",
 | |
|         help="only run tests matching given mark expression.  "
 | |
|              "example: -m 'mark1 and not mark2'."
 | |
|              )
 | |
| 
 | |
|     group.addoption("--markers", action="store_true", help=
 | |
|         "show markers (builtin, plugin and per-project ones).")
 | |
| 
 | |
|     parser.addini("markers", "markers for test functions", 'linelist')
 | |
| 
 | |
| def pytest_cmdline_main(config):
 | |
|     if config.option.markers:
 | |
|         config.pluginmanager.do_configure(config)
 | |
|         tw = py.io.TerminalWriter()
 | |
|         for line in config.getini("markers"):
 | |
|             name, rest = line.split(":", 1)
 | |
|             tw.write("@pytest.mark.%s:" %  name, bold=True)
 | |
|             tw.line(rest)
 | |
|             tw.line()
 | |
|         config.pluginmanager.do_unconfigure(config)
 | |
|         return 0
 | |
| pytest_cmdline_main.tryfirst = True
 | |
| 
 | |
| def pytest_collection_modifyitems(items, config):
 | |
|     keywordexpr = config.option.keyword
 | |
|     matchexpr = config.option.markexpr
 | |
|     if not keywordexpr and not matchexpr:
 | |
|         return
 | |
|     selectuntil = False
 | |
|     if keywordexpr[-1:] == ":":
 | |
|         selectuntil = True
 | |
|         keywordexpr = keywordexpr[:-1]
 | |
| 
 | |
|     remaining = []
 | |
|     deselected = []
 | |
|     for colitem in items:
 | |
|         if keywordexpr and not matchkeyword(colitem, keywordexpr):
 | |
|             deselected.append(colitem)
 | |
|         else:
 | |
|             if selectuntil:
 | |
|                 keywordexpr = None
 | |
|             if matchexpr:
 | |
|                 if not matchmark(colitem, matchexpr):
 | |
|                     deselected.append(colitem)
 | |
|                     continue
 | |
|             remaining.append(colitem)
 | |
| 
 | |
|     if deselected:
 | |
|         config.hook.pytest_deselected(items=deselected)
 | |
|         items[:] = remaining
 | |
| 
 | |
| class BoolDict:
 | |
|     def __init__(self, mydict):
 | |
|         self._mydict = mydict
 | |
|     def __getitem__(self, name):
 | |
|         return name in self._mydict
 | |
| 
 | |
| class SubstringDict:
 | |
|     def __init__(self, mydict):
 | |
|         self._mydict = mydict
 | |
|     def __getitem__(self, name):
 | |
|         for key in self._mydict:
 | |
|             if name in key:
 | |
|                 return True
 | |
|         return False
 | |
| 
 | |
| def matchmark(colitem, matchexpr):
 | |
|     return eval(matchexpr, {}, BoolDict(colitem.keywords))
 | |
| 
 | |
| def matchkeyword(colitem, keywordexpr):
 | |
|     keywordexpr = keywordexpr.replace("-", "not ")
 | |
|     return eval(keywordexpr, {}, SubstringDict(colitem.keywords))
 | |
| 
 | |
| def pytest_configure(config):
 | |
|     if config.option.strict:
 | |
|         pytest.mark._config = config
 | |
| 
 | |
| class MarkGenerator:
 | |
|     """ Factory for :class:`MarkDecorator` objects - exposed as
 | |
|     a ``py.test.mark`` singleton instance.  Example::
 | |
| 
 | |
|          import py
 | |
|          @py.test.mark.slowtest
 | |
|          def test_function():
 | |
|             pass
 | |
| 
 | |
|     will set a 'slowtest' :class:`MarkInfo` object
 | |
|     on the ``test_function`` object. """
 | |
| 
 | |
|     def __getattr__(self, name):
 | |
|         if name[0] == "_":
 | |
|             raise AttributeError(name)
 | |
|         if hasattr(self, '_config'):
 | |
|             self._check(name)
 | |
|         return MarkDecorator(name)
 | |
| 
 | |
|     def _check(self, name):
 | |
|         try:
 | |
|             if name in self._markers:
 | |
|                 return
 | |
|         except AttributeError:
 | |
|             pass
 | |
|         self._markers = l = set()
 | |
|         for line in self._config.getini("markers"):
 | |
|             beginning = line.split(":", 1)
 | |
|             x = beginning[0].split("(", 1)[0]
 | |
|             l.add(x)
 | |
|         if name not in self._markers:
 | |
|             raise AttributeError("%r not a registered marker" % (name,))
 | |
| 
 | |
| class MarkDecorator:
 | |
|     """ A decorator for test functions and test classes.  When applied
 | |
|     it will create :class:`MarkInfo` objects which may be
 | |
|     :ref:`retrieved by hooks as item keywords <excontrolskip>`.
 | |
|     MarkDecorator instances are often created like this::
 | |
| 
 | |
|         mark1 = py.test.mark.NAME              # simple MarkDecorator
 | |
|         mark2 = py.test.mark.NAME(name1=value) # parametrized MarkDecorator
 | |
| 
 | |
|     and can then be applied as decorators to test functions::
 | |
| 
 | |
|         @mark2
 | |
|         def test_function():
 | |
|             pass
 | |
|     """
 | |
|     def __init__(self, name, args=None, kwargs=None):
 | |
|         self.markname = name
 | |
|         self.args = args or ()
 | |
|         self.kwargs = kwargs or {}
 | |
| 
 | |
|     def __repr__(self):
 | |
|         d = self.__dict__.copy()
 | |
|         name = d.pop('markname')
 | |
|         return "<MarkDecorator %r %r>" %(name, d)
 | |
| 
 | |
|     def __call__(self, *args, **kwargs):
 | |
|         """ if passed a single callable argument: decorate it with mark info.
 | |
|             otherwise add *args/**kwargs in-place to mark information. """
 | |
|         if args:
 | |
|             func = args[0]
 | |
|             if len(args) == 1 and hasattr(func, '__call__') or \
 | |
|                hasattr(func, '__bases__'):
 | |
|                 if hasattr(func, '__bases__'):
 | |
|                     if hasattr(func, 'pytestmark'):
 | |
|                         l = func.pytestmark
 | |
|                         if not isinstance(l, list):
 | |
|                            func.pytestmark = [l, self]
 | |
|                         else:
 | |
|                            l.append(self)
 | |
|                     else:
 | |
|                        func.pytestmark = [self]
 | |
|                 else:
 | |
|                     holder = getattr(func, self.markname, None)
 | |
|                     if holder is None:
 | |
|                         holder = MarkInfo(self.markname, self.args, self.kwargs)
 | |
|                         setattr(func, self.markname, holder)
 | |
|                     else:
 | |
|                         holder.add(self.args, self.kwargs)
 | |
|                 return func
 | |
|         kw = self.kwargs.copy()
 | |
|         kw.update(kwargs)
 | |
|         args = self.args + args
 | |
|         return self.__class__(self.markname, args=args, kwargs=kw)
 | |
| 
 | |
| class MarkInfo:
 | |
|     """ Marking object created by :class:`MarkDecorator` instances. """
 | |
|     def __init__(self, name, args, kwargs):
 | |
|         #: name of attribute
 | |
|         self.name = name
 | |
|         #: positional argument list, empty if none specified
 | |
|         self.args = args
 | |
|         #: keyword argument dictionary, empty if nothing specified
 | |
|         self.kwargs = kwargs
 | |
|         self._arglist = [(args, kwargs.copy())]
 | |
| 
 | |
|     def __repr__(self):
 | |
|         return "<MarkInfo %r args=%r kwargs=%r>" % (
 | |
|                 self.name, self.args, self.kwargs)
 | |
| 
 | |
|     def add(self, args, kwargs):
 | |
|         """ add a MarkInfo with the given args and kwargs. """
 | |
|         self._arglist.append((args, kwargs))
 | |
|         self.args += args
 | |
|         self.kwargs.update(kwargs)
 | |
| 
 | |
|     def __iter__(self):
 | |
|         """ yield MarkInfo objects each relating to a marking-call. """
 | |
|         for args, kwargs in self._arglist:
 | |
|             yield MarkInfo(self.name, args, kwargs)
 | |
| 
 |