File: test_directory.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 (193 lines) | stat: -rw-r--r-- 7,128 bytes parent folder | download | duplicates (2)
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
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
import base64
import glob
import json
import os
import shutil
import tempfile
from unittest import mock

import yaml

from tests.unit import base
from chirp import chirp_common
from chirp import directory


class TestDirectory(base.BaseTest):
    def cleanUp(self):
        shutil.rmtree(self.tempdir)

    def setUp(self):
        super(TestDirectory, self).setUp()
        self.tempdir = tempfile.mkdtemp()
        directory.enable_reregistrations()

        class FakeAlias(chirp_common.Alias):
            VENDOR = 'Taylor'
            MODEL = 'Barmaster 2000'
            VARIANT = 'A'

        @directory.register
        class FakeRadio(chirp_common.CloneModeRadio):
            VENDOR = 'Dan'
            MODEL = 'Foomaster 9000'
            VARIANT = 'R'
            ALIASES = [FakeAlias]

            @classmethod
            def match_model(cls, file_data, image_file):
                return file_data == b'thisisrawdata'

        self.test_class = FakeRadio

    def _test_detect_finds_our_class(self, tempfn):
        radio = directory.get_radio_by_image(tempfn)
        self.assertTrue(isinstance(radio, self.test_class))
        return radio

    def test_detect_with_no_metadata(self):
        fn = os.path.join(self.tempdir, 'testfile')
        with open(fn, 'wb') as f:
            f.write(b'thisisrawdata')
            f.flush()
        self._test_detect_finds_our_class(fn)

    def test_detect_with_metadata_base_class(self):
        fn = os.path.join(self.tempdir, 'testfile')
        with open(fn, 'wb') as f:
            f.write(b'thisisrawdata')
            f.write(self.test_class.MAGIC + b'-')
            f.write(self.test_class(None)._make_metadata())
            f.flush()
        self._test_detect_finds_our_class(fn)

    def test_detect_with_metadata_alias_class(self):
        fn = os.path.join(self.tempdir, 'testfile')
        with open(fn, 'wb') as f:
            f.write(b'thisisrawdata')
            f.write(self.test_class.MAGIC + b'-')
            FakeAlias = self.test_class.ALIASES[0]
            fake_metadata = base64.b64encode(json.dumps(
                {'vendor': FakeAlias.VENDOR,
                 'model': FakeAlias.MODEL,
                 'variant': FakeAlias.VARIANT,
                 }).encode())
            f.write(fake_metadata)
            f.flush()
        radio = self._test_detect_finds_our_class(fn)
        self.assertEqual('Taylor', radio.VENDOR)
        self.assertEqual('Barmaster 2000', radio.MODEL)
        self.assertEqual('A', radio.VARIANT)


class TestDetectBruteForce(base.BaseTest):
    def test_detect_all(self):
        # Attempt a brute-force detection of all test images.
        #
        # This confirms that no test image is detected by more than one
        # radio class. If it is, fail and report those classes.

        path = os.path.dirname(__file__)
        path = os.path.join(path, '..', 'images', '*.img')
        test_images = glob.glob(path)
        self.assertNotEqual(0, len(test_images))
        for image in test_images:
            detections = []
            filedata = open(image, 'rb').read()
            for cls in directory.RADIO_TO_DRV:
                if not hasattr(cls, 'match_model'):
                    continue
                if cls.match_model(filedata, image):
                    detections.append(cls)
            if len(detections) > 1:
                raise Exception('Detection of %s failed: %s' % (image,
                                                                detections))


class TestAliasMap(base.BaseTest):
    def test_uniqueness(self):
        directory_models = {}
        for rclass in directory.DRV_TO_RADIO.values():
            for cls in [rclass] + rclass.ALIASES:
                # Make sure there are no duplicates
                directory_models.setdefault(cls.VENDOR, set())
                fullmodel = '%s%s' % (cls.MODEL, cls.VARIANT)
                self.assertNotIn(fullmodel,
                                 directory_models[cls.VENDOR])
                directory_models[cls.VENDOR].add(fullmodel)

        aliases = yaml.load(open(os.path.join(os.path.dirname(__file__),
                                              '..', '..', 'chirp', 'share',
                                              'model_alias_map.yaml')).read(),
                            Loader=yaml.FullLoader)
        for vendor, models in sorted(aliases.items()):
            directory_models.setdefault(vendor, set())
            my_aliases = set([x['model'] for x in models])
            vendor = vendor.split('/')[0]
            for model in models:
                # Make sure the thing we tell users to use is in the
                # directory
                try:
                    alt_vendor, alt_model = model['alt'].split(' ', 1)
                except ValueError:
                    alt_vendor = vendor
                    alt_model = model['alt']

                # Aliases may reference other aliases?
                self.assertIn(alt_model,
                              directory_models[alt_vendor] | my_aliases,
                              '%s %s not found for %s %s' % (
                                  alt_vendor, alt_model,
                                  vendor, model['model']))

                # Make sure the alias model is NOT in the directory
                # before we add it to ensure there are no duplicates
                self.assertNotIn(model['model'], directory_models[vendor])
                directory_models[vendor].add(model['model'])


class TestDetectedBy(base.BaseTest):
    @mock.patch('chirp.directory.DRV_TO_RADIO', new={})
    @mock.patch('chirp.directory.RADIO_TO_DRV', new={})
    def test_detected_isolation(self):
        @directory.register
        class BaseRadio(chirp_common.CloneModeRadio):
            VENDOR = 'CHIRP'
            MODEL = 'Base'

        @directory.register
        class SubRadio1(BaseRadio):
            MODEL = 'Sub1'

        @directory.register
        @directory.detected_by(SubRadio1)
        class SubRadio2(SubRadio1):
            MODEL = 'Sub2'

        # BaseRadio should not think it detects the subs
        self.assertEqual([BaseRadio], BaseRadio.detected_models())

        # Sub1 detects both itself and Sub2
        self.assertEqual([SubRadio1, SubRadio2], SubRadio1.detected_models())

        # If include_self=False, Sub1 should not include itself
        self.assertEqual([SubRadio2],
                         SubRadio1.detected_models(include_self=False))

        # Sub2 does not also detect Sub1
        self.assertEqual([SubRadio2], SubRadio2.detected_models())

    @mock.patch('chirp.directory.DRV_TO_RADIO', new={})
    @mock.patch('chirp.directory.RADIO_TO_DRV', new={})
    def test_detected_include_self(self):
        class BaseRadio(chirp_common.CloneModeRadio):
            VENDOR = 'CHIRP'
            MODEL = 'Base'

        @directory.register
        @directory.detected_by(BaseRadio)
        class SubRadio(BaseRadio):
            MODEL = 'Sub'

        # BaseRadio should not include itself since it is not registered
        self.assertEqual([SubRadio], BaseRadio.detected_models())