File: __init__.py

package info (click to toggle)
chirp 1%3A20251108-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 18,728 kB
  • sloc: python: 156,369; ansic: 296; sh: 219; xml: 24; makefile: 19
file content (148 lines) | stat: -rw-r--r-- 5,429 bytes parent folder | download
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