personalization: EnumParam: implement as value_map, not enum.IntEnum

Because of (1) and (2) below, enum.IntEnum is the wrong tool for
EnumParam. Simplify and fix both problems by using a dict as value map,
with capability for unlimited label string, and unlimited type of value.

In short:

(1) EnumParam's enum labels do not live in the python namespace; they are
    chosen to make good UI labels and CSV values.

(2) enum values should support any type that the given PES attribute
    needs (int, bool, bytes, ...), not only int.

In practice:

(1)
The first purpose of enum.IntEnum is to use the names of an enum in the
python namespace like "MY_LABEL". This means that the enum labels must
be valid python identifiers.

By using enum.IntEnum, we make it impossible to use enum labels like
"SUCI-on" (dash not allowed in python identifier) or "True" (keyword not
allowed as python identifier) or "2" (numeric constant cannot be a
python identifier). IOW, these labels are no longer supported to appear
in a drop-down select box in a web UI.

(2)
The second purpose of enum.IntEnum is to ensure that all values have a
checked type, i.e. int.

By using enum.IntEnum, we can only write int values directly to a PES
attribute. In practice, besides int, some use cases need bool or bytes
etc., i.e. EnumParam should be capable of storing *any* type as value,
as dictated by what needs to be put into the ProfileElementSequence.

History: this patch is the original version of EnumParam, which was
modified during CR to use enum.IntEnum.

Future: new ConfigurableParameters coming up for CR would like to
introduce non-python-identifier enum labels, and non-int values:
- MncLen (labels "2" and "3")
- EuiccMandatoryServiceParam (values True and False)
- EfUstServiceParam like SuciInUsim (labels "SUCI-in-UE" and
  "SUCI-in-USIM")

Change-Id: I690ceccf0ec7ef7067bcaa5cec1303cdaf0f78a4
Jenkins: skip-card-test
This commit is contained in:
Neels Hofmeyr
2026-06-26 20:52:34 +02:00
parent ec0abeb0be
commit 2554c60e82
3 changed files with 98 additions and 106 deletions
@@ -148,7 +148,7 @@ class ConfigurableParameterTest(unittest.TestCase):
Paramtest(param_cls=p13n.AlgorithmID,
val='usim-test',
expect_clean_val=3,
expect_val='usim_test'),
expect_val='usim-test'),
Paramtest(param_cls=p13n.AlgorithmID,
val=1,
@@ -161,7 +161,7 @@ class ConfigurableParameterTest(unittest.TestCase):
Paramtest(param_cls=p13n.AlgorithmID,
val=3,
expect_clean_val=3,
expect_val='usim_test'),
expect_val='usim-test'),
Paramtest(param_cls=p13n.K,
val='01020304050607080910111213141516',
@@ -558,7 +558,7 @@ class TestEnumParam(unittest.TestCase):
def test_validate_by_name_exact(self):
self.assertEqual(p13n.AlgorithmID.validate_val('Milenage'), 1)
self.assertEqual(p13n.AlgorithmID.validate_val('TUAK'), 2)
self.assertEqual(p13n.AlgorithmID.validate_val('usim_test'), 3)
self.assertEqual(p13n.AlgorithmID.validate_val('usim-test'), 3)
def test_validate_by_int(self):
self.assertEqual(p13n.AlgorithmID.validate_val(1), 1)
@@ -571,7 +571,7 @@ class TestEnumParam(unittest.TestCase):
self.assertEqual(p13n.AlgorithmID.validate_val('tuak'), 2)
def test_validate_fuzzy_hyphen_underscore(self):
# 'usim-test' has a hyphen; enum member is 'usim_test' — must fuzzy-match
# 'usim-test' has a hyphen; enum member is 'usim-test' — must fuzzy-match
self.assertEqual(p13n.AlgorithmID.validate_val('usim-test'), 3)
def test_validate_invalid_name(self):
@@ -608,7 +608,7 @@ class TestEnumParam(unittest.TestCase):
def test_map_val_known(self):
self.assertEqual(p13n.AlgorithmID.map_val_to_name(1), 'Milenage')
self.assertEqual(p13n.AlgorithmID.map_val_to_name(2), 'TUAK')
self.assertEqual(p13n.AlgorithmID.map_val_to_name(3), 'usim_test')
self.assertEqual(p13n.AlgorithmID.map_val_to_name(3), 'usim-test')
def test_map_val_unknown_nonstrict(self):
self.assertIsNone(p13n.AlgorithmID.map_val_to_name(99))
@@ -622,7 +622,9 @@ class TestEnumParam(unittest.TestCase):
def test_name_normalize(self):
self.assertEqual(p13n.AlgorithmID.name_normalize('Milenage'), 'Milenage')
self.assertEqual(p13n.AlgorithmID.name_normalize('milenage'), 'Milenage')
self.assertEqual(p13n.AlgorithmID.name_normalize('usim-test'), 'usim_test')
self.assertEqual(p13n.AlgorithmID.name_normalize('usimtest'), 'usim-test')
self.assertEqual(p13n.AlgorithmID.name_normalize('usim_test'), 'usim-test')
self.assertEqual(p13n.AlgorithmID.name_normalize('USIM Test'), 'usim-test')
# --- clean_name_str ---
@@ -77,7 +77,7 @@ ok: TS48v5_SAIP2.1A_NoBERTLV.der AlgorithmID(val= 'TUAK':str)
ok: TS48v5_SAIP2.1A_NoBERTLV.der AlgorithmID(val= 'usim-test':str)
clean_val= 3:int
read_back_val= {'Algorithm': 'usim_test'}:{str}
read_back_val= {'Algorithm': 'usim-test'}:{str}
ok: TS48v5_SAIP2.1A_NoBERTLV.der AlgorithmID(val= 1:int)
clean_val= 1:int
@@ -89,7 +89,7 @@ ok: TS48v5_SAIP2.1A_NoBERTLV.der AlgorithmID(val= 2:int)
ok: TS48v5_SAIP2.1A_NoBERTLV.der AlgorithmID(val= 3:int)
clean_val= 3:int
read_back_val= {'Algorithm': 'usim_test'}:{str}
read_back_val= {'Algorithm': 'usim-test'}:{str}
ok: TS48v5_SAIP2.1A_NoBERTLV.der K(val= '01020304050607080910111213141516':str)
clean_val= b'\x01\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16':bytes
@@ -777,7 +777,7 @@ ok: TS48v5_SAIP2.3_BERTLV_SUCI.der AlgorithmID(val= 'TUAK':str)
ok: TS48v5_SAIP2.3_BERTLV_SUCI.der AlgorithmID(val= 'usim-test':str)
clean_val= 3:int
read_back_val= {'Algorithm': 'usim_test'}:{str}
read_back_val= {'Algorithm': 'usim-test'}:{str}
ok: TS48v5_SAIP2.3_BERTLV_SUCI.der AlgorithmID(val= 1:int)
clean_val= 1:int
@@ -789,7 +789,7 @@ ok: TS48v5_SAIP2.3_BERTLV_SUCI.der AlgorithmID(val= 2:int)
ok: TS48v5_SAIP2.3_BERTLV_SUCI.der AlgorithmID(val= 3:int)
clean_val= 3:int
read_back_val= {'Algorithm': 'usim_test'}:{str}
read_back_val= {'Algorithm': 'usim-test'}:{str}
ok: TS48v5_SAIP2.3_BERTLV_SUCI.der K(val= '01020304050607080910111213141516':str)
clean_val= b'\x01\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16':bytes