1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148
|
import functools
import glob
import logging
import os
from types import FunctionType
import unittest
import pytest
import yaml
from chirp import directory
from tests import test_banks
from tests import test_brute_force
from tests import test_clone
from tests import test_copy_all
from tests import test_detect
from tests import test_edges
from tests import test_features
from tests import test_settings
LOG = logging.getLogger('testadapter')
class TestAdapterMeta(type):
"""Generate a subclass of a TestCase for our radio.
This wraps each of the test functions so they can be marked by pytest
independently.
Only works with a single parent!
"""
def __new__(cls, name, parents, dct):
for attrname, attr in list(parents[0].__dict__.items()):
if (isinstance(attr, FunctionType) and
attrname.startswith('test') and
not hasattr(attr, 'pytestmark')):
# This is our wrapper, just so it can be independently marked
# by pytest without affecting the parent class
@functools.wraps(attr)
def wrapper(self, name, *a, **k):
# This is a hacky super() replacement
return getattr(parents[0], name)(self, *a, **k)
# Make this an override in the child class
dct[attrname] = functools.partialmethod(wrapper, attrname)
return super(TestAdapterMeta, cls).__new__(cls, name, parents, dct)
def _get_sub_devices(rclass, testimage):
try:
radio = rclass(None)
rf = radio.get_features()
except Exception as e:
print('Failed to get features for %s: %s' % (rclass, e))
# FIXME: If the driver fails to run get_features with no memobj
# we should not arrest the test load. This appears to happen for
# the Puxing777 for some reason, and not all the time. Figure that
# out, but until then, assume crash means "no sub devices".
return [rclass]
if rf.has_sub_devices:
# Radios with sub-devices may need to look at the image to determine
# what those are. That's slow, so only do it for these.
radio = rclass(testimage)
return radio.get_sub_devices()
else:
return [rclass]
def _load_tests(loader, tests, pattern, suite=None):
if not suite:
suite = unittest.TestSuite()
if 'CHIRP_TESTIMG' in os.environ:
images = os.environ['CHIRP_TESTIMG'].split()
else:
images = glob.glob("tests/images/*.img")
images = [os.path.basename(img) for img in images]
tests = [os.path.splitext(os.path.basename(img))[0] for img in images]
base = os.path.dirname(os.path.abspath(__file__))
base = os.path.join(base, 'images')
images = [os.path.join(base, img) for img in images]
tests = {img: os.path.splitext(os.path.basename(img))[0] for img in images}
if pattern == 'test*.py':
# This default is meaningless for us
pattern = None
driver_test_cases = (test_edges.TestCaseEdges,
test_edges.TestBitwiseStrict,
test_brute_force.TestCaseBruteForce,
test_banks.TestCaseBanks,
test_detect.TestCaseDetect,
test_clone.TestCaseClone,
test_settings.TestCaseSettings,
test_features.TestCaseFeatures,
test_copy_all.TestCaseCopyAll)
# Load our list of dynamic XFAIL tests
with open('tests/driver_xfails.yaml') as xflist:
xfail_list = yaml.load(xflist, Loader=yaml.SafeLoader)
for image, test in tests.items():
rclass = directory.get_radio(test)
if hasattr(rclass, '_orig_rclass'):
rclass = rclass._orig_rclass
module = rclass.__module__.split('.')[-1]
subdevs = _get_sub_devices(rclass, image)
has_subdevs = subdevs != [rclass]
for index, device in enumerate(subdevs):
if not isinstance(device, type):
device = device.__class__
rclassid = directory.radio_class_id(device)
xfails = xfail_list.get(rclassid, [])
for case in driver_test_cases:
tc = TestAdapterMeta(
"%s_%s" % (case.__name__, rclassid),
(case,),
{'RADIO_CLASS': rclass,
'SUB_DEVICE': index if has_subdevs else None,
'TEST_IMAGE': image})
# Mark the class with the driver module name
tc = getattr(pytest.mark, module)(tc)
# Look for any XFAILs and mark those test functions
for xfail in xfails:
if xfail['class'] == case.__name__:
# This is like decorating it.
setattr(tc, xfail['test'],
pytest.mark.xfail(reason=xfail['reason'])(
getattr(tc, xfail['test'])))
suite.addTests(loader.loadTestsFromTestCase(tc))
return suite
def load_tests(loader, tests, pattern, suite=None):
try:
return _load_tests(loader, tests, pattern, suite=suite)
except Exception as e:
import traceback
print('Failed to load: %s' % e)
print(traceback.format_exc())
raise
|