laforge has submitted this change. ( 
https://gerrit.osmocom.org/c/pysim/+/38195?usp=email )

Change subject: filesystem: pass total_len to construct of when encoding file 
contents
......................................................................

filesystem: pass total_len to construct of when encoding file contents

In our construct models we frequently use a context parameter "total_len",
we also pass this parameter to construct when we decode files, but we
do not pass it when we generate files. This is a problem, because when
total_len is used in the construct model, this parameter must be known
also when decoding the file.

Let's make sure that the total_len is properly determined and and passed
to construct (via pyosmocom).

Related: OS#5714
Change-Id: I1b7a51594fbc5d9fe01132c39354a2fa88d53f9b
---
M pySim/filesystem.py
M pySim/gsm_r.py
M pySim/runtime.py
M pySim/sysmocom_sja2.py
M pySim/ts_31_102.py
M pySim/ts_31_102_telecom.py
M pySim/ts_51_011.py
M tests/pySim-shell_test/file_content/test_record_uicc.ok
M tests/unittests/test_files.py
9 files changed, 348 insertions(+), 41 deletions(-)

Approvals:
  laforge: Looks good to me, approved
  Jenkins Builder: Verified




diff --git a/pySim/filesystem.py b/pySim/filesystem.py
index c55bcbe..6e8852a 100644
--- a/pySim/filesystem.py
+++ b/pySim/filesystem.py
@@ -743,7 +743,26 @@
             return t.to_dict()
         return {'raw': raw_bin_data.hex()}

-    def encode_bin(self, abstract_data: dict) -> bytearray:
+    def __get_size(self, total_len: Optional[int] = None) -> Optional[int]:
+        """Get the size (total length) of the file"""
+
+        # Caller has provided the actual total length of the file, this should 
be the default case
+        if total_len is not None:
+            return total_len
+
+        if self.size is None:
+            return None
+
+        # Alternatively use the recommended size from the specification
+        if self.size[1] is not None:
+            return self.size[1]
+        # In case no recommended size is specified, use the minimum size
+        if self.size[0] is not None:
+            return self.size[0]
+
+        return None
+
+    def encode_bin(self, abstract_data: dict, total_len: Optional[int] = None) 
-> bytearray:
         """Encode abstract representation into raw (binary) data.

         A derived class would typically provide an _encode_bin() or 
_encode_hex() method
@@ -752,17 +771,18 @@

         Args:
             abstract_data : dict representing the decoded data
+            total_len : expected total length of the encoded data (file size)
         Returns:
             binary encoded data
         """
         method = getattr(self, '_encode_bin', None)
         if callable(method):
-            return method(abstract_data)
+            return method(abstract_data, total_len = 
self.__get_size(total_len))
         method = getattr(self, '_encode_hex', None)
         if callable(method):
-            return h2b(method(abstract_data))
+            return h2b(method(abstract_data, total_len = 
self.__get_size(total_len)))
         if self._construct:
-            return build_construct(self._construct, abstract_data)
+            return build_construct(self._construct, abstract_data, 
{'total_len' : self.__get_size(total_len)})
         if self._tlv:
             t = self._tlv() if inspect.isclass(self._tlv) else self._tlv
             t.from_dict(abstract_data)
@@ -770,7 +790,7 @@
         raise NotImplementedError(
             "%s encoder not yet implemented. Patches welcome." % self)

-    def encode_hex(self, abstract_data: dict) -> str:
+    def encode_hex(self, abstract_data: dict, total_len: Optional[int] = None) 
-> str:
         """Encode abstract representation into raw (hex string) data.

         A derived class would typically provide an _encode_bin() or 
_encode_hex() method
@@ -779,18 +799,19 @@

         Args:
             abstract_data : dict representing the decoded data
+            total_len : expected total length of the encoded data (file size)
         Returns:
             hex string encoded data
         """
         method = getattr(self, '_encode_hex', None)
         if callable(method):
-            return method(abstract_data)
+            return method(abstract_data, total_len = 
self.__get_size(total_len))
         method = getattr(self, '_encode_bin', None)
         if callable(method):
-            raw_bin_data = method(abstract_data)
+            raw_bin_data = method(abstract_data, total_len = 
self.__get_size(total_len))
             return b2h(raw_bin_data)
         if self._construct:
-            return b2h(build_construct(self._construct, abstract_data))
+            return b2h(build_construct(self._construct, abstract_data, 
{'total_len':self.__get_size(total_len)}))
         if self._tlv:
             t = self._tlv() if inspect.isclass(self._tlv) else self._tlv
             t.from_dict(abstract_data)
@@ -1030,7 +1051,26 @@
             return t.to_dict()
         return {'raw': raw_hex_data}

-    def encode_record_hex(self, abstract_data: dict, record_nr: int) -> str:
+    def __get_rec_len(self, total_len: Optional[int] = None) -> Optional[int]:
+        """Get the length (total length) of the file record"""
+
+        # Caller has provided the actual total length of the record, this 
should be the default case
+        if total_len is not None:
+            return total_len
+
+        if self.rec_len is None:
+            return None
+
+        # Alternatively use the recommended length from the specification
+        if self.rec_len[1] is not None:
+            return self.rec_len[1]
+        # In case no recommended length is specified, use the minimum length
+        if self.rec_len[0] is not None:
+            return self.rec_len[0]
+
+        return None
+
+    def encode_record_hex(self, abstract_data: dict, record_nr: int, 
total_len: Optional[int] = None) -> str:
         """Encode abstract representation into raw (hex string) data.

         A derived class would typically provide an _encode_record_bin() or 
_encode_record_hex()
@@ -1040,18 +1080,19 @@
         Args:
             abstract_data : dict representing the decoded data
             record_nr : record number (1 for first record, ...)
+            total_len : expected total length of the encoded data (record 
length)
         Returns:
             hex string encoded data
         """
         method = getattr(self, '_encode_record_hex', None)
         if callable(method):
-            return method(abstract_data, record_nr=record_nr)
+            return method(abstract_data, record_nr=record_nr, total_len = 
self.__get_rec_len(total_len))
         method = getattr(self, '_encode_record_bin', None)
         if callable(method):
-            raw_bin_data = method(abstract_data, record_nr=record_nr)
+            raw_bin_data = method(abstract_data, record_nr=record_nr, 
total_len = self.__get_rec_len(total_len))
             return b2h(raw_bin_data)
         if self._construct:
-            return b2h(build_construct(self._construct, abstract_data))
+            return b2h(build_construct(self._construct, abstract_data, 
{'total_len':self.__get_rec_len(total_len)}))
         if self._tlv:
             t = self._tlv() if inspect.isclass(self._tlv) else self._tlv
             t.from_dict(abstract_data)
@@ -1059,7 +1100,7 @@
         raise NotImplementedError(
             "%s encoder not yet implemented. Patches welcome." % self)

-    def encode_record_bin(self, abstract_data: dict, record_nr : int) -> 
bytearray:
+    def encode_record_bin(self, abstract_data: dict, record_nr : int, 
total_len: Optional[int] = None) -> bytearray:
         """Encode abstract representation into raw (binary) data.

         A derived class would typically provide an _encode_record_bin() or 
_encode_record_hex()
@@ -1069,17 +1110,18 @@
         Args:
             abstract_data : dict representing the decoded data
             record_nr : record number (1 for first record, ...)
+            total_len : expected total length of the encoded data (record 
length)
         Returns:
             binary encoded data
         """
         method = getattr(self, '_encode_record_bin', None)
         if callable(method):
-            return method(abstract_data, record_nr=record_nr)
+            return method(abstract_data, record_nr=record_nr, total_len = 
self.__get_rec_len(total_len))
         method = getattr(self, '_encode_record_hex', None)
         if callable(method):
-            return h2b(method(abstract_data, record_nr=record_nr))
+            return h2b(method(abstract_data, record_nr=record_nr, total_len = 
self.__get_rec_len(total_len)))
         if self._construct:
-            return build_construct(self._construct, abstract_data)
+            return build_construct(self._construct, abstract_data, 
{'total_len':self.__get_rec_len(total_len)})
         if self._tlv:
             t = self._tlv() if inspect.isclass(self._tlv) else self._tlv
             t.from_dict(abstract_data)
@@ -1224,7 +1266,20 @@
             return t.to_dict()
         return {'raw': raw_hex_data}

-    def encode_record_hex(self, abstract_data: dict) -> str:
+    def __get_rec_len(self, total_len: Optional[int] = None) -> Optional[int]:
+        """Get the length (total length) of the file record"""
+
+        # Caller has provided the actual total length of the record, this 
should be the default case
+        if total_len is not None:
+            return total_len
+
+        # Alternatively use the record length from the specification
+        if self.rec_len:
+            return self.rec_len
+
+        return None
+
+    def encode_record_hex(self, abstract_data: dict, total_len: Optional[int] 
= None) -> str:
         """Encode abstract representation into raw (hex string) data.

         A derived class would typically provide an _encode_record_bin() or 
_encode_record_hex()
@@ -1233,17 +1288,19 @@

         Args:
             abstract_data : dict representing the decoded data
+            total_len : expected total length of the encoded data (record 
length)
         Returns:
             hex string encoded data
         """
         method = getattr(self, '_encode_record_hex', None)
         if callable(method):
-            return method(abstract_data)
+            return method(abstract_data, total_len = 
self.__get_rec_len(total_len))
         method = getattr(self, '_encode_record_bin', None)
         if callable(method):
-            return b2h(method(abstract_data))
+            return b2h(method(abstract_data, total_len = 
self.__get_rec_len(total_len)))
         if self._construct:
-            return b2h(filter_dict(build_construct(self._construct, 
abstract_data)))
+            return b2h(filter_dict(build_construct(self._construct, 
abstract_data,
+                                                   
{'total_len':self.__get_rec_len(total_len)})))
         if self._tlv:
             t = self._tlv() if inspect.isclass(self._tlv) else self._tlv
             t.from_dict(abstract_data)
@@ -1251,7 +1308,7 @@
         raise NotImplementedError(
             "%s encoder not yet implemented. Patches welcome." % self)

-    def encode_record_bin(self, abstract_data: dict) -> bytearray:
+    def encode_record_bin(self, abstract_data: dict, total_len: Optional[int] 
= None) -> bytearray:
         """Encode abstract representation into raw (binary) data.

         A derived class would typically provide an _encode_record_bin() or 
_encode_record_hex()
@@ -1260,17 +1317,19 @@

         Args:
             abstract_data : dict representing the decoded data
+            total_len : expected total length of the encoded data (record 
length)
         Returns:
             binary encoded data
         """
         method = getattr(self, '_encode_record_bin', None)
         if callable(method):
-            return method(abstract_data)
+            return method(abstract_data, total_len = 
self.__get_rec_len(total_len))
         method = getattr(self, '_encode_record_hex', None)
         if callable(method):
-            return h2b(method(abstract_data))
+            return h2b(method(abstract_data, total_len = 
self.__get_rec_len(total_len)))
         if self._construct:
-            return filter_dict(build_construct(self._construct, abstract_data))
+            return filter_dict(build_construct(self._construct, abstract_data,
+                                               
{'total_len':self.__get_rec_len(total_len)}))
         if self._tlv:
             t = self._tlv() if inspect.isclass(self._tlv) else self._tlv
             t.from_dict(abstract_data)
@@ -1283,8 +1342,8 @@
                   for i in range(0, len(raw_bin_data), self.rec_len)]
         return [self.decode_record_bin(x) for x in chunks]

-    def _encode_bin(self, abstract_data) -> bytes:
-        chunks = [self.encode_record_bin(x) for x in abstract_data]
+    def _encode_bin(self, abstract_data, **kwargs) -> bytes:
+        chunks = [self.encode_record_bin(x, total_len = 
kwargs.get('total_len', None)) for x in abstract_data]
         # FIXME: pad to file size
         return b''.join(chunks)

diff --git a/pySim/gsm_r.py b/pySim/gsm_r.py
index b29fbc4..bc54c72 100644
--- a/pySim/gsm_r.py
+++ b/pySim/gsm_r.py
@@ -290,7 +290,7 @@
         else:
             return parse_construct(self.construct_others, raw_bin_data)

-    def _encode_record_bin(self, abstract_data : dict, record_nr : int) -> 
bytearray:
+    def _encode_record_bin(self, abstract_data : dict, record_nr : int, 
**kwargs) -> bytearray:
         r = None
         if record_nr == 1:
             r = self.construct_first.build(abstract_data)
diff --git a/pySim/runtime.py b/pySim/runtime.py
index 3714832..0ee15d0 100644
--- a/pySim/runtime.py
+++ b/pySim/runtime.py
@@ -524,7 +524,7 @@
         Args:
             data : abstract data which is to be encoded and written
         """
-        data_hex = self.selected_file.encode_hex(data)
+        data_hex = self.selected_file.encode_hex(data, 
self.selected_file_size())
         return self.update_binary(data_hex)

     def read_record(self, rec_nr: int = 0):
@@ -574,7 +574,7 @@
             rec_nr : Record number to read
             data_hex : Abstract data to be written
         """
-        data_hex = self.selected_file.encode_record_hex(data, rec_nr)
+        data_hex = self.selected_file.encode_record_hex(data, rec_nr, 
self.selected_file_record_len())
         return self.update_record(rec_nr, data_hex)

     def retrieve_data(self, tag: int = 0):
diff --git a/pySim/sysmocom_sja2.py b/pySim/sysmocom_sja2.py
index 2c6d66a..bef955a 100644
--- a/pySim/sysmocom_sja2.py
+++ b/pySim/sysmocom_sja2.py
@@ -252,7 +252,7 @@
         else:
             return parse_construct(self._construct, raw_bin_data)

-    def _encode_bin(self, abstract_data: dict) -> bytearray:
+    def _encode_bin(self, abstract_data: dict, **kwargs) -> bytearray:
         if abstract_data['cfg']['algorithm'] == 'tuak':
             return build_construct(self._constr_tuak, abstract_data)
         else:
diff --git a/pySim/ts_31_102.py b/pySim/ts_31_102.py
index e27d2db..ab5ee54 100644
--- a/pySim/ts_31_102.py
+++ b/pySim/ts_31_102.py
@@ -344,7 +344,7 @@
                 out.append({k: v})
         return out

-    def _encode_hex(self, in_json):
+    def _encode_hex(self, in_json, **kwargs):
         out_bytes = self._encode_prot_scheme_id_list(
             in_json['prot_scheme_id_list'])
         d = self._expand_pubkey_list(in_json['hnet_pubkey_list'])
@@ -396,8 +396,8 @@
             'hnet_pubkey_list':    hnet_pubkey_list
         }

-    def _encode_bin(self, in_json):
-        return h2b(self._encode_hex(in_json))
+    def _encode_bin(self, in_json, **kwargs):
+        return h2b(self._encode_hex(in_json, **kwargs))


 class EF_LI(TransRecEF):
diff --git a/pySim/ts_31_102_telecom.py b/pySim/ts_31_102_telecom.py
index 93154bb..a949070 100644
--- a/pySim/ts_31_102_telecom.py
+++ b/pySim/ts_31_102_telecom.py
@@ -59,7 +59,7 @@
                     ret[service_nr]['description'] = self.table[service_nr]
         return ret

-    def _encode_bin(self, in_json):
+    def _encode_bin(self, in_json, **kwargs):
         # compute the required binary size
         bin_len = 0
         for srv in in_json.keys():
diff --git a/pySim/ts_51_011.py b/pySim/ts_51_011.py
index 26dce8b..1070b42 100644
--- a/pySim/ts_51_011.py
+++ b/pySim/ts_51_011.py
@@ -358,7 +358,7 @@
     def _decode_hex(self, raw_hex):
         return {'imsi': dec_imsi(raw_hex)}

-    def _encode_hex(self, abstract):
+    def _encode_hex(self, abstract, **kwargs):
         return enc_imsi(abstract['imsi'])

     @with_default_category('File-Specific Commands')
@@ -446,7 +446,7 @@
             }
         return ret

-    def _encode_bin(self, in_json):
+    def _encode_bin(self, in_json, **kwargs):
         # compute the required binary size
         bin_len = 0
         for srv in in_json.keys():
@@ -969,7 +969,7 @@
     def _decode_hex(self, raw_hex):
         return {'iccid': dec_iccid(raw_hex)}

-    def _encode_hex(self, abstract):
+    def _encode_hex(self, abstract, **kwargs):
         return enc_iccid(abstract['iccid'])

 # TS 102 221 Section 13.3 / TS 31.101 Secction 13 / TS 51.011 Section 10.1.2
diff --git a/tests/pySim-shell_test/file_content/test_record_uicc.ok 
b/tests/pySim-shell_test/file_content/test_record_uicc.ok
index 268740e..14183eb 100644
--- a/tests/pySim-shell_test/file_content/test_record_uicc.ok
+++ b/tests/pySim-shell_test/file_content/test_record_uicc.ok
@@ -12,7 +12,7 @@
 010 0123456789abcdef0123456789abcdef0123456789abcdef0123456789
 [
     {
-        "alpha_id": null,
+        "alpha_id": "",
         "len_of_bcd": 7,
         "ton_npi": {
             "ext": true,
@@ -60,7 +60,7 @@
         "ext4_record_id": 255
     },
     {
-        "alpha_id": null,
+        "alpha_id": "",
         "len_of_bcd": 7,
         "ton_npi": {
             "ext": true,
@@ -120,7 +120,7 @@
         "ext4_record_id": 255
     },
     {
-        "alpha_id": null,
+        "alpha_id": "",
         "len_of_bcd": 7,
         "ton_npi": {
             "ext": true,
@@ -133,7 +133,7 @@
     }
 ]
 {
-    "alpha_id": null,
+    "alpha_id": "",
     "len_of_bcd": 7,
     "ton_npi": {
         "ext": true,
diff --git a/tests/unittests/test_files.py b/tests/unittests/test_files.py
index 2bd3aea..a3ccf06 100755
--- a/tests/unittests/test_files.py
+++ b/tests/unittests/test_files.py
@@ -30,6 +30,9 @@
 import pySim.gsm_r
 import pySim.cdma_ruim

+from construct import Int8ub, Struct, Padding, this
+from osmocom.tlv import BER_TLV_IE
+
 def get_qualified_name(c):
     """return the qualified (by module) name of a class."""
     return "%s.%s" % (c.__module__, c.__name__)
@@ -288,6 +291,251 @@
                         re_dec = inst.decode_hex(encoded)
                         self.assertEqual(decoded, re_dec)

+
+class filesystem_enc_dec_test(unittest.TestCase):
+    """ The following set of tests is to verify the code paths in 
filesystem.py. There are several methods to encode
+    or decode a file. Depending on which methods (encode_hex, decode_hex, 
etc.) or structs (_construct, _tlv) are
+    define in the related file object, the encoding/decoding will take a 
different code path. In this test we will
+    try out all of the different encoding/decoding variants by defining one 
test file for each variant. Then we will
+    run an encoding/decoding cycle on each of the test files.
+
+    The test files will also include a padding that is dependent on the 
total_len keyword argument that is passed
+    via the construct context or via **kwargs in case the hand written 
encoding methods (encode_hex, encode_record_hex,
+    etc.) are used. This will ensure that total_len is passed correctly in all 
possible variants.
+    """
+
+    def test_encode_TransparentEF(self):
+
+        class TransparentEF_construct(TransparentEF):
+            def __init__(self, fid='0000', sfid=None, name='EF.DUMMY', 
size=(2, 2),
+                         desc='dummy TransparentEF file to test 
encoding/decoding via _construct'):
+                super().__init__(fid, sfid=sfid, name=name, desc=desc, 
size=size)
+                self._construct = Struct('test'/Int8ub, 
Padding(this._.total_len-1))
+
+        class TransparentEF_encode_hex(TransparentEF):
+            def __init__(self, fid='0000', sfid=None, name='EF.DUMMY', 
size=(2, 2),
+                         desc='dummy TransparentEF file to test manual 
encoding/decoding via _encode/decode_hex'):
+                super().__init__(fid, sfid=sfid, name=name, desc=desc, 
size=size)
+            def _encode_hex(self, in_json, **kwargs):
+                return "%02x" % in_json['test'] + "00" * 
(kwargs.get('total_len') -1)
+            def _decode_hex(self, raw_hex):
+                return {'test': int(raw_hex[0:2],16)}
+
+        class TransparentEF_encode_bin(TransparentEF):
+            def __init__(self, fid='0000', sfid=None, name='EF.DUMMY', 
size=(2, 2),
+                         desc='dummy TransparentEF file to test manual 
encoding/decoding via _encode/decode_bin'):
+                super().__init__(fid, sfid=sfid, name=name, desc=desc, 
size=size)
+            def _encode_bin(self, in_json, **kwargs):
+                return h2b("%02x" % in_json['test'] + "00" * 
(kwargs.get('total_len') -1))
+            def _decode_bin(self, raw_bin_data: bytearray):
+                return {'test': int(b2h(raw_bin_data[0:1]),16)}
+
+        class TransparentEF_tlv(TransparentEF):
+            class TestTlv(BER_TLV_IE, tag=0x81):
+                _construct = Int8ub
+            def __init__(self, fid='0000', sfid=None, name='EF.DUMMY', 
size=(1, 1),
+                         desc='dummy TransparentEF file to test 
encoding/decoding via _tlv'):
+                super().__init__(fid, sfid=sfid, name=name, desc=desc, 
size=size)
+                self._tlv = TransparentEF_tlv.TestTlv
+
+        class TransparentEF_raw(TransparentEF):
+            class TestTlv(BER_TLV_IE, tag=0x81):
+                _construct = Int8ub
+            def __init__(self, fid='0000', sfid=None, name='EF.DUMMY', 
size=(1, 1),
+                         desc='dummy TransparentEF file to test raw 
encoding/decoding'):
+                super().__init__(fid, sfid=sfid, name=name, desc=desc, 
size=size)
+
+        def do_encdec_test(file):
+            res = file.encode_hex({'test':0x41})
+            self.assertEqual(res,hexstr("4100"))
+            res = file.encode_bin({'test':0x41})
+            self.assertEqual(b2h(res),hexstr("4100"))
+            res = file.encode_hex({'test':0x41}, total_len=3)
+            self.assertEqual(res,hexstr("410000"))
+            res = file.encode_bin({'test':0x41}, total_len=3)
+            self.assertEqual(b2h(res),hexstr("410000"))
+            res = file.decode_hex("4100")
+            self.assertEqual(res,{'test':0x41})
+            res = file.decode_bin(b'\x41\x01')
+            self.assertEqual(res,{'test':0x41})
+
+        def do_encdec_test_tlv(file):
+            res = file.encode_hex({'test_tlv':0x41})
+            self.assertEqual(res,hexstr("810141"))
+            res = file.encode_bin({'test_tlv':0x41})
+            self.assertEqual(b2h(res),hexstr("810141"))
+            res = file.decode_hex(hexstr("810141"))
+            self.assertEqual(res,{'test_tlv':0x41})
+            res = file.decode_bin(h2b("810141"))
+            self.assertEqual(res,{'test_tlv':0x41})
+
+        def do_encdec_test_raw(file):
+            res = file.decode_hex("41")
+            self.assertEqual(res,{'raw':'41'})
+            res = file.decode_bin(b'\x41')
+            self.assertEqual(res,{'raw':'41'})
+
+        do_encdec_test(TransparentEF_construct())
+        do_encdec_test(TransparentEF_encode_hex())
+        do_encdec_test(TransparentEF_encode_bin())
+        do_encdec_test_tlv(TransparentEF_tlv())
+        do_encdec_test_raw(TransparentEF_raw())
+
+    def test_encode_LinFixedEF(self):
+
+        class LinFixedEF_construct(LinFixedEF):
+            def __init__(self, fid='0000', sfid=None, name='EF.DUMMY',
+                         desc='dummy LinFixedEF file to test encoding/decoding 
via _construct', **kwargs):
+                super().__init__(fid=fid, sfid=sfid, name=name, desc=desc, 
rec_len=(2, 2), **kwargs)
+                self._construct = Struct('test'/Int8ub, 
Padding(this._.total_len-1))
+
+        class LinFixedEF_encode_hex(LinFixedEF):
+            def __init__(self, fid='0000', sfid=None, name='EF.DUMMY',
+                         desc='dummy LinFixedEF file to test manual 
encoding/decoding via _encode/decode_hex', **kwargs):
+                super().__init__(fid=fid, sfid=sfid, name=name, desc=desc, 
rec_len=(2, 2), **kwargs)
+            def _encode_record_hex(self, in_json, **kwargs):
+                return "%02x" % in_json['test'] + "00" * 
(kwargs.get('total_len') -1)
+            def _decode_record_hex(self, in_hex, **kwargs):
+                return {'test': int(in_hex[0:2],16)}
+
+        class LinFixedEF_encode_bin(LinFixedEF):
+            def __init__(self, fid='0000', sfid=None, name='EF.DUMMY',
+                         desc='dummy LinFixedEF file to test manual 
encoding/decoding via _encode/decode_bin', **kwargs):
+                super().__init__(fid=fid, sfid=sfid, name=name, desc=desc, 
rec_len=(2, 2), **kwargs)
+            def _encode_record_bin(self, in_json, **kwargs):
+                return h2b("%02x" % in_json['test'] + "00" * 
(kwargs.get('total_len') -1))
+            def _decode_record_bin(self, in_bin, **kwargs):
+                return {'test': int(b2h(in_bin[0:1]),16)}
+
+        class LinFixedEF_tlv(LinFixedEF):
+            class TestTlv(BER_TLV_IE, tag=0x81):
+                _construct = Int8ub
+            def __init__(self, fid='0000', sfid=None, name='EF.DUMMY',
+                         desc='dummy LinFixedEF file to test encoding/decoding 
via _tlv', **kwargs):
+                super().__init__(fid=fid, sfid=sfid, name=name, desc=desc, 
rec_len=(1, 1), **kwargs)
+                self._tlv = LinFixedEF_tlv.TestTlv
+
+        class LinFixedEF_raw(LinFixedEF):
+            class TestTlv(BER_TLV_IE, tag=0x81):
+                _construct = Int8ub
+            def __init__(self, fid='0000', sfid=None, name='EF.DUMMY',
+                         desc='dummy LinFixedEF file to test raw 
encoding/decoding', **kwargs):
+                super().__init__(fid=fid, sfid=sfid, name=name, desc=desc, 
rec_len=(1, 1), **kwargs)
+
+        def do_encdec_test(file):
+            res = file.encode_record_hex({'test':0x41}, 1)
+            self.assertEqual(res,hexstr("4100"))
+            res = file.encode_record_bin({'test':0x41}, 1)
+            self.assertEqual(b2h(res),hexstr("4100"))
+            res = file.encode_record_hex({'test':0x41}, 1, total_len=3)
+            self.assertEqual(res,hexstr("410000"))
+            res = file.encode_record_bin({'test':0x41}, 1, total_len=3)
+            self.assertEqual(b2h(res),hexstr("410000"))
+            res = file.decode_record_hex("4100", 1)
+            self.assertEqual(res,{'test':0x41})
+            res = file.decode_record_bin(b'\x41\x00', 1)
+            self.assertEqual(res,{'test':0x41})
+
+        def do_encdec_test_tlv(file):
+            res = file.encode_record_hex({'test_tlv':0x41}, 1)
+            self.assertEqual(res,hexstr("810141"))
+            res = file.encode_record_bin({'test_tlv':0x41}, 1)
+            self.assertEqual(b2h(res),hexstr("810141"))
+            res = file.decode_record_hex(hexstr("810141"), 1)
+            self.assertEqual(res,{'test_tlv':0x41})
+            res = file.decode_record_bin(h2b("810141"), 1)
+            self.assertEqual(res,{'test_tlv':0x41})
+
+        def do_encdec_test_raw(file):
+            res = file.decode_record_hex("41", 1)
+            self.assertEqual(res,{'raw':'41'})
+            res = file.decode_record_bin(b'\x41', 1)
+            self.assertEqual(res,{'raw':'41'})
+
+        do_encdec_test(LinFixedEF_construct())
+        do_encdec_test(LinFixedEF_encode_hex())
+        do_encdec_test(LinFixedEF_encode_bin())
+        do_encdec_test_tlv(LinFixedEF_tlv())
+        do_encdec_test_raw(LinFixedEF_raw())
+
+    def test_encode_TransRecEF(self):
+
+        class TransRecEF_construct(TransRecEF):
+            def __init__(self, fid='0000', sfid=None, name='EF.DUMMY', 
size=(2, 2), rec_len=2,
+                         desc='dummy TransRecEF file to test encoding/decoding 
via _construct', **kwargs):
+                super().__init__(fid=fid, sfid=sfid, name=name, desc=desc, 
size=size, rec_len=rec_len, **kwargs)
+                self._construct = Struct('test'/Int8ub, 
Padding(this._.total_len-1))
+
+        class TransRecEF_encode_hex(TransRecEF):
+            def __init__(self, fid='0000', sfid=None, name='EF.DUMMY', 
size=(2, 2), rec_len=2,
+                         desc='dummy TransRecEF file to test manual 
encoding/decoding via _encode/decode_hex', **kwargs):
+                super().__init__(fid=fid, sfid=sfid, name=name, desc=desc, 
size=size, rec_len=rec_len, **kwargs)
+            def _encode_record_hex(self, in_json, **kwargs):
+                return "%02x" % in_json['test'] + "00" * 
(kwargs.get('total_len') -1)
+            def _decode_record_hex(self, in_hex, **kwargs):
+                return {'test': int(in_hex[0:2],16)}
+
+        class TransRecEF_encode_bin(TransRecEF):
+            def __init__(self, fid='0000', sfid=None, name='EF.DUMMY', 
size=(2, 2), rec_len=2,
+                         desc='dummy TransRecEF file to test manual 
encoding/decoding via _encode/decode_bin', **kwargs):
+                super().__init__(fid=fid, sfid=sfid, name=name, desc=desc, 
size=size, rec_len=rec_len, **kwargs)
+            def _encode_record_bin(self, in_json, **kwargs):
+                return h2b("%02x" % in_json['test'] + "00" * 
(kwargs.get('total_len') -1))
+            def _decode_record_bin(self, in_bin, **kwargs):
+                return {'test': int(b2h(in_bin[0:1]),16)}
+
+        class TransRecEF_tlv(TransRecEF):
+            class TestTlv(BER_TLV_IE, tag=0x81):
+                _construct = Int8ub
+            def __init__(self, fid='0000', sfid=None, name='EF.DUMMY', 
size=(1, 1), rec_len=1,
+                         desc='dummy TransRecEF file to test encoding/decoding 
via _tlv', **kwargs):
+                super().__init__(fid=fid, sfid=sfid, name=name, desc=desc, 
size=size, rec_len=rec_len, **kwargs)
+                self._tlv = TransRecEF_tlv.TestTlv
+
+        class TransRecEF_raw(TransRecEF):
+            class TestTlv(BER_TLV_IE, tag=0x81):
+                _construct = Int8ub
+            def __init__(self, fid='0000', sfid=None, name='EF.DUMMY', 
size=(1, 1), rec_len=1,
+                         desc='dummy TransRecEF file to test raw 
encoding/decoding', **kwargs):
+                super().__init__(fid=fid, sfid=sfid, name=name, desc=desc, 
size=size, rec_len=rec_len, **kwargs)
+
+        def do_encdec_test(file):
+            res = file.encode_record_hex({'test':0x41})
+            self.assertEqual(res,hexstr("4100"))
+            res = file.encode_record_bin({'test':0x41})
+            self.assertEqual(b2h(res),hexstr("4100"))
+            res = file.encode_record_hex({'test':0x41}, total_len=3)
+            self.assertEqual(res,hexstr("410000"))
+            res = file.encode_record_bin({'test':0x41}, total_len=3)
+            self.assertEqual(b2h(res),hexstr("410000"))
+            res = file.decode_record_hex("4100")
+            self.assertEqual(res,{'test':0x41})
+            res = file.decode_record_bin(b'\x41\x00')
+            self.assertEqual(res,{'test':0x41})
+
+        def do_encdec_test_tlv(file):
+            res = file.encode_record_hex({'test_tlv':0x41})
+            self.assertEqual(res,hexstr("810141"))
+            res = file.encode_record_bin({'test_tlv':0x41})
+            self.assertEqual(b2h(res),hexstr("810141"))
+            res = file.decode_record_hex(hexstr("810141"))
+            self.assertEqual(res,{'test_tlv':0x41})
+            res = file.decode_record_bin(h2b("810141"))
+            self.assertEqual(res,{'test_tlv':0x41})
+
+        def do_encdec_test_raw(file):
+            res = file.decode_record_hex("41")
+            self.assertEqual(res,{'raw':'41'})
+            res = file.decode_record_bin(b'\x41')
+            self.assertEqual(res,{'raw':'41'})
+
+        do_encdec_test(TransRecEF_construct())
+        do_encdec_test(TransRecEF_encode_hex())
+        do_encdec_test(TransRecEF_encode_bin())
+        do_encdec_test_tlv(TransRecEF_tlv())
+        do_encdec_test_raw(TransRecEF_raw())
+
+
 if __name__ == '__main__':
     logger = logging.getLogger()
     logger.setLevel(logging.DEBUG)

--
To view, visit https://gerrit.osmocom.org/c/pysim/+/38195?usp=email
To unsubscribe, or for help writing mail filters, visit 
https://gerrit.osmocom.org/settings?usp=email

Gerrit-MessageType: merged
Gerrit-Project: pysim
Gerrit-Branch: master
Gerrit-Change-Id: I1b7a51594fbc5d9fe01132c39354a2fa88d53f9b
Gerrit-Change-Number: 38195
Gerrit-PatchSet: 8
Gerrit-Owner: dexter <[email protected]>
Gerrit-Reviewer: Jenkins Builder
Gerrit-Reviewer: laforge <[email protected]>

Reply via email to