]> Cypherpunks.ru repositories - pyderasn.git/blobdiff - tests/test_pyderasn.py
Raise copyright years
[pyderasn.git] / tests / test_pyderasn.py
index c6621c94bb8eb467fbc60f461c9abe3e5b7dc642..e8f8bdf4570e1c52adbf2b7a7c5a827c47a2099c 100644 (file)
@@ -1,6 +1,6 @@
 # coding: utf-8
 # PyDERASN -- Python ASN.1 DER/CER/BER codec with abstract structures
-# Copyright (C) 2017-2020 Sergey Matveev <stargrave@stargrave.org>
+# Copyright (C) 2017-2024 Sergey Matveev <stargrave@stargrave.org>
 #
 # This program is free software: you can redistribute it and/or modify
 # it under the terms of the GNU Lesser General Public License as
@@ -32,6 +32,7 @@ from string import whitespace
 from time import mktime
 from time import time
 from unittest import TestCase
+from unittest.mock import patch
 
 from dateutil.tz import UTC
 from hypothesis import assume
@@ -53,19 +54,9 @@ from hypothesis.strategies import sampled_from
 from hypothesis.strategies import sets
 from hypothesis.strategies import text
 from hypothesis.strategies import tuples
-from six import assertRaisesRegex
-from six import binary_type
-from six import byte2int
-from six import indexbytes
-from six import int2byte
-from six import iterbytes
-from six import PY2
-from six import text_type
-from six import unichr as six_unichr
-from six.moves import xrange as six_xrange
-from six.moves.cPickle import dumps as pickle_dumps
-from six.moves.cPickle import HIGHEST_PROTOCOL as pickle_proto
-from six.moves.cPickle import loads as pickle_loads
+from pickle import dumps as pickle_dumps
+from pickle import HIGHEST_PROTOCOL as pickle_proto
+from pickle import loads as pickle_loads
 
 from pyderasn import _pp
 from pyderasn import abs_decode_path
@@ -132,6 +123,7 @@ from pyderasn import UTCTime
 from pyderasn import UTF8String
 from pyderasn import VideotexString
 from pyderasn import VisibleString
+import pyderasn
 
 
 max_examples = environ.get("MAX_EXAMPLES")
@@ -170,7 +162,7 @@ def register_class(klass):
 def assert_exceeding_data(self, call, junk):
     if len(junk) <= 0:
         return
-    with assertRaisesRegex(self, ExceedingData, "%d trailing bytes" % len(junk)) as err:
+    with self.assertRaisesRegex(ExceedingData, "%d trailing bytes" % len(junk)) as err:
         call()
     repr(err)
 
@@ -194,8 +186,8 @@ class TestTagCoder(TestCase):
         self.assertEqual(tag_decode(raw), (klass, form, num))
         self.assertEqual(len(raw), 1)
         self.assertEqual(
-            byte2int(tag_encode(klass=klass, form=form, num=0)),
-            byte2int(raw) & (1 << 7 | 1 << 6 | 1 << 5),
+            tag_encode(klass=klass, form=form, num=0)[0],
+            raw[0] & (1 << 7 | 1 << 6 | 1 << 5),
         )
         stripped, tlen, tail = tag_strip(memoryview(raw + junk))
         self.assertSequenceEqual(stripped.tobytes(), raw)
@@ -214,11 +206,11 @@ class TestTagCoder(TestCase):
         self.assertEqual(tag_decode(raw), (klass, form, num))
         self.assertGreater(len(raw), 1)
         self.assertEqual(
-            byte2int(tag_encode(klass=klass, form=form, num=0)) | 31,
-            byte2int(raw[:1]),
+            tag_encode(klass=klass, form=form, num=0)[0] | 31,
+            raw[0],
         )
-        self.assertEqual(byte2int(raw[-1:]) & 0x80, 0)
-        self.assertTrue(all(b & 0x80 > 0 for b in iterbytes(raw[1:-1])))
+        self.assertEqual(raw[-1] & 0x80, 0)
+        self.assertTrue(all(b & 0x80 > 0 for b in raw[1:-1]))
         stripped, tlen, tail = tag_strip(memoryview(raw + junk))
         self.assertSequenceEqual(stripped.tobytes(), raw)
         self.assertEqual(tlen, len(raw))
@@ -230,7 +222,7 @@ class TestTagCoder(TestCase):
         raw = bytearray(tag_encode(num=num))
         for i in range(1, len(raw)):
             raw[i] |= 0x80
-        with assertRaisesRegex(self, DecodeError, "unfinished tag"):
+        with self.assertRaisesRegex(DecodeError, "unfinished tag"):
             tag_strip(bytes(raw))
 
     def test_go_vectors_valid(self):
@@ -272,8 +264,8 @@ class TestTagCoder(TestCase):
         integers(min_value=0, max_value=2),
     )
     def test_long_instead_of_short(self, l, dummy_num):
-        octets = (b"\x00" * dummy_num) + int2byte(l)
-        octets = int2byte((dummy_num + 1) | 0x80) + octets
+        octets = (b"\x00" * dummy_num) + bytes([l])
+        octets = bytes([(dummy_num + 1) | 0x80]) + octets
         with self.assertRaises(DecodeError):
             len_decode(octets)
 
@@ -281,13 +273,13 @@ class TestTagCoder(TestCase):
     def test_leading_zero_byte(self, klass, form, num):
         raw = tag_encode(klass=klass, form=form, num=num)
         raw = b"".join((raw[:1], b"\x80", raw[1:]))
-        with assertRaisesRegex(self, DecodeError, "leading zero byte"):
+        with self.assertRaisesRegex(DecodeError, "leading zero byte"):
             tag_strip(raw)
 
     @given(tag_classes, tag_forms, integers(max_value=30, min_value=0))
     def test_unexpected_long_form(self, klass, form, num):
-        raw = int2byte(klass | form | 31) + int2byte(num)
-        with assertRaisesRegex(self, DecodeError, "unexpected long form"):
+        raw = bytes([klass | form | 31, num])
+        with self.assertRaisesRegex(DecodeError, "unexpected long form"):
             tag_strip(raw)
 
 
@@ -314,9 +306,9 @@ class TestLenCoder(TestCase):
         raw = len_encode(l) + junk
         decoded, llen, tail = len_decode(memoryview(raw))
         self.assertEqual(decoded, l)
-        self.assertEqual((llen - 1) | 0x80, byte2int(raw))
+        self.assertEqual((llen - 1) | 0x80, raw[0])
         self.assertEqual(llen, len(raw) - len(junk))
-        self.assertNotEqual(indexbytes(raw, 1), 0)
+        self.assertNotEqual(raw[1], 0)
         self.assertSequenceEqual(tail.tobytes(), junk)
 
     def test_empty(self):
@@ -335,8 +327,6 @@ text_printable = text(alphabet=printable, min_size=1)
 @composite
 def text_letters(draw):
     result = draw(text(alphabet=ascii_letters, min_size=1))
-    if PY2:
-        result = result.encode("ascii")
     return result
 
 
@@ -689,7 +679,7 @@ class TestBoolean(CommonMixin, TestCase):
             repr(obj)
             list(obj.pps())
 
-    @given(integers(min_value=2))
+    @given(integers(min_value=2, max_value=10))
     def test_invalid_len(self, l):
         with self.assertRaises(InvalidLength):
             Boolean().decode(b"".join((
@@ -700,17 +690,13 @@ class TestBoolean(CommonMixin, TestCase):
 
     @given(integers(min_value=0 + 1, max_value=255 - 1))
     def test_ber_value(self, value):
-        with assertRaisesRegex(self, DecodeError, "unacceptable Boolean value"):
+        with self.assertRaisesRegex(DecodeError, "unacceptable Boolean value"):
             Boolean().decode(b"".join((
                 Boolean.tag_default,
                 len_encode(1),
-                int2byte(value),
+                bytes([value]),
             )))
-        encoded = b"".join((
-            Boolean.tag_default,
-            len_encode(1),
-            int2byte(value),
-        ))
+        encoded = b"".join((Boolean.tag_default, len_encode(1), bytes([value])))
         obj, _ = Boolean().decode(encoded, ctx={"bered": True})
         list(Boolean().decode_evgen(encoded, ctx={"bered": True}))
         self.assertTrue(bool(obj))
@@ -730,7 +716,7 @@ class TestBoolean(CommonMixin, TestCase):
         encoded = expl + LENINDEF + Boolean(False).encode()
         with self.assertRaises(LenIndefForm):
             Boolean(expl=expl).decode(encoded + junk)
-        with assertRaisesRegex(self, DecodeError, "no EOC"):
+        with self.assertRaisesRegex(DecodeError, "no EOC"):
             Boolean(expl=expl).decode(encoded + junk, ctx={"bered": True})
         obj, tail = Boolean(expl=expl).decode(
             encoded + EOC + junk,
@@ -945,24 +931,24 @@ class TestInteger(CommonMixin, TestCase):
         with self.assertRaises(BoundsError) as err:
             Integer(value=values[0], bounds=(values[1], values[2]))
         repr(err.exception)
-        with assertRaisesRegex(self, DecodeError, "bounds") as err:
+        with self.assertRaisesRegex(DecodeError, "bounds") as err:
             Integer(bounds=(values[1], values[2])).decode(
                 Integer(values[0]).encode()
             )
         repr(err.exception)
-        with assertRaisesRegex(self, DecodeError, "bounds") as err:
+        with self.assertRaisesRegex(DecodeError, "bounds") as err:
             Integer(bounds=(values[1], values[2])).decode(
                 encode2pass(Integer(values[0]))
             )
         with self.assertRaises(BoundsError) as err:
             Integer(value=values[2], bounds=(values[0], values[1]))
         repr(err.exception)
-        with assertRaisesRegex(self, DecodeError, "bounds") as err:
+        with self.assertRaisesRegex(DecodeError, "bounds") as err:
             Integer(bounds=(values[0], values[1])).decode(
                 Integer(values[2]).encode()
             )
         repr(err.exception)
-        with assertRaisesRegex(self, DecodeError, "bounds") as err:
+        with self.assertRaisesRegex(DecodeError, "bounds") as err:
             Integer(bounds=(values[0], values[1])).decode(
                 encode2pass(Integer(values[2]))
             )
@@ -1286,6 +1272,8 @@ def bit_string_values_strategy(draw, schema=None, value_required=False, do_expl=
         if generation_choice == 2 or draw(booleans()):
             return draw(binary(max_size=len(schema) // 8))
         if generation_choice == 3 or draw(booleans()):
+            if len(schema) == 0:
+                return ()
             return tuple(draw(lists(sampled_from([name for name, _ in schema]))))
         return None
     value = _value(value_required)
@@ -1649,7 +1637,7 @@ class TestBitString(CommonMixin, TestCase):
             BitString().decode(b"".join((
                 BitString.tag_default,
                 len_encode(1),
-                int2byte(pad_size),
+                bytes([pad_size]),
             )))
 
     def test_go_vectors_invalid(self):
@@ -1721,7 +1709,7 @@ class TestBitString(CommonMixin, TestCase):
         payload_expected = b""
         bit_len_expected = 0
         for chunk_input in chunk_inputs:
-            if isinstance(chunk_input, binary_type):
+            if isinstance(chunk_input, bytes):
                 chunks.append(BitString(chunk_input).encode())
                 payload_expected += chunk_input
                 bit_len_expected += len(chunk_input) * 8
@@ -1753,7 +1741,7 @@ class TestBitString(CommonMixin, TestCase):
             b"".join(chunks) +
             chunk_last.encode()
         )
-        with assertRaisesRegex(self, DecodeError, "unallowed BER"):
+        with self.assertRaisesRegex(DecodeError, "unallowed BER"):
             BitString(impl=tag_encode(impl)).decode(encoded_indefinite)
         for lenindef_expected, encoded in (
                 (True, encoded_indefinite),
@@ -1793,7 +1781,7 @@ class TestBitString(CommonMixin, TestCase):
         decode_path_strat,
     )
     def test_ber_definite_too_short(self, offset, decode_path):
-        with assertRaisesRegex(self, DecodeError, "longer than data") as err:
+        with self.assertRaisesRegex(DecodeError, "longer than data") as err:
             BitString().decode(
                 tag_encode(3, form=TagFormConstructed) + len_encode(1),
                 offset=offset,
@@ -1808,7 +1796,7 @@ class TestBitString(CommonMixin, TestCase):
         decode_path_strat,
     )
     def test_ber_definite_no_data(self, offset, decode_path):
-        with assertRaisesRegex(self, DecodeError, "zero length") as err:
+        with self.assertRaisesRegex(DecodeError, "zero length") as err:
             BitString().decode(
                 tag_encode(3, form=TagFormConstructed) + len_encode(0),
                 offset=offset,
@@ -1843,7 +1831,7 @@ class TestBitString(CommonMixin, TestCase):
     def test_ber_definite_chunk_out_of_bounds(self, offset, decode_path, chunks):
         bs = BitString(b"data").encode()
         bs_longer = BitString(b"data-longer").encode()
-        with assertRaisesRegex(self, DecodeError, "chunk out of bounds") as err:
+        with self.assertRaisesRegex(DecodeError, "chunk out of bounds") as err:
             BitString().decode(
                 (
                     tag_encode(3, form=TagFormConstructed) +
@@ -1863,7 +1851,7 @@ class TestBitString(CommonMixin, TestCase):
         decode_path_strat,
     )
     def test_ber_indefinite_no_chunks(self, offset, decode_path):
-        with assertRaisesRegex(self, DecodeError, "no chunks") as err:
+        with self.assertRaisesRegex(DecodeError, "no chunks") as err:
             BitString().decode(
                 tag_encode(3, form=TagFormConstructed) + LENINDEF + EOC,
                 offset=offset,
@@ -1883,7 +1871,7 @@ class TestBitString(CommonMixin, TestCase):
         chunks.append(bs_short)
         offset = d.draw(integers(min_value=0))
         decode_path = d.draw(decode_path_strat)
-        with assertRaisesRegex(self, DecodeError, "multiple of 8 bits") as err:
+        with self.assertRaisesRegex(DecodeError, "multiple of 8 bits") as err:
             BitString().decode(
                 (
                     tag_encode(3, form=TagFormConstructed) +
@@ -1984,7 +1972,7 @@ class TestOctetString(CommonMixin, TestCase):
 
     def test_invalid_value_type(self):
         with self.assertRaises(InvalidValueType) as err:
-            OctetString(text_type(123))
+            OctetString(str(123))
         repr(err.exception)
 
     @given(booleans())
@@ -2045,12 +2033,12 @@ class TestOctetString(CommonMixin, TestCase):
         with self.assertRaises(BoundsError) as err:
             OctetString(value=value, bounds=(bound_min, bound_max))
         repr(err.exception)
-        with assertRaisesRegex(self, DecodeError, "bounds") as err:
+        with self.assertRaisesRegex(DecodeError, "bounds") as err:
             OctetString(bounds=(bound_min, bound_max)).decode(
                 OctetString(value).encode()
             )
         repr(err.exception)
-        with assertRaisesRegex(self, DecodeError, "bounds") as err:
+        with self.assertRaisesRegex(DecodeError, "bounds") as err:
             OctetString(bounds=(bound_min, bound_max)).decode(
                 encode2pass(OctetString(value))
             )
@@ -2058,12 +2046,12 @@ class TestOctetString(CommonMixin, TestCase):
         with self.assertRaises(BoundsError) as err:
             OctetString(value=value, bounds=(bound_min, bound_max))
         repr(err.exception)
-        with assertRaisesRegex(self, DecodeError, "bounds") as err:
+        with self.assertRaisesRegex(DecodeError, "bounds") as err:
             OctetString(bounds=(bound_min, bound_max)).decode(
                 OctetString(value).encode()
             )
         repr(err.exception)
-        with assertRaisesRegex(self, DecodeError, "bounds") as err:
+        with self.assertRaisesRegex(DecodeError, "bounds") as err:
             OctetString(bounds=(bound_min, bound_max)).decode(
                 encode2pass(OctetString(value))
             )
@@ -2228,8 +2216,18 @@ class TestOctetString(CommonMixin, TestCase):
         integers(min_value=0),
         binary(max_size=5),
         decode_path_strat,
+        booleans(),
     )
-    def test_symmetric(self, values, value, tag_expl, offset, tail_junk, decode_path):
+    def test_symmetric(
+            self,
+            values,
+            value,
+            tag_expl,
+            offset,
+            tail_junk,
+            decode_path,
+            keep_memoryview,
+    ):
         for klass in (OctetString, OctetStringInherited):
             _, _, _, _, default, optional, _decoded = values
             obj = klass(
@@ -2257,6 +2255,7 @@ class TestOctetString(CommonMixin, TestCase):
                 obj_expled.decod(obj_expled_cer, ctx={"bered": True}).encode(),
                 obj_expled_encoded,
             )
+            ctx_dummy["keep_memoryview"] = keep_memoryview
             ctx_copied = deepcopy(ctx_dummy)
             obj_decoded, tail = obj_expled.decode(
                 obj_expled_encoded + tail_junk,
@@ -2272,6 +2271,10 @@ class TestOctetString(CommonMixin, TestCase):
             self.assertNotEqual(obj_decoded, obj)
             self.assertEqual(bytes(obj_decoded), bytes(obj_expled))
             self.assertEqual(bytes(obj_decoded), bytes(obj))
+            self.assertIsInstance(
+                obj_decoded._value,
+                memoryview if keep_memoryview else bytes,
+            )
             self.assertSequenceEqual(obj_decoded.encode(), obj_expled_encoded)
             self.assertSequenceEqual(obj_decoded.expl_tag, tag_expl)
             self.assertEqual(obj_decoded.expl_tlen, len(tag_expl))
@@ -2336,7 +2339,7 @@ class TestOctetString(CommonMixin, TestCase):
         chunks_len_expected = []
         payload_expected = b""
         for chunk_input in chunk_inputs:
-            if isinstance(chunk_input, binary_type):
+            if isinstance(chunk_input, bytes):
                 chunks.append(OctetString(chunk_input).encode())
                 payload_expected += chunk_input
                 chunks_len_expected.append(len(chunk_input))
@@ -2358,7 +2361,7 @@ class TestOctetString(CommonMixin, TestCase):
             len_encode(len(b"".join(chunks))) +
             b"".join(chunks)
         )
-        with assertRaisesRegex(self, DecodeError, "unallowed BER"):
+        with self.assertRaisesRegex(DecodeError, "unallowed BER"):
             OctetString(impl=tag_encode(impl)).decode(encoded_indefinite)
         for lenindef_expected, encoded in (
                 (True, encoded_indefinite),
@@ -2397,7 +2400,7 @@ class TestOctetString(CommonMixin, TestCase):
         decode_path_strat,
     )
     def test_ber_definite_too_short(self, offset, decode_path):
-        with assertRaisesRegex(self, DecodeError, "longer than data") as err:
+        with self.assertRaisesRegex(DecodeError, "longer than data") as err:
             OctetString().decode(
                 tag_encode(4, form=TagFormConstructed) + len_encode(1),
                 offset=offset,
@@ -2432,7 +2435,7 @@ class TestOctetString(CommonMixin, TestCase):
     def test_ber_definite_chunk_out_of_bounds(self, offset, decode_path, chunks):
         bs = OctetString(b"data").encode()
         bs_longer = OctetString(b"data-longer").encode()
-        with assertRaisesRegex(self, DecodeError, "chunk out of bounds") as err:
+        with self.assertRaisesRegex(DecodeError, "chunk out of bounds") as err:
             OctetString().decode(
                 (
                     tag_encode(4, form=TagFormConstructed) +
@@ -2692,8 +2695,8 @@ def oid_strategy(draw):
     if first_arc in (0, 1):
         second_arc = draw(integers(min_value=0, max_value=39))
     else:
-        second_arc = draw(integers(min_value=0))
-    other_arcs = draw(lists(integers(min_value=0)))
+        second_arc = draw(integers(min_value=0, max_value=1 << 63))
+    other_arcs = draw(lists(integers(min_value=0, max_value=1 << 63)))
     return tuple([first_arc, second_arc] + other_arcs)
 
 
@@ -2921,24 +2924,30 @@ class TestObjectIdentifier(CommonMixin, TestCase):
             len_encode(len(data)),
             data,
         ))
-        with assertRaisesRegex(self, DecodeError, "unfinished OID"):
+        with self.assertRaisesRegex(DecodeError, "unfinished OID"):
             obj.decode(data)
 
-    @given(integers(min_value=0))
+    @given(integers(min_value=0, max_value=1 << 63))
     def test_invalid_short(self, value):
         with self.assertRaises(InvalidOID):
             ObjectIdentifier((value,))
         with self.assertRaises(InvalidOID):
             ObjectIdentifier("%d" % value)
 
-    @given(integers(min_value=3), integers(min_value=0))
+    @given(
+        integers(min_value=3, max_value=1 << 63),
+        integers(min_value=0, max_value=1 << 63),
+    )
     def test_invalid_first_arc(self, first_arc, second_arc):
         with self.assertRaises(InvalidOID):
             ObjectIdentifier((first_arc, second_arc))
         with self.assertRaises(InvalidOID):
             ObjectIdentifier("%d.%d" % (first_arc, second_arc))
 
-    @given(integers(min_value=0, max_value=1), integers(min_value=40))
+    @given(
+        integers(min_value=0, max_value=1),
+        integers(min_value=40, max_value=1 << 63),
+    )
     def test_invalid_second_arc(self, first_arc, second_arc):
         with self.assertRaises(InvalidOID):
             ObjectIdentifier((first_arc, second_arc))
@@ -3082,6 +3091,10 @@ class TestObjectIdentifier(CommonMixin, TestCase):
                 data,
             )))
 
+    def test_go_non_minimal_encoding(self):
+        with self.assertRaises(DecodeError):
+            ObjectIdentifier().decode(hexdec("060a2a80864886f70d01010b"))
+
     def test_x690_vector(self):
         self.assertEqual(
             ObjectIdentifier().decode(hexdec("0603883703"))[0],
@@ -3101,7 +3114,7 @@ class TestObjectIdentifier(CommonMixin, TestCase):
         obj = copy(obj)
         self.assertTrue(obj.ber_encoded)
         self.assertTrue(obj.bered)
-        with assertRaisesRegex(self, DecodeError, "non normalized arc encoding"):
+        with self.assertRaisesRegex(DecodeError, "non normalized arc encoding"):
             ObjectIdentifier().decode(tampered)
 
     @given(data_strategy())
@@ -3156,7 +3169,7 @@ class TestObjectIdentifier(CommonMixin, TestCase):
         obj = copy(obj)
         self.assertTrue(obj.ber_encoded)
         self.assertTrue(obj.bered)
-        with assertRaisesRegex(self, DecodeError, "non normalized arc encoding"):
+        with self.assertRaisesRegex(DecodeError, "non normalized arc encoding"):
             ObjectIdentifier().decode(tampered)
 
 
@@ -3194,7 +3207,7 @@ class TestEnumerated(CommonMixin, TestCase):
     base_klass = EWhatever
 
     def test_schema_required(self):
-        with assertRaisesRegex(self, ValueError, "schema must be specified"):
+        with self.assertRaisesRegex(ValueError, "schema must be specified"):
             Enumerated()
 
     def test_invalid_value_type(self):
@@ -3476,7 +3489,7 @@ class StringMixin(object):
         repr(err.exception)
 
     def text_alphabet(self):
-        return "".join(six_unichr(c) for c in six_xrange(256))
+        return "".join(chr(c) for c in range(256))
 
     @given(booleans())
     def test_optional(self, optional):
@@ -3490,7 +3503,7 @@ class StringMixin(object):
         repr(obj)
         list(obj.pps())
         pprint(obj, big_blobs=True, with_decode_path=True)
-        text_type(obj)
+        str(obj)
         with self.assertRaises(ObjNotReady) as err:
             obj.encode()
         repr(err.exception)
@@ -3502,7 +3515,7 @@ class StringMixin(object):
         repr(obj)
         list(obj.pps())
         pprint(obj, big_blobs=True, with_decode_path=True)
-        text_type(obj)
+        str(obj)
 
     @given(data_strategy())
     def test_comparison(self, d):
@@ -3515,7 +3528,7 @@ class StringMixin(object):
         self.assertEqual(obj1 == obj2, value1 == value2)
         self.assertEqual(obj1 != obj2, value1 != value2)
         self.assertEqual(obj1 == bytes(obj2), value1 == value2)
-        self.assertEqual(obj1 == text_type(obj2), value1 == value2)
+        self.assertEqual(obj1 == str(obj2), value1 == value2)
         obj1 = self.base_klass(value1, impl=tag1)
         obj2 = self.base_klass(value1, impl=tag2)
         self.assertEqual(obj1 == obj2, tag1 == tag2)
@@ -3540,12 +3553,12 @@ class StringMixin(object):
         with self.assertRaises(BoundsError) as err:
             self.base_klass(value=value, bounds=(bound_min, bound_max))
         repr(err.exception)
-        with assertRaisesRegex(self, DecodeError, "bounds") as err:
+        with self.assertRaisesRegex(DecodeError, "bounds") as err:
             self.base_klass(bounds=(bound_min, bound_max)).decode(
                 self.base_klass(value).encode()
             )
         repr(err.exception)
-        with assertRaisesRegex(self, DecodeError, "bounds") as err:
+        with self.assertRaisesRegex(DecodeError, "bounds") as err:
             self.base_klass(bounds=(bound_min, bound_max)).decode(
                 encode2pass(self.base_klass(value))
             )
@@ -3553,12 +3566,12 @@ class StringMixin(object):
         with self.assertRaises(BoundsError) as err:
             self.base_klass(value=value, bounds=(bound_min, bound_max))
         repr(err.exception)
-        with assertRaisesRegex(self, DecodeError, "bounds") as err:
+        with self.assertRaisesRegex(DecodeError, "bounds") as err:
             self.base_klass(bounds=(bound_min, bound_max)).decode(
                 self.base_klass(value).encode()
             )
         repr(err.exception)
-        with assertRaisesRegex(self, DecodeError, "bounds") as err:
+        with self.assertRaisesRegex(DecodeError, "bounds") as err:
             self.base_klass(bounds=(bound_min, bound_max)).decode(
                 encode2pass(self.base_klass(value))
             )
@@ -3759,8 +3772,8 @@ class StringMixin(object):
         self.assertNotEqual(obj_decoded, obj)
         self.assertEqual(bytes(obj_decoded), bytes(obj_expled))
         self.assertEqual(bytes(obj_decoded), bytes(obj))
-        self.assertEqual(text_type(obj_decoded), text_type(obj_expled))
-        self.assertEqual(text_type(obj_decoded), text_type(obj))
+        self.assertEqual(str(obj_decoded), str(obj_expled))
+        self.assertEqual(str(obj_decoded), str(obj))
         self.assertSequenceEqual(obj_decoded.encode(), obj_expled_encoded)
         self.assertSequenceEqual(obj_decoded.expl_tag, tag_expl)
         self.assertEqual(obj_decoded.expl_tlen, len(tag_expl))
@@ -3799,7 +3812,7 @@ class StringMixin(object):
 
 
 cyrillic_letters = text(
-    alphabet="".join(six_unichr(i) for i in list(range(0x0410, 0x044f + 1))),
+    alphabet="".join(chr(i) for i in list(range(0x0410, 0x044f + 1))),
     min_size=1,
     max_size=5,
 )
@@ -3840,7 +3853,7 @@ class TestNumericString(StringMixin, CommonMixin, TestCase):
 
     @given(text(alphabet=ascii_letters, min_size=1, max_size=5))
     def test_non_numeric(self, non_numeric_text):
-        with assertRaisesRegex(self, DecodeError, "alphabet value"):
+        with self.assertRaisesRegex(DecodeError, "alphabet value"):
             self.base_klass(non_numeric_text)
 
     @given(
@@ -3890,7 +3903,7 @@ class TestPrintableString(
 
     @given(text(alphabet=sorted(set(whitespace) - set(" ")), min_size=1, max_size=5))
     def test_non_printable(self, non_printable_text):
-        with assertRaisesRegex(self, DecodeError, "alphabet value"):
+        with self.assertRaisesRegex(DecodeError, "alphabet value"):
             self.base_klass(non_printable_text)
 
     @given(
@@ -3924,7 +3937,7 @@ class TestPrintableString(
             for prop in kwargs.keys():
                 self.assertFalse(getattr(obj, prop))
             s += c
-            with assertRaisesRegex(self, DecodeError, "alphabet value"):
+            with self.assertRaisesRegex(DecodeError, "alphabet value"):
                 self.base_klass(s)
             self.base_klass(s, **kwargs)
             klass = self.base_klass(**kwargs)
@@ -3964,7 +3977,7 @@ class TestIA5String(
     base_klass = IA5String
 
     def text_alphabet(self):
-        return "".join(six_unichr(c) for c in six_xrange(128))
+        return "".join(chr(c) for c in range(128))
 
     @given(integers(min_value=128, max_value=255))
     def test_alphabet_bad(self, code):
@@ -4356,7 +4369,7 @@ class TestGeneralizedTime(TimeMixin, CommonMixin, TestCase):
             self.assertFalse(obj_encoded.endswith(b"0Z"))
 
     def test_repr_not_ready(self):
-        unicode(GeneralizedTime()) if PY2 else str(GeneralizedTime())
+        str(GeneralizedTime())
         repr(GeneralizedTime())
 
     def test_x690_vector_valid(self):
@@ -4437,8 +4450,7 @@ class TestGeneralizedTime(TimeMixin, CommonMixin, TestCase):
     @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
     @given(data_strategy())
     def test_valid_ber(self, d):
-        min_year = 1901 if PY2 else 2
-        year = d.draw(integers(min_value=min_year, max_value=9999))
+        year = d.draw(integers(min_value=2, max_value=9999))
         month = d.draw(integers(min_value=1, max_value=12))
         day = d.draw(integers(min_value=1, max_value=28))
         hours = d.draw(integers(min_value=0, max_value=23))
@@ -4693,7 +4705,7 @@ class TestGeneralizedTime(TimeMixin, CommonMixin, TestCase):
 
     def test_ns_fractions(self):
         GeneralizedTime(b"20010101000000.000001Z")
-        with assertRaisesRegex(self, DecodeError, "only microsecond fractions"):
+        with self.assertRaisesRegex(DecodeError, "only microsecond fractions"):
             GeneralizedTime(b"20010101000000.0000001Z")
 
     def test_non_pure_integers(self):
@@ -4734,7 +4746,7 @@ class TestGeneralizedTime(TimeMixin, CommonMixin, TestCase):
                 GeneralizedTime(data)
 
     def test_aware(self):
-        with assertRaisesRegex(self, ValueError, "only naive"):
+        with self.assertRaisesRegex(ValueError, "only naive"):
             GeneralizedTime(datetime(2000, 1, 1, 1, tzinfo=UTC))
 
 
@@ -4749,7 +4761,7 @@ class TestUTCTime(TimeMixin, CommonMixin, TestCase):
         pass
 
     def test_repr_not_ready(self):
-        unicode(GeneralizedTime()) if PY2 else str(GeneralizedTime())
+        str(GeneralizedTime())
         repr(UTCTime())
 
     def test_x690_vector_valid(self):
@@ -5071,9 +5083,17 @@ class TestUTCTime(TimeMixin, CommonMixin, TestCase):
             )
 
     def test_aware(self):
-        with assertRaisesRegex(self, ValueError, "only naive"):
+        with self.assertRaisesRegex(ValueError, "only naive"):
             UTCTime(datetime(2000, 1, 1, 1, tzinfo=UTC))
 
+    def test_raises_if_no_dateutil(self):
+        with patch("pyderasn.tzUTC", new="missing"):
+            with self.assertRaisesRegex(NotImplementedError, "dateutil"):
+                UTCTime(datetime.now()).totzdatetime()
+
+    def test_tzinfo_gives_datetime_with_tzutc_tzinfo(self):
+        self.assertEqual(UTCTime(datetime.now()).totzdatetime().tzinfo, UTC)
+
 
 @composite
 def tlv_value_strategy(draw):
@@ -5262,8 +5282,18 @@ class TestAny(CommonMixin, TestCase):
         integers(min_value=0),
         binary(max_size=5),
         decode_path_strat,
+        booleans(),
     )
-    def test_symmetric(self, values, value, tag_expl, offset, tail_junk, decode_path):
+    def test_symmetric(
+            self,
+            values,
+            value,
+            tag_expl,
+            offset,
+            tail_junk,
+            decode_path,
+            keep_memoryview,
+    ):
         for klass in (Any, AnyInherited):
             _, _, optional, _decoded = values
             obj = klass(value=value, optional=optional, _decoded=_decoded)
@@ -5283,6 +5313,7 @@ class TestAny(CommonMixin, TestCase):
             list(obj_expled.pps())
             pprint(obj_expled, big_blobs=True, with_decode_path=True)
             obj_expled_encoded = obj_expled.encode()
+            ctx_dummy["keep_memoryview"] = keep_memoryview
             ctx_copied = deepcopy(ctx_dummy)
             obj_decoded, tail = obj_expled.decode(
                 obj_expled_encoded + tail_junk,
@@ -5297,6 +5328,10 @@ class TestAny(CommonMixin, TestCase):
             self.assertEqual(obj_decoded, obj_expled)
             self.assertEqual(bytes(obj_decoded), bytes(obj_expled))
             self.assertEqual(bytes(obj_decoded), bytes(obj))
+            self.assertIsInstance(
+                obj_decoded._value,
+                memoryview if keep_memoryview else bytes,
+            )
             self.assertSequenceEqual(obj_decoded.encode(), obj_expled_encoded)
             self.assertSequenceEqual(obj_decoded.expl_tag, tag_expl)
             self.assertEqual(obj_decoded.expl_tlen, len(tag_expl))
@@ -5474,11 +5509,11 @@ class TestChoice(CommonMixin, TestCase):
     base_klass = Wahl
 
     def test_schema_required(self):
-        with assertRaisesRegex(self, ValueError, "schema must be specified"):
+        with self.assertRaisesRegex(ValueError, "schema must be specified"):
             Choice()
 
     def test_impl_forbidden(self):
-        with assertRaisesRegex(self, ValueError, "no implicit tag allowed"):
+        with self.assertRaisesRegex(ValueError, "no implicit tag allowed"):
             Choice(impl=b"whatever")
 
     def test_invalid_value_type(self):
@@ -5949,7 +5984,7 @@ def sequences_strategy(draw, seq_klass):
     return seq_outer, expect_outers
 
 
-class SeqMixing(object):
+class SeqMixin(object):
     def test_invalid_value_type(self):
         with self.assertRaises(InvalidValueType) as err:
             self.base_klass(123)
@@ -6370,9 +6405,9 @@ class SeqMixing(object):
                 for (n, v), t in zip(_schema, tags)
             ]
         seq_with_default = SeqWithDefault()
-        with assertRaisesRegex(self, DecodeError, "DEFAULT value met"):
+        with self.assertRaisesRegex(DecodeError, "DEFAULT value met"):
             seq_with_default.decode(seq_encoded)
-        with assertRaisesRegex(self, DecodeError, "DEFAULT value met"):
+        with self.assertRaisesRegex(DecodeError, "DEFAULT value met"):
             list(seq_with_default.decode_evgen(seq_encoded))
         for ctx in ({"bered": True}, {"allow_default_values": True}):
             seq_decoded, _ = seq_with_default.decode(seq_encoded, ctx=ctx)
@@ -6463,7 +6498,7 @@ class SeqMixing(object):
         self.assertTrue(decoded.bered)
 
 
-class TestSequence(SeqMixing, CommonMixin, TestCase):
+class TestSequence(SeqMixin, CommonMixin, TestCase):
     base_klass = Sequence
 
     @given(
@@ -6481,7 +6516,7 @@ class TestSequence(SeqMixing, CommonMixin, TestCase):
             len_encode(len(int_encoded + junk)),
             int_encoded + junk,
         ))
-        with assertRaisesRegex(self, DecodeError, "remaining"):
+        with self.assertRaisesRegex(DecodeError, "remaining"):
             Seq().decode(junked)
 
     @given(sets(text_letters(), min_size=2))
@@ -6509,7 +6544,7 @@ class TestSequence(SeqMixing, CommonMixin, TestCase):
         self.assertEqual(seq["ok"], True)
 
 
-class TestSet(SeqMixing, CommonMixin, TestCase):
+class TestSet(SeqMixin, CommonMixin, TestCase):
     base_klass = Set
 
     @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
@@ -6553,7 +6588,7 @@ class TestSet(SeqMixing, CommonMixin, TestCase):
             len_encode(len(encoded)),
             encoded,
         ))
-        with assertRaisesRegex(self, DecodeError, "unordered SET"):
+        with self.assertRaisesRegex(DecodeError, "unordered SET"):
             seq.decode(seq_encoded)
         for ctx in ({"bered": True}, {"allow_unordered_set": True}):
             seq_decoded, _ = Seq().decode(seq_encoded, ctx=ctx)
@@ -6624,7 +6659,7 @@ def seqof_values_strategy(draw, schema=None, do_expl=False):
     )
 
 
-class SeqOfMixing(object):
+class SeqOfMixin(object):
     def test_invalid_value_type(self):
         with self.assertRaises(InvalidValueType) as err:
             self.base_klass(123)
@@ -6638,7 +6673,7 @@ class SeqOfMixing(object):
         repr(err.exception)
 
     def test_schema_required(self):
-        with assertRaisesRegex(self, ValueError, "schema must be specified"):
+        with self.assertRaisesRegex(ValueError, "schema must be specified"):
             self.base_klass.__mro__[1]()
 
     @given(booleans(), booleans(), binary(min_size=1), binary(min_size=1))
@@ -6726,16 +6761,16 @@ class SeqOfMixing(object):
             schema = Boolean()
         bound_min = d.draw(integers(min_value=1, max_value=1 << 7))
         bound_max = d.draw(integers(min_value=bound_min, max_value=1 << 7))
-        value = [Boolean(False)] * d.draw(integers(max_value=bound_min - 1))
+        value = [Boolean(False)] * d.draw(integers(min_value=0, max_value=bound_min - 1))
         with self.assertRaises(BoundsError) as err:
             SeqOf(value=value, bounds=(bound_min, bound_max))
         repr(err.exception)
-        with assertRaisesRegex(self, DecodeError, "bounds") as err:
+        with self.assertRaisesRegex(DecodeError, "bounds") as err:
             SeqOf(bounds=(bound_min, bound_max)).decode(
                 SeqOf(value).encode()
             )
         repr(err.exception)
-        with assertRaisesRegex(self, DecodeError, "bounds") as err:
+        with self.assertRaisesRegex(DecodeError, "bounds") as err:
             SeqOf(bounds=(bound_min, bound_max)).decode(
                 encode2pass(SeqOf(value))
             )
@@ -6746,12 +6781,12 @@ class SeqOfMixing(object):
         with self.assertRaises(BoundsError) as err:
             SeqOf(value=value, bounds=(bound_min, bound_max))
         repr(err.exception)
-        with assertRaisesRegex(self, DecodeError, "bounds") as err:
+        with self.assertRaisesRegex(DecodeError, "bounds") as err:
             SeqOf(bounds=(bound_min, bound_max)).decode(
                 SeqOf(value).encode()
             )
         repr(err.exception)
-        with assertRaisesRegex(self, DecodeError, "bounds") as err:
+        with self.assertRaisesRegex(DecodeError, "bounds") as err:
             SeqOf(bounds=(bound_min, bound_max)).decode(
                 encode2pass(SeqOf(value))
             )
@@ -7094,7 +7129,7 @@ class SeqOfMixing(object):
         self.assertTrue(decoded.bered)
 
 
-class TestSequenceOf(SeqOfMixing, CommonMixin, TestCase):
+class TestSequenceOf(SeqOfMixin, CommonMixin, TestCase):
     class SeqOf(SequenceOf):
         schema = "whatever"
     base_klass = SeqOf
@@ -7109,8 +7144,8 @@ class TestSequenceOf(SeqOfMixing, CommonMixin, TestCase):
         register_class(SeqOf)
         seqof = SeqOf()
         pickle_dumps(seqof)
-        seqof = seqof(iter(six_xrange(10)))
-        with assertRaisesRegex(self, ValueError, "iterator"):
+        seqof = seqof(iter(range(10)))
+        with self.assertRaisesRegex(ValueError, "iterator"):
             pickle_dumps(seqof)
 
     def test_iterator_bounds(self):
@@ -7120,7 +7155,7 @@ class TestSequenceOf(SeqOfMixing, CommonMixin, TestCase):
         seqof = None
 
         def gen(n):
-            for i in six_xrange(n):
+            for i in range(n):
                 yield Integer(i)
         for n in (9, 21):
             seqof = SeqOf(gen(n))
@@ -7140,7 +7175,7 @@ class TestSequenceOf(SeqOfMixing, CommonMixin, TestCase):
             bounds = (1, float("+inf"))
 
         def gen():
-            for i in six_xrange(10):
+            for i in range(10):
                 yield Integer(i)
         seqof = SeqOf(gen())
         self.assertTrue(seqof.ready)
@@ -7155,7 +7190,7 @@ class TestSequenceOf(SeqOfMixing, CommonMixin, TestCase):
             bounds = (1, float("+inf"))
 
         def gen():
-            for i in six_xrange(10):
+            for i in range(10):
                 yield Integer(i)
         seqof = SeqOf(gen())
         self.assertTrue(seqof.ready)
@@ -7178,7 +7213,7 @@ class TestSequenceOf(SeqOfMixing, CommonMixin, TestCase):
         self.assertFalse(seqof.ready)
 
 
-class TestSetOf(SeqOfMixing, CommonMixin, TestCase):
+class TestSetOf(SeqOfMixin, CommonMixin, TestCase):
     class SeqOf(SetOf):
         schema = "whatever"
     base_klass = SeqOf
@@ -7224,7 +7259,7 @@ class TestSetOf(SeqOfMixing, CommonMixin, TestCase):
         class Seq(SetOf):
             schema = OctetString()
         seq = Seq()
-        with assertRaisesRegex(self, DecodeError, "unordered SET OF"):
+        with self.assertRaisesRegex(DecodeError, "unordered SET OF"):
             seq.decode(seq_encoded)
 
         for ctx in ({"bered": True}, {"allow_unordered_set": True}):
@@ -7382,10 +7417,10 @@ class TestGoMarshalVectors(TestCase):
         seq["erste"] = PrintableString("test")
         self.assertSequenceEqual(seq.encode(), hexdec("3006130474657374"))
         # Asterisk is actually not allowable
-        PrintableString._allowable_chars |= set(b"*")
+        pyderasn.PRINTABLE_ALLOWABLE_CHARS |= set(b"*")
         seq["erste"] = PrintableString("test*")
         self.assertSequenceEqual(seq.encode(), hexdec("30071305746573742a"))
-        PrintableString._allowable_chars -= set(b"*")
+        pyderasn.PRINTABLE_ALLOWABLE_CHARS -= set(b"*")
 
         class Seq(Sequence):
             schema = (
@@ -7431,7 +7466,11 @@ class TestPP(TestCase):
     def test_oid_printing(self, d):
         oids = {
             str(ObjectIdentifier(k)): v * 2
-            for k, v in d.draw(dictionaries(oid_strategy(), text_letters())).items()
+            for k, v in d.draw(dictionaries(
+                oid_strategy(),
+                text_letters(),
+                min_size=1,
+            )).items()
         }
         chosen = d.draw(sampled_from(sorted(oids)))
         chosen_id = oids[chosen]
@@ -7701,7 +7740,7 @@ class TestDefinesByPath(TestCase):
             ("oid", oid),
             ("tgt", OctetString(Integer(123).encode() + b"junk")),
         ))
-        with assertRaisesRegex(self, DecodeError, "remaining data"):
+        with self.assertRaisesRegex(DecodeError, "remaining data"):
             Seq().decode(seq.encode())
 
     def test_remaining_data_seqof(self):
@@ -7722,7 +7761,7 @@ class TestDefinesByPath(TestCase):
             ("oid", oid),
             ("tgt", SeqOf([OctetString(Integer(123).encode() + b"junk")])),
         ))
-        with assertRaisesRegex(self, DecodeError, "remaining data"):
+        with self.assertRaisesRegex(DecodeError, "remaining data"):
             Seq().decode(seq.encode())
 
 
@@ -7776,7 +7815,7 @@ class TestStrictDefaultExistence(TestCase):
             raw = seq.encode()
             chosen_choice = "int%d" % chosen
             seq.specs[chosen_choice] = seq.specs[chosen_choice](default=123)
-            with assertRaisesRegex(self, DecodeError, "DEFAULT value met"):
+            with self.assertRaisesRegex(DecodeError, "DEFAULT value met"):
                 seq.decode(raw)
             decoded, _ = seq.decode(raw, ctx={"allow_default_values": True})
             self.assertTrue(decoded.ber_encoded)
@@ -7844,7 +7883,7 @@ class TestExplOOB(TestCase):
         expl = tag_ctxc(123)
         raw = Integer(123).encode() + Integer(234).encode()
         raw = b"".join((expl, len_encode(len(raw)), raw))
-        with assertRaisesRegex(self, DecodeError, "explicit tag out-of-bound"):
+        with self.assertRaisesRegex(DecodeError, "explicit tag out-of-bound"):
             Integer(expl=expl).decode(raw)
         Integer(expl=expl).decode(raw, ctx={"allow_expl_oob": True})
 
@@ -7855,7 +7894,7 @@ class TestPickleDifferentVersion(TestCase):
         import pyderasn
         version_orig = pyderasn.__version__
         pyderasn.__version__ += "different"
-        with assertRaisesRegex(self, ValueError, "different PyDERASN version"):
+        with self.assertRaisesRegex(ValueError, "different PyDERASN version"):
             pickle_loads(pickled)
         pyderasn.__version__ = version_orig
         pickle_loads(pickled)