mirror of
https://github.com/qgis/QGIS.git
synced 2025-02-27 00:33:48 -05:00
155 lines
5.3 KiB
Python
155 lines
5.3 KiB
Python
|
import logging
|
||
|
from unittest import TestSuite
|
||
|
|
||
|
from nose2.events import Plugin
|
||
|
|
||
|
log = logging.getLogger(__name__)
|
||
|
undefined = object()
|
||
|
|
||
|
|
||
|
class AttributeSelector(Plugin):
|
||
|
|
||
|
"""Filter tests by attribute"""
|
||
|
|
||
|
def __init__(self):
|
||
|
self.attribs = []
|
||
|
self.eval_attribs = []
|
||
|
self.addArgument(
|
||
|
self.attribs, "A", "attribute",
|
||
|
"Select tests with matching attribute")
|
||
|
self.addArgument(
|
||
|
self.eval_attribs, "E", "eval-attribute",
|
||
|
"Select tests for whose attributes the "
|
||
|
"given Python expression evalures to True")
|
||
|
|
||
|
def handleArgs(self, args):
|
||
|
"""Register if any attribs defined"""
|
||
|
if self.attribs or self.eval_attribs:
|
||
|
self.register()
|
||
|
|
||
|
def moduleLoadedSuite(self, event):
|
||
|
"""Filter event.suite by specified attributes"""
|
||
|
log.debug('Attribute selector attribs %s/%s',
|
||
|
self.attribs, self.eval_attribs)
|
||
|
attribs = []
|
||
|
for attr in self.eval_attribs:
|
||
|
def eval_in_context(expr, obj):
|
||
|
try:
|
||
|
return eval(expr, None, ContextHelper(obj))
|
||
|
except Exception as e:
|
||
|
log.warning(
|
||
|
"%s raised exception %s with test %s", expr, e, obj)
|
||
|
return False
|
||
|
attribs.append([(attr, eval_in_context)])
|
||
|
for attr in self.attribs:
|
||
|
# all attributes within an attribute group must match
|
||
|
attr_group = []
|
||
|
for attrib in attr.strip().split(","):
|
||
|
# don't die on trailing comma
|
||
|
if not attrib:
|
||
|
continue
|
||
|
items = attrib.split("=", 1)
|
||
|
if len(items) > 1:
|
||
|
# "name=value"
|
||
|
# -> 'str(obj.name) == value' must be True
|
||
|
key, value = items
|
||
|
else:
|
||
|
key = items[0]
|
||
|
if key[0] == "!":
|
||
|
# "!name"
|
||
|
# 'bool(obj.name)' must be False
|
||
|
key = key[1:]
|
||
|
value = False
|
||
|
else:
|
||
|
# "name"
|
||
|
# -> 'bool(obj.name)' must be True
|
||
|
value = True
|
||
|
attr_group.append((key, value))
|
||
|
attribs.append(attr_group)
|
||
|
if not attribs:
|
||
|
return
|
||
|
|
||
|
event.suite = self.filterSuite(event.suite, attribs)
|
||
|
|
||
|
def filterSuite(self, suite, attribs):
|
||
|
# FIXME probably need to copy or something to allow suites w/custom attrs to work or iter and remove instead of
|
||
|
# recreating
|
||
|
new_suite = suite.__class__()
|
||
|
|
||
|
for test in suite:
|
||
|
if isinstance(test, TestSuite):
|
||
|
new_suite.addTest(self.filterSuite(test, attribs))
|
||
|
elif self.validateAttrib(test, attribs):
|
||
|
new_suite.addTest(test)
|
||
|
return new_suite
|
||
|
|
||
|
def validateAttrib(self, test, attribs):
|
||
|
any_ = False
|
||
|
for group in attribs:
|
||
|
match = True
|
||
|
for key, value in group:
|
||
|
neg = False
|
||
|
if key.startswith('!'):
|
||
|
neg, key = True, key[1:]
|
||
|
obj_value = _get_attr(test, key)
|
||
|
if callable(value):
|
||
|
if not value(key, test):
|
||
|
match = False
|
||
|
break
|
||
|
elif value is True:
|
||
|
# value must exist and be True
|
||
|
if not bool(obj_value):
|
||
|
match = False
|
||
|
break
|
||
|
elif value is False:
|
||
|
# value must not exist or be False
|
||
|
if bool(obj_value):
|
||
|
match = False
|
||
|
break
|
||
|
elif type(obj_value) in (list, tuple):
|
||
|
# value must be found in the list attribute
|
||
|
found = str(value).lower() in [str(x).lower()
|
||
|
for x in obj_value]
|
||
|
if found and neg:
|
||
|
match = False
|
||
|
break
|
||
|
elif not found and not neg:
|
||
|
match = False
|
||
|
break
|
||
|
else:
|
||
|
# value must match, convert to string and compare
|
||
|
if (value != obj_value
|
||
|
and str(value).lower() != str(obj_value).lower()):
|
||
|
match = False
|
||
|
break
|
||
|
any_ = any_ or match
|
||
|
return any_
|
||
|
|
||
|
|
||
|
# helpers
|
||
|
|
||
|
def _get_attr(test, key):
|
||
|
# FIXME for vals that are lists (or just mutable?), combine all levels
|
||
|
val = getattr(test, key, undefined)
|
||
|
if val is not undefined:
|
||
|
return val
|
||
|
if hasattr(test, '_testFunc'):
|
||
|
val = getattr(test._testFunc, key, undefined)
|
||
|
if val is not undefined:
|
||
|
return val
|
||
|
elif hasattr(test, '_testMethodName'):
|
||
|
meth = getattr(test, test._testMethodName, undefined)
|
||
|
if meth is not undefined:
|
||
|
val = getattr(meth, key, undefined)
|
||
|
if val is not undefined:
|
||
|
return val
|
||
|
|
||
|
|
||
|
class ContextHelper:
|
||
|
|
||
|
def __init__(self, obj):
|
||
|
self.obj = obj
|
||
|
|
||
|
def __getitem__(self, name):
|
||
|
return _get_attr(self.obj, name)
|