chirp/tests/test_brute_force.py
Dan Smith ad73f0b53f Check mem.extra structure when comparing memories
This makes us include the *structure* of mem.extra in the
assertEqualMems() check to make sure it doesn't change before and after
a modification.
2023-11-05 10:58:51 -08:00

194 lines
7.0 KiB
Python

import logging
from chirp import chirp_common
from chirp import errors
from tests import base
LOG = logging.getLogger(__name__)
class TestCaseBruteForce(base.DriverTest):
def set_and_compare(self, m, **kwargs):
msgs = self.radio.validate_memory(m)
if msgs:
# If the radio correctly refuses memories it can't
# store, don't fail
return
self.radio.set_memory(chirp_common.FrozenMemory(m))
ret_m = self.radio.get_memory(m.number)
# Damned Baofeng radios don't seem to properly store
# shift and direction, so be gracious here
if m.duplex == "split" and ret_m.duplex in ["-", "+"]:
ret_m.offset = ret_m.freq + \
(ret_m.offset * int(ret_m.duplex + "1"))
ret_m.duplex = "split"
self.assertEqualMem(m, ret_m, **kwargs)
def test_tone(self):
m = self.get_mem()
for tone in chirp_common.TONES:
for tmode in self.rf.valid_tmodes:
if tmode not in chirp_common.TONE_MODES:
continue
elif tmode in ["DTCS", "DTCS-R", "Cross"]:
continue # We'll test DCS and Cross tones separately
m.tmode = tmode
if tmode == "":
pass
elif tmode == "Tone":
m.rtone = tone
elif tmode in ["TSQL", "TSQL-R"]:
if self.rf.has_ctone:
m.ctone = tone
else:
m.rtone = tone
else:
self.fail("Unknown tone mode `%s'" % tmode)
try:
self.set_and_compare(m)
except errors.UnsupportedToneError:
# If a radio doesn't support a particular tone value,
# don't punish it
pass
@base.requires_feature('has_dtcs')
def test_dtcs(self):
m = self.get_mem()
m.tmode = "DTCS"
for code in self.rf.valid_dtcs_codes:
m.dtcs = code
self.set_and_compare(m)
if not self.rf.has_dtcs_polarity:
return
for pol in self.rf.valid_dtcs_pols:
m.dtcs_polarity = pol
self.set_and_compare(m)
@base.requires_feature('has_cross')
def test_cross(self):
m = self.get_mem()
m.tmode = "Cross"
# No fair asking a radio to detect two identical tones as Cross instead
# of TSQL
m.rtone = 100.0
m.ctone = 107.2
m.dtcs = 506
m.rx_dtcs = 516
for cross_mode in self.rf.valid_cross_modes:
m.cross_mode = cross_mode
self.set_and_compare(m)
@base.requires_feature('valid_duplexes')
def test_duplex(self):
m = self.get_mem()
if 'duplex' in m.immutable:
self.skipTest('Test memory has immutable duplex')
for duplex in self.rf.valid_duplexes:
if duplex not in ["", "-", "+", "split"]:
continue
if duplex == 'split':
self.assertTrue(self.rf.can_odd_split,
'Radio supports split but does not set '
'can_odd_split=True in features')
m.offset = self.rf.valid_bands[0][1] - 100000
else:
m.offset = chirp_common.to_kHz(int(m.tuning_step) * 2)
m.duplex = duplex
# Ignore the offset because we do some fudging on this and we
# don't necessarily know the best step to use. What we care about
# is duplex here.
self.set_and_compare(m, ignore=['offset'])
if self.rf.can_odd_split:
self.assertIn('split', self.rf.valid_duplexes,
'Radio claims can_odd_split but split not in '
'valid_duplexes')
@base.requires_feature('valid_skips')
def test_skip(self):
mem = self.get_mem()
lo, hi = self.rf.memory_bounds
# Walk through several memories, specifically across 8 and 16,
# as many radio use bitfields for skip flags.
for i in range(max(5, lo), min(25, hi)):
# Walk through the skip flags twice each, to make sure we can
# toggle them on..off..on
for skip in self.rf.valid_skips * 2:
m = mem.dupe()
if 'empty' not in m.immutable:
m.empty = False
m.number = i
m.skip = skip
self.set_and_compare(m)
# Delete the memory each time because some radios are
# dynamically allocated and will run out of space here.
self.radio.erase_memory(m.number)
@base.requires_feature('valid_modes')
def test_mode(self):
m = self.get_mem()
if 'mode' in m.immutable:
self.skipTest('Test memory has immutable duplex')
def ensure_urcall(call):
lst = self.radio.get_urcall_list()
lst[0] = call
self.radio.set_urcall_list(lst)
def ensure_rptcall(call):
lst = self.radio.get_repeater_call_list()
lst[0] = call
self.radio.set_repeater_call_list(lst)
def freq_is_ok(freq):
for lo, hi in self.rf.valid_bands:
if freq > lo and freq < hi:
return True
return False
successes = 0
for mode in self.rf.valid_modes:
self.assertIn(mode, chirp_common.MODES,
'Radio exposes non-standard mode')
tmp = m.dupe()
if mode == "DV" and \
isinstance(self.radio,
chirp_common.IcomDstarSupport):
tmp = chirp_common.DVMemory()
try:
ensure_urcall(tmp.dv_urcall)
ensure_rptcall(tmp.dv_rpt1call)
ensure_rptcall(tmp.dv_rpt2call)
except IndexError:
if self.rf.requires_call_lists:
raise
else:
# This radio may not do call lists at all,
# so let it slide
pass
if mode == "FM" and freq_is_ok(tmp.freq + 100000000):
# Some radios don't support FM below approximately 30MHz,
# so jump up by 100MHz, if they support that
tmp.freq += 100000000
tmp.mode = mode
if self.rf.validate_memory(tmp):
# A result (of error messages) from validate means the radio
# thinks this is invalid, so don't fail the test
LOG.warning('Failed to validate %s: %s' % (
tmp, self.rf.validate_memory(tmp)))
continue
# Ignore tuning_step because changing modes may cause step changes
# in some radios
self.set_and_compare(tmp, ignore=['tuning_step'])
successes += 1