]> Cypherpunks.ru repositories - pyderasn.git/blob - tests/test_pyderasn.py
Valid DER SET ordering
[pyderasn.git] / tests / test_pyderasn.py
1 # coding: utf-8
2 # PyDERASN -- Python ASN.1 DER/BER codec with abstract structures
3 # Copyright (C) 2017-2020 Sergey Matveev <stargrave@stargrave.org>
4 #
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Lesser General Public License as
7 # published by the Free Software Foundation, version 3 of the License.
8 #
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 # GNU Lesser General Public License for more details.
13 #
14 # You should have received a copy of the GNU Lesser General Public
15 # License along with this program.  If not, see
16 # <http://www.gnu.org/licenses/>.
17
18 from copy import copy
19 from copy import deepcopy
20 from datetime import datetime
21 from datetime import timedelta
22 from importlib import import_module
23 from os import environ
24 from random import random
25 from string import ascii_letters
26 from string import digits
27 from string import printable
28 from string import whitespace
29 from time import mktime
30 from time import time
31 from unittest import TestCase
32
33 from hypothesis import assume
34 from hypothesis import given
35 from hypothesis import settings
36 from hypothesis.strategies import binary
37 from hypothesis.strategies import booleans
38 from hypothesis.strategies import composite
39 from hypothesis.strategies import data as data_strategy
40 from hypothesis.strategies import datetimes
41 from hypothesis.strategies import dictionaries
42 from hypothesis.strategies import integers
43 from hypothesis.strategies import just
44 from hypothesis.strategies import lists
45 from hypothesis.strategies import none
46 from hypothesis.strategies import one_of
47 from hypothesis.strategies import permutations
48 from hypothesis.strategies import sampled_from
49 from hypothesis.strategies import sets
50 from hypothesis.strategies import text
51 from hypothesis.strategies import tuples
52 from six import assertRaisesRegex
53 from six import binary_type
54 from six import byte2int
55 from six import indexbytes
56 from six import int2byte
57 from six import iterbytes
58 from six import PY2
59 from six import text_type
60 from six import unichr as six_unichr
61 from six.moves.cPickle import dumps as pickle_dumps
62 from six.moves.cPickle import HIGHEST_PROTOCOL as pickle_proto
63 from six.moves.cPickle import loads as pickle_loads
64
65 from pyderasn import _pp
66 from pyderasn import abs_decode_path
67 from pyderasn import Any
68 from pyderasn import BitString
69 from pyderasn import BMPString
70 from pyderasn import Boolean
71 from pyderasn import BoundsError
72 from pyderasn import Choice
73 from pyderasn import DecodeError
74 from pyderasn import DecodePathDefBy
75 from pyderasn import Enumerated
76 from pyderasn import EOC
77 from pyderasn import EOC_LEN
78 from pyderasn import ExceedingData
79 from pyderasn import GeneralizedTime
80 from pyderasn import GeneralString
81 from pyderasn import GraphicString
82 from pyderasn import hexdec
83 from pyderasn import hexenc
84 from pyderasn import IA5String
85 from pyderasn import Integer
86 from pyderasn import InvalidLength
87 from pyderasn import InvalidOID
88 from pyderasn import InvalidValueType
89 from pyderasn import len_decode
90 from pyderasn import len_encode
91 from pyderasn import LEN_YYMMDDHHMMSSZ
92 from pyderasn import LEN_YYYYMMDDHHMMSSDMZ
93 from pyderasn import LEN_YYYYMMDDHHMMSSZ
94 from pyderasn import LENINDEF
95 from pyderasn import LenIndefForm
96 from pyderasn import NotEnoughData
97 from pyderasn import Null
98 from pyderasn import NumericString
99 from pyderasn import ObjectIdentifier
100 from pyderasn import ObjNotReady
101 from pyderasn import ObjUnknown
102 from pyderasn import OctetString
103 from pyderasn import pp_console_row
104 from pyderasn import pprint
105 from pyderasn import PrintableString
106 from pyderasn import Sequence
107 from pyderasn import SequenceOf
108 from pyderasn import Set
109 from pyderasn import SetOf
110 from pyderasn import tag_ctxc
111 from pyderasn import tag_ctxp
112 from pyderasn import tag_decode
113 from pyderasn import tag_encode
114 from pyderasn import tag_strip
115 from pyderasn import TagClassApplication
116 from pyderasn import TagClassContext
117 from pyderasn import TagClassPrivate
118 from pyderasn import TagClassUniversal
119 from pyderasn import TagFormConstructed
120 from pyderasn import TagFormPrimitive
121 from pyderasn import TagMismatch
122 from pyderasn import TeletexString
123 from pyderasn import UniversalString
124 from pyderasn import UTCTime
125 from pyderasn import UTF8String
126 from pyderasn import VideotexString
127 from pyderasn import VisibleString
128
129
130 max_examples = environ.get("MAX_EXAMPLES")
131 settings.register_profile("local", settings(
132     deadline=5000,
133     **({"max_examples": int(max_examples)} if max_examples else {})
134 ))
135 settings.load_profile("local")
136 LONG_TEST_MAX_EXAMPLES = settings().max_examples * 4
137
138 tag_classes = sampled_from((
139     TagClassApplication,
140     TagClassContext,
141     TagClassPrivate,
142     TagClassUniversal,
143 ))
144 tag_forms = sampled_from((TagFormConstructed, TagFormPrimitive))
145 decode_path_strat = lists(integers(), max_size=3).map(
146     lambda decode_path: tuple(str(dp) for dp in decode_path)
147 )
148 ctx_dummy = dictionaries(integers(), integers(), min_size=2, max_size=4).example()
149 copy_funcs = (
150     copy,
151     lambda obj: pickle_loads(pickle_dumps(obj, pickle_proto)),
152 )
153 self_module = import_module(__name__)
154
155
156 def register_class(klass):
157     klassname = klass.__name__ + str(time()).replace(".", "")
158     klass.__name__ = klassname
159     klass.__qualname__ = klassname
160     setattr(self_module, klassname, klass)
161
162
163 def assert_exceeding_data(self, call, junk):
164     if len(junk) <= 0:
165         return
166     with assertRaisesRegex(self, ExceedingData, "%d trailing bytes" % len(junk)) as err:
167         call()
168     repr(err)
169
170
171 class TestHex(TestCase):
172     @given(binary())
173     def test_symmetric(self, data):
174         self.assertEqual(hexdec(hexenc(data)), data)
175
176
177 class TestTagCoder(TestCase):
178     @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
179     @given(
180         tag_classes,
181         tag_forms,
182         integers(min_value=0, max_value=30),
183         binary(max_size=5),
184     )
185     def test_short(self, klass, form, num, junk):
186         raw = tag_encode(klass=klass, form=form, num=num)
187         self.assertEqual(tag_decode(raw), (klass, form, num))
188         self.assertEqual(len(raw), 1)
189         self.assertEqual(
190             byte2int(tag_encode(klass=klass, form=form, num=0)),
191             byte2int(raw) & (1 << 7 | 1 << 6 | 1 << 5),
192         )
193         stripped, tlen, tail = tag_strip(memoryview(raw + junk))
194         self.assertSequenceEqual(stripped.tobytes(), raw)
195         self.assertEqual(tlen, len(raw))
196         self.assertSequenceEqual(tail, junk)
197
198     @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
199     @given(
200         tag_classes,
201         tag_forms,
202         integers(min_value=31),
203         binary(max_size=5),
204     )
205     def test_long(self, klass, form, num, junk):
206         raw = tag_encode(klass=klass, form=form, num=num)
207         self.assertEqual(tag_decode(raw), (klass, form, num))
208         self.assertGreater(len(raw), 1)
209         self.assertEqual(
210             byte2int(tag_encode(klass=klass, form=form, num=0)) | 31,
211             byte2int(raw[:1]),
212         )
213         self.assertEqual(byte2int(raw[-1:]) & 0x80, 0)
214         self.assertTrue(all(b & 0x80 > 0 for b in iterbytes(raw[1:-1])))
215         stripped, tlen, tail = tag_strip(memoryview(raw + junk))
216         self.assertSequenceEqual(stripped.tobytes(), raw)
217         self.assertEqual(tlen, len(raw))
218         self.assertSequenceEqual(tail, junk)
219
220     @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
221     @given(integers(min_value=31))
222     def test_unfinished_tag(self, num):
223         raw = bytearray(tag_encode(num=num))
224         for i in range(1, len(raw)):
225             raw[i] |= 0x80
226         with assertRaisesRegex(self, DecodeError, "unfinished tag"):
227             tag_strip(bytes(raw))
228
229     def test_go_vectors_valid(self):
230         for data, (eklass, etag, elen, eform) in (
231                 (b"\x80\x01", (TagClassContext, 0, 1, TagFormPrimitive)),
232                 (b"\xa0\x01", (TagClassContext, 0, 1, TagFormConstructed)),
233                 (b"\x02\x00", (TagClassUniversal, 2, 0, TagFormPrimitive)),
234                 (b"\xfe\x00", (TagClassPrivate, 30, 0, TagFormConstructed)),
235                 (b"\x1f\x1f\x00", (TagClassUniversal, 31, 0, TagFormPrimitive)),
236                 (b"\x1f\x81\x00\x00", (TagClassUniversal, 128, 0, TagFormPrimitive)),
237                 (b"\x1f\x81\x80\x01\x00", (TagClassUniversal, 0x4001, 0, TagFormPrimitive)),
238                 (b"\x00\x81\x80", (TagClassUniversal, 0, 128, TagFormPrimitive)),
239                 (b"\x00\x82\x01\x00", (TagClassUniversal, 0, 256, TagFormPrimitive)),
240                 (b"\xa0\x84\x7f\xff\xff\xff", (TagClassContext, 0, 0x7fffffff, TagFormConstructed)),
241         ):
242             tag, _, len_encoded = tag_strip(memoryview(data))
243             klass, form, num = tag_decode(tag)
244             _len, _, tail = len_decode(len_encoded)
245             self.assertSequenceEqual(tail, b"")
246             self.assertEqual(klass, eklass)
247             self.assertEqual(num, etag)
248             self.assertEqual(_len, elen)
249             self.assertEqual(form, eform)
250
251     def test_go_vectors_invalid(self):
252         for data in (
253                 b"\x00\x83\x01\x00",
254                 b"\x1f\x85",
255                 b"\x30\x80",
256                 b"\xa0\x82\x00\xff",
257                 b"\xa0\x81\x7f",
258         ):
259             with self.assertRaises(DecodeError):
260                 _, _, len_encoded = tag_strip(memoryview(data))
261                 len_decode(len_encoded)
262
263     @given(
264         integers(min_value=0, max_value=127),
265         integers(min_value=0, max_value=2),
266     )
267     def test_long_instead_of_short(self, l, dummy_num):
268         octets = (b"\x00" * dummy_num) + int2byte(l)
269         octets = int2byte((dummy_num + 1) | 0x80) + octets
270         with self.assertRaises(DecodeError):
271             len_decode(octets)
272
273
274 class TestLenCoder(TestCase):
275     @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
276     @given(
277         integers(min_value=0, max_value=127),
278         binary(max_size=5),
279     )
280     def test_short(self, l, junk):
281         raw = len_encode(l) + junk
282         decoded, llen, tail = len_decode(memoryview(raw))
283         self.assertEqual(decoded, l)
284         self.assertEqual(llen, 1)
285         self.assertEqual(len(raw), 1 + len(junk))
286         self.assertEqual(tail.tobytes(), junk)
287
288     @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
289     @given(
290         integers(min_value=128),
291         binary(max_size=5),
292     )
293     def test_long(self, l, junk):
294         raw = len_encode(l) + junk
295         decoded, llen, tail = len_decode(memoryview(raw))
296         self.assertEqual(decoded, l)
297         self.assertEqual((llen - 1) | 0x80, byte2int(raw))
298         self.assertEqual(llen, len(raw) - len(junk))
299         self.assertNotEqual(indexbytes(raw, 1), 0)
300         self.assertSequenceEqual(tail.tobytes(), junk)
301
302     def test_empty(self):
303         with self.assertRaises(NotEnoughData):
304             len_decode(b"")
305
306     @given(integers(min_value=128))
307     def test_stripped(self, _len):
308         with self.assertRaises(NotEnoughData):
309             len_decode(len_encode(_len)[:-1])
310
311
312 text_printable = text(alphabet=printable, min_size=1)
313
314
315 @composite
316 def text_letters(draw):
317     result = draw(text(alphabet=ascii_letters, min_size=1))
318     if PY2:
319         result = result.encode("ascii")
320     return result
321
322
323 class CommonMixin(object):
324     def test_tag_default(self):
325         obj = self.base_klass()
326         self.assertEqual(obj.tag, obj.tag_default)
327
328     def test_simultaneous_impl_expl(self):
329         with self.assertRaises(ValueError):
330             self.base_klass(impl=b"whatever", expl=b"whenever")
331
332     @given(binary(min_size=1), integers(), integers(), integers())
333     def test_decoded(self, impl, offset, llen, vlen):
334         obj = self.base_klass(impl=impl, _decoded=(offset, llen, vlen))
335         self.assertEqual(obj.offset, offset)
336         self.assertEqual(obj.llen, llen)
337         self.assertEqual(obj.vlen, vlen)
338         self.assertEqual(obj.tlen, len(impl))
339         self.assertEqual(obj.tlvlen, obj.tlen + obj.llen + obj.vlen)
340
341     @given(binary(min_size=1))
342     def test_impl_inherited(self, impl_tag):
343         class Inherited(self.base_klass):
344             impl = impl_tag
345         obj = Inherited()
346         self.assertSequenceEqual(obj.impl, impl_tag)
347         self.assertFalse(obj.expled)
348         if obj.ready:
349             tag_class, _, tag_num = tag_decode(impl_tag)
350             self.assertEqual(obj.tag_order, (tag_class, tag_num))
351
352     @given(binary(min_size=1))
353     def test_expl_inherited(self, expl_tag):
354         class Inherited(self.base_klass):
355             expl = expl_tag
356         obj = Inherited()
357         self.assertSequenceEqual(obj.expl, expl_tag)
358         self.assertTrue(obj.expled)
359         if obj.ready:
360             tag_class, _, tag_num = tag_decode(expl_tag)
361             self.assertEqual(obj.tag_order, (tag_class, tag_num))
362
363     def assert_copied_basic_fields(self, obj, obj_copied):
364         self.assertEqual(obj, obj_copied)
365         self.assertSequenceEqual(obj.tag, obj_copied.tag)
366         self.assertEqual(obj.expl_tag, obj_copied.expl_tag)
367         self.assertEqual(obj.default, obj_copied.default)
368         self.assertEqual(obj.optional, obj_copied.optional)
369         self.assertEqual(obj.offset, obj_copied.offset)
370         self.assertEqual(obj.llen, obj_copied.llen)
371         self.assertEqual(obj.vlen, obj_copied.vlen)
372         if obj.ready:
373             self.assertEqual(obj.tag_order, obj_copied.tag_order)
374
375
376 @composite
377 def boolean_values_strategy(draw, do_expl=False):
378     value = draw(one_of(none(), booleans()))
379     impl = None
380     expl = None
381     if do_expl:
382         expl = draw(one_of(none(), integers(min_value=1).map(tag_encode)))
383     else:
384         impl = draw(one_of(none(), integers(min_value=1).map(tag_encode)))
385     default = draw(one_of(none(), booleans()))
386     optional = draw(one_of(none(), booleans()))
387     _decoded = (
388         draw(integers(min_value=0)),
389         draw(integers(min_value=0)),
390         draw(integers(min_value=0)),
391     )
392     return (value, impl, expl, default, optional, _decoded)
393
394
395 class BooleanInherited(Boolean):
396     pass
397
398
399 class TestBoolean(CommonMixin, TestCase):
400     base_klass = Boolean
401
402     def test_invalid_value_type(self):
403         with self.assertRaises(InvalidValueType) as err:
404             Boolean(123)
405         repr(err.exception)
406
407     @given(booleans())
408     def test_optional(self, optional):
409         obj = Boolean(default=Boolean(False), optional=optional)
410         self.assertTrue(obj.optional)
411
412     @given(booleans())
413     def test_ready(self, value):
414         obj = Boolean()
415         self.assertFalse(obj.ready)
416         repr(obj)
417         list(obj.pps())
418         pprint(obj, big_blobs=True, with_decode_path=True)
419         with self.assertRaises(ObjNotReady) as err:
420             obj.encode()
421         repr(err.exception)
422         obj = Boolean(value)
423         self.assertTrue(obj.ready)
424         repr(obj)
425         list(obj.pps())
426         pprint(obj, big_blobs=True, with_decode_path=True)
427
428     @given(booleans(), booleans(), binary(min_size=1), binary(min_size=1))
429     def test_comparison(self, value1, value2, tag1, tag2):
430         for klass in (Boolean, BooleanInherited):
431             obj1 = klass(value1)
432             obj2 = klass(value2)
433             self.assertEqual(obj1 == obj2, value1 == value2)
434             self.assertEqual(obj1 != obj2, value1 != value2)
435             self.assertEqual(obj1 == bool(obj2), value1 == value2)
436             obj1 = klass(value1, impl=tag1)
437             obj2 = klass(value1, impl=tag2)
438             self.assertEqual(obj1 == obj2, tag1 == tag2)
439             self.assertEqual(obj1 != obj2, tag1 != tag2)
440
441     @given(data_strategy())
442     def test_call(self, d):
443         for klass in (Boolean, BooleanInherited):
444             (
445                 value_initial,
446                 impl_initial,
447                 expl_initial,
448                 default_initial,
449                 optional_initial,
450                 _decoded_initial,
451             ) = d.draw(boolean_values_strategy())
452             obj_initial = klass(
453                 value_initial,
454                 impl_initial,
455                 expl_initial,
456                 default_initial,
457                 optional_initial or False,
458                 _decoded_initial,
459             )
460             (
461                 value,
462                 impl,
463                 expl,
464                 default,
465                 optional,
466                 _decoded,
467             ) = d.draw(boolean_values_strategy(do_expl=impl_initial is None))
468             obj = obj_initial(value, impl, expl, default, optional)
469             if obj.ready:
470                 value_expected = default if value is None else value
471                 value_expected = (
472                     default_initial if value_expected is None
473                     else value_expected
474                 )
475                 self.assertEqual(obj, value_expected)
476             self.assertEqual(obj.tag, impl or impl_initial or obj.tag_default)
477             self.assertEqual(obj.expl_tag, expl or expl_initial)
478             self.assertEqual(
479                 obj.default,
480                 default_initial if default is None else default,
481             )
482             if obj.default is None:
483                 optional = optional_initial if optional is None else optional
484                 optional = False if optional is None else optional
485             else:
486                 optional = True
487             self.assertEqual(obj.optional, optional)
488
489     @given(boolean_values_strategy())
490     def test_copy(self, values):
491         for klass in (Boolean, BooleanInherited):
492             obj = klass(*values)
493             for copy_func in copy_funcs:
494                 obj_copied = copy_func(obj)
495                 self.assert_copied_basic_fields(obj, obj_copied)
496
497     @given(
498         booleans(),
499         integers(min_value=1).map(tag_encode),
500     )
501     def test_stripped(self, value, tag_impl):
502         obj = Boolean(value, impl=tag_impl)
503         with self.assertRaises(NotEnoughData):
504             obj.decode(obj.encode()[:-1])
505
506     @given(
507         booleans(),
508         integers(min_value=1).map(tag_ctxc),
509     )
510     def test_stripped_expl(self, value, tag_expl):
511         obj = Boolean(value, expl=tag_expl)
512         with self.assertRaises(NotEnoughData):
513             obj.decode(obj.encode()[:-1])
514
515     @given(
516         integers(min_value=31),
517         integers(min_value=0),
518         decode_path_strat,
519     )
520     def test_bad_tag(self, tag, offset, decode_path):
521         with self.assertRaises(DecodeError) as err:
522             Boolean().decode(
523                 tag_encode(tag)[:-1],
524                 offset=offset,
525                 decode_path=decode_path,
526             )
527         repr(err.exception)
528         self.assertEqual(err.exception.offset, offset)
529         self.assertEqual(err.exception.decode_path, decode_path)
530
531     @given(
532         integers(min_value=31),
533         integers(min_value=0),
534         decode_path_strat,
535     )
536     def test_bad_expl_tag(self, tag, offset, decode_path):
537         with self.assertRaises(DecodeError) as err:
538             Boolean(expl=Boolean.tag_default).decode(
539                 tag_encode(tag)[:-1],
540                 offset=offset,
541                 decode_path=decode_path,
542             )
543         repr(err.exception)
544         self.assertEqual(err.exception.offset, offset)
545         self.assertEqual(err.exception.decode_path, decode_path)
546
547     @given(
548         integers(min_value=128),
549         integers(min_value=0),
550         decode_path_strat,
551     )
552     def test_bad_len(self, l, offset, decode_path):
553         with self.assertRaises(DecodeError) as err:
554             Boolean().decode(
555                 Boolean.tag_default + len_encode(l)[:-1],
556                 offset=offset,
557                 decode_path=decode_path,
558             )
559         repr(err.exception)
560         self.assertEqual(err.exception.offset, offset)
561         self.assertEqual(err.exception.decode_path, decode_path)
562
563     @given(
564         integers(min_value=128),
565         integers(min_value=0),
566         decode_path_strat,
567     )
568     def test_bad_expl_len(self, l, offset, decode_path):
569         with self.assertRaises(DecodeError) as err:
570             Boolean(expl=Boolean.tag_default).decode(
571                 Boolean.tag_default + len_encode(l)[:-1],
572                 offset=offset,
573                 decode_path=decode_path,
574             )
575         repr(err.exception)
576         self.assertEqual(err.exception.offset, offset)
577         self.assertEqual(err.exception.decode_path, decode_path)
578
579     @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
580     @given(
581         boolean_values_strategy(),
582         booleans(),
583         integers(min_value=1).map(tag_ctxc),
584         integers(min_value=0),
585         binary(max_size=5),
586     )
587     def test_symmetric(self, values, value, tag_expl, offset, tail_junk):
588         for klass in (Boolean, BooleanInherited):
589             _, _, _, default, optional, _decoded = values
590             obj = klass(
591                 value=value,
592                 default=default,
593                 optional=optional,
594                 _decoded=_decoded,
595             )
596             repr(obj)
597             list(obj.pps())
598             pprint(obj, big_blobs=True, with_decode_path=True)
599             self.assertFalse(obj.expled)
600             obj_encoded = obj.encode()
601             obj_expled = obj(value, expl=tag_expl)
602             self.assertTrue(obj_expled.expled)
603             repr(obj_expled)
604             list(obj_expled.pps())
605             pprint(obj_expled, big_blobs=True, with_decode_path=True)
606             obj_expled_hex_encoded = obj_expled.hexencode()
607             ctx_copied = deepcopy(ctx_dummy)
608             obj_decoded, tail = obj_expled.hexdecode(
609                 obj_expled_hex_encoded + hexenc(tail_junk),
610                 offset=offset,
611                 ctx=ctx_copied,
612             )
613             self.assertDictEqual(ctx_copied, ctx_dummy)
614             repr(obj_decoded)
615             list(obj_decoded.pps())
616             pprint(obj_decoded, big_blobs=True, with_decode_path=True)
617             self.assertEqual(tail, tail_junk)
618             self.assertEqual(obj_decoded, obj_expled)
619             self.assertNotEqual(obj_decoded, obj)
620             self.assertEqual(bool(obj_decoded), bool(obj_expled))
621             self.assertEqual(bool(obj_decoded), bool(obj))
622             self.assertSequenceEqual(obj_decoded.hexencode(), obj_expled_hex_encoded)
623             self.assertSequenceEqual(obj_decoded.expl_tag, tag_expl)
624             self.assertEqual(obj_decoded.expl_tlen, len(tag_expl))
625             self.assertEqual(
626                 obj_decoded.expl_llen,
627                 len(len_encode(len(obj_encoded))),
628             )
629             self.assertEqual(obj_decoded.tlvlen, len(obj_encoded))
630             self.assertEqual(obj_decoded.expl_vlen, len(obj_encoded))
631             self.assertEqual(
632                 obj_decoded.offset,
633                 offset + obj_decoded.expl_tlen + obj_decoded.expl_llen,
634             )
635             self.assertEqual(obj_decoded.expl_offset, offset)
636             assert_exceeding_data(
637                 self,
638                 lambda: obj_expled.hexdecod(obj_expled_hex_encoded + hexenc(tail_junk)),
639                 tail_junk,
640             )
641
642     @given(integers(min_value=2))
643     def test_invalid_len(self, l):
644         with self.assertRaises(InvalidLength):
645             Boolean().decode(b"".join((
646                 Boolean.tag_default,
647                 len_encode(l),
648                 b"\x00" * l,
649             )))
650
651     @given(integers(min_value=0 + 1, max_value=255 - 1))
652     def test_ber_value(self, value):
653         with assertRaisesRegex(self, DecodeError, "unacceptable Boolean value"):
654             Boolean().decode(b"".join((
655                 Boolean.tag_default,
656                 len_encode(1),
657                 int2byte(value),
658             )))
659         obj, _ = Boolean().decode(
660             b"".join((
661                 Boolean.tag_default,
662                 len_encode(1),
663                 int2byte(value),
664             )),
665             ctx={"bered": True},
666         )
667         self.assertTrue(bool(obj))
668         self.assertTrue(obj.ber_encoded)
669         self.assertFalse(obj.lenindef)
670         self.assertTrue(obj.bered)
671         obj = copy(obj)
672         self.assertTrue(obj.ber_encoded)
673         self.assertFalse(obj.lenindef)
674         self.assertTrue(obj.bered)
675
676     @given(
677         integers(min_value=1).map(tag_ctxc),
678         binary().filter(lambda x: not x.startswith(EOC)),
679     )
680     def test_ber_expl_no_eoc(self, expl, junk):
681         encoded = expl + LENINDEF + Boolean(False).encode()
682         with self.assertRaises(LenIndefForm):
683             Boolean(expl=expl).decode(encoded + junk)
684         with assertRaisesRegex(self, DecodeError, "no EOC"):
685             Boolean(expl=expl).decode(encoded + junk, ctx={"bered": True})
686         obj, tail = Boolean(expl=expl).decode(
687             encoded + EOC + junk,
688             ctx={"bered": True},
689         )
690         self.assertTrue(obj.expl_lenindef)
691         self.assertFalse(obj.lenindef)
692         self.assertFalse(obj.ber_encoded)
693         self.assertTrue(obj.bered)
694         obj = copy(obj)
695         self.assertTrue(obj.expl_lenindef)
696         self.assertFalse(obj.lenindef)
697         self.assertFalse(obj.ber_encoded)
698         self.assertTrue(obj.bered)
699         self.assertSequenceEqual(tail, junk)
700         repr(obj)
701         list(obj.pps())
702         pprint(obj, big_blobs=True, with_decode_path=True)
703
704     @given(
705         integers(min_value=1).map(tag_ctxc),
706         lists(
707             booleans(),
708             min_size=1,
709             max_size=5,
710         ),
711     )
712     def test_ber_expl(self, expl, values):
713         encoded = b""
714         for value in values:
715             encoded += (
716                 expl +
717                 LENINDEF +
718                 Boolean(value).encode() +
719                 EOC
720             )
721         encoded = SequenceOf.tag_default + len_encode(len(encoded)) + encoded
722
723         class SeqOf(SequenceOf):
724             schema = Boolean(expl=expl)
725         with self.assertRaises(LenIndefForm):
726             SeqOf().decode(encoded)
727         seqof, tail = SeqOf().decode(encoded, ctx={"bered": True})
728         self.assertSequenceEqual(tail, b"")
729         self.assertSequenceEqual([bool(v) for v in seqof], values)
730         self.assertSetEqual(
731             set(
732                 (
733                     v.tlvlen,
734                     v.expl_tlvlen,
735                     v.expl_tlen,
736                     v.expl_llen,
737                     v.ber_encoded,
738                     v.lenindef,
739                     v.expl_lenindef,
740                     v.bered,
741                 ) for v in seqof
742             ),
743             set(((
744                 3 + EOC_LEN,
745                 len(expl) + 1 + 3 + EOC_LEN,
746                 len(expl),
747                 1,
748                 False,
749                 False,
750                 True,
751                 True,
752             ),)),
753         )
754         repr(seqof)
755         list(seqof.pps())
756         pprint(seqof, big_blobs=True, with_decode_path=True)
757
758
759 @composite
760 def integer_values_strategy(draw, do_expl=False):
761     bound_min, value, default, bound_max = sorted(draw(sets(
762         integers(),
763         min_size=4,
764         max_size=4,
765     )))
766     if draw(booleans()):
767         value = None
768     _specs = None
769     if draw(booleans()):
770         _specs = draw(sets(text_letters()))
771         values = draw(sets(
772             integers(),
773             min_size=len(_specs),
774             max_size=len(_specs),
775         ))
776         _specs = list(zip(_specs, values))
777     bounds = None
778     if draw(booleans()):
779         bounds = (bound_min, bound_max)
780     impl = None
781     expl = None
782     if do_expl:
783         expl = draw(one_of(none(), integers(min_value=1).map(tag_encode)))
784     else:
785         impl = draw(one_of(none(), integers(min_value=1).map(tag_encode)))
786     if draw(booleans()):
787         default = None
788     optional = draw(one_of(none(), booleans()))
789     _decoded = (
790         draw(integers(min_value=0)),
791         draw(integers(min_value=0)),
792         draw(integers(min_value=0)),
793     )
794     return (value, bounds, impl, expl, default, optional, _specs, _decoded)
795
796
797 class IntegerInherited(Integer):
798     pass
799
800
801 class TestInteger(CommonMixin, TestCase):
802     base_klass = Integer
803
804     def test_invalid_value_type(self):
805         with self.assertRaises(InvalidValueType) as err:
806             Integer(12.3)
807         repr(err.exception)
808
809     @given(sets(text_letters(), min_size=2))
810     def test_unknown_name(self, names_input):
811         missing = names_input.pop()
812
813         class Int(Integer):
814             schema = [(n, 123) for n in names_input]
815         with self.assertRaises(ObjUnknown) as err:
816             Int(missing)
817         repr(err.exception)
818
819     @given(sets(text_letters(), min_size=2))
820     def test_known_name(self, names_input):
821         class Int(Integer):
822             schema = [(n, 123) for n in names_input]
823         Int(names_input.pop())
824
825     @given(booleans())
826     def test_optional(self, optional):
827         obj = Integer(default=Integer(0), optional=optional)
828         self.assertTrue(obj.optional)
829
830     @given(integers())
831     def test_ready(self, value):
832         obj = Integer()
833         self.assertFalse(obj.ready)
834         repr(obj)
835         list(obj.pps())
836         pprint(obj, big_blobs=True, with_decode_path=True)
837         with self.assertRaises(ObjNotReady) as err:
838             obj.encode()
839         repr(err.exception)
840         obj = Integer(value)
841         self.assertTrue(obj.ready)
842         repr(obj)
843         list(obj.pps())
844         pprint(obj, big_blobs=True, with_decode_path=True)
845         hash(obj)
846
847     @given(integers(), integers(), binary(min_size=1), binary(min_size=1))
848     def test_comparison(self, value1, value2, tag1, tag2):
849         for klass in (Integer, IntegerInherited):
850             obj1 = klass(value1)
851             obj2 = klass(value2)
852             self.assertEqual(obj1 == obj2, value1 == value2)
853             self.assertEqual(obj1 != obj2, value1 != value2)
854             self.assertEqual(obj1 == int(obj2), value1 == value2)
855             obj1 = klass(value1, impl=tag1)
856             obj2 = klass(value1, impl=tag2)
857             self.assertEqual(obj1 == obj2, tag1 == tag2)
858             self.assertEqual(obj1 != obj2, tag1 != tag2)
859
860     @given(lists(integers()))
861     def test_sorted_works(self, values):
862         self.assertSequenceEqual(
863             [int(v) for v in sorted(Integer(v) for v in values)],
864             sorted(values),
865         )
866
867     @given(data_strategy())
868     def test_named(self, d):
869         names_input = list(d.draw(sets(text_letters(), min_size=1)))
870         values_input = list(d.draw(sets(
871             integers(),
872             min_size=len(names_input),
873             max_size=len(names_input),
874         )))
875         chosen_name = d.draw(sampled_from(names_input))
876         names_input = dict(zip(names_input, values_input))
877
878         class Int(Integer):
879             schema = names_input
880         _int = Int(chosen_name)
881         self.assertEqual(_int.named, chosen_name)
882         self.assertEqual(int(_int), names_input[chosen_name])
883
884     @given(integers(), integers(min_value=0), integers(min_value=0))
885     def test_bounds_satisfied(self, bound_min, bound_delta, value_delta):
886         value = bound_min + value_delta
887         bound_max = value + bound_delta
888         Integer(value=value, bounds=(bound_min, bound_max))
889
890     @given(sets(integers(), min_size=3, max_size=3))
891     def test_bounds_unsatisfied(self, values):
892         values = sorted(values)
893         with self.assertRaises(BoundsError) as err:
894             Integer(value=values[0], bounds=(values[1], values[2]))
895         repr(err.exception)
896         with assertRaisesRegex(self, DecodeError, "bounds") as err:
897             Integer(bounds=(values[1], values[2])).decode(
898                 Integer(values[0]).encode()
899             )
900         repr(err.exception)
901         with self.assertRaises(BoundsError) as err:
902             Integer(value=values[2], bounds=(values[0], values[1]))
903         repr(err.exception)
904         with assertRaisesRegex(self, DecodeError, "bounds") as err:
905             Integer(bounds=(values[0], values[1])).decode(
906                 Integer(values[2]).encode()
907             )
908         repr(err.exception)
909
910     @given(data_strategy())
911     def test_call(self, d):
912         for klass in (Integer, IntegerInherited):
913             (
914                 value_initial,
915                 bounds_initial,
916                 impl_initial,
917                 expl_initial,
918                 default_initial,
919                 optional_initial,
920                 _specs_initial,
921                 _decoded_initial,
922             ) = d.draw(integer_values_strategy())
923             obj_initial = klass(
924                 value_initial,
925                 bounds_initial,
926                 impl_initial,
927                 expl_initial,
928                 default_initial,
929                 optional_initial or False,
930                 _specs_initial,
931                 _decoded_initial,
932             )
933             (
934                 value,
935                 bounds,
936                 impl,
937                 expl,
938                 default,
939                 optional,
940                 _,
941                 _decoded,
942             ) = d.draw(integer_values_strategy(do_expl=impl_initial is None))
943             if (default is None) and (obj_initial.default is not None):
944                 bounds = None
945             if (
946                     (bounds is None) and
947                     (value is not None) and
948                     (bounds_initial is not None) and
949                     not (bounds_initial[0] <= value <= bounds_initial[1])
950             ):
951                 value = None
952             if (
953                     (bounds is None) and
954                     (default is not None) and
955                     (bounds_initial is not None) and
956                     not (bounds_initial[0] <= default <= bounds_initial[1])
957             ):
958                 default = None
959             obj = obj_initial(value, bounds, impl, expl, default, optional)
960             if obj.ready:
961                 value_expected = default if value is None else value
962                 value_expected = (
963                     default_initial if value_expected is None
964                     else value_expected
965                 )
966                 self.assertEqual(obj, value_expected)
967             self.assertEqual(obj.tag, impl or impl_initial or obj.tag_default)
968             self.assertEqual(obj.expl_tag, expl or expl_initial)
969             self.assertEqual(
970                 obj.default,
971                 default_initial if default is None else default,
972             )
973             if obj.default is None:
974                 optional = optional_initial if optional is None else optional
975                 optional = False if optional is None else optional
976             else:
977                 optional = True
978             self.assertEqual(obj.optional, optional)
979             self.assertEqual(
980                 (obj._bound_min, obj._bound_max),
981                 bounds or bounds_initial or (float("-inf"), float("+inf")),
982             )
983             self.assertEqual(
984                 obj.specs,
985                 {} if _specs_initial is None else dict(_specs_initial),
986             )
987
988     @given(integer_values_strategy())
989     def test_copy(self, values):
990         for klass in (Integer, IntegerInherited):
991             obj = klass(*values)
992             for copy_func in copy_funcs:
993                 obj_copied = copy_func(obj)
994                 self.assert_copied_basic_fields(obj, obj_copied)
995                 self.assertEqual(obj.specs, obj_copied.specs)
996                 self.assertEqual(obj._bound_min, obj_copied._bound_min)
997                 self.assertEqual(obj._bound_max, obj_copied._bound_max)
998                 self.assertEqual(obj._value, obj_copied._value)
999
1000     @given(
1001         integers(),
1002         integers(min_value=1).map(tag_encode),
1003     )
1004     def test_stripped(self, value, tag_impl):
1005         obj = Integer(value, impl=tag_impl)
1006         with self.assertRaises(NotEnoughData):
1007             obj.decode(obj.encode()[:-1])
1008
1009     @given(
1010         integers(),
1011         integers(min_value=1).map(tag_ctxc),
1012     )
1013     def test_stripped_expl(self, value, tag_expl):
1014         obj = Integer(value, expl=tag_expl)
1015         with self.assertRaises(NotEnoughData):
1016             obj.decode(obj.encode()[:-1])
1017
1018     def test_zero_len(self):
1019         with self.assertRaises(NotEnoughData):
1020             Integer().decode(b"".join((
1021                 Integer.tag_default,
1022                 len_encode(0),
1023             )))
1024
1025     @given(
1026         integers(min_value=31),
1027         integers(min_value=0),
1028         decode_path_strat,
1029     )
1030     def test_bad_tag(self, tag, offset, decode_path):
1031         with self.assertRaises(DecodeError) as err:
1032             Integer().decode(
1033                 tag_encode(tag)[:-1],
1034                 offset=offset,
1035                 decode_path=decode_path,
1036             )
1037         repr(err.exception)
1038         self.assertEqual(err.exception.offset, offset)
1039         self.assertEqual(err.exception.decode_path, decode_path)
1040
1041     @given(
1042         integers(min_value=128),
1043         integers(min_value=0),
1044         decode_path_strat,
1045     )
1046     def test_bad_len(self, l, offset, decode_path):
1047         with self.assertRaises(DecodeError) as err:
1048             Integer().decode(
1049                 Integer.tag_default + len_encode(l)[:-1],
1050                 offset=offset,
1051                 decode_path=decode_path,
1052             )
1053         repr(err.exception)
1054         self.assertEqual(err.exception.offset, offset)
1055         self.assertEqual(err.exception.decode_path, decode_path)
1056
1057     @given(
1058         sets(integers(), min_size=2, max_size=2),
1059         integers(min_value=0),
1060         decode_path_strat,
1061     )
1062     def test_invalid_bounds_while_decoding(self, ints, offset, decode_path):
1063         value, bound_min = list(sorted(ints))
1064
1065         class Int(Integer):
1066             bounds = (bound_min, bound_min)
1067         with self.assertRaises(DecodeError) as err:
1068             Int().decode(
1069                 Integer(value).encode(),
1070                 offset=offset,
1071                 decode_path=decode_path,
1072             )
1073         repr(err.exception)
1074         self.assertEqual(err.exception.offset, offset)
1075         self.assertEqual(err.exception.decode_path, decode_path)
1076
1077     @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
1078     @given(
1079         integer_values_strategy(),
1080         integers(),
1081         integers(min_value=1).map(tag_ctxc),
1082         integers(min_value=0),
1083         binary(max_size=5),
1084     )
1085     def test_symmetric(self, values, value, tag_expl, offset, tail_junk):
1086         for klass in (Integer, IntegerInherited):
1087             _, _, _, _, default, optional, _, _decoded = values
1088             obj = klass(
1089                 value=value,
1090                 default=default,
1091                 optional=optional,
1092                 _decoded=_decoded,
1093             )
1094             repr(obj)
1095             list(obj.pps())
1096             pprint(obj, big_blobs=True, with_decode_path=True)
1097             self.assertFalse(obj.expled)
1098             obj_encoded = obj.encode()
1099             obj_expled = obj(value, expl=tag_expl)
1100             self.assertTrue(obj_expled.expled)
1101             repr(obj_expled)
1102             list(obj_expled.pps())
1103             pprint(obj_expled, big_blobs=True, with_decode_path=True)
1104             obj_expled_encoded = obj_expled.encode()
1105             ctx_copied = deepcopy(ctx_dummy)
1106             obj_decoded, tail = obj_expled.decode(
1107                 obj_expled_encoded + tail_junk,
1108                 offset=offset,
1109                 ctx=ctx_copied,
1110             )
1111             self.assertDictEqual(ctx_copied, ctx_dummy)
1112             repr(obj_decoded)
1113             list(obj_decoded.pps())
1114             pprint(obj_decoded, big_blobs=True, with_decode_path=True)
1115             self.assertEqual(tail, tail_junk)
1116             self.assertEqual(obj_decoded, obj_expled)
1117             self.assertNotEqual(obj_decoded, obj)
1118             self.assertEqual(int(obj_decoded), int(obj_expled))
1119             self.assertEqual(int(obj_decoded), int(obj))
1120             self.assertSequenceEqual(obj_decoded.encode(), obj_expled_encoded)
1121             self.assertSequenceEqual(obj_decoded.expl_tag, tag_expl)
1122             self.assertEqual(obj_decoded.expl_tlen, len(tag_expl))
1123             self.assertEqual(
1124                 obj_decoded.expl_llen,
1125                 len(len_encode(len(obj_encoded))),
1126             )
1127             self.assertEqual(obj_decoded.tlvlen, len(obj_encoded))
1128             self.assertEqual(obj_decoded.expl_vlen, len(obj_encoded))
1129             self.assertEqual(
1130                 obj_decoded.offset,
1131                 offset + obj_decoded.expl_tlen + obj_decoded.expl_llen,
1132             )
1133             self.assertEqual(obj_decoded.expl_offset, offset)
1134             assert_exceeding_data(
1135                 self,
1136                 lambda: obj_expled.decod(obj_expled_encoded + tail_junk),
1137                 tail_junk,
1138             )
1139
1140     def test_go_vectors_valid(self):
1141         for data, expect in ((
1142                 (b"\x00", 0),
1143                 (b"\x7f", 127),
1144                 (b"\x80", -128),
1145                 (b"\xff\x7f", -129),
1146                 (b"\xff", -1),
1147                 (b"\x01", 1),
1148                 (b"\x00\xff", 255),
1149                 (b"\xff\x00", -256),
1150                 (b"\x01\x00", 256),
1151                 (b"\x00\x80", 128),
1152                 (b"\x01\x00", 256),
1153                 (b"\x80\x00\x00\x00\x00\x00\x00\x00", -9223372036854775808),
1154                 (b"\x80\x00\x00\x00", -2147483648),
1155         )):
1156             self.assertEqual(
1157                 Integer().decode(b"".join((
1158                     Integer.tag_default,
1159                     len_encode(len(data)),
1160                     data,
1161                 )))[0],
1162                 expect,
1163             )
1164
1165     def test_go_vectors_invalid(self):
1166         for data in ((
1167                 b"\x00\x7f",
1168                 b"\xff\xf0",
1169         )):
1170             with self.assertRaises(DecodeError):
1171                 Integer().decode(b"".join((
1172                     Integer.tag_default,
1173                     len_encode(len(data)),
1174                     data,
1175                 )))
1176
1177
1178 @composite
1179 def bit_string_values_strategy(draw, schema=None, value_required=False, do_expl=False):
1180     if schema is None:
1181         schema = ()
1182         if draw(booleans()):
1183             schema = draw(sets(text_letters(), min_size=1, max_size=256))
1184             bits = draw(sets(
1185                 integers(min_value=0, max_value=255),
1186                 min_size=len(schema),
1187                 max_size=len(schema),
1188             ))
1189             schema = list(zip(schema, bits))
1190
1191     def _value(value_required):
1192         if not value_required and draw(booleans()):
1193             return None
1194         generation_choice = 0
1195         if value_required:
1196             generation_choice = draw(sampled_from((1, 2, 3)))
1197         if generation_choice == 1 or draw(booleans()):
1198             return "'%s'B" % "".join(draw(lists(
1199                 sampled_from(("0", "1")),
1200                 max_size=len(schema),
1201             )))
1202         if generation_choice == 2 or draw(booleans()):
1203             return draw(binary(max_size=len(schema) // 8))
1204         if generation_choice == 3 or draw(booleans()):
1205             return tuple(draw(lists(sampled_from([name for name, _ in schema]))))
1206         return None
1207     value = _value(value_required)
1208     default = _value(value_required=False)
1209     impl = None
1210     expl = None
1211     if do_expl:
1212         expl = draw(one_of(none(), integers(min_value=1).map(tag_encode)))
1213     else:
1214         impl = draw(one_of(none(), integers(min_value=1).map(tag_encode)))
1215     optional = draw(one_of(none(), booleans()))
1216     _decoded = (
1217         draw(integers(min_value=0)),
1218         draw(integers(min_value=0)),
1219         draw(integers(min_value=0)),
1220     )
1221     return (schema, value, impl, expl, default, optional, _decoded)
1222
1223
1224 class BitStringInherited(BitString):
1225     pass
1226
1227
1228 class TestBitString(CommonMixin, TestCase):
1229     base_klass = BitString
1230
1231     @given(lists(booleans()))
1232     def test_b_encoding(self, bits):
1233         obj = BitString("'%s'B" % "".join("1" if bit else "0" for bit in bits))
1234         self.assertEqual(obj.bit_len, len(bits))
1235         self.assertSequenceEqual(list(obj), bits)
1236         for i, bit in enumerate(bits):
1237             self.assertEqual(obj[i], bit)
1238
1239     @given(lists(booleans()))
1240     def test_out_of_bounds_bits(self, bits):
1241         obj = BitString("'%s'B" % "".join("1" if bit else "0" for bit in bits))
1242         for i in range(len(bits), len(bits) * 2):
1243             self.assertFalse(obj[i])
1244
1245     def test_bad_b_encoding(self):
1246         with self.assertRaises(ValueError):
1247             BitString("'010120101'B")
1248
1249     @given(
1250         integers(min_value=1, max_value=255),
1251         integers(min_value=1, max_value=255),
1252     )
1253     def test_named_are_stripped(self, leading_zeros, trailing_zeros):
1254         obj = BitString("'%s1%s'B" % (("0" * leading_zeros), ("0" * trailing_zeros)))
1255         self.assertEqual(obj.bit_len, leading_zeros + 1 + trailing_zeros)
1256         self.assertGreater(len(obj.encode()), (leading_zeros + 1 + trailing_zeros) // 8)
1257
1258         class BS(BitString):
1259             schema = (("whatever", 0),)
1260         obj = BS("'%s1%s'B" % (("0" * leading_zeros), ("0" * trailing_zeros)))
1261         self.assertEqual(obj.bit_len, leading_zeros + 1)
1262         self.assertGreater(len(obj.encode()), (leading_zeros + 1) // 8)
1263
1264     def test_zero_len(self):
1265         with self.assertRaises(NotEnoughData):
1266             BitString().decode(b"".join((
1267                 BitString.tag_default,
1268                 len_encode(0),
1269             )))
1270
1271     def test_invalid_value_type(self):
1272         with self.assertRaises(InvalidValueType) as err:
1273             BitString(123)
1274         repr(err.exception)
1275         with self.assertRaises(InvalidValueType) as err:
1276             BitString(u"123")
1277         repr(err.exception)
1278
1279     def test_obj_unknown(self):
1280         with self.assertRaises(ObjUnknown) as err:
1281             BitString(b"whatever")["whenever"]
1282         repr(err.exception)
1283
1284     def test_get_invalid_type(self):
1285         with self.assertRaises(InvalidValueType) as err:
1286             BitString(b"whatever")[(1, 2, 3)]
1287         repr(err.exception)
1288
1289     @given(data_strategy())
1290     def test_unknown_name(self, d):
1291         _schema = d.draw(sets(text_letters(), min_size=2, max_size=5))
1292         missing = _schema.pop()
1293
1294         class BS(BitString):
1295             schema = [(n, i) for i, n in enumerate(_schema)]
1296         with self.assertRaises(ObjUnknown) as err:
1297             BS((missing,))
1298         repr(err.exception)
1299
1300     @given(booleans())
1301     def test_optional(self, optional):
1302         obj = BitString(default=BitString(b""), optional=optional)
1303         self.assertTrue(obj.optional)
1304
1305     @given(binary())
1306     def test_ready(self, value):
1307         obj = BitString()
1308         self.assertFalse(obj.ready)
1309         repr(obj)
1310         list(obj.pps())
1311         pprint(obj, big_blobs=True, with_decode_path=True)
1312         with self.assertRaises(ObjNotReady) as err:
1313             obj.encode()
1314         repr(err.exception)
1315         obj = BitString(value)
1316         self.assertTrue(obj.ready)
1317         repr(obj)
1318         list(obj.pps())
1319         pprint(obj, big_blobs=True, with_decode_path=True)
1320
1321     @given(
1322         tuples(integers(min_value=0), binary()),
1323         tuples(integers(min_value=0), binary()),
1324         binary(min_size=1),
1325         binary(min_size=1),
1326     )
1327     def test_comparison(self, value1, value2, tag1, tag2):
1328         for klass in (BitString, BitStringInherited):
1329             obj1 = klass(value1)
1330             obj2 = klass(value2)
1331             self.assertEqual(obj1 == obj2, value1 == value2)
1332             self.assertEqual(obj1 != obj2, value1 != value2)
1333             self.assertEqual(obj1 == bytes(obj2), value1[1] == value2[1])
1334             obj1 = klass(value1, impl=tag1)
1335             obj2 = klass(value1, impl=tag2)
1336             self.assertEqual(obj1 == obj2, tag1 == tag2)
1337             self.assertEqual(obj1 != obj2, tag1 != tag2)
1338
1339     @given(data_strategy())
1340     def test_call(self, d):
1341         for klass in (BitString, BitStringInherited):
1342             (
1343                 schema_initial,
1344                 value_initial,
1345                 impl_initial,
1346                 expl_initial,
1347                 default_initial,
1348                 optional_initial,
1349                 _decoded_initial,
1350             ) = d.draw(bit_string_values_strategy())
1351
1352             class BS(klass):
1353                 schema = schema_initial
1354             obj_initial = BS(
1355                 value=value_initial,
1356                 impl=impl_initial,
1357                 expl=expl_initial,
1358                 default=default_initial,
1359                 optional=optional_initial or False,
1360                 _decoded=_decoded_initial,
1361             )
1362             (
1363                 _,
1364                 value,
1365                 impl,
1366                 expl,
1367                 default,
1368                 optional,
1369                 _decoded,
1370             ) = d.draw(bit_string_values_strategy(
1371                 schema=schema_initial,
1372                 do_expl=impl_initial is None,
1373             ))
1374             obj = obj_initial(
1375                 value=value,
1376                 impl=impl,
1377                 expl=expl,
1378                 default=default,
1379                 optional=optional,
1380             )
1381             self.assertEqual(obj.tag, impl or impl_initial or obj.tag_default)
1382             self.assertEqual(obj.expl_tag, expl or expl_initial)
1383             if obj.default is None:
1384                 optional = optional_initial if optional is None else optional
1385                 optional = False if optional is None else optional
1386             else:
1387                 optional = True
1388             self.assertEqual(obj.optional, optional)
1389             self.assertEqual(obj.specs, obj_initial.specs)
1390
1391     @given(bit_string_values_strategy())
1392     def test_copy(self, values):
1393         for klass in (BitString, BitStringInherited):
1394             _schema, value, impl, expl, default, optional, _decoded = values
1395
1396             class BS(klass):
1397                 schema = _schema
1398             register_class(BS)
1399             obj = BS(
1400                 value=value,
1401                 impl=impl,
1402                 expl=expl,
1403                 default=default,
1404                 optional=optional or False,
1405                 _decoded=_decoded,
1406             )
1407             for copy_func in copy_funcs:
1408                 obj_copied = copy_func(obj)
1409                 self.assert_copied_basic_fields(obj, obj_copied)
1410                 self.assertEqual(obj.specs, obj_copied.specs)
1411                 self.assertEqual(obj._value, obj_copied._value)
1412
1413     @given(
1414         binary(),
1415         integers(min_value=1).map(tag_encode),
1416     )
1417     def test_stripped(self, value, tag_impl):
1418         obj = BitString(value, impl=tag_impl)
1419         with self.assertRaises(NotEnoughData):
1420             obj.decode(obj.encode()[:-1])
1421
1422     @given(
1423         binary(),
1424         integers(min_value=1).map(tag_ctxc),
1425     )
1426     def test_stripped_expl(self, value, tag_expl):
1427         obj = BitString(value, expl=tag_expl)
1428         with self.assertRaises(NotEnoughData):
1429             obj.decode(obj.encode()[:-1])
1430
1431     @given(
1432         integers(min_value=31),
1433         integers(min_value=0),
1434         decode_path_strat,
1435     )
1436     def test_bad_tag(self, tag, offset, decode_path):
1437         with self.assertRaises(DecodeError) as err:
1438             BitString().decode(
1439                 tag_encode(tag)[:-1],
1440                 offset=offset,
1441                 decode_path=decode_path,
1442             )
1443         repr(err.exception)
1444         self.assertEqual(err.exception.offset, offset)
1445         self.assertEqual(err.exception.decode_path, decode_path)
1446
1447     @given(
1448         integers(min_value=128),
1449         integers(min_value=0),
1450         decode_path_strat,
1451     )
1452     def test_bad_len(self, l, offset, decode_path):
1453         with self.assertRaises(DecodeError) as err:
1454             BitString().decode(
1455                 BitString.tag_default + len_encode(l)[:-1],
1456                 offset=offset,
1457                 decode_path=decode_path,
1458             )
1459         repr(err.exception)
1460         self.assertEqual(err.exception.offset, offset)
1461         self.assertEqual(err.exception.decode_path, decode_path)
1462
1463     @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
1464     @given(data_strategy())
1465     def test_symmetric(self, d):
1466         (
1467             _schema,
1468             value,
1469             _,
1470             _,
1471             default,
1472             optional,
1473             _decoded,
1474         ) = d.draw(bit_string_values_strategy(value_required=True))
1475         tail_junk = d.draw(binary(max_size=5))
1476         tag_expl = tag_ctxc(d.draw(integers(min_value=1)))
1477         offset = d.draw(integers(min_value=0))
1478         for klass in (BitString, BitStringInherited):
1479             class BS(klass):
1480                 schema = _schema
1481             obj = BS(
1482                 value=value,
1483                 default=default,
1484                 optional=optional,
1485                 _decoded=_decoded,
1486             )
1487             repr(obj)
1488             list(obj.pps())
1489             pprint(obj, big_blobs=True, with_decode_path=True)
1490             self.assertFalse(obj.expled)
1491             obj_encoded = obj.encode()
1492             obj_expled = obj(value, expl=tag_expl)
1493             self.assertTrue(obj_expled.expled)
1494             repr(obj_expled)
1495             list(obj_expled.pps())
1496             pprint(obj_expled, big_blobs=True, with_decode_path=True)
1497             obj_expled_encoded = obj_expled.encode()
1498             ctx_copied = deepcopy(ctx_dummy)
1499             obj_decoded, tail = obj_expled.decode(
1500                 obj_expled_encoded + tail_junk,
1501                 offset=offset,
1502                 ctx=ctx_copied,
1503             )
1504             self.assertDictEqual(ctx_copied, ctx_dummy)
1505             repr(obj_decoded)
1506             list(obj_decoded.pps())
1507             pprint(obj_decoded, big_blobs=True, with_decode_path=True)
1508             self.assertEqual(tail, tail_junk)
1509             self.assertEqual(obj_decoded, obj_expled)
1510             self.assertNotEqual(obj_decoded, obj)
1511             self.assertEqual(bytes(obj_decoded), bytes(obj_expled))
1512             self.assertEqual(bytes(obj_decoded), bytes(obj))
1513             self.assertSequenceEqual(obj_decoded.encode(), obj_expled_encoded)
1514             self.assertSequenceEqual(obj_decoded.expl_tag, tag_expl)
1515             self.assertEqual(obj_decoded.expl_tlen, len(tag_expl))
1516             self.assertEqual(
1517                 obj_decoded.expl_llen,
1518                 len(len_encode(len(obj_encoded))),
1519             )
1520             self.assertEqual(obj_decoded.tlvlen, len(obj_encoded))
1521             self.assertEqual(obj_decoded.expl_vlen, len(obj_encoded))
1522             self.assertEqual(
1523                 obj_decoded.offset,
1524                 offset + obj_decoded.expl_tlen + obj_decoded.expl_llen,
1525             )
1526             self.assertEqual(obj_decoded.expl_offset, offset)
1527             if isinstance(value, tuple):
1528                 self.assertSetEqual(set(value), set(obj_decoded.named))
1529                 for name in value:
1530                     obj_decoded[name]
1531             assert_exceeding_data(
1532                 self,
1533                 lambda: obj_expled.decod(obj_expled_encoded + tail_junk),
1534                 tail_junk,
1535             )
1536
1537     @given(integers(min_value=1, max_value=255))
1538     def test_bad_zero_value(self, pad_size):
1539         with self.assertRaises(DecodeError):
1540             BitString().decode(b"".join((
1541                 BitString.tag_default,
1542                 len_encode(1),
1543                 int2byte(pad_size),
1544             )))
1545
1546     def test_go_vectors_invalid(self):
1547         for data in ((
1548                 b"\x07\x01",
1549                 b"\x07\x40",
1550                 b"\x08\x00",
1551         )):
1552             with self.assertRaises(DecodeError):
1553                 BitString().decode(b"".join((
1554                     BitString.tag_default,
1555                     len_encode(2),
1556                     data,
1557                 )))
1558
1559     def test_go_vectors_valid(self):
1560         obj, _ = BitString().decode(b"".join((
1561             BitString.tag_default,
1562             len_encode(1),
1563             b"\x00",
1564         )))
1565         self.assertEqual(bytes(obj), b"")
1566         self.assertEqual(obj.bit_len, 0)
1567
1568         obj, _ = BitString().decode(b"".join((
1569             BitString.tag_default,
1570             len_encode(2),
1571             b"\x07\x00",
1572         )))
1573         self.assertEqual(bytes(obj), b"\x00")
1574         self.assertEqual(obj.bit_len, 1)
1575
1576         obj = BitString((16, b"\x82\x40"))
1577         self.assertTrue(obj[0])
1578         self.assertFalse(obj[1])
1579         self.assertTrue(obj[6])
1580         self.assertTrue(obj[9])
1581         self.assertFalse(obj[17])
1582
1583     @given(
1584         integers(min_value=1, max_value=30),
1585         lists(
1586             one_of(
1587                 binary(min_size=1, max_size=5),
1588                 lists(
1589                     binary(min_size=1, max_size=5),
1590                     min_size=1,
1591                     max_size=3,
1592                 ),
1593             ),
1594             min_size=0,
1595             max_size=3,
1596         ),
1597         lists(booleans(), min_size=1),
1598         binary(),
1599     )
1600     def test_constructed(self, impl, chunk_inputs, chunk_last_bits, junk):
1601         def chunk_constructed(contents):
1602             return (
1603                 tag_encode(form=TagFormConstructed, num=3) +
1604                 LENINDEF +
1605                 b"".join(BitString(content).encode() for content in contents) +
1606                 EOC
1607             )
1608         chunks = []
1609         payload_expected = b""
1610         bit_len_expected = 0
1611         for chunk_input in chunk_inputs:
1612             if isinstance(chunk_input, binary_type):
1613                 chunks.append(BitString(chunk_input).encode())
1614                 payload_expected += chunk_input
1615                 bit_len_expected += len(chunk_input) * 8
1616             else:
1617                 chunks.append(chunk_constructed(chunk_input))
1618                 payload = b"".join(chunk_input)
1619                 payload_expected += payload
1620                 bit_len_expected += len(payload) * 8
1621         chunk_last = BitString("'%s'B" % "".join(
1622             "1" if bit else "0" for bit in chunk_last_bits
1623         ))
1624         payload_expected += bytes(chunk_last)
1625         bit_len_expected += chunk_last.bit_len
1626         encoded_indefinite = (
1627             tag_encode(form=TagFormConstructed, num=impl) +
1628             LENINDEF +
1629             b"".join(chunks) +
1630             chunk_last.encode() +
1631             EOC
1632         )
1633         encoded_definite = (
1634             tag_encode(form=TagFormConstructed, num=impl) +
1635             len_encode(len(b"".join(chunks) + chunk_last.encode())) +
1636             b"".join(chunks) +
1637             chunk_last.encode()
1638         )
1639         with assertRaisesRegex(self, DecodeError, "unallowed BER"):
1640             BitString(impl=tag_encode(impl)).decode(encoded_indefinite)
1641         for lenindef_expected, encoded in (
1642                 (True, encoded_indefinite),
1643                 (False, encoded_definite),
1644         ):
1645             obj, tail = BitString(impl=tag_encode(impl)).decode(
1646                 encoded + junk,
1647                 ctx={"bered": True},
1648             )
1649             self.assertSequenceEqual(tail, junk)
1650             self.assertEqual(obj.bit_len, bit_len_expected)
1651             self.assertSequenceEqual(bytes(obj), payload_expected)
1652             self.assertTrue(obj.ber_encoded)
1653             self.assertEqual(obj.lenindef, lenindef_expected)
1654             self.assertTrue(obj.bered)
1655             obj = copy(obj)
1656             self.assertTrue(obj.ber_encoded)
1657             self.assertEqual(obj.lenindef, lenindef_expected)
1658             self.assertTrue(obj.bered)
1659             self.assertEqual(len(encoded), obj.tlvlen)
1660             repr(obj)
1661             list(obj.pps())
1662             pprint(obj, big_blobs=True, with_decode_path=True)
1663
1664     @given(
1665         integers(min_value=0),
1666         decode_path_strat,
1667     )
1668     def test_ber_definite_too_short(self, offset, decode_path):
1669         with assertRaisesRegex(self, DecodeError, "longer than data") as err:
1670             BitString().decode(
1671                 tag_encode(3, form=TagFormConstructed) + len_encode(1),
1672                 offset=offset,
1673                 decode_path=decode_path,
1674                 ctx={"bered": True},
1675             )
1676         self.assertEqual(err.exception.decode_path, decode_path)
1677         self.assertEqual(err.exception.offset, offset)
1678
1679     @given(
1680         integers(min_value=0),
1681         decode_path_strat,
1682     )
1683     def test_ber_definite_no_data(self, offset, decode_path):
1684         with assertRaisesRegex(self, DecodeError, "zero length") as err:
1685             BitString().decode(
1686                 tag_encode(3, form=TagFormConstructed) + len_encode(0),
1687                 offset=offset,
1688                 decode_path=decode_path,
1689                 ctx={"bered": True},
1690             )
1691         self.assertEqual(err.exception.decode_path, decode_path)
1692         self.assertEqual(err.exception.offset, offset)
1693
1694     @given(
1695         integers(min_value=0),
1696         decode_path_strat,
1697         integers(min_value=1, max_value=3),
1698     )
1699     def test_ber_indefinite_no_eoc(self, offset, decode_path, chunks):
1700         bs = BitString(b"data").encode()
1701         with self.assertRaises(NotEnoughData) as err:
1702             BitString().decode(
1703                 tag_encode(3, form=TagFormConstructed) + LENINDEF + chunks * bs,
1704                 offset=offset,
1705                 decode_path=decode_path,
1706                 ctx={"bered": True},
1707             )
1708         self.assertEqual(err.exception.decode_path, decode_path + (str(chunks),))
1709         self.assertEqual(err.exception.offset, offset + 1 + 1 + chunks * len(bs))
1710
1711     @given(
1712         integers(min_value=0),
1713         decode_path_strat,
1714         integers(min_value=1, max_value=3),
1715     )
1716     def test_ber_definite_chunk_out_of_bounds(self, offset, decode_path, chunks):
1717         bs = BitString(b"data").encode()
1718         bs_longer = BitString(b"data-longer").encode()
1719         with assertRaisesRegex(self, DecodeError, "chunk out of bounds") as err:
1720             BitString().decode(
1721                 (
1722                     tag_encode(3, form=TagFormConstructed) +
1723                     len_encode((chunks + 1) * len(bs)) +
1724                     chunks * bs +
1725                     bs_longer
1726                 ),
1727                 offset=offset,
1728                 decode_path=decode_path,
1729                 ctx={"bered": True},
1730             )
1731         self.assertEqual(err.exception.decode_path, decode_path + (str(chunks),))
1732         self.assertEqual(err.exception.offset, offset + 1 + 1 + chunks * len(bs))
1733
1734     @given(
1735         integers(min_value=0),
1736         decode_path_strat,
1737     )
1738     def test_ber_indefinite_no_chunks(self, offset, decode_path):
1739         with assertRaisesRegex(self, DecodeError, "no chunks") as err:
1740             BitString().decode(
1741                 tag_encode(3, form=TagFormConstructed) + LENINDEF + EOC,
1742                 offset=offset,
1743                 decode_path=decode_path,
1744                 ctx={"bered": True},
1745             )
1746         self.assertEqual(err.exception.decode_path, decode_path)
1747         self.assertEqual(err.exception.offset, offset)
1748
1749     @given(data_strategy())
1750     def test_ber_indefinite_not_multiple(self, d):
1751         bs_short = BitString("'A'H").encode()
1752         bs_full = BitString("'AA'H").encode()
1753         chunks = [bs_full for _ in range(d.draw(integers(min_value=0, max_value=3)))]
1754         chunks.append(bs_short)
1755         d.draw(permutations(chunks))
1756         chunks.append(bs_short)
1757         offset = d.draw(integers(min_value=0))
1758         decode_path = d.draw(decode_path_strat)
1759         with assertRaisesRegex(self, DecodeError, "multiple of 8 bits") as err:
1760             BitString().decode(
1761                 (
1762                     tag_encode(3, form=TagFormConstructed) +
1763                     LENINDEF +
1764                     b"".join(chunks) +
1765                     EOC
1766                 ),
1767                 offset=offset,
1768                 decode_path=decode_path,
1769                 ctx={"bered": True},
1770             )
1771         self.assertEqual(
1772             err.exception.decode_path,
1773             decode_path + (str(chunks.index(bs_short)),),
1774         )
1775         self.assertEqual(
1776             err.exception.offset,
1777             offset + 1 + 1 + chunks.index(bs_short) * len(bs_full),
1778         )
1779
1780     def test_x690_vector(self):
1781         vector = BitString("'0A3B5F291CD'H")
1782         obj, tail = BitString().decode(hexdec("0307040A3B5F291CD0"))
1783         self.assertSequenceEqual(tail, b"")
1784         self.assertEqual(obj, vector)
1785         obj, tail = BitString().decode(
1786             hexdec("23800303000A3B0305045F291CD00000"),
1787             ctx={"bered": True},
1788         )
1789         self.assertSequenceEqual(tail, b"")
1790         self.assertEqual(obj, vector)
1791         self.assertTrue(obj.ber_encoded)
1792         self.assertTrue(obj.lenindef)
1793         self.assertTrue(obj.bered)
1794         obj = copy(obj)
1795         self.assertTrue(obj.ber_encoded)
1796         self.assertTrue(obj.lenindef)
1797         self.assertTrue(obj.bered)
1798
1799
1800 @composite
1801 def octet_string_values_strategy(draw, do_expl=False):
1802     bound_min, bound_max = sorted(draw(sets(
1803         integers(min_value=0, max_value=1 << 7),
1804         min_size=2,
1805         max_size=2,
1806     )))
1807     value = draw(one_of(
1808         none(),
1809         binary(min_size=bound_min, max_size=bound_max),
1810     ))
1811     default = draw(one_of(
1812         none(),
1813         binary(min_size=bound_min, max_size=bound_max),
1814     ))
1815     bounds = None
1816     if draw(booleans()):
1817         bounds = (bound_min, bound_max)
1818     impl = None
1819     expl = None
1820     if do_expl:
1821         expl = draw(one_of(none(), integers(min_value=1).map(tag_encode)))
1822     else:
1823         impl = draw(one_of(none(), integers(min_value=1).map(tag_encode)))
1824     optional = draw(one_of(none(), booleans()))
1825     _decoded = (
1826         draw(integers(min_value=0)),
1827         draw(integers(min_value=0)),
1828         draw(integers(min_value=0)),
1829     )
1830     return (value, bounds, impl, expl, default, optional, _decoded)
1831
1832
1833 class OctetStringInherited(OctetString):
1834     pass
1835
1836
1837 class TestOctetString(CommonMixin, TestCase):
1838     base_klass = OctetString
1839
1840     def test_invalid_value_type(self):
1841         with self.assertRaises(InvalidValueType) as err:
1842             OctetString(text_type(123))
1843         repr(err.exception)
1844
1845     @given(booleans())
1846     def test_optional(self, optional):
1847         obj = OctetString(default=OctetString(b""), optional=optional)
1848         self.assertTrue(obj.optional)
1849
1850     @given(binary())
1851     def test_ready(self, value):
1852         obj = OctetString()
1853         self.assertFalse(obj.ready)
1854         repr(obj)
1855         list(obj.pps())
1856         pprint(obj, big_blobs=True, with_decode_path=True)
1857         with self.assertRaises(ObjNotReady) as err:
1858             obj.encode()
1859         repr(err.exception)
1860         obj = OctetString(value)
1861         self.assertTrue(obj.ready)
1862         repr(obj)
1863         list(obj.pps())
1864         pprint(obj, big_blobs=True, with_decode_path=True)
1865
1866     @given(binary(), binary(), binary(min_size=1), binary(min_size=1))
1867     def test_comparison(self, value1, value2, tag1, tag2):
1868         for klass in (OctetString, OctetStringInherited):
1869             obj1 = klass(value1)
1870             obj2 = klass(value2)
1871             self.assertEqual(obj1 == obj2, value1 == value2)
1872             self.assertEqual(obj1 != obj2, value1 != value2)
1873             self.assertEqual(obj1 == bytes(obj2), value1 == value2)
1874             obj1 = klass(value1, impl=tag1)
1875             obj2 = klass(value1, impl=tag2)
1876             self.assertEqual(obj1 == obj2, tag1 == tag2)
1877             self.assertEqual(obj1 != obj2, tag1 != tag2)
1878
1879     @given(lists(binary()))
1880     def test_sorted_works(self, values):
1881         self.assertSequenceEqual(
1882             [bytes(v) for v in sorted(OctetString(v) for v in values)],
1883             sorted(values),
1884         )
1885
1886     @given(data_strategy())
1887     def test_bounds_satisfied(self, d):
1888         bound_min = d.draw(integers(min_value=0, max_value=1 << 7))
1889         bound_max = d.draw(integers(min_value=bound_min, max_value=1 << 7))
1890         value = d.draw(binary(min_size=bound_min, max_size=bound_max))
1891         OctetString(value=value, bounds=(bound_min, bound_max))
1892
1893     @given(data_strategy())
1894     def test_bounds_unsatisfied(self, d):
1895         bound_min = d.draw(integers(min_value=1, max_value=1 << 7))
1896         bound_max = d.draw(integers(min_value=bound_min, max_value=1 << 7))
1897         value = d.draw(binary(max_size=bound_min - 1))
1898         with self.assertRaises(BoundsError) as err:
1899             OctetString(value=value, bounds=(bound_min, bound_max))
1900         repr(err.exception)
1901         with assertRaisesRegex(self, DecodeError, "bounds") as err:
1902             OctetString(bounds=(bound_min, bound_max)).decode(
1903                 OctetString(value).encode()
1904             )
1905         repr(err.exception)
1906         value = d.draw(binary(min_size=bound_max + 1))
1907         with self.assertRaises(BoundsError) as err:
1908             OctetString(value=value, bounds=(bound_min, bound_max))
1909         repr(err.exception)
1910         with assertRaisesRegex(self, DecodeError, "bounds") as err:
1911             OctetString(bounds=(bound_min, bound_max)).decode(
1912                 OctetString(value).encode()
1913             )
1914         repr(err.exception)
1915
1916     @given(data_strategy())
1917     def test_call(self, d):
1918         for klass in (OctetString, OctetStringInherited):
1919             (
1920                 value_initial,
1921                 bounds_initial,
1922                 impl_initial,
1923                 expl_initial,
1924                 default_initial,
1925                 optional_initial,
1926                 _decoded_initial,
1927             ) = d.draw(octet_string_values_strategy())
1928             obj_initial = klass(
1929                 value_initial,
1930                 bounds_initial,
1931                 impl_initial,
1932                 expl_initial,
1933                 default_initial,
1934                 optional_initial or False,
1935                 _decoded_initial,
1936             )
1937             (
1938                 value,
1939                 bounds,
1940                 impl,
1941                 expl,
1942                 default,
1943                 optional,
1944                 _decoded,
1945             ) = d.draw(octet_string_values_strategy(do_expl=impl_initial is None))
1946             if (default is None) and (obj_initial.default is not None):
1947                 bounds = None
1948             if (
1949                     (bounds is None) and
1950                     (value is not None) and
1951                     (bounds_initial is not None) and
1952                     not (bounds_initial[0] <= len(value) <= bounds_initial[1])
1953             ):
1954                 value = None
1955             if (
1956                     (bounds is None) and
1957                     (default is not None) and
1958                     (bounds_initial is not None) and
1959                     not (bounds_initial[0] <= len(default) <= bounds_initial[1])
1960             ):
1961                 default = None
1962             obj = obj_initial(value, bounds, impl, expl, default, optional)
1963             if obj.ready:
1964                 value_expected = default if value is None else value
1965                 value_expected = (
1966                     default_initial if value_expected is None
1967                     else value_expected
1968                 )
1969                 self.assertEqual(obj, value_expected)
1970             self.assertEqual(obj.tag, impl or impl_initial or obj.tag_default)
1971             self.assertEqual(obj.expl_tag, expl or expl_initial)
1972             self.assertEqual(
1973                 obj.default,
1974                 default_initial if default is None else default,
1975             )
1976             if obj.default is None:
1977                 optional = optional_initial if optional is None else optional
1978                 optional = False if optional is None else optional
1979             else:
1980                 optional = True
1981             self.assertEqual(obj.optional, optional)
1982             self.assertEqual(
1983                 (obj._bound_min, obj._bound_max),
1984                 bounds or bounds_initial or (0, float("+inf")),
1985             )
1986
1987     @given(octet_string_values_strategy())
1988     def test_copy(self, values):
1989         for klass in (OctetString, OctetStringInherited):
1990             obj = klass(*values)
1991             for copy_func in copy_funcs:
1992                 obj_copied = copy_func(obj)
1993                 self.assert_copied_basic_fields(obj, obj_copied)
1994                 self.assertEqual(obj._bound_min, obj_copied._bound_min)
1995                 self.assertEqual(obj._bound_max, obj_copied._bound_max)
1996                 self.assertEqual(obj._value, obj_copied._value)
1997
1998     @given(
1999         binary(),
2000         integers(min_value=1).map(tag_encode),
2001     )
2002     def test_stripped(self, value, tag_impl):
2003         obj = OctetString(value, impl=tag_impl)
2004         with self.assertRaises(NotEnoughData):
2005             obj.decode(obj.encode()[:-1])
2006
2007     @given(
2008         binary(),
2009         integers(min_value=1).map(tag_ctxc),
2010     )
2011     def test_stripped_expl(self, value, tag_expl):
2012         obj = OctetString(value, expl=tag_expl)
2013         with self.assertRaises(NotEnoughData):
2014             obj.decode(obj.encode()[:-1])
2015
2016     @given(
2017         integers(min_value=31),
2018         integers(min_value=0),
2019         decode_path_strat,
2020     )
2021     def test_bad_tag(self, tag, offset, decode_path):
2022         with self.assertRaises(DecodeError) as err:
2023             OctetString().decode(
2024                 tag_encode(tag)[:-1],
2025                 offset=offset,
2026                 decode_path=decode_path,
2027             )
2028         repr(err.exception)
2029         self.assertEqual(err.exception.offset, offset)
2030         self.assertEqual(err.exception.decode_path, decode_path)
2031
2032     @given(
2033         integers(min_value=128),
2034         integers(min_value=0),
2035         decode_path_strat,
2036     )
2037     def test_bad_len(self, l, offset, decode_path):
2038         with self.assertRaises(DecodeError) as err:
2039             OctetString().decode(
2040                 OctetString.tag_default + len_encode(l)[:-1],
2041                 offset=offset,
2042                 decode_path=decode_path,
2043             )
2044         repr(err.exception)
2045         self.assertEqual(err.exception.offset, offset)
2046         self.assertEqual(err.exception.decode_path, decode_path)
2047
2048     @given(
2049         sets(integers(min_value=0, max_value=10), min_size=2, max_size=2),
2050         integers(min_value=0),
2051         decode_path_strat,
2052     )
2053     def test_invalid_bounds_while_decoding(self, ints, offset, decode_path):
2054         value, bound_min = list(sorted(ints))
2055
2056         class String(OctetString):
2057             bounds = (bound_min, bound_min)
2058         with self.assertRaises(DecodeError) as err:
2059             String().decode(
2060                 OctetString(b"\x00" * value).encode(),
2061                 offset=offset,
2062                 decode_path=decode_path,
2063             )
2064         repr(err.exception)
2065         self.assertEqual(err.exception.offset, offset)
2066         self.assertEqual(err.exception.decode_path, decode_path)
2067
2068     @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
2069     @given(
2070         octet_string_values_strategy(),
2071         binary(),
2072         integers(min_value=1).map(tag_ctxc),
2073         integers(min_value=0),
2074         binary(max_size=5),
2075     )
2076     def test_symmetric(self, values, value, tag_expl, offset, tail_junk):
2077         for klass in (OctetString, OctetStringInherited):
2078             _, _, _, _, default, optional, _decoded = values
2079             obj = klass(
2080                 value=value,
2081                 default=default,
2082                 optional=optional,
2083                 _decoded=_decoded,
2084             )
2085             repr(obj)
2086             list(obj.pps())
2087             pprint(obj, big_blobs=True, with_decode_path=True)
2088             self.assertFalse(obj.expled)
2089             obj_encoded = obj.encode()
2090             obj_expled = obj(value, expl=tag_expl)
2091             self.assertTrue(obj_expled.expled)
2092             repr(obj_expled)
2093             list(obj_expled.pps())
2094             pprint(obj_expled, big_blobs=True, with_decode_path=True)
2095             obj_expled_encoded = obj_expled.encode()
2096             ctx_copied = deepcopy(ctx_dummy)
2097             obj_decoded, tail = obj_expled.decode(
2098                 obj_expled_encoded + tail_junk,
2099                 offset=offset,
2100                 ctx=ctx_copied,
2101             )
2102             self.assertDictEqual(ctx_copied, ctx_dummy)
2103             repr(obj_decoded)
2104             list(obj_decoded.pps())
2105             pprint(obj_decoded, big_blobs=True, with_decode_path=True)
2106             self.assertEqual(tail, tail_junk)
2107             self.assertEqual(obj_decoded, obj_expled)
2108             self.assertNotEqual(obj_decoded, obj)
2109             self.assertEqual(bytes(obj_decoded), bytes(obj_expled))
2110             self.assertEqual(bytes(obj_decoded), bytes(obj))
2111             self.assertSequenceEqual(obj_decoded.encode(), obj_expled_encoded)
2112             self.assertSequenceEqual(obj_decoded.expl_tag, tag_expl)
2113             self.assertEqual(obj_decoded.expl_tlen, len(tag_expl))
2114             self.assertEqual(
2115                 obj_decoded.expl_llen,
2116                 len(len_encode(len(obj_encoded))),
2117             )
2118             self.assertEqual(obj_decoded.tlvlen, len(obj_encoded))
2119             self.assertEqual(obj_decoded.expl_vlen, len(obj_encoded))
2120             self.assertEqual(
2121                 obj_decoded.offset,
2122                 offset + obj_decoded.expl_tlen + obj_decoded.expl_llen,
2123             )
2124             self.assertEqual(obj_decoded.expl_offset, offset)
2125             assert_exceeding_data(
2126                 self,
2127                 lambda: obj_expled.decod(obj_expled_encoded + tail_junk),
2128                 tail_junk,
2129             )
2130
2131     @given(
2132         integers(min_value=1, max_value=30),
2133         lists(
2134             one_of(
2135                 binary(min_size=1, max_size=5),
2136                 lists(
2137                     binary(min_size=1, max_size=5),
2138                     min_size=1,
2139                     max_size=3,
2140                 ),
2141             ),
2142             min_size=1,
2143             max_size=3,
2144         ),
2145         binary(),
2146     )
2147     def test_constructed(self, impl, chunk_inputs, junk):
2148         def chunk_constructed(contents):
2149             return (
2150                 tag_encode(form=TagFormConstructed, num=4) +
2151                 LENINDEF +
2152                 b"".join(OctetString(content).encode() for content in contents) +
2153                 EOC
2154             )
2155         chunks = []
2156         payload_expected = b""
2157         for chunk_input in chunk_inputs:
2158             if isinstance(chunk_input, binary_type):
2159                 chunks.append(OctetString(chunk_input).encode())
2160                 payload_expected += chunk_input
2161             else:
2162                 chunks.append(chunk_constructed(chunk_input))
2163                 payload = b"".join(chunk_input)
2164                 payload_expected += payload
2165         encoded_indefinite = (
2166             tag_encode(form=TagFormConstructed, num=impl) +
2167             LENINDEF +
2168             b"".join(chunks) +
2169             EOC
2170         )
2171         encoded_definite = (
2172             tag_encode(form=TagFormConstructed, num=impl) +
2173             len_encode(len(b"".join(chunks))) +
2174             b"".join(chunks)
2175         )
2176         with assertRaisesRegex(self, DecodeError, "unallowed BER"):
2177             OctetString(impl=tag_encode(impl)).decode(encoded_indefinite)
2178         for lenindef_expected, encoded in (
2179                 (True, encoded_indefinite),
2180                 (False, encoded_definite),
2181         ):
2182             obj, tail = OctetString(impl=tag_encode(impl)).decode(
2183                 encoded + junk,
2184                 ctx={"bered": True},
2185             )
2186             self.assertSequenceEqual(tail, junk)
2187             self.assertSequenceEqual(bytes(obj), payload_expected)
2188             self.assertTrue(obj.ber_encoded)
2189             self.assertEqual(obj.lenindef, lenindef_expected)
2190             self.assertTrue(obj.bered)
2191             obj = copy(obj)
2192             self.assertTrue(obj.ber_encoded)
2193             self.assertEqual(obj.lenindef, lenindef_expected)
2194             self.assertTrue(obj.bered)
2195             self.assertEqual(len(encoded), obj.tlvlen)
2196             repr(obj)
2197             list(obj.pps())
2198             pprint(obj, big_blobs=True, with_decode_path=True)
2199
2200     @given(
2201         integers(min_value=0),
2202         decode_path_strat,
2203     )
2204     def test_ber_definite_too_short(self, offset, decode_path):
2205         with assertRaisesRegex(self, DecodeError, "longer than data") as err:
2206             OctetString().decode(
2207                 tag_encode(4, form=TagFormConstructed) + len_encode(1),
2208                 offset=offset,
2209                 decode_path=decode_path,
2210                 ctx={"bered": True},
2211             )
2212         self.assertEqual(err.exception.decode_path, decode_path)
2213         self.assertEqual(err.exception.offset, offset)
2214
2215     @given(
2216         integers(min_value=0),
2217         decode_path_strat,
2218         integers(min_value=1, max_value=3),
2219     )
2220     def test_ber_indefinite_no_eoc(self, offset, decode_path, chunks):
2221         bs = OctetString(b"data").encode()
2222         with self.assertRaises(NotEnoughData) as err:
2223             OctetString().decode(
2224                 tag_encode(4, form=TagFormConstructed) + LENINDEF + chunks * bs,
2225                 offset=offset,
2226                 decode_path=decode_path,
2227                 ctx={"bered": True},
2228             )
2229         self.assertEqual(err.exception.decode_path, decode_path + (str(chunks),))
2230         self.assertEqual(err.exception.offset, offset + 1 + 1 + chunks * len(bs))
2231
2232     @given(
2233         integers(min_value=0),
2234         decode_path_strat,
2235         integers(min_value=1, max_value=3),
2236     )
2237     def test_ber_definite_chunk_out_of_bounds(self, offset, decode_path, chunks):
2238         bs = OctetString(b"data").encode()
2239         bs_longer = OctetString(b"data-longer").encode()
2240         with assertRaisesRegex(self, DecodeError, "chunk out of bounds") as err:
2241             OctetString().decode(
2242                 (
2243                     tag_encode(4, form=TagFormConstructed) +
2244                     len_encode((chunks + 1) * len(bs)) +
2245                     chunks * bs +
2246                     bs_longer
2247                 ),
2248                 offset=offset,
2249                 decode_path=decode_path,
2250                 ctx={"bered": True},
2251             )
2252         self.assertEqual(err.exception.decode_path, decode_path + (str(chunks),))
2253         self.assertEqual(err.exception.offset, offset + 1 + 1 + chunks * len(bs))
2254
2255
2256 @composite
2257 def null_values_strategy(draw, do_expl=False):
2258     impl = None
2259     expl = None
2260     if do_expl:
2261         expl = draw(one_of(none(), integers(min_value=1).map(tag_encode)))
2262     else:
2263         impl = draw(one_of(none(), integers(min_value=1).map(tag_encode)))
2264     optional = draw(one_of(none(), booleans()))
2265     _decoded = (
2266         draw(integers(min_value=0)),
2267         draw(integers(min_value=0)),
2268         draw(integers(min_value=0)),
2269     )
2270     return (impl, expl, optional, _decoded)
2271
2272
2273 class NullInherited(Null):
2274     pass
2275
2276
2277 class TestNull(CommonMixin, TestCase):
2278     base_klass = Null
2279
2280     def test_ready(self):
2281         obj = Null()
2282         self.assertTrue(obj.ready)
2283         repr(obj)
2284         list(obj.pps())
2285         pprint(obj, big_blobs=True, with_decode_path=True)
2286
2287     @given(binary(min_size=1), binary(min_size=1))
2288     def test_comparison(self, tag1, tag2):
2289         for klass in (Null, NullInherited):
2290             obj1 = klass(impl=tag1)
2291             obj2 = klass(impl=tag2)
2292             self.assertEqual(obj1 == obj2, tag1 == tag2)
2293             self.assertEqual(obj1 != obj2, tag1 != tag2)
2294             self.assertNotEqual(obj1, tag2)
2295
2296     @given(data_strategy())
2297     def test_call(self, d):
2298         for klass in (Null, NullInherited):
2299             (
2300                 impl_initial,
2301                 expl_initial,
2302                 optional_initial,
2303                 _decoded_initial,
2304             ) = d.draw(null_values_strategy())
2305             obj_initial = klass(
2306                 impl=impl_initial,
2307                 expl=expl_initial,
2308                 optional=optional_initial or False,
2309                 _decoded=_decoded_initial,
2310             )
2311             (
2312                 impl,
2313                 expl,
2314                 optional,
2315                 _decoded,
2316             ) = d.draw(null_values_strategy(do_expl=impl_initial is None))
2317             obj = obj_initial(impl=impl, expl=expl, optional=optional)
2318             self.assertEqual(obj.tag, impl or impl_initial or obj.tag_default)
2319             self.assertEqual(obj.expl_tag, expl or expl_initial)
2320             optional = optional_initial if optional is None else optional
2321             optional = False if optional is None else optional
2322             self.assertEqual(obj.optional, optional)
2323
2324     @given(null_values_strategy())
2325     def test_copy(self, values):
2326         for klass in (Null, NullInherited):
2327             impl, expl, optional, _decoded = values
2328             obj = klass(
2329                 impl=impl,
2330                 expl=expl,
2331                 optional=optional or False,
2332                 _decoded=_decoded,
2333             )
2334             for copy_func in copy_funcs:
2335                 obj_copied = copy_func(obj)
2336                 self.assert_copied_basic_fields(obj, obj_copied)
2337
2338     @given(integers(min_value=1).map(tag_encode))
2339     def test_stripped(self, tag_impl):
2340         obj = Null(impl=tag_impl)
2341         with self.assertRaises(NotEnoughData):
2342             obj.decode(obj.encode()[:-1])
2343
2344     @given(integers(min_value=1).map(tag_ctxc))
2345     def test_stripped_expl(self, tag_expl):
2346         obj = Null(expl=tag_expl)
2347         with self.assertRaises(NotEnoughData):
2348             obj.decode(obj.encode()[:-1])
2349
2350     @given(
2351         integers(min_value=31),
2352         integers(min_value=0),
2353         decode_path_strat,
2354     )
2355     def test_bad_tag(self, tag, offset, decode_path):
2356         with self.assertRaises(DecodeError) as err:
2357             Null().decode(
2358                 tag_encode(tag)[:-1],
2359                 offset=offset,
2360                 decode_path=decode_path,
2361             )
2362         repr(err.exception)
2363         self.assertEqual(err.exception.offset, offset)
2364         self.assertEqual(err.exception.decode_path, decode_path)
2365
2366     @given(
2367         integers(min_value=128),
2368         integers(min_value=0),
2369         decode_path_strat,
2370     )
2371     def test_bad_len(self, l, offset, decode_path):
2372         with self.assertRaises(DecodeError) as err:
2373             Null().decode(
2374                 Null.tag_default + len_encode(l)[:-1],
2375                 offset=offset,
2376                 decode_path=decode_path,
2377             )
2378         repr(err.exception)
2379         self.assertEqual(err.exception.offset, offset)
2380         self.assertEqual(err.exception.decode_path, decode_path)
2381
2382     @given(binary(min_size=1))
2383     def test_tag_mismatch(self, impl):
2384         assume(impl != Null.tag_default)
2385         with self.assertRaises(TagMismatch):
2386             Null(impl=impl).decode(Null().encode())
2387
2388     @given(
2389         null_values_strategy(),
2390         integers(min_value=1).map(tag_ctxc),
2391         integers(min_value=0),
2392         binary(max_size=5),
2393     )
2394     def test_symmetric(self, values, tag_expl, offset, tail_junk):
2395         for klass in (Null, NullInherited):
2396             _, _, optional, _decoded = values
2397             obj = klass(optional=optional, _decoded=_decoded)
2398             repr(obj)
2399             list(obj.pps())
2400             pprint(obj, big_blobs=True, with_decode_path=True)
2401             self.assertFalse(obj.expled)
2402             obj_encoded = obj.encode()
2403             obj_expled = obj(expl=tag_expl)
2404             self.assertTrue(obj_expled.expled)
2405             repr(obj_expled)
2406             list(obj_expled.pps())
2407             pprint(obj_expled, big_blobs=True, with_decode_path=True)
2408             obj_expled_encoded = obj_expled.encode()
2409             ctx_copied = deepcopy(ctx_dummy)
2410             obj_decoded, tail = obj_expled.decode(
2411                 obj_expled_encoded + tail_junk,
2412                 offset=offset,
2413                 ctx=ctx_copied,
2414             )
2415             self.assertDictEqual(ctx_copied, ctx_dummy)
2416             repr(obj_decoded)
2417             list(obj_decoded.pps())
2418             pprint(obj_decoded, big_blobs=True, with_decode_path=True)
2419             self.assertEqual(tail, tail_junk)
2420             self.assertEqual(obj_decoded, obj_expled)
2421             self.assertNotEqual(obj_decoded, obj)
2422             self.assertSequenceEqual(obj_decoded.encode(), obj_expled_encoded)
2423             self.assertSequenceEqual(obj_decoded.expl_tag, tag_expl)
2424             self.assertEqual(obj_decoded.expl_tlen, len(tag_expl))
2425             self.assertEqual(
2426                 obj_decoded.expl_llen,
2427                 len(len_encode(len(obj_encoded))),
2428             )
2429             self.assertEqual(obj_decoded.tlvlen, len(obj_encoded))
2430             self.assertEqual(obj_decoded.expl_vlen, len(obj_encoded))
2431             self.assertEqual(
2432                 obj_decoded.offset,
2433                 offset + obj_decoded.expl_tlen + obj_decoded.expl_llen,
2434             )
2435             self.assertEqual(obj_decoded.expl_offset, offset)
2436             assert_exceeding_data(
2437                 self,
2438                 lambda: obj_expled.decod(obj_expled_encoded + tail_junk),
2439                 tail_junk,
2440             )
2441
2442     @given(integers(min_value=1))
2443     def test_invalid_len(self, l):
2444         with self.assertRaises(InvalidLength):
2445             Null().decode(b"".join((
2446                 Null.tag_default,
2447                 len_encode(l),
2448             )))
2449
2450
2451 @composite
2452 def oid_strategy(draw):
2453     first_arc = draw(integers(min_value=0, max_value=2))
2454     second_arc = 0
2455     if first_arc in (0, 1):
2456         second_arc = draw(integers(min_value=0, max_value=39))
2457     else:
2458         second_arc = draw(integers(min_value=0))
2459     other_arcs = draw(lists(integers(min_value=0)))
2460     return tuple([first_arc, second_arc] + other_arcs)
2461
2462
2463 @composite
2464 def oid_values_strategy(draw, do_expl=False):
2465     value = draw(one_of(none(), oid_strategy()))
2466     impl = None
2467     expl = None
2468     if do_expl:
2469         expl = draw(one_of(none(), integers(min_value=1).map(tag_encode)))
2470     else:
2471         impl = draw(one_of(none(), integers(min_value=1).map(tag_encode)))
2472     default = draw(one_of(none(), oid_strategy()))
2473     optional = draw(one_of(none(), booleans()))
2474     _decoded = (
2475         draw(integers(min_value=0)),
2476         draw(integers(min_value=0)),
2477         draw(integers(min_value=0)),
2478     )
2479     return (value, impl, expl, default, optional, _decoded)
2480
2481
2482 class ObjectIdentifierInherited(ObjectIdentifier):
2483     pass
2484
2485
2486 class TestObjectIdentifier(CommonMixin, TestCase):
2487     base_klass = ObjectIdentifier
2488
2489     def test_invalid_value_type(self):
2490         with self.assertRaises(InvalidValueType) as err:
2491             ObjectIdentifier(123)
2492         repr(err.exception)
2493
2494     @given(booleans())
2495     def test_optional(self, optional):
2496         obj = ObjectIdentifier(default=ObjectIdentifier("1.2.3"), optional=optional)
2497         self.assertTrue(obj.optional)
2498
2499     @given(oid_strategy())
2500     def test_ready(self, value):
2501         obj = ObjectIdentifier()
2502         self.assertFalse(obj.ready)
2503         repr(obj)
2504         list(obj.pps())
2505         pprint(obj, big_blobs=True, with_decode_path=True)
2506         with self.assertRaises(ObjNotReady) as err:
2507             obj.encode()
2508         repr(err.exception)
2509         obj = ObjectIdentifier(value)
2510         self.assertTrue(obj.ready)
2511         self.assertFalse(obj.ber_encoded)
2512         repr(obj)
2513         list(obj.pps())
2514         pprint(obj, big_blobs=True, with_decode_path=True)
2515         hash(obj)
2516
2517     @given(oid_strategy(), oid_strategy(), binary(min_size=1), binary(min_size=1))
2518     def test_comparison(self, value1, value2, tag1, tag2):
2519         for klass in (ObjectIdentifier, ObjectIdentifierInherited):
2520             obj1 = klass(value1)
2521             obj2 = klass(value2)
2522             self.assertEqual(obj1 == obj2, value1 == value2)
2523             self.assertEqual(obj1 != obj2, value1 != value2)
2524             self.assertEqual(obj1 == tuple(obj2), value1 == value2)
2525             self.assertEqual(str(obj1) == str(obj2), value1 == value2)
2526             obj1 = klass(value1, impl=tag1)
2527             obj2 = klass(value1, impl=tag2)
2528             self.assertEqual(obj1 == obj2, tag1 == tag2)
2529             self.assertEqual(obj1 != obj2, tag1 != tag2)
2530
2531     @given(lists(oid_strategy()))
2532     def test_sorted_works(self, values):
2533         self.assertSequenceEqual(
2534             [tuple(v) for v in sorted(ObjectIdentifier(v) for v in values)],
2535             sorted(values),
2536         )
2537
2538     @given(data_strategy())
2539     def test_call(self, d):
2540         for klass in (ObjectIdentifier, ObjectIdentifierInherited):
2541             (
2542                 value_initial,
2543                 impl_initial,
2544                 expl_initial,
2545                 default_initial,
2546                 optional_initial,
2547                 _decoded_initial,
2548             ) = d.draw(oid_values_strategy())
2549             obj_initial = klass(
2550                 value=value_initial,
2551                 impl=impl_initial,
2552                 expl=expl_initial,
2553                 default=default_initial,
2554                 optional=optional_initial or False,
2555                 _decoded=_decoded_initial,
2556             )
2557             (
2558                 value,
2559                 impl,
2560                 expl,
2561                 default,
2562                 optional,
2563                 _decoded,
2564             ) = d.draw(oid_values_strategy(do_expl=impl_initial is None))
2565             obj = obj_initial(
2566                 value=value,
2567                 impl=impl,
2568                 expl=expl,
2569                 default=default,
2570                 optional=optional,
2571             )
2572             if obj.ready:
2573                 value_expected = default if value is None else value
2574                 value_expected = (
2575                     default_initial if value_expected is None
2576                     else value_expected
2577                 )
2578                 self.assertEqual(obj, value_expected)
2579             self.assertEqual(obj.tag, impl or impl_initial or obj.tag_default)
2580             self.assertEqual(obj.expl_tag, expl or expl_initial)
2581             self.assertEqual(
2582                 obj.default,
2583                 default_initial if default is None else default,
2584             )
2585             if obj.default is None:
2586                 optional = optional_initial if optional is None else optional
2587                 optional = False if optional is None else optional
2588             else:
2589                 optional = True
2590             self.assertEqual(obj.optional, optional)
2591
2592     @given(oid_values_strategy())
2593     def test_copy(self, values):
2594         for klass in (ObjectIdentifier, ObjectIdentifierInherited):
2595             (
2596                 value,
2597                 impl,
2598                 expl,
2599                 default,
2600                 optional,
2601                 _decoded,
2602             ) = values
2603             obj = klass(
2604                 value=value,
2605                 impl=impl,
2606                 expl=expl,
2607                 default=default,
2608                 optional=optional,
2609                 _decoded=_decoded,
2610             )
2611             for copy_func in copy_funcs:
2612                 obj_copied = copy_func(obj)
2613                 self.assert_copied_basic_fields(obj, obj_copied)
2614                 self.assertEqual(obj._value, obj_copied._value)
2615
2616     @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
2617     @given(
2618         oid_strategy(),
2619         integers(min_value=1).map(tag_encode),
2620     )
2621     def test_stripped(self, value, tag_impl):
2622         obj = ObjectIdentifier(value, impl=tag_impl)
2623         with self.assertRaises(NotEnoughData):
2624             obj.decode(obj.encode()[:-1])
2625
2626     @given(
2627         oid_strategy(),
2628         integers(min_value=1).map(tag_ctxc),
2629     )
2630     def test_stripped_expl(self, value, tag_expl):
2631         obj = ObjectIdentifier(value, expl=tag_expl)
2632         with self.assertRaises(NotEnoughData):
2633             obj.decode(obj.encode()[:-1])
2634
2635     @given(
2636         integers(min_value=31),
2637         integers(min_value=0),
2638         decode_path_strat,
2639     )
2640     def test_bad_tag(self, tag, offset, decode_path):
2641         with self.assertRaises(DecodeError) as err:
2642             ObjectIdentifier().decode(
2643                 tag_encode(tag)[:-1],
2644                 offset=offset,
2645                 decode_path=decode_path,
2646             )
2647         repr(err.exception)
2648         self.assertEqual(err.exception.offset, offset)
2649         self.assertEqual(err.exception.decode_path, decode_path)
2650
2651     @given(
2652         integers(min_value=128),
2653         integers(min_value=0),
2654         decode_path_strat,
2655     )
2656     def test_bad_len(self, l, offset, decode_path):
2657         with self.assertRaises(DecodeError) as err:
2658             ObjectIdentifier().decode(
2659                 ObjectIdentifier.tag_default + len_encode(l)[:-1],
2660                 offset=offset,
2661                 decode_path=decode_path,
2662             )
2663         repr(err.exception)
2664         self.assertEqual(err.exception.offset, offset)
2665         self.assertEqual(err.exception.decode_path, decode_path)
2666
2667     def test_zero_oid(self):
2668         with self.assertRaises(NotEnoughData):
2669             ObjectIdentifier().decode(
2670                 b"".join((ObjectIdentifier.tag_default, len_encode(0)))
2671             )
2672
2673     @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
2674     @given(oid_strategy())
2675     def test_unfinished_oid(self, value):
2676         assume(list(value)[-1] > 255)
2677         obj_encoded = ObjectIdentifier(value).encode()
2678         obj, _ = ObjectIdentifier().decode(obj_encoded)
2679         data = obj_encoded[obj.tlen + obj.llen:-1]
2680         data = b"".join((
2681             ObjectIdentifier.tag_default,
2682             len_encode(len(data)),
2683             data,
2684         ))
2685         with assertRaisesRegex(self, DecodeError, "unfinished OID"):
2686             obj.decode(data)
2687
2688     @given(integers(min_value=0))
2689     def test_invalid_short(self, value):
2690         with self.assertRaises(InvalidOID):
2691             ObjectIdentifier((value,))
2692         with self.assertRaises(InvalidOID):
2693             ObjectIdentifier("%d" % value)
2694
2695     @given(integers(min_value=3), integers(min_value=0))
2696     def test_invalid_first_arc(self, first_arc, second_arc):
2697         with self.assertRaises(InvalidOID):
2698             ObjectIdentifier((first_arc, second_arc))
2699         with self.assertRaises(InvalidOID):
2700             ObjectIdentifier("%d.%d" % (first_arc, second_arc))
2701
2702     @given(integers(min_value=0, max_value=1), integers(min_value=40))
2703     def test_invalid_second_arc(self, first_arc, second_arc):
2704         with self.assertRaises(InvalidOID):
2705             ObjectIdentifier((first_arc, second_arc))
2706         with self.assertRaises(InvalidOID):
2707             ObjectIdentifier("%d.%d" % (first_arc, second_arc))
2708
2709     @given(text(alphabet=ascii_letters + ".", min_size=1))
2710     def test_junk(self, oid):
2711         with self.assertRaises(InvalidOID):
2712             ObjectIdentifier(oid)
2713
2714     @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
2715     @given(oid_strategy())
2716     def test_validness(self, oid):
2717         obj = ObjectIdentifier(oid)
2718         self.assertEqual(obj, ObjectIdentifier(".".join(str(arc) for arc in oid)))
2719         str(obj)
2720         repr(obj)
2721         pprint(obj, big_blobs=True, with_decode_path=True)
2722
2723     @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
2724     @given(
2725         oid_values_strategy(),
2726         oid_strategy(),
2727         integers(min_value=1).map(tag_ctxc),
2728         integers(min_value=0),
2729         binary(max_size=5),
2730     )
2731     def test_symmetric(self, values, value, tag_expl, offset, tail_junk):
2732         for klass in (ObjectIdentifier, ObjectIdentifierInherited):
2733             _, _, _, default, optional, _decoded = values
2734             obj = klass(
2735                 value=value,
2736                 default=default,
2737                 optional=optional,
2738                 _decoded=_decoded,
2739             )
2740             repr(obj)
2741             list(obj.pps())
2742             pprint(obj, big_blobs=True, with_decode_path=True)
2743             self.assertFalse(obj.expled)
2744             obj_encoded = obj.encode()
2745             obj_expled = obj(value, expl=tag_expl)
2746             self.assertTrue(obj_expled.expled)
2747             repr(obj_expled)
2748             list(obj_expled.pps())
2749             pprint(obj_expled, big_blobs=True, with_decode_path=True)
2750             obj_expled_encoded = obj_expled.encode()
2751             ctx_copied = deepcopy(ctx_dummy)
2752             obj_decoded, tail = obj_expled.decode(
2753                 obj_expled_encoded + tail_junk,
2754                 offset=offset,
2755                 ctx=ctx_copied,
2756             )
2757             self.assertDictEqual(ctx_copied, ctx_dummy)
2758             repr(obj_decoded)
2759             list(obj_decoded.pps())
2760             pprint(obj_decoded, big_blobs=True, with_decode_path=True)
2761             self.assertEqual(tail, tail_junk)
2762             self.assertEqual(obj_decoded, obj_expled)
2763             self.assertNotEqual(obj_decoded, obj)
2764             self.assertEqual(tuple(obj_decoded), tuple(obj_expled))
2765             self.assertEqual(tuple(obj_decoded), tuple(obj))
2766             self.assertSequenceEqual(obj_decoded.encode(), obj_expled_encoded)
2767             self.assertSequenceEqual(obj_decoded.expl_tag, tag_expl)
2768             self.assertEqual(obj_decoded.expl_tlen, len(tag_expl))
2769             self.assertEqual(
2770                 obj_decoded.expl_llen,
2771                 len(len_encode(len(obj_encoded))),
2772             )
2773             self.assertEqual(obj_decoded.tlvlen, len(obj_encoded))
2774             self.assertEqual(obj_decoded.expl_vlen, len(obj_encoded))
2775             self.assertEqual(
2776                 obj_decoded.offset,
2777                 offset + obj_decoded.expl_tlen + obj_decoded.expl_llen,
2778             )
2779             self.assertEqual(obj_decoded.expl_offset, offset)
2780             assert_exceeding_data(
2781                 self,
2782                 lambda: obj_expled.decod(obj_expled_encoded + tail_junk),
2783                 tail_junk,
2784             )
2785
2786     @given(
2787         oid_strategy().map(ObjectIdentifier),
2788         oid_strategy().map(ObjectIdentifier),
2789     )
2790     def test_add(self, oid1, oid2):
2791         oid_expect = ObjectIdentifier(str(oid1) + "." + str(oid2))
2792         for oid_to_add in (oid2, tuple(oid2)):
2793             self.assertEqual(oid1 + oid_to_add, oid_expect)
2794         with self.assertRaises(InvalidValueType):
2795             oid1 + str(oid2)
2796
2797     def test_go_vectors_valid(self):
2798         for data, expect in (
2799                 (b"\x55", (2, 5)),
2800                 (b"\x55\x02", (2, 5, 2)),
2801                 (b"\x55\x02\xc0\x00", (2, 5, 2, 8192)),
2802                 (b"\x81\x34\x03", (2, 100, 3)),
2803         ):
2804             self.assertEqual(
2805                 ObjectIdentifier().decode(b"".join((
2806                     ObjectIdentifier.tag_default,
2807                     len_encode(len(data)),
2808                     data,
2809                 )))[0],
2810                 expect,
2811             )
2812
2813     def test_go_vectors_invalid(self):
2814         data = b"\x55\x02\xc0\x80\x80\x80\x80"
2815         with self.assertRaises(DecodeError):
2816             ObjectIdentifier().decode(b"".join((
2817                 Integer.tag_default,
2818                 len_encode(len(data)),
2819                 data,
2820             )))
2821
2822     def test_x690_vector(self):
2823         self.assertEqual(
2824             ObjectIdentifier().decode(hexdec("0603883703"))[0],
2825             ObjectIdentifier((2, 999, 3)),
2826         )
2827
2828     def test_nonnormalized_first_arc(self):
2829         tampered = (
2830             ObjectIdentifier.tag_default +
2831             len_encode(2) +
2832             b'\x80' +
2833             ObjectIdentifier((1, 0)).encode()[-1:]
2834         )
2835         obj, _ = ObjectIdentifier().decode(tampered, ctx={"bered": True})
2836         self.assertTrue(obj.ber_encoded)
2837         self.assertTrue(obj.bered)
2838         obj = copy(obj)
2839         self.assertTrue(obj.ber_encoded)
2840         self.assertTrue(obj.bered)
2841         with assertRaisesRegex(self, DecodeError, "non normalized arc encoding"):
2842             ObjectIdentifier().decode(tampered)
2843
2844     @given(data_strategy())
2845     def test_negative_arcs(self, d):
2846         oid = list(d.draw(oid_strategy()))
2847         if len(oid) == 2:
2848             return
2849         idx = d.draw(integers(min_value=3, max_value=len(oid)))
2850         oid[idx - 1] *= -1
2851         if oid[idx - 1] == 0:
2852             oid[idx - 1] = -1
2853         with self.assertRaises(InvalidOID):
2854             ObjectIdentifier(tuple(oid))
2855         with self.assertRaises(InvalidOID):
2856             ObjectIdentifier(".".join(str(i) for i in oid))
2857
2858     @given(data_strategy())
2859     def test_plused_arcs(self, d):
2860         oid = [str(arc) for arc in d.draw(oid_strategy())]
2861         idx = d.draw(integers(min_value=0, max_value=len(oid)))
2862         oid[idx - 1] = "+" + oid[idx - 1]
2863         with self.assertRaises(InvalidOID):
2864             ObjectIdentifier(".".join(str(i) for i in oid))
2865
2866     @given(data_strategy())
2867     def test_nonnormalized_arcs(self, d):
2868         arcs = d.draw(lists(
2869             integers(min_value=0, max_value=100),
2870             min_size=1,
2871             max_size=5,
2872         ))
2873         dered = ObjectIdentifier((1, 0) + tuple(arcs)).encode()
2874         _, _, lv = tag_strip(dered)
2875         _, _, v = len_decode(lv)
2876         v_no_first_arc = v[1:]
2877         idx_for_tamper = d.draw(integers(
2878             min_value=0,
2879             max_value=len(v_no_first_arc) - 1,
2880         ))
2881         tampered = list(bytearray(v_no_first_arc))
2882         for _ in range(d.draw(integers(min_value=1, max_value=3))):
2883             tampered.insert(idx_for_tamper, 0x80)
2884         tampered = bytes(bytearray(tampered))
2885         tampered = (
2886             ObjectIdentifier.tag_default +
2887             len_encode(len(tampered)) +
2888             tampered
2889         )
2890         obj, _ = ObjectIdentifier().decode(tampered, ctx={"bered": True})
2891         self.assertTrue(obj.ber_encoded)
2892         self.assertTrue(obj.bered)
2893         obj = copy(obj)
2894         self.assertTrue(obj.ber_encoded)
2895         self.assertTrue(obj.bered)
2896         with assertRaisesRegex(self, DecodeError, "non normalized arc encoding"):
2897             ObjectIdentifier().decode(tampered)
2898
2899
2900 @composite
2901 def enumerated_values_strategy(draw, schema=None, do_expl=False):
2902     if schema is None:
2903         schema = list(draw(sets(text_printable, min_size=1, max_size=3)))
2904         values = list(draw(sets(
2905             integers(),
2906             min_size=len(schema),
2907             max_size=len(schema),
2908         )))
2909         schema = list(zip(schema, values))
2910     value = draw(one_of(none(), sampled_from([k for k, v in schema])))
2911     impl = None
2912     expl = None
2913     if do_expl:
2914         expl = draw(one_of(none(), integers(min_value=1).map(tag_encode)))
2915     else:
2916         impl = draw(one_of(none(), integers(min_value=1).map(tag_encode)))
2917     default = draw(one_of(none(), sampled_from([v for k, v in schema])))
2918     optional = draw(one_of(none(), booleans()))
2919     _decoded = (
2920         draw(integers(min_value=0)),
2921         draw(integers(min_value=0)),
2922         draw(integers(min_value=0)),
2923     )
2924     return (schema, value, impl, expl, default, optional, _decoded)
2925
2926
2927 class TestEnumerated(CommonMixin, TestCase):
2928     class EWhatever(Enumerated):
2929         schema = (("whatever", 0),)
2930
2931     base_klass = EWhatever
2932
2933     def test_schema_required(self):
2934         with assertRaisesRegex(self, ValueError, "schema must be specified"):
2935             Enumerated()
2936
2937     def test_invalid_value_type(self):
2938         with self.assertRaises(InvalidValueType) as err:
2939             self.base_klass((1, 2))
2940         repr(err.exception)
2941
2942     @given(sets(text_letters(), min_size=2))
2943     def test_unknown_name(self, schema_input):
2944         missing = schema_input.pop()
2945
2946         class E(Enumerated):
2947             schema = [(n, 123) for n in schema_input]
2948         with self.assertRaises(ObjUnknown) as err:
2949             E(missing)
2950         repr(err.exception)
2951
2952     @given(
2953         sets(text_letters(), min_size=2),
2954         sets(integers(), min_size=2),
2955     )
2956     def test_unknown_value(self, schema_input, values_input):
2957         schema_input.pop()
2958         missing_value = values_input.pop()
2959         _input = list(zip(schema_input, values_input))
2960
2961         class E(Enumerated):
2962             schema = _input
2963         with self.assertRaises(DecodeError) as err:
2964             E(missing_value)
2965         repr(err.exception)
2966
2967     @given(booleans())
2968     def test_optional(self, optional):
2969         obj = self.base_klass(default="whatever", optional=optional)
2970         self.assertTrue(obj.optional)
2971
2972     def test_ready(self):
2973         obj = self.base_klass()
2974         self.assertFalse(obj.ready)
2975         repr(obj)
2976         list(obj.pps())
2977         pprint(obj, big_blobs=True, with_decode_path=True)
2978         with self.assertRaises(ObjNotReady) as err:
2979             obj.encode()
2980         repr(err.exception)
2981         obj = self.base_klass("whatever")
2982         self.assertTrue(obj.ready)
2983         repr(obj)
2984         list(obj.pps())
2985         pprint(obj, big_blobs=True, with_decode_path=True)
2986
2987     @given(integers(), integers(), binary(min_size=1), binary(min_size=1))
2988     def test_comparison(self, value1, value2, tag1, tag2):
2989         class E(Enumerated):
2990             schema = (
2991                 ("whatever0", value1),
2992                 ("whatever1", value2),
2993             )
2994
2995         class EInherited(E):
2996             pass
2997         for klass in (E, EInherited):
2998             obj1 = klass(value1)
2999             obj2 = klass(value2)
3000             self.assertEqual(obj1 == obj2, value1 == value2)
3001             self.assertEqual(obj1 != obj2, value1 != value2)
3002             self.assertEqual(obj1 == int(obj2), value1 == value2)
3003             obj1 = klass(value1, impl=tag1)
3004             obj2 = klass(value1, impl=tag2)
3005             self.assertEqual(obj1 == obj2, tag1 == tag2)
3006             self.assertEqual(obj1 != obj2, tag1 != tag2)
3007
3008     @given(data_strategy())
3009     def test_call(self, d):
3010         (
3011             schema_initial,
3012             value_initial,
3013             impl_initial,
3014             expl_initial,
3015             default_initial,
3016             optional_initial,
3017             _decoded_initial,
3018         ) = d.draw(enumerated_values_strategy())
3019
3020         class E(Enumerated):
3021             schema = schema_initial
3022         obj_initial = E(
3023             value=value_initial,
3024             impl=impl_initial,
3025             expl=expl_initial,
3026             default=default_initial,
3027             optional=optional_initial or False,
3028             _decoded=_decoded_initial,
3029         )
3030         (
3031             _,
3032             value,
3033             impl,
3034             expl,
3035             default,
3036             optional,
3037             _decoded,
3038         ) = d.draw(enumerated_values_strategy(
3039             schema=schema_initial,
3040             do_expl=impl_initial is None,
3041         ))
3042         obj = obj_initial(
3043             value=value,
3044             impl=impl,
3045             expl=expl,
3046             default=default,
3047             optional=optional,
3048         )
3049         if obj.ready:
3050             value_expected = default if value is None else value
3051             value_expected = (
3052                 default_initial if value_expected is None
3053                 else value_expected
3054             )
3055             self.assertEqual(
3056                 int(obj),
3057                 dict(schema_initial).get(value_expected, value_expected),
3058             )
3059         self.assertEqual(obj.tag, impl or impl_initial or obj.tag_default)
3060         self.assertEqual(obj.expl_tag, expl or expl_initial)
3061         self.assertEqual(
3062             obj.default,
3063             default_initial if default is None else default,
3064         )
3065         if obj.default is None:
3066             optional = optional_initial if optional is None else optional
3067             optional = False if optional is None else optional
3068         else:
3069             optional = True
3070         self.assertEqual(obj.optional, optional)
3071         self.assertEqual(obj.specs, dict(schema_initial))
3072
3073     @given(enumerated_values_strategy())
3074     def test_copy(self, values):
3075         schema_input, value, impl, expl, default, optional, _decoded = values
3076
3077         class E(Enumerated):
3078             schema = schema_input
3079         register_class(E)
3080         obj = E(
3081             value=value,
3082             impl=impl,
3083             expl=expl,
3084             default=default,
3085             optional=optional,
3086             _decoded=_decoded,
3087         )
3088         for copy_func in copy_funcs:
3089             obj_copied = copy_func(obj)
3090             self.assert_copied_basic_fields(obj, obj_copied)
3091             self.assertEqual(obj.specs, obj_copied.specs)
3092
3093     @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
3094     @given(data_strategy())
3095     def test_symmetric(self, d):
3096         schema_input, _, _, _, default, optional, _decoded = d.draw(
3097             enumerated_values_strategy(),
3098         )
3099         tag_expl = d.draw(integers(min_value=1).map(tag_ctxc))
3100         offset = d.draw(integers(min_value=0))
3101         value = d.draw(sampled_from(sorted([v for _, v in schema_input])))
3102         tail_junk = d.draw(binary(max_size=5))
3103
3104         class E(Enumerated):
3105             schema = schema_input
3106         obj = E(
3107             value=value,
3108             default=default,
3109             optional=optional,
3110             _decoded=_decoded,
3111         )
3112         repr(obj)
3113         list(obj.pps())
3114         pprint(obj, big_blobs=True, with_decode_path=True)
3115         self.assertFalse(obj.expled)
3116         obj_encoded = obj.encode()
3117         obj_expled = obj(value, expl=tag_expl)
3118         self.assertTrue(obj_expled.expled)
3119         repr(obj_expled)
3120         list(obj_expled.pps())
3121         pprint(obj_expled, big_blobs=True, with_decode_path=True)
3122         obj_expled_encoded = obj_expled.encode()
3123         ctx_copied = deepcopy(ctx_dummy)
3124         obj_decoded, tail = obj_expled.decode(
3125             obj_expled_encoded + tail_junk,
3126             offset=offset,
3127             ctx=ctx_copied,
3128         )
3129         self.assertDictEqual(ctx_copied, ctx_dummy)
3130         repr(obj_decoded)
3131         list(obj_decoded.pps())
3132         pprint(obj_decoded, big_blobs=True, with_decode_path=True)
3133         self.assertEqual(tail, tail_junk)
3134         self.assertEqual(obj_decoded, obj_expled)
3135         self.assertNotEqual(obj_decoded, obj)
3136         self.assertEqual(int(obj_decoded), int(obj_expled))
3137         self.assertEqual(int(obj_decoded), int(obj))
3138         self.assertSequenceEqual(obj_decoded.encode(), obj_expled_encoded)
3139         self.assertEqual(obj_decoded.expl_tag, tag_expl)
3140         self.assertEqual(obj_decoded.expl_tlen, len(tag_expl))
3141         self.assertEqual(
3142             obj_decoded.expl_llen,
3143             len(len_encode(len(obj_encoded))),
3144         )
3145         self.assertEqual(obj_decoded.tlvlen, len(obj_encoded))
3146         self.assertEqual(obj_decoded.expl_vlen, len(obj_encoded))
3147         self.assertEqual(
3148             obj_decoded.offset,
3149             offset + obj_decoded.expl_tlen + obj_decoded.expl_llen,
3150         )
3151         self.assertEqual(obj_decoded.expl_offset, offset)
3152         assert_exceeding_data(
3153             self,
3154             lambda: obj_expled.decod(obj_expled_encoded + tail_junk),
3155             tail_junk,
3156         )
3157
3158
3159 @composite
3160 def string_values_strategy(draw, alphabet, do_expl=False):
3161     bound_min, bound_max = sorted(draw(sets(
3162         integers(min_value=0, max_value=1 << 7),
3163         min_size=2,
3164         max_size=2,
3165     )))
3166     value = draw(one_of(
3167         none(),
3168         text(alphabet=alphabet, min_size=bound_min, max_size=bound_max),
3169     ))
3170     default = draw(one_of(
3171         none(),
3172         text(alphabet=alphabet, min_size=bound_min, max_size=bound_max),
3173     ))
3174     bounds = None
3175     if draw(booleans()):
3176         bounds = (bound_min, bound_max)
3177     impl = None
3178     expl = None
3179     if do_expl:
3180         expl = draw(one_of(none(), integers(min_value=1).map(tag_encode)))
3181     else:
3182         impl = draw(one_of(none(), integers(min_value=1).map(tag_encode)))
3183     optional = draw(one_of(none(), booleans()))
3184     _decoded = (
3185         draw(integers(min_value=0)),
3186         draw(integers(min_value=0)),
3187         draw(integers(min_value=0)),
3188     )
3189     return (value, bounds, impl, expl, default, optional, _decoded)
3190
3191
3192 class StringMixin(object):
3193     def test_invalid_value_type(self):
3194         with self.assertRaises(InvalidValueType) as err:
3195             self.base_klass((1, 2))
3196         repr(err.exception)
3197
3198     def text_alphabet(self):
3199         if self.base_klass.encoding in ("ascii", "iso-8859-1"):
3200             return printable + whitespace
3201         return None
3202
3203     @given(booleans())
3204     def test_optional(self, optional):
3205         obj = self.base_klass(default=self.base_klass(""), optional=optional)
3206         self.assertTrue(obj.optional)
3207
3208     @given(data_strategy())
3209     def test_ready(self, d):
3210         obj = self.base_klass()
3211         self.assertFalse(obj.ready)
3212         repr(obj)
3213         list(obj.pps())
3214         pprint(obj, big_blobs=True, with_decode_path=True)
3215         text_type(obj)
3216         with self.assertRaises(ObjNotReady) as err:
3217             obj.encode()
3218         repr(err.exception)
3219         value = d.draw(text(alphabet=self.text_alphabet()))
3220         obj = self.base_klass(value)
3221         self.assertTrue(obj.ready)
3222         repr(obj)
3223         list(obj.pps())
3224         pprint(obj, big_blobs=True, with_decode_path=True)
3225         text_type(obj)
3226
3227     @given(data_strategy())
3228     def test_comparison(self, d):
3229         value1 = d.draw(text(alphabet=self.text_alphabet()))
3230         value2 = d.draw(text(alphabet=self.text_alphabet()))
3231         tag1 = d.draw(binary(min_size=1))
3232         tag2 = d.draw(binary(min_size=1))
3233         obj1 = self.base_klass(value1)
3234         obj2 = self.base_klass(value2)
3235         self.assertEqual(obj1 == obj2, value1 == value2)
3236         self.assertEqual(obj1 != obj2, value1 != value2)
3237         self.assertEqual(obj1 == bytes(obj2), value1 == value2)
3238         self.assertEqual(obj1 == text_type(obj2), value1 == value2)
3239         obj1 = self.base_klass(value1, impl=tag1)
3240         obj2 = self.base_klass(value1, impl=tag2)
3241         self.assertEqual(obj1 == obj2, tag1 == tag2)
3242         self.assertEqual(obj1 != obj2, tag1 != tag2)
3243
3244     @given(data_strategy())
3245     def test_bounds_satisfied(self, d):
3246         bound_min = d.draw(integers(min_value=0, max_value=1 << 7))
3247         bound_max = d.draw(integers(min_value=bound_min, max_value=1 << 7))
3248         value = d.draw(text(
3249             alphabet=self.text_alphabet(),
3250             min_size=bound_min,
3251             max_size=bound_max,
3252         ))
3253         self.base_klass(value=value, bounds=(bound_min, bound_max))
3254
3255     @given(data_strategy())
3256     def test_bounds_unsatisfied(self, d):
3257         bound_min = d.draw(integers(min_value=1, max_value=1 << 7))
3258         bound_max = d.draw(integers(min_value=bound_min, max_value=1 << 7))
3259         value = d.draw(text(alphabet=self.text_alphabet(), max_size=bound_min - 1))
3260         with self.assertRaises(BoundsError) as err:
3261             self.base_klass(value=value, bounds=(bound_min, bound_max))
3262         repr(err.exception)
3263         with assertRaisesRegex(self, DecodeError, "bounds") as err:
3264             self.base_klass(bounds=(bound_min, bound_max)).decode(
3265                 self.base_klass(value).encode()
3266             )
3267         repr(err.exception)
3268         value = d.draw(text(alphabet=self.text_alphabet(), min_size=bound_max + 1))
3269         with self.assertRaises(BoundsError) as err:
3270             self.base_klass(value=value, bounds=(bound_min, bound_max))
3271         repr(err.exception)
3272         with assertRaisesRegex(self, DecodeError, "bounds") as err:
3273             self.base_klass(bounds=(bound_min, bound_max)).decode(
3274                 self.base_klass(value).encode()
3275             )
3276         repr(err.exception)
3277
3278     @given(data_strategy())
3279     def test_call(self, d):
3280         (
3281             value_initial,
3282             bounds_initial,
3283             impl_initial,
3284             expl_initial,
3285             default_initial,
3286             optional_initial,
3287             _decoded_initial,
3288         ) = d.draw(string_values_strategy(self.text_alphabet()))
3289         obj_initial = self.base_klass(
3290             value_initial,
3291             bounds_initial,
3292             impl_initial,
3293             expl_initial,
3294             default_initial,
3295             optional_initial or False,
3296             _decoded_initial,
3297         )
3298         (
3299             value,
3300             bounds,
3301             impl,
3302             expl,
3303             default,
3304             optional,
3305             _decoded,
3306         ) = d.draw(string_values_strategy(
3307             self.text_alphabet(),
3308             do_expl=impl_initial is None,
3309         ))
3310         if (default is None) and (obj_initial.default is not None):
3311             bounds = None
3312         if (
3313                 (bounds is None) and
3314                 (value is not None) and
3315                 (bounds_initial is not None) and
3316                 not (bounds_initial[0] <= len(value) <= bounds_initial[1])
3317         ):
3318             value = None
3319         if (
3320                 (bounds is None) and
3321                 (default is not None) and
3322                 (bounds_initial is not None) and
3323                 not (bounds_initial[0] <= len(default) <= bounds_initial[1])
3324         ):
3325             default = None
3326         obj = obj_initial(value, bounds, impl, expl, default, optional)
3327         if obj.ready:
3328             value_expected = default if value is None else value
3329             value_expected = (
3330                 default_initial if value_expected is None
3331                 else value_expected
3332             )
3333             self.assertEqual(obj, value_expected)
3334         self.assertEqual(obj.tag, impl or impl_initial or obj.tag_default)
3335         self.assertEqual(obj.expl_tag, expl or expl_initial)
3336         self.assertEqual(
3337             obj.default,
3338             default_initial if default is None else default,
3339         )
3340         if obj.default is None:
3341             optional = optional_initial if optional is None else optional
3342             optional = False if optional is None else optional
3343         else:
3344             optional = True
3345         self.assertEqual(obj.optional, optional)
3346         self.assertEqual(
3347             (obj._bound_min, obj._bound_max),
3348             bounds or bounds_initial or (0, float("+inf")),
3349         )
3350
3351     @given(data_strategy())
3352     def test_copy(self, d):
3353         values = d.draw(string_values_strategy(self.text_alphabet()))
3354         obj = self.base_klass(*values)
3355         for copy_func in copy_funcs:
3356             obj_copied = copy_func(obj)
3357             self.assert_copied_basic_fields(obj, obj_copied)
3358             self.assertEqual(obj._bound_min, obj_copied._bound_min)
3359             self.assertEqual(obj._bound_max, obj_copied._bound_max)
3360             self.assertEqual(obj._value, obj_copied._value)
3361
3362     @given(data_strategy())
3363     def test_stripped(self, d):
3364         value = d.draw(text(alphabet=self.text_alphabet()))
3365         tag_impl = tag_encode(d.draw(integers(min_value=1)))
3366         obj = self.base_klass(value, impl=tag_impl)
3367         with self.assertRaises(NotEnoughData):
3368             obj.decode(obj.encode()[:-1])
3369
3370     @given(data_strategy())
3371     def test_stripped_expl(self, d):
3372         value = d.draw(text(alphabet=self.text_alphabet()))
3373         tag_expl = tag_ctxc(d.draw(integers(min_value=1)))
3374         obj = self.base_klass(value, expl=tag_expl)
3375         with self.assertRaises(NotEnoughData):
3376             obj.decode(obj.encode()[:-1])
3377
3378     @given(
3379         integers(min_value=31),
3380         integers(min_value=0),
3381         decode_path_strat,
3382     )
3383     def test_bad_tag(self, tag, offset, decode_path):
3384         with self.assertRaises(DecodeError) as err:
3385             self.base_klass().decode(
3386                 tag_encode(tag)[:-1],
3387                 offset=offset,
3388                 decode_path=decode_path,
3389             )
3390         repr(err.exception)
3391         self.assertEqual(err.exception.offset, offset)
3392         self.assertEqual(err.exception.decode_path, decode_path)
3393
3394     @given(
3395         integers(min_value=128),
3396         integers(min_value=0),
3397         decode_path_strat,
3398     )
3399     def test_bad_len(self, l, offset, decode_path):
3400         with self.assertRaises(DecodeError) as err:
3401             self.base_klass().decode(
3402                 self.base_klass.tag_default + len_encode(l)[:-1],
3403                 offset=offset,
3404                 decode_path=decode_path,
3405             )
3406         repr(err.exception)
3407         self.assertEqual(err.exception.offset, offset)
3408         self.assertEqual(err.exception.decode_path, decode_path)
3409
3410     @given(
3411         sets(integers(min_value=0, max_value=10), min_size=2, max_size=2),
3412         integers(min_value=0),
3413         decode_path_strat,
3414     )
3415     def test_invalid_bounds_while_decoding(self, ints, offset, decode_path):
3416         value, bound_min = list(sorted(ints))
3417
3418         class String(self.base_klass):
3419             # Multiply this value by four, to satisfy UTF-32 bounds
3420             # (4 bytes per character) validation
3421             bounds = (bound_min * 4, bound_min * 4)
3422         with self.assertRaises(DecodeError) as err:
3423             String().decode(
3424                 self.base_klass(b"\x00\x00\x00\x00" * value).encode(),
3425                 offset=offset,
3426                 decode_path=decode_path,
3427             )
3428         repr(err.exception)
3429         self.assertEqual(err.exception.offset, offset)
3430         self.assertEqual(err.exception.decode_path, decode_path)
3431
3432     @given(data_strategy())
3433     def test_symmetric(self, d):
3434         values = d.draw(string_values_strategy(self.text_alphabet()))
3435         value = d.draw(text(alphabet=self.text_alphabet()))
3436         tag_expl = tag_ctxc(d.draw(integers(min_value=1)))
3437         offset = d.draw(integers(min_value=0))
3438         tail_junk = d.draw(binary(max_size=5))
3439         _, _, _, _, default, optional, _decoded = values
3440         obj = self.base_klass(
3441             value=value,
3442             default=default,
3443             optional=optional,
3444             _decoded=_decoded,
3445         )
3446         repr(obj)
3447         list(obj.pps())
3448         pprint(obj, big_blobs=True, with_decode_path=True)
3449         self.assertFalse(obj.expled)
3450         obj_encoded = obj.encode()
3451         obj_expled = obj(value, expl=tag_expl)
3452         self.assertTrue(obj_expled.expled)
3453         repr(obj_expled)
3454         list(obj_expled.pps())
3455         pprint(obj_expled, big_blobs=True, with_decode_path=True)
3456         obj_expled_encoded = obj_expled.encode()
3457         ctx_copied = deepcopy(ctx_dummy)
3458         obj_decoded, tail = obj_expled.decode(
3459             obj_expled_encoded + tail_junk,
3460             offset=offset,
3461             ctx=ctx_copied,
3462         )
3463         self.assertDictEqual(ctx_copied, ctx_dummy)
3464         repr(obj_decoded)
3465         list(obj_decoded.pps())
3466         pprint(obj_decoded, big_blobs=True, with_decode_path=True)
3467         self.assertEqual(tail, tail_junk)
3468         self.assertEqual(obj_decoded, obj_expled)
3469         self.assertNotEqual(obj_decoded, obj)
3470         self.assertEqual(bytes(obj_decoded), bytes(obj_expled))
3471         self.assertEqual(bytes(obj_decoded), bytes(obj))
3472         self.assertEqual(text_type(obj_decoded), text_type(obj_expled))
3473         self.assertEqual(text_type(obj_decoded), text_type(obj))
3474         self.assertSequenceEqual(obj_decoded.encode(), obj_expled_encoded)
3475         self.assertSequenceEqual(obj_decoded.expl_tag, tag_expl)
3476         self.assertEqual(obj_decoded.expl_tlen, len(tag_expl))
3477         self.assertEqual(
3478             obj_decoded.expl_llen,
3479             len(len_encode(len(obj_encoded))),
3480         )
3481         self.assertEqual(obj_decoded.tlvlen, len(obj_encoded))
3482         self.assertEqual(obj_decoded.expl_vlen, len(obj_encoded))
3483         self.assertEqual(
3484             obj_decoded.offset,
3485             offset + obj_decoded.expl_tlen + obj_decoded.expl_llen,
3486         )
3487         self.assertEqual(obj_decoded.expl_offset, offset)
3488         assert_exceeding_data(
3489             self,
3490             lambda: obj_expled.decod(obj_expled_encoded + tail_junk),
3491             tail_junk,
3492         )
3493
3494
3495 class TestUTF8String(StringMixin, CommonMixin, TestCase):
3496     base_klass = UTF8String
3497
3498
3499 cyrillic_letters = text(
3500     alphabet="".join(six_unichr(i) for i in list(range(0x0410, 0x044f + 1))),
3501     min_size=1,
3502     max_size=5,
3503 )
3504
3505
3506 class UnicodeDecodeErrorMixin(object):
3507     @given(cyrillic_letters)
3508     def test_unicode_decode_error(self, cyrillic_text):
3509         with self.assertRaises(DecodeError):
3510             self.base_klass(cyrillic_text)
3511
3512
3513 class TestNumericString(StringMixin, CommonMixin, TestCase):
3514     base_klass = NumericString
3515
3516     def text_alphabet(self):
3517         return digits + " "
3518
3519     @given(text(alphabet=ascii_letters, min_size=1, max_size=5))
3520     def test_non_numeric(self, non_numeric_text):
3521         with assertRaisesRegex(self, DecodeError, "non-numeric"):
3522             self.base_klass(non_numeric_text)
3523
3524     @given(
3525         sets(integers(min_value=0, max_value=10), min_size=2, max_size=2),
3526         integers(min_value=0),
3527         decode_path_strat,
3528     )
3529     def test_invalid_bounds_while_decoding(self, ints, offset, decode_path):
3530         value, bound_min = list(sorted(ints))
3531
3532         class String(self.base_klass):
3533             bounds = (bound_min, bound_min)
3534         with self.assertRaises(DecodeError) as err:
3535             String().decode(
3536                 self.base_klass(b"1" * value).encode(),
3537                 offset=offset,
3538                 decode_path=decode_path,
3539             )
3540         repr(err.exception)
3541         self.assertEqual(err.exception.offset, offset)
3542         self.assertEqual(err.exception.decode_path, decode_path)
3543
3544
3545 class TestPrintableString(
3546         UnicodeDecodeErrorMixin,
3547         StringMixin,
3548         CommonMixin,
3549         TestCase,
3550 ):
3551     base_klass = PrintableString
3552
3553     def text_alphabet(self):
3554         return ascii_letters + digits + " '()+,-./:=?"
3555
3556     @given(text(alphabet=sorted(set(whitespace) - set(" ")), min_size=1, max_size=5))
3557     def test_non_printable(self, non_printable_text):
3558         with assertRaisesRegex(self, DecodeError, "non-printable"):
3559             self.base_klass(non_printable_text)
3560
3561     @given(
3562         sets(integers(min_value=0, max_value=10), min_size=2, max_size=2),
3563         integers(min_value=0),
3564         decode_path_strat,
3565     )
3566     def test_invalid_bounds_while_decoding(self, ints, offset, decode_path):
3567         value, bound_min = list(sorted(ints))
3568
3569         class String(self.base_klass):
3570             bounds = (bound_min, bound_min)
3571         with self.assertRaises(DecodeError) as err:
3572             String().decode(
3573                 self.base_klass(b"1" * value).encode(),
3574                 offset=offset,
3575                 decode_path=decode_path,
3576             )
3577         repr(err.exception)
3578         self.assertEqual(err.exception.offset, offset)
3579         self.assertEqual(err.exception.decode_path, decode_path)
3580
3581     def test_allowable_invalid_chars(self):
3582         for c, kwargs in (
3583                 ("*", {"allow_asterisk": True}),
3584                 ("&", {"allow_ampersand": True}),
3585                 ("&*", {"allow_asterisk": True, "allow_ampersand": True}),
3586         ):
3587             s = "hello invalid"
3588             obj = self.base_klass(s)
3589             for prop in kwargs.keys():
3590                 self.assertFalse(getattr(obj, prop))
3591             s += c
3592             with assertRaisesRegex(self, DecodeError, "non-printable"):
3593                 self.base_klass(s)
3594             self.base_klass(s, **kwargs)
3595             klass = self.base_klass(**kwargs)
3596             obj = klass(s)
3597             for prop in kwargs.keys():
3598                 self.assertTrue(getattr(obj, prop))
3599             obj = copy(obj)
3600             obj(s)
3601             for prop in kwargs.keys():
3602                 self.assertTrue(getattr(obj, prop))
3603
3604
3605 class TestTeletexString(
3606         UnicodeDecodeErrorMixin,
3607         StringMixin,
3608         CommonMixin,
3609         TestCase,
3610 ):
3611     base_klass = TeletexString
3612
3613
3614 class TestVideotexString(
3615         UnicodeDecodeErrorMixin,
3616         StringMixin,
3617         CommonMixin,
3618         TestCase,
3619 ):
3620     base_klass = VideotexString
3621
3622
3623 class TestIA5String(
3624         UnicodeDecodeErrorMixin,
3625         StringMixin,
3626         CommonMixin,
3627         TestCase,
3628 ):
3629     base_klass = IA5String
3630
3631
3632 class TestGraphicString(
3633         UnicodeDecodeErrorMixin,
3634         StringMixin,
3635         CommonMixin,
3636         TestCase,
3637 ):
3638     base_klass = GraphicString
3639
3640
3641 class TestVisibleString(
3642         UnicodeDecodeErrorMixin,
3643         StringMixin,
3644         CommonMixin,
3645         TestCase,
3646 ):
3647     base_klass = VisibleString
3648
3649     def test_x690_vector(self):
3650         obj, tail = VisibleString().decode(hexdec("1A054A6F6E6573"))
3651         self.assertSequenceEqual(tail, b"")
3652         self.assertEqual(str(obj), "Jones")
3653         self.assertFalse(obj.ber_encoded)
3654         self.assertFalse(obj.lenindef)
3655         self.assertFalse(obj.bered)
3656
3657         obj, tail = VisibleString().decode(
3658             hexdec("3A0904034A6F6E04026573"),
3659             ctx={"bered": True},
3660         )
3661         self.assertSequenceEqual(tail, b"")
3662         self.assertEqual(str(obj), "Jones")
3663         self.assertTrue(obj.ber_encoded)
3664         self.assertFalse(obj.lenindef)
3665         self.assertTrue(obj.bered)
3666         obj = copy(obj)
3667         self.assertTrue(obj.ber_encoded)
3668         self.assertFalse(obj.lenindef)
3669         self.assertTrue(obj.bered)
3670
3671         obj, tail = VisibleString().decode(
3672             hexdec("3A8004034A6F6E040265730000"),
3673             ctx={"bered": True},
3674         )
3675         self.assertSequenceEqual(tail, b"")
3676         self.assertEqual(str(obj), "Jones")
3677         self.assertTrue(obj.ber_encoded)
3678         self.assertTrue(obj.lenindef)
3679         self.assertTrue(obj.bered)
3680         obj = copy(obj)
3681         self.assertTrue(obj.ber_encoded)
3682         self.assertTrue(obj.lenindef)
3683         self.assertTrue(obj.bered)
3684
3685
3686 class TestGeneralString(
3687         UnicodeDecodeErrorMixin,
3688         StringMixin,
3689         CommonMixin,
3690         TestCase,
3691 ):
3692     base_klass = GeneralString
3693
3694
3695 class TestUniversalString(StringMixin, CommonMixin, TestCase):
3696     base_klass = UniversalString
3697
3698
3699 class TestBMPString(StringMixin, CommonMixin, TestCase):
3700     base_klass = BMPString
3701
3702
3703 @composite
3704 def generalized_time_values_strategy(
3705         draw,
3706         min_datetime,
3707         max_datetime,
3708         omit_ms=False,
3709         do_expl=False,
3710 ):
3711     value = None
3712     if draw(booleans()):
3713         value = draw(datetimes(min_value=min_datetime, max_value=max_datetime))
3714         if omit_ms:
3715             value = value.replace(microsecond=0)
3716     default = None
3717     if draw(booleans()):
3718         default = draw(datetimes(min_value=min_datetime, max_value=max_datetime))
3719         if omit_ms:
3720             default = default.replace(microsecond=0)
3721     impl = None
3722     expl = None
3723     if do_expl:
3724         expl = draw(one_of(none(), integers(min_value=1).map(tag_encode)))
3725     else:
3726         impl = draw(one_of(none(), integers(min_value=1).map(tag_encode)))
3727     optional = draw(one_of(none(), booleans()))
3728     _decoded = (
3729         draw(integers(min_value=0)),
3730         draw(integers(min_value=0)),
3731         draw(integers(min_value=0)),
3732     )
3733     return (value, impl, expl, default, optional, _decoded)
3734
3735
3736 class TimeMixin(object):
3737     def test_invalid_value_type(self):
3738         with self.assertRaises(InvalidValueType) as err:
3739             self.base_klass(datetime.now().timetuple())
3740         repr(err.exception)
3741
3742     @given(data_strategy())
3743     def test_optional(self, d):
3744         default = d.draw(datetimes(
3745             min_value=self.min_datetime,
3746             max_value=self.max_datetime,
3747         ))
3748         optional = d.draw(booleans())
3749         obj = self.base_klass(default=default, optional=optional)
3750         self.assertTrue(obj.optional)
3751
3752     @given(data_strategy())
3753     def test_ready(self, d):
3754         obj = self.base_klass()
3755         self.assertFalse(obj.ready)
3756         repr(obj)
3757         list(obj.pps())
3758         pprint(obj, big_blobs=True, with_decode_path=True)
3759         with self.assertRaises(ObjNotReady) as err:
3760             obj.encode()
3761         repr(err.exception)
3762         value = d.draw(datetimes(
3763             min_value=self.min_datetime,
3764             max_value=self.max_datetime,
3765         ))
3766         obj = self.base_klass(value)
3767         self.assertTrue(obj.ready)
3768         repr(obj)
3769         list(obj.pps())
3770         pprint(obj, big_blobs=True, with_decode_path=True)
3771
3772     @given(data_strategy())
3773     def test_comparison(self, d):
3774         value1 = d.draw(datetimes(
3775             min_value=self.min_datetime,
3776             max_value=self.max_datetime,
3777         ))
3778         value2 = d.draw(datetimes(
3779             min_value=self.min_datetime,
3780             max_value=self.max_datetime,
3781         ))
3782         tag1 = d.draw(binary(min_size=1))
3783         tag2 = d.draw(binary(min_size=1))
3784         if self.omit_ms:
3785             value1 = value1.replace(microsecond=0)
3786             value2 = value2.replace(microsecond=0)
3787         obj1 = self.base_klass(value1)
3788         obj2 = self.base_klass(value2)
3789         self.assertEqual(obj1 == obj2, value1 == value2)
3790         self.assertEqual(obj1 != obj2, value1 != value2)
3791         self.assertEqual(obj1 == obj2.todatetime(), value1 == value2)
3792         self.assertEqual(obj1 == bytes(obj2), value1 == value2)
3793         obj1 = self.base_klass(value1, impl=tag1)
3794         obj2 = self.base_klass(value1, impl=tag2)
3795         self.assertEqual(obj1 == obj2, tag1 == tag2)
3796         self.assertEqual(obj1 != obj2, tag1 != tag2)
3797
3798     @given(data_strategy())
3799     def test_call(self, d):
3800         (
3801             value_initial,
3802             impl_initial,
3803             expl_initial,
3804             default_initial,
3805             optional_initial,
3806             _decoded_initial,
3807         ) = d.draw(generalized_time_values_strategy(
3808             min_datetime=self.min_datetime,
3809             max_datetime=self.max_datetime,
3810             omit_ms=self.omit_ms,
3811         ))
3812         obj_initial = self.base_klass(
3813             value=value_initial,
3814             impl=impl_initial,
3815             expl=expl_initial,
3816             default=default_initial,
3817             optional=optional_initial or False,
3818             _decoded=_decoded_initial,
3819         )
3820         (
3821             value,
3822             impl,
3823             expl,
3824             default,
3825             optional,
3826             _decoded,
3827         ) = d.draw(generalized_time_values_strategy(
3828             min_datetime=self.min_datetime,
3829             max_datetime=self.max_datetime,
3830             omit_ms=self.omit_ms,
3831             do_expl=impl_initial is None,
3832         ))
3833         obj = obj_initial(
3834             value=value,
3835             impl=impl,
3836             expl=expl,
3837             default=default,
3838             optional=optional,
3839         )
3840         if obj.ready:
3841             value_expected = default if value is None else value
3842             value_expected = (
3843                 default_initial if value_expected is None
3844                 else value_expected
3845             )
3846             self.assertEqual(obj, value_expected)
3847         self.assertEqual(obj.tag, impl or impl_initial or obj.tag_default)
3848         self.assertEqual(obj.expl_tag, expl or expl_initial)
3849         self.assertEqual(
3850             obj.default,
3851             default_initial if default is None else default,
3852         )
3853         if obj.default is None:
3854             optional = optional_initial if optional is None else optional
3855             optional = False if optional is None else optional
3856         else:
3857             optional = True
3858         self.assertEqual(obj.optional, optional)
3859
3860     @given(data_strategy())
3861     def test_copy(self, d):
3862         values = d.draw(generalized_time_values_strategy(
3863             min_datetime=self.min_datetime,
3864             max_datetime=self.max_datetime,
3865         ))
3866         obj = self.base_klass(*values)
3867         for copy_func in copy_funcs:
3868             obj_copied = copy_func(obj)
3869             self.assert_copied_basic_fields(obj, obj_copied)
3870             self.assertEqual(obj._value, obj_copied._value)
3871
3872     @given(data_strategy())
3873     def test_stripped(self, d):
3874         value = d.draw(datetimes(
3875             min_value=self.min_datetime,
3876             max_value=self.max_datetime,
3877         ))
3878         tag_impl = tag_encode(d.draw(integers(min_value=1)))
3879         obj = self.base_klass(value, impl=tag_impl)
3880         with self.assertRaises(NotEnoughData):
3881             obj.decode(obj.encode()[:-1])
3882
3883     @given(data_strategy())
3884     def test_stripped_expl(self, d):
3885         value = d.draw(datetimes(
3886             min_value=self.min_datetime,
3887             max_value=self.max_datetime,
3888         ))
3889         tag_expl = tag_ctxc(d.draw(integers(min_value=1)))
3890         obj = self.base_klass(value, expl=tag_expl)
3891         with self.assertRaises(NotEnoughData):
3892             obj.decode(obj.encode()[:-1])
3893
3894     @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
3895     @given(data_strategy())
3896     def test_symmetric(self, d):
3897         values = d.draw(generalized_time_values_strategy(
3898             min_datetime=self.min_datetime,
3899             max_datetime=self.max_datetime,
3900         ))
3901         value = d.draw(datetimes(
3902             min_value=self.min_datetime,
3903             max_value=self.max_datetime,
3904         ))
3905         tag_expl = tag_ctxc(d.draw(integers(min_value=1)))
3906         offset = d.draw(integers(min_value=0))
3907         tail_junk = d.draw(binary(max_size=5))
3908         _, _, _, default, optional, _decoded = values
3909         obj = self.base_klass(
3910             value=value,
3911             default=default,
3912             optional=optional,
3913             _decoded=_decoded,
3914         )
3915         repr(obj)
3916         list(obj.pps())
3917         pprint(obj, big_blobs=True, with_decode_path=True)
3918         self.assertFalse(obj.expled)
3919         obj_encoded = obj.encode()
3920         self.additional_symmetric_check(value, obj_encoded)
3921         obj_expled = obj(value, expl=tag_expl)
3922         self.assertTrue(obj_expled.expled)
3923         repr(obj_expled)
3924         list(obj_expled.pps())
3925         pprint(obj_expled, big_blobs=True, with_decode_path=True)
3926         obj_expled_encoded = obj_expled.encode()
3927         ctx_copied = deepcopy(ctx_dummy)
3928         obj_decoded, tail = obj_expled.decode(
3929             obj_expled_encoded + tail_junk,
3930             offset=offset,
3931             ctx=ctx_copied,
3932         )
3933         self.assertDictEqual(ctx_copied, ctx_dummy)
3934         repr(obj_decoded)
3935         list(obj_decoded.pps())
3936         pprint(obj_decoded, big_blobs=True, with_decode_path=True)
3937         self.assertEqual(tail, tail_junk)
3938         self.assertEqual(obj_decoded, obj_expled)
3939         self.assertEqual(obj_decoded.todatetime(), obj_expled.todatetime())
3940         self.assertEqual(obj_decoded.todatetime(), obj.todatetime())
3941         self.assertSequenceEqual(obj_decoded.encode(), obj_expled_encoded)
3942         self.assertSequenceEqual(obj_decoded.expl_tag, tag_expl)
3943         self.assertEqual(obj_decoded.expl_tlen, len(tag_expl))
3944         self.assertEqual(
3945             obj_decoded.expl_llen,
3946             len(len_encode(len(obj_encoded))),
3947         )
3948         self.assertEqual(obj_decoded.tlvlen, len(obj_encoded))
3949         self.assertEqual(obj_decoded.expl_vlen, len(obj_encoded))
3950         self.assertEqual(
3951             obj_decoded.offset,
3952             offset + obj_decoded.expl_tlen + obj_decoded.expl_llen,
3953         )
3954         self.assertEqual(obj_decoded.expl_offset, offset)
3955         assert_exceeding_data(
3956             self,
3957             lambda: obj_expled.decod(obj_expled_encoded + tail_junk),
3958             tail_junk,
3959         )
3960
3961
3962 class TestGeneralizedTime(TimeMixin, CommonMixin, TestCase):
3963     base_klass = GeneralizedTime
3964     omit_ms = False
3965     min_datetime = datetime(1900, 1, 1)
3966     max_datetime = datetime(9999, 12, 31)
3967
3968     def additional_symmetric_check(self, value, obj_encoded):
3969         if value.microsecond > 0:
3970             self.assertFalse(obj_encoded.endswith(b"0Z"))
3971
3972     def test_repr_not_ready(self):
3973         unicode(GeneralizedTime()) if PY2 else str(GeneralizedTime())
3974         repr(GeneralizedTime())
3975
3976     def test_x690_vector_valid(self):
3977         for data in ((
3978                 b"19920521000000Z",
3979                 b"19920622123421Z",
3980                 b"19920722132100.3Z",
3981         )):
3982             GeneralizedTime(data)
3983
3984     def test_x690_vector_invalid(self):
3985         for data in ((
3986                 b"19920520240000Z",
3987                 b"19920622123421.0Z",
3988                 b"19920722132100.30Z",
3989         )):
3990             with self.assertRaises(DecodeError) as err:
3991                 GeneralizedTime(data)
3992             repr(err.exception)
3993
3994     def test_go_vectors_invalid(self):
3995         for data in ((
3996                 b"20100102030405",
3997                 b"00000100000000Z",
3998                 b"20101302030405Z",
3999                 b"20100002030405Z",
4000                 b"20100100030405Z",
4001                 b"20100132030405Z",
4002                 b"20100231030405Z",
4003                 b"20100102240405Z",
4004                 b"20100102036005Z",
4005                 b"20100102030460Z",
4006                 b"-20100102030410Z",
4007                 b"2010-0102030410Z",
4008                 b"2010-0002030410Z",
4009                 b"201001-02030410Z",
4010                 b"20100102-030410Z",
4011                 b"2010010203-0410Z",
4012                 b"201001020304-10Z",
4013                 # These ones are INVALID in *DER*, but accepted
4014                 # by Go's encoding/asn1
4015                 b"20100102030405+0607",
4016                 b"20100102030405-0607",
4017         )):
4018             with self.assertRaises(DecodeError) as err:
4019                 GeneralizedTime(data)
4020             repr(err.exception)
4021
4022     def test_go_vectors_valid(self):
4023         self.assertEqual(
4024             GeneralizedTime(b"20100102030405Z").todatetime(),
4025             datetime(2010, 1, 2, 3, 4, 5, 0),
4026         )
4027
4028     def test_go_vectors_valid_ber(self):
4029         for data in ((
4030                 b"20100102030405+0607",
4031                 b"20100102030405-0607",
4032         )):
4033             GeneralizedTime(data, ctx={"bered": True})
4034
4035     def test_utc_offsets(self):
4036         """Some know equal UTC offsets
4037         """
4038         dts = [
4039             GeneralizedTime(data.encode("ascii"), ctx={"bered": True})
4040             for data in (
4041                 "200101011830Z",
4042                 "200101012230+04",
4043                 "200101011130-0700",
4044                 "200101011500-03:30",
4045             )
4046         ]
4047         self.assertEqual(dts[0], dts[1])
4048         self.assertEqual(dts[0], dts[2])
4049         self.assertEqual(dts[0], dts[3])
4050
4051     @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
4052     @given(data_strategy())
4053     def test_valid_ber(self, d):
4054         min_year = 1901 if PY2 else 2
4055         year = d.draw(integers(min_value=min_year, max_value=9999))
4056         month = d.draw(integers(min_value=1, max_value=12))
4057         day = d.draw(integers(min_value=1, max_value=28))
4058         hours = d.draw(integers(min_value=0, max_value=23))
4059         data = "%04d%02d%02d%02d" % (year, month, day, hours)
4060         dt = datetime(year, month, day, hours)
4061         fractions_sign = d.draw(sampled_from("  ,."))
4062         fractions = None
4063         if fractions_sign != " ":
4064             fractions = random()
4065         if d.draw(booleans()):
4066             minutes = d.draw(integers(min_value=0, max_value=59))
4067             data += "%02d" % minutes
4068             dt += timedelta(seconds=60 * minutes)
4069             if d.draw(booleans()):
4070                 seconds = d.draw(integers(min_value=0, max_value=59))
4071                 data += "%02d" % seconds
4072                 dt += timedelta(seconds=seconds)
4073                 if fractions is not None:
4074                     dt += timedelta(microseconds=10**6 * fractions)
4075             elif fractions is not None:
4076                 dt += timedelta(seconds=60 * fractions)
4077         elif fractions is not None:
4078             dt += timedelta(seconds=3600 * fractions)
4079         if fractions is not None:
4080             data += fractions_sign + str(fractions)[2:]
4081         if d.draw(booleans()):
4082             data += "Z"
4083         elif d.draw(booleans()):
4084             offset_hour = d.draw(integers(min_value=0, max_value=13))
4085             sign = 1
4086             if d.draw(booleans()):
4087                 data += "-"
4088                 sign = -1
4089             else:
4090                 data += "+"
4091             dt -= timedelta(seconds=sign * 3600 * offset_hour)
4092             data += "%02d" % offset_hour
4093             minutes_separator = d.draw(sampled_from((None, "", ":")))
4094             if minutes_separator is not None:
4095                 offset_minute = d.draw(integers(min_value=0, max_value=59))
4096                 dt -= timedelta(seconds=sign * 60 * offset_minute)
4097                 data += "%s%02d" % (minutes_separator, offset_minute)
4098         data = data.encode("ascii")
4099         data_der = GeneralizedTime.tag_default + len_encode(len(data)) + data
4100         try:
4101             GeneralizedTime().decod(data_der)
4102         except DecodeError:
4103             dered = False
4104         else:
4105             dered = True
4106         obj = GeneralizedTime().decod(data_der, ctx={"bered": True})
4107         if dt.year > 1970:
4108             self.assertEqual(
4109                 mktime(obj.todatetime().timetuple()),
4110                 mktime(dt.timetuple()),
4111             )
4112         elif not PY2:
4113             self.assertEqual(obj.todatetime().timestamp(), dt.timestamp())
4114         self.assertEqual(obj.ber_encoded, not dered)
4115         self.assertEqual(obj.bered, not dered)
4116         self.assertEqual(obj.ber_raw, None if dered else data)
4117         self.assertEqual(obj.encode() == data_der, dered)
4118         repr(obj)
4119         bytes(obj)
4120         str(obj)
4121
4122     def test_invalid_ber(self):
4123         for data in ((
4124                 # "00010203040506.07",
4125                 "-0010203040506.07",
4126                 "0001-203040506.07",
4127                 "000102-3040506.07",
4128                 "00010203-40506.07",
4129                 "0001020304-506.07",
4130                 "000102030405-6.07",
4131                 "00010203040506.-7",
4132                 "+0010203040506.07",
4133                 "0001+203040506.07",
4134                 "000102+3040506.07",
4135                 "00010203+40506.07",
4136                 "0001020304+506.07",
4137                 "000102030405+6.07",
4138                 "00010203040506.+7",
4139                 " 0010203040506.07",
4140                 "0001 203040506.07",
4141                 "000102 3040506.07",
4142                 "00010203 40506.07",
4143                 "0001020304 506.07",
4144                 "000102030405 6.07",
4145                 "00010203040506. 7",
4146                 "001 0203040506.07",
4147                 "00012 03040506.07",
4148                 "0001023 040506.07",
4149                 "000102034 0506.07",
4150                 "00010203045 06.07",
4151                 "0001020304056 .07",
4152                 "00010203040506.7 ",
4153                 "00010203040506.",
4154                 "0001020304050607",
4155
4156                 "-0010203040506",
4157                 "0001-203040506",
4158                 "000102-3040506",
4159                 "00010203-40506",
4160                 "0001020304-506",
4161                 "000102030405-6",
4162                 "0001+203040506",
4163                 "000102+3040506",
4164                 "00010203+40506",
4165                 "0001020304+506",
4166                 "000102030405+6",
4167                 " 0010203040506",
4168                 "0001 203040506",
4169                 "000102 3040506",
4170                 "00010203 40506",
4171                 "0001020304 506",
4172                 "000102030405 6",
4173                 "001 0203040506",
4174                 "00012 03040506",
4175                 "0001023 040506",
4176                 "000102034 0506",
4177                 "00010203045 06",
4178                 "0001020304056 ",
4179
4180                 "-00102030405.07",
4181                 "0001-2030405.07",
4182                 "000102-30405.07",
4183                 "00010203-405.07",
4184                 "0001020304-5.07",
4185                 "000102030405.-7",
4186                 "+00102030405.07",
4187                 "0001+2030405.07",
4188                 "00010203+405.07",
4189                 "0001020304+5.07",
4190                 "000102030405.+7",
4191                 " 00102030405.07",
4192                 "0001 2030405.07",
4193                 "000102 30405.07",
4194                 "00010203 405.07",
4195                 "0001020304 5.07",
4196                 "000102030405. 7",
4197                 "001 02030405.07",
4198                 "00012 030405.07",
4199                 "0001023 0405.07",
4200                 "000102034 05.07",
4201                 "00010203045 .07",
4202                 "000102030405.7 ",
4203                 "000102030405.",
4204
4205                 "-001020304.07",
4206                 "0001-20304.07",
4207                 "000102-304.07",
4208                 "00010203-4.07",
4209                 "0001020304.-7",
4210                 "+001020304.07",
4211                 "0001+20304.07",
4212                 "00010203+4.07",
4213                 "0001020304.+7",
4214                 " 001020304.07",
4215                 "0001 20304.07",
4216                 "000102 304.07",
4217                 "00010203 4.07",
4218                 "0001020304. 7",
4219                 "001 020304.07",
4220                 "00012 0304.07",
4221                 "0001023 04.07",
4222                 "000102034 .07",
4223                 "0001020304.7 ",
4224                 "0001020304.",
4225
4226                 "00010203",
4227                 "00010203040506Y",
4228                 "0001010100+0001",
4229                 "0001010100+00:01",
4230                 "0001010100+01",
4231
4232                 "00010203040506.07+15",
4233                 "00010203040506.07-15",
4234                 "00010203040506.07+14:60",
4235                 "00010203040506.07+1460",
4236                 "00010203040506.07-1460",
4237                 "00010203040506.07+00:60",
4238                 "00010203040506.07-00:60",
4239
4240                 "00010203040506+15",
4241                 "00010203040506-15",
4242                 "00010203040506+14:60",
4243                 "00010203040506+1460",
4244                 "00010203040506-1460",
4245                 "00010203040506+00:60",
4246                 "00010203040506-00:60",
4247
4248                 "0001020304050.07",
4249                 "00010203040.07",
4250                 "000102030.07",
4251                 "0001020304050",
4252                 "00010203040",
4253                 "000102030",
4254         )):
4255             with self.assertRaises(DecodeError):
4256                 GeneralizedTime(data.encode("ascii"), ctx={"bered": True})
4257             data = data.replace(".", ",")
4258             with self.assertRaises(DecodeError):
4259                 GeneralizedTime(data.encode("ascii"), ctx={"bered": True})
4260
4261     @given(
4262         binary(
4263             min_size=(LEN_YYYYMMDDHHMMSSZ - 1) // 2,
4264             max_size=(LEN_YYYYMMDDHHMMSSZ - 1) // 2,
4265         ),
4266         binary(min_size=1, max_size=1),
4267         binary(
4268             min_size=(LEN_YYYYMMDDHHMMSSZ - 1) // 2,
4269             max_size=(LEN_YYYYMMDDHHMMSSZ - 1) // 2,
4270         ),
4271     )
4272     def test_junk(self, part0, part1, part2):
4273         junk = part0 + part1 + part2
4274         assume(not (set(junk) <= set(digits.encode("ascii"))))
4275         with self.assertRaises(DecodeError):
4276             GeneralizedTime().decode(
4277                 GeneralizedTime.tag_default +
4278                 len_encode(len(junk)) +
4279                 junk
4280             )
4281
4282     @given(
4283         binary(
4284             min_size=(LEN_YYYYMMDDHHMMSSDMZ - 1) // 2,
4285             max_size=(LEN_YYYYMMDDHHMMSSDMZ - 1) // 2,
4286         ),
4287         binary(min_size=1, max_size=1),
4288         binary(
4289             min_size=(LEN_YYYYMMDDHHMMSSDMZ - 1) // 2,
4290             max_size=(LEN_YYYYMMDDHHMMSSDMZ - 1) // 2,
4291         ),
4292     )
4293     def test_junk_dm(self, part0, part1, part2):
4294         junk = part0 + part1 + part2
4295         assume(not (set(junk) <= set(digits.encode("ascii"))))
4296         with self.assertRaises(DecodeError):
4297             GeneralizedTime().decode(
4298                 GeneralizedTime.tag_default +
4299                 len_encode(len(junk)) +
4300                 junk
4301             )
4302
4303     def test_ns_fractions(self):
4304         GeneralizedTime(b"20010101000000.000001Z")
4305         with assertRaisesRegex(self, DecodeError, "only microsecond fractions"):
4306             GeneralizedTime(b"20010101000000.0000001Z")
4307
4308     def test_non_pure_integers(self):
4309         for data in ((
4310                 # b"20000102030405Z,
4311                 b"+2000102030405Z",
4312                 b"2000+102030405Z",
4313                 b"200001+2030405Z",
4314                 b"20000102+30405Z",
4315                 b"2000010203+405Z",
4316                 b"200001020304+5Z",
4317                 b"20000102030405.+6Z",
4318                 b"20000102030405.-6Z",
4319                 b"_2000102030405Z",
4320                 b"2000_102030405Z",
4321                 b"200001_2030405Z",
4322                 b"20000102_30405Z",
4323                 b"2000010203_405Z",
4324                 b"200001020304_5Z",
4325                 b"20000102030405._6Z",
4326                 b"20000102030405.6_Z",
4327                 b" 2000102030405Z",
4328                 b"2000 102030405Z",
4329                 b"200001 2030405Z",
4330                 b"20000102 30405Z",
4331                 b"2000010203 405Z",
4332                 b"200001020304 5Z",
4333                 b"20000102030405. 6Z",
4334                 b"200 0102030405Z",
4335                 b"20001 02030405Z",
4336                 b"2000012 030405Z",
4337                 b"200001023 0405Z",
4338                 b"20000102034 05Z",
4339                 b"2000010203045 Z",
4340                 b"20000102030405.6 Z",
4341         )):
4342             with self.assertRaises(DecodeError):
4343                 GeneralizedTime(data)
4344
4345
4346 class TestUTCTime(TimeMixin, CommonMixin, TestCase):
4347     base_klass = UTCTime
4348     omit_ms = True
4349     min_datetime = datetime(2000, 1, 1)
4350     max_datetime = datetime(2049, 12, 31)
4351
4352     def additional_symmetric_check(self, value, obj_encoded):
4353         pass
4354
4355     def test_repr_not_ready(self):
4356         unicode(GeneralizedTime()) if PY2 else str(GeneralizedTime())
4357         repr(UTCTime())
4358
4359     def test_x690_vector_valid(self):
4360         for data in ((
4361                 b"920521000000Z",
4362                 b"920622123421Z",
4363                 b"920722132100Z",
4364         )):
4365             UTCTime(data)
4366
4367     def test_x690_vector_invalid(self):
4368         for data in ((
4369                 b"920520240000Z",
4370                 b"9207221321Z",
4371         )):
4372             with self.assertRaises(DecodeError) as err:
4373                 UTCTime(data)
4374             repr(err.exception)
4375
4376     def test_go_vectors_invalid(self):
4377         for data in ((
4378                 b"a10506234540Z",
4379                 b"91a506234540Z",
4380                 b"9105a6234540Z",
4381                 b"910506a34540Z",
4382                 b"910506334a40Z",
4383                 b"91050633444aZ",
4384                 b"910506334461Z",
4385                 b"910506334400Za",
4386                 b"000100000000Z",
4387                 b"101302030405Z",
4388                 b"100002030405Z",
4389                 b"100100030405Z",
4390                 b"100132030405Z",
4391                 b"100231030405Z",
4392                 b"100102240405Z",
4393                 b"100102036005Z",
4394                 b"100102030460Z",
4395                 b"-100102030410Z",
4396                 b"10-0102030410Z",
4397                 b"10-0002030410Z",
4398                 b"1001-02030410Z",
4399                 b"100102-030410Z",
4400                 b"10010203-0410Z",
4401                 b"1001020304-10Z",
4402                 # These ones are INVALID in *DER*, but accepted
4403                 # by Go's encoding/asn1
4404                 b"910506164540-0700",
4405                 b"910506164540+0730",
4406                 b"9105062345Z",
4407                 b"5105062345Z",
4408         )):
4409             with self.assertRaises(DecodeError) as err:
4410                 UTCTime(data)
4411             repr(err.exception)
4412
4413     def test_go_vectors_valid(self):
4414         self.assertEqual(
4415             UTCTime(b"910506234540Z").todatetime(),
4416             datetime(1991, 5, 6, 23, 45, 40, 0),
4417         )
4418
4419     def test_non_pure_integers(self):
4420         for data in ((
4421                 # b"000102030405Z",
4422                 b"+10102030405Z",
4423                 b"00+102030405Z",
4424                 b"0001+2030405Z",
4425                 b"000102+30405Z",
4426                 b"00010203+405Z",
4427                 b"0001020304+5Z",
4428                 b"_10102030405Z",
4429                 b"00_102030405Z",
4430                 b"0001_2030405Z",
4431                 b"000102_30405Z",
4432                 b"00010203_405Z",
4433                 b"0001020304_5Z",
4434                 b"00010203045_Z",
4435                 b" 10102030405Z",
4436                 b"00 102030405Z",
4437                 b"0001 2030405Z",
4438                 b"000102 30405Z",
4439                 b"00010203 405Z",
4440                 b"0001020304 5Z",
4441                 b"1 0102030405Z",
4442                 b"001 02030405Z",
4443                 b"00012 030405Z",
4444                 b"0001023 0405Z",
4445                 b"000102034 05Z",
4446                 b"00010203045 Z",
4447         )):
4448             with self.assertRaises(DecodeError):
4449                 UTCTime(data)
4450
4451     def test_x680_vector_valid_ber(self):
4452         for data, dt in ((
4453                 (b"8201021200Z", datetime(1982, 1, 2, 12)),
4454                 (b"8201020700-0500", datetime(1982, 1, 2, 12)),
4455                 (b"0101021200Z", datetime(2001, 1, 2, 12)),
4456                 (b"0101020700-0500", datetime(2001, 1, 2, 12)),
4457         )):
4458             data_der = UTCTime.tag_default + len_encode(len(data)) + data
4459             obj = UTCTime().decod(data_der, ctx={"bered": True})
4460             self.assertEqual(obj, dt)
4461             self.assertEqual(obj.todatetime(), dt)
4462             self.assertTrue(obj.ber_encoded)
4463             self.assertTrue(obj.bered)
4464             self.assertEqual(obj.ber_raw, data)
4465             self.assertNotEqual(obj.encode(), data_der)
4466             repr(obj)
4467
4468     def test_go_vectors_valid_ber(self):
4469         for data in ((
4470                 b"910506164540-0700",
4471                 b"910506164540+0730",
4472                 b"9105062345Z",
4473                 b"5105062345Z",
4474         )):
4475             data = UTCTime.tag_default + len_encode(len(data)) + data
4476             obj = UTCTime().decod(data, ctx={"bered": True})
4477             self.assertTrue(obj.ber_encoded)
4478             self.assertTrue(obj.bered)
4479             self.assertNotEqual(obj.encode(), data)
4480             repr(obj)
4481
4482     @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
4483     @given(data_strategy())
4484     def test_valid_ber(self, d):
4485         year = d.draw(integers(min_value=0, max_value=99))
4486         month = d.draw(integers(min_value=1, max_value=12))
4487         day = d.draw(integers(min_value=1, max_value=28))
4488         hours = d.draw(integers(min_value=0, max_value=23))
4489         minute = d.draw(integers(min_value=0, max_value=59))
4490         data = "%02d%02d%02d%02d%02d" % (year, month, day, hours, minute)
4491         dt = datetime(
4492             year + (2000 if year < 50 else 1900),
4493             month,
4494             day,
4495             hours,
4496             minute,
4497         )
4498         dered = False
4499         if d.draw(booleans()):
4500             dered = True
4501             seconds = d.draw(integers(min_value=0, max_value=59))
4502             data += "%02d" % seconds
4503             dt += timedelta(seconds=seconds)
4504         if d.draw(booleans()):
4505             data += "Z"
4506         else:
4507             dered = False
4508             offset_hour = d.draw(integers(min_value=0, max_value=13))
4509             offset_minute = d.draw(integers(min_value=0, max_value=59))
4510             offset = timedelta(seconds=offset_hour * 3600 + offset_minute * 60)
4511             if d.draw(booleans()):
4512                 dt += offset
4513                 data += "-"
4514             else:
4515                 dt -= offset
4516                 data += "+"
4517             data += "%02d%02d" % (offset_hour, offset_minute)
4518         data = data.encode("ascii")
4519         data_der = UTCTime.tag_default + len_encode(len(data)) + data
4520         obj = UTCTime().decod(data_der, ctx={"bered": True})
4521         self.assertEqual(obj, dt)
4522         self.assertEqual(obj.todatetime(), dt)
4523         self.assertEqual(obj.ber_encoded, not dered)
4524         self.assertEqual(obj.bered, not dered)
4525         self.assertEqual(obj.ber_raw, None if dered else data)
4526         self.assertEqual(obj.encode() == data_der, dered)
4527         repr(obj)
4528         bytes(obj)
4529         str(obj)
4530
4531     def test_invalid_ber(self):
4532         for data in ((
4533                 # b"0001020304Z",
4534                 b"-101020304Z",
4535                 b"00-1020304Z",
4536                 b"0001-20304Z",
4537                 b"000102-304Z",
4538                 b"000102-104Z",
4539                 b"00000203-4Z",
4540                 b"+101020304Z",
4541                 b"00+1020304Z",
4542                 b"0001+20304Z",
4543                 b"000102+304Z",
4544                 b"000102+104Z",
4545                 b"00000203+4Z",
4546                 b" 101020304Z",
4547                 b"00 1020304Z",
4548                 b"0001 20304Z",
4549                 b"000102 304Z",
4550                 b"000102 104Z",
4551                 b"00000203 4Z",
4552                 b"1 01020304Z",
4553                 b"001 020304Z",
4554                 b"00012 0304Z",
4555                 b"0001023 04Z",
4556                 b"0001021 04Z",
4557                 b"000002034 Z",
4558                 b"0013020304Z",
4559                 b"0001000304Z",
4560                 b"0001320304Z",
4561                 b"0001022404Z",
4562                 b"0001020360Z",
4563                 b"0002300304Z",
4564                 b"0001020304",
4565                 b"0001020304T",
4566                 b"0001020304+",
4567                 b"0001020304-",
4568                 b"0001020304+0",
4569                 b"0001020304+00",
4570                 b"0001020304+000",
4571                 b"0001020304+000Z",
4572                 b"0001020304+0000Z",
4573                 b"0001020304+-101",
4574                 b"0001020304+01-1",
4575                 b"0001020304+0060",
4576                 b"0001020304+1401",
4577                 b"5001010000+0001",
4578                 b"000102030Z",
4579                 b"0001020Z",
4580         )):
4581             with self.assertRaises(DecodeError):
4582                 UTCTime(data, ctx={"bered": True})
4583             data = data[:8] + data[8+2:]
4584             with self.assertRaises(DecodeError):
4585                 UTCTime(data, ctx={"bered": True})
4586
4587         for data in ((
4588                 # b"000102030405Z",
4589                 b"-10102030405Z",
4590                 b"00-102030405Z",
4591                 b"0001-2030405Z",
4592                 b"000102-30405Z",
4593                 b"000102-10405Z",
4594                 b"00000203-405Z",
4595                 b"0000020304-5Z",
4596                 b"+10102030405Z",
4597                 b"00+102030405Z",
4598                 b"0001+2030405Z",
4599                 b"000102+30405Z",
4600                 b"000102+10405Z",
4601                 b"00000203+405Z",
4602                 b"0000020304+5Z",
4603                 b" 10102030405Z",
4604                 b"00 102030405Z",
4605                 b"0001 2030405Z",
4606                 b"000102 30405Z",
4607                 b"000102 10405Z",
4608                 b"00000203 405Z",
4609                 b"0000020304 5Z",
4610                 b"1 0102030405Z",
4611                 b"001 02030405Z",
4612                 b"00012 030405Z",
4613                 b"0001023 0405Z",
4614                 b"0001021 0405Z",
4615                 b"000002034 05Z",
4616                 b"00000203045 Z",
4617                 b"001302030405Z",
4618                 b"000100030405Z",
4619                 b"000132030405Z",
4620                 b"000102240405Z",
4621                 b"000102036005Z",
4622                 b"000230030405Z",
4623                 b"000102030460Z",
4624                 b"000102030405",
4625                 b"000102030405T",
4626                 b"000102030405+",
4627                 b"000102030405-",
4628                 b"000102030405+0",
4629                 b"000102030405+00",
4630                 b"000102030405+000",
4631                 b"000102030405+000Z",
4632                 b"000102030405+0000Z",
4633                 b"000102030405+-101",
4634                 b"000102030405+01-1",
4635                 b"000102030405+0060",
4636                 b"000102030405+1401",
4637                 b"500101000002+0003",
4638         )):
4639             with self.assertRaises(DecodeError):
4640                 UTCTime(data, ctx={"bered": True})
4641
4642     @given(integers(min_value=0, max_value=49))
4643     def test_pre50(self, year):
4644         self.assertEqual(
4645             UTCTime(("%02d1231235959Z" % year).encode("ascii")).todatetime().year,
4646             2000 + year,
4647         )
4648
4649     @given(integers(min_value=50, max_value=99))
4650     def test_post50(self, year):
4651         self.assertEqual(
4652             UTCTime(("%02d1231235959Z" % year).encode("ascii")).todatetime().year,
4653             1900 + year,
4654         )
4655
4656     @given(
4657         binary(
4658             min_size=(LEN_YYMMDDHHMMSSZ - 1) // 2,
4659             max_size=(LEN_YYMMDDHHMMSSZ - 1) // 2,
4660         ),
4661         binary(min_size=1, max_size=1),
4662         binary(
4663             min_size=(LEN_YYMMDDHHMMSSZ - 1) // 2,
4664             max_size=(LEN_YYMMDDHHMMSSZ - 1) // 2,
4665         ),
4666     )
4667     def test_junk(self, part0, part1, part2):
4668         junk = part0 + part1 + part2
4669         assume(not (set(junk) <= set(digits.encode("ascii"))))
4670         with self.assertRaises(DecodeError):
4671             UTCTime().decode(
4672                 UTCTime.tag_default +
4673                 len_encode(len(junk)) +
4674                 junk
4675             )
4676
4677
4678 @composite
4679 def tlv_value_strategy(draw):
4680     tag_num = draw(integers(min_value=1))
4681     data = draw(binary())
4682     return b"".join((tag_encode(tag_num), len_encode(len(data)), data))
4683
4684
4685 @composite
4686 def any_values_strategy(draw, do_expl=False):
4687     value = draw(one_of(none(), tlv_value_strategy()))
4688     expl = None
4689     if do_expl:
4690         expl = draw(one_of(none(), integers(min_value=1).map(tag_encode)))
4691     optional = draw(one_of(none(), booleans()))
4692     _decoded = (
4693         draw(integers(min_value=0)),
4694         draw(integers(min_value=0)),
4695         draw(integers(min_value=0)),
4696     )
4697     return (value, expl, optional, _decoded)
4698
4699
4700 class AnyInherited(Any):
4701     pass
4702
4703
4704 class TestAny(CommonMixin, TestCase):
4705     base_klass = Any
4706
4707     def test_invalid_value_type(self):
4708         with self.assertRaises(InvalidValueType) as err:
4709             Any(123)
4710         repr(err.exception)
4711
4712     @given(booleans())
4713     def test_optional(self, optional):
4714         obj = Any(optional=optional)
4715         self.assertEqual(obj.optional, optional)
4716
4717     @given(tlv_value_strategy())
4718     def test_ready(self, value):
4719         obj = Any()
4720         self.assertFalse(obj.ready)
4721         repr(obj)
4722         list(obj.pps())
4723         pprint(obj, big_blobs=True, with_decode_path=True)
4724         with self.assertRaises(ObjNotReady) as err:
4725             obj.encode()
4726         repr(err.exception)
4727         obj = Any(value)
4728         self.assertTrue(obj.ready)
4729         repr(obj)
4730         list(obj.pps())
4731         pprint(obj, big_blobs=True, with_decode_path=True)
4732
4733     @given(integers())
4734     def test_basic(self, value):
4735         integer_encoded = Integer(value).encode()
4736         for obj in (
4737                 Any(integer_encoded),
4738                 Any(Integer(value)),
4739                 Any(Any(Integer(value))),
4740         ):
4741             self.assertSequenceEqual(bytes(obj), integer_encoded)
4742             self.assertEqual(
4743                 obj.decode(obj.encode())[0].vlen,
4744                 len(integer_encoded),
4745             )
4746             repr(obj)
4747             list(obj.pps())
4748             pprint(obj, big_blobs=True, with_decode_path=True)
4749             self.assertSequenceEqual(obj.encode(), integer_encoded)
4750
4751     @given(tlv_value_strategy(), tlv_value_strategy())
4752     def test_comparison(self, value1, value2):
4753         for klass in (Any, AnyInherited):
4754             obj1 = klass(value1)
4755             obj2 = klass(value2)
4756             self.assertEqual(obj1 == obj2, value1 == value2)
4757             self.assertEqual(obj1 != obj2, value1 != value2)
4758             self.assertEqual(obj1 == bytes(obj2), value1 == value2)
4759
4760     @given(data_strategy())
4761     def test_call(self, d):
4762         for klass in (Any, AnyInherited):
4763             (
4764                 value_initial,
4765                 expl_initial,
4766                 optional_initial,
4767                 _decoded_initial,
4768             ) = d.draw(any_values_strategy())
4769             obj_initial = klass(
4770                 value_initial,
4771                 expl_initial,
4772                 optional_initial or False,
4773                 _decoded_initial,
4774             )
4775             (
4776                 value,
4777                 expl,
4778                 optional,
4779                 _decoded,
4780             ) = d.draw(any_values_strategy(do_expl=True))
4781             obj = obj_initial(value, expl, optional)
4782             if obj.ready:
4783                 value_expected = None if value is None else value
4784                 self.assertEqual(obj, value_expected)
4785             self.assertEqual(obj.expl_tag, expl or expl_initial)
4786             if obj.default is None:
4787                 optional = optional_initial if optional is None else optional
4788                 optional = False if optional is None else optional
4789             self.assertEqual(obj.optional, optional)
4790
4791     def test_simultaneous_impl_expl(self):
4792         # override it, as Any does not have implicit tag
4793         pass
4794
4795     def test_decoded(self):
4796         # override it, as Any does not have implicit tag
4797         pass
4798
4799     @given(any_values_strategy())
4800     def test_copy(self, values):
4801         for klass in (Any, AnyInherited):
4802             obj = klass(*values)
4803             for copy_func in copy_funcs:
4804                 obj_copied = copy_func(obj)
4805                 self.assert_copied_basic_fields(obj, obj_copied)
4806                 self.assertEqual(obj._value, obj_copied._value)
4807
4808     @given(binary().map(OctetString))
4809     def test_stripped(self, value):
4810         obj = Any(value)
4811         with self.assertRaises(NotEnoughData):
4812             obj.decode(obj.encode()[:-1])
4813
4814     @given(
4815         tlv_value_strategy(),
4816         integers(min_value=1).map(tag_ctxc),
4817     )
4818     def test_stripped_expl(self, value, tag_expl):
4819         obj = Any(value, expl=tag_expl)
4820         with self.assertRaises(NotEnoughData):
4821             obj.decode(obj.encode()[:-1])
4822
4823     @given(
4824         integers(min_value=31),
4825         integers(min_value=0),
4826         decode_path_strat,
4827     )
4828     def test_bad_tag(self, tag, offset, decode_path):
4829         with self.assertRaises(DecodeError) as err:
4830             Any().decode(
4831                 tag_encode(tag)[:-1],
4832                 offset=offset,
4833                 decode_path=decode_path,
4834             )
4835         repr(err.exception)
4836         self.assertEqual(err.exception.offset, offset)
4837         self.assertEqual(err.exception.decode_path, decode_path)
4838
4839     @given(
4840         integers(min_value=128),
4841         integers(min_value=0),
4842         decode_path_strat,
4843     )
4844     def test_bad_len(self, l, offset, decode_path):
4845         with self.assertRaises(DecodeError) as err:
4846             Any().decode(
4847                 Any.tag_default + len_encode(l)[:-1],
4848                 offset=offset,
4849                 decode_path=decode_path,
4850             )
4851         repr(err.exception)
4852         self.assertEqual(err.exception.offset, offset)
4853         self.assertEqual(err.exception.decode_path, decode_path)
4854
4855     @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
4856     @given(
4857         any_values_strategy(),
4858         integers().map(lambda x: Integer(x).encode()),
4859         integers(min_value=1).map(tag_ctxc),
4860         integers(min_value=0),
4861         binary(max_size=5),
4862     )
4863     def test_symmetric(self, values, value, tag_expl, offset, tail_junk):
4864         for klass in (Any, AnyInherited):
4865             _, _, optional, _decoded = values
4866             obj = klass(value=value, optional=optional, _decoded=_decoded)
4867             repr(obj)
4868             list(obj.pps())
4869             pprint(obj, big_blobs=True, with_decode_path=True)
4870             self.assertFalse(obj.expled)
4871             tag_class, _, tag_num = tag_decode(tag_strip(value)[0])
4872             self.assertEqual(obj.tag_order, (tag_class, tag_num))
4873             obj_encoded = obj.encode()
4874             obj_expled = obj(value, expl=tag_expl)
4875             self.assertTrue(obj_expled.expled)
4876             tag_class, _, tag_num = tag_decode(tag_expl)
4877             self.assertEqual(obj_expled.tag_order, (tag_class, tag_num))
4878             repr(obj_expled)
4879             list(obj_expled.pps())
4880             pprint(obj_expled, big_blobs=True, with_decode_path=True)
4881             obj_expled_encoded = obj_expled.encode()
4882             ctx_copied = deepcopy(ctx_dummy)
4883             obj_decoded, tail = obj_expled.decode(
4884                 obj_expled_encoded + tail_junk,
4885                 offset=offset,
4886                 ctx=ctx_copied,
4887             )
4888             self.assertDictEqual(ctx_copied, ctx_dummy)
4889             repr(obj_decoded)
4890             list(obj_decoded.pps())
4891             pprint(obj_decoded, big_blobs=True, with_decode_path=True)
4892             self.assertEqual(tail, tail_junk)
4893             self.assertEqual(obj_decoded, obj_expled)
4894             self.assertEqual(bytes(obj_decoded), bytes(obj_expled))
4895             self.assertEqual(bytes(obj_decoded), bytes(obj))
4896             self.assertSequenceEqual(obj_decoded.encode(), obj_expled_encoded)
4897             self.assertSequenceEqual(obj_decoded.expl_tag, tag_expl)
4898             self.assertEqual(obj_decoded.expl_tlen, len(tag_expl))
4899             self.assertEqual(
4900                 obj_decoded.expl_llen,
4901                 len(len_encode(len(obj_encoded))),
4902             )
4903             self.assertEqual(obj_decoded.tlvlen, len(obj_encoded))
4904             self.assertEqual(obj_decoded.expl_vlen, len(obj_encoded))
4905             self.assertEqual(
4906                 obj_decoded.offset,
4907                 offset + obj_decoded.expl_tlen + obj_decoded.expl_llen,
4908             )
4909             self.assertEqual(obj_decoded.expl_offset, offset)
4910             self.assertEqual(obj_decoded.tlen, 0)
4911             self.assertEqual(obj_decoded.llen, 0)
4912             self.assertEqual(obj_decoded.vlen, len(value))
4913             assert_exceeding_data(
4914                 self,
4915                 lambda: obj_expled.decod(obj_expled_encoded + tail_junk),
4916                 tail_junk,
4917             )
4918
4919     @given(
4920         integers(min_value=1).map(tag_ctxc),
4921         integers(min_value=0, max_value=3),
4922         integers(min_value=0),
4923         decode_path_strat,
4924         binary(),
4925     )
4926     def test_indefinite(self, expl, chunks, offset, decode_path, junk):
4927         chunk = Boolean(False, expl=expl).encode()
4928         encoded = (
4929             OctetString.tag_default +
4930             LENINDEF +
4931             b"".join([chunk] * chunks) +
4932             EOC
4933         )
4934         with self.assertRaises(LenIndefForm):
4935             Any().decode(
4936                 encoded + junk,
4937                 offset=offset,
4938                 decode_path=decode_path,
4939             )
4940         obj, tail = Any().decode(
4941             encoded + junk,
4942             offset=offset,
4943             decode_path=decode_path,
4944             ctx={"bered": True},
4945         )
4946         self.assertSequenceEqual(tail, junk)
4947         self.assertEqual(obj.offset, offset)
4948         self.assertEqual(obj.tlvlen, len(encoded))
4949         self.assertTrue(obj.lenindef)
4950         self.assertFalse(obj.ber_encoded)
4951         self.assertTrue(obj.bered)
4952         obj = copy(obj)
4953         self.assertTrue(obj.lenindef)
4954         self.assertFalse(obj.ber_encoded)
4955         self.assertTrue(obj.bered)
4956         repr(obj)
4957         list(obj.pps())
4958         pprint(obj, big_blobs=True, with_decode_path=True)
4959         with self.assertRaises(NotEnoughData) as err:
4960             Any().decode(
4961                 encoded[:-1],
4962                 offset=offset,
4963                 decode_path=decode_path,
4964                 ctx={"bered": True},
4965             )
4966         self.assertEqual(err.exception.offset, offset + 1 + 1 + len(chunk) * chunks)
4967         self.assertEqual(err.exception.decode_path, decode_path + (str(chunks),))
4968
4969         class SeqOf(SequenceOf):
4970             schema = Boolean(expl=expl)
4971
4972         class Seq(Sequence):
4973             schema = (
4974                 ("type", ObjectIdentifier(defines=((("value",), {
4975                     ObjectIdentifier("1.2.3"): SeqOf(impl=OctetString.tag_default),
4976                 }),))),
4977                 ("value", Any()),
4978             )
4979         seq = Seq((
4980             ("type", ObjectIdentifier("1.2.3")),
4981             ("value", Any(encoded)),
4982         ))
4983         seq_encoded = seq.encode()
4984         seq_decoded, _ = Seq().decode(seq_encoded, ctx={"bered": True})
4985         self.assertIsNotNone(seq_decoded["value"].defined)
4986         repr(seq_decoded)
4987         list(seq_decoded.pps())
4988         pprint(seq_decoded, big_blobs=True, with_decode_path=True)
4989         self.assertTrue(seq_decoded.bered)
4990         self.assertFalse(seq_decoded["type"].bered)
4991         self.assertTrue(seq_decoded["value"].bered)
4992
4993         chunk = chunk[:-1] + b"\x01"
4994         chunks = b"".join([chunk] * (chunks + 1))
4995         encoded = OctetString.tag_default + len_encode(len(chunks)) + chunks
4996         seq = Seq((
4997             ("type", ObjectIdentifier("1.2.3")),
4998             ("value", Any(encoded)),
4999         ))
5000         seq_encoded = seq.encode()
5001         seq_decoded, _ = Seq().decode(seq_encoded, ctx={"bered": True})
5002         self.assertIsNotNone(seq_decoded["value"].defined)
5003         repr(seq_decoded)
5004         list(seq_decoded.pps())
5005         pprint(seq_decoded, big_blobs=True, with_decode_path=True)
5006         self.assertTrue(seq_decoded.bered)
5007         self.assertFalse(seq_decoded["type"].bered)
5008         self.assertTrue(seq_decoded["value"].bered)
5009
5010
5011 @composite
5012 def choice_values_strategy(draw, value_required=False, schema=None, do_expl=False):
5013     if schema is None:
5014         names = list(draw(sets(text_letters(), min_size=1, max_size=5)))
5015         tags = [{tag_type: tag_value} for tag_type, tag_value in draw(sets(
5016             one_of(
5017                 tuples(just("impl"), integers(min_value=0).map(tag_encode)),
5018                 tuples(just("expl"), integers(min_value=0).map(tag_ctxp)),
5019             ),
5020             min_size=len(names),
5021             max_size=len(names),
5022         ))]
5023         schema = [
5024             (name, Integer(**tag_kwargs))
5025             for name, tag_kwargs in zip(names, tags)
5026         ]
5027     value = None
5028     if value_required or draw(booleans()):
5029         value = draw(tuples(
5030             sampled_from([name for name, _ in schema]),
5031             integers().map(Integer),
5032         ))
5033     expl = None
5034     if do_expl:
5035         expl = draw(one_of(none(), integers(min_value=1).map(tag_encode)))
5036     default = draw(one_of(
5037         none(),
5038         tuples(sampled_from([name for name, _ in schema]), integers().map(Integer)),
5039     ))
5040     optional = draw(one_of(none(), booleans()))
5041     _decoded = (
5042         draw(integers(min_value=0)),
5043         draw(integers(min_value=0)),
5044         draw(integers(min_value=0)),
5045     )
5046     return (schema, value, expl, default, optional, _decoded)
5047
5048
5049 class ChoiceInherited(Choice):
5050     pass
5051
5052
5053 class TestChoice(CommonMixin, TestCase):
5054     class Wahl(Choice):
5055         schema = (("whatever", Boolean()),)
5056     base_klass = Wahl
5057
5058     def test_schema_required(self):
5059         with assertRaisesRegex(self, ValueError, "schema must be specified"):
5060             Choice()
5061
5062     def test_impl_forbidden(self):
5063         with assertRaisesRegex(self, ValueError, "no implicit tag allowed"):
5064             Choice(impl=b"whatever")
5065
5066     def test_invalid_value_type(self):
5067         with self.assertRaises(InvalidValueType) as err:
5068             self.base_klass(123)
5069         repr(err.exception)
5070         with self.assertRaises(ObjUnknown) as err:
5071             self.base_klass(("whenever", Boolean(False)))
5072         repr(err.exception)
5073         with self.assertRaises(InvalidValueType) as err:
5074             self.base_klass(("whatever", Integer(123)))
5075         repr(err.exception)
5076
5077     @given(booleans())
5078     def test_optional(self, optional):
5079         obj = self.base_klass(
5080             default=self.base_klass(("whatever", Boolean(False))),
5081             optional=optional,
5082         )
5083         self.assertTrue(obj.optional)
5084
5085     @given(booleans())
5086     def test_ready(self, value):
5087         obj = self.base_klass()
5088         self.assertFalse(obj.ready)
5089         repr(obj)
5090         list(obj.pps())
5091         pprint(obj, big_blobs=True, with_decode_path=True)
5092         self.assertIsNone(obj["whatever"])
5093         with self.assertRaises(ObjNotReady) as err:
5094             obj.encode()
5095         repr(err.exception)
5096         obj["whatever"] = Boolean()
5097         self.assertFalse(obj.ready)
5098         repr(obj)
5099         list(obj.pps())
5100         pprint(obj, big_blobs=True, with_decode_path=True)
5101         obj["whatever"] = Boolean(value)
5102         self.assertTrue(obj.ready)
5103         repr(obj)
5104         list(obj.pps())
5105         pprint(obj, big_blobs=True, with_decode_path=True)
5106
5107     @given(booleans(), booleans())
5108     def test_comparison(self, value1, value2):
5109         class WahlInherited(self.base_klass):
5110             pass
5111         for klass in (self.base_klass, WahlInherited):
5112             obj1 = klass(("whatever", Boolean(value1)))
5113             obj2 = klass(("whatever", Boolean(value2)))
5114             self.assertEqual(obj1 == obj2, value1 == value2)
5115             self.assertEqual(obj1 != obj2, value1 != value2)
5116             self.assertEqual(obj1 == obj2._value, value1 == value2)
5117             self.assertFalse(obj1 == obj2._value[1])
5118
5119     @given(data_strategy())
5120     def test_call(self, d):
5121         for klass in (Choice, ChoiceInherited):
5122             (
5123                 schema_initial,
5124                 value_initial,
5125                 expl_initial,
5126                 default_initial,
5127                 optional_initial,
5128                 _decoded_initial,
5129             ) = d.draw(choice_values_strategy())
5130
5131             class Wahl(klass):
5132                 schema = schema_initial
5133             obj_initial = Wahl(
5134                 value=value_initial,
5135                 expl=expl_initial,
5136                 default=default_initial,
5137                 optional=optional_initial or False,
5138                 _decoded=_decoded_initial,
5139             )
5140             (
5141                 _,
5142                 value,
5143                 expl,
5144                 default,
5145                 optional,
5146                 _decoded,
5147             ) = d.draw(choice_values_strategy(schema=schema_initial, do_expl=True))
5148             obj = obj_initial(value, expl, default, optional)
5149             if obj.ready:
5150                 value_expected = default if value is None else value
5151                 value_expected = (
5152                     default_initial if value_expected is None
5153                     else value_expected
5154                 )
5155                 self.assertEqual(obj.choice, value_expected[0])
5156                 self.assertEqual(obj.value, int(value_expected[1]))
5157             self.assertEqual(obj.expl_tag, expl or expl_initial)
5158             default_expect = default_initial if default is None else default
5159             if default_expect is not None:
5160                 self.assertEqual(obj.default.choice, default_expect[0])
5161                 self.assertEqual(obj.default.value, int(default_expect[1]))
5162             if obj.default is None:
5163                 optional = optional_initial if optional is None else optional
5164                 optional = False if optional is None else optional
5165             else:
5166                 optional = True
5167             self.assertEqual(obj.optional, optional)
5168             self.assertEqual(obj.specs, obj_initial.specs)
5169
5170     def test_simultaneous_impl_expl(self):
5171         # override it, as Any does not have implicit tag
5172         pass
5173
5174     def test_decoded(self):
5175         # override it, as Any does not have implicit tag
5176         pass
5177
5178     @given(choice_values_strategy())
5179     def test_copy(self, values):
5180         _schema, value, expl, default, optional, _decoded = values
5181
5182         class Wahl(self.base_klass):
5183             schema = _schema
5184         register_class(Wahl)
5185         obj = Wahl(
5186             value=value,
5187             expl=expl,
5188             default=default,
5189             optional=optional or False,
5190             _decoded=_decoded,
5191         )
5192         for copy_func in copy_funcs:
5193             obj_copied = copy_func(obj)
5194             self.assertIsNone(obj.tag)
5195             self.assertIsNone(obj_copied.tag)
5196             # hack for assert_copied_basic_fields
5197             obj.tag = "whatever"
5198             obj_copied.tag = "whatever"
5199             self.assert_copied_basic_fields(obj, obj_copied)
5200             obj.tag = None
5201             self.assertEqual(obj._value, obj_copied._value)
5202             self.assertEqual(obj.specs, obj_copied.specs)
5203
5204     @given(booleans())
5205     def test_stripped(self, value):
5206         obj = self.base_klass(("whatever", Boolean(value)))
5207         with self.assertRaises(NotEnoughData):
5208             obj.decode(obj.encode()[:-1])
5209
5210     @given(
5211         booleans(),
5212         integers(min_value=1).map(tag_ctxc),
5213     )
5214     def test_stripped_expl(self, value, tag_expl):
5215         obj = self.base_klass(("whatever", Boolean(value)), expl=tag_expl)
5216         with self.assertRaises(NotEnoughData):
5217             obj.decode(obj.encode()[:-1])
5218
5219     @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
5220     @given(data_strategy())
5221     def test_symmetric(self, d):
5222         _schema, value, _, default, optional, _decoded = d.draw(
5223             choice_values_strategy(value_required=True)
5224         )
5225         tag_expl = tag_ctxc(d.draw(integers(min_value=1)))
5226         offset = d.draw(integers(min_value=0))
5227         tail_junk = d.draw(binary(max_size=5))
5228
5229         class Wahl(self.base_klass):
5230             schema = _schema
5231         obj = Wahl(
5232             value=value,
5233             default=default,
5234             optional=optional,
5235             _decoded=_decoded,
5236         )
5237         repr(obj)
5238         list(obj.pps())
5239         pprint(obj, big_blobs=True, with_decode_path=True)
5240         self.assertFalse(obj.expled)
5241         self.assertEqual(obj.tag_order, obj.value.tag_order)
5242         obj_encoded = obj.encode()
5243         obj_expled = obj(value, expl=tag_expl)
5244         self.assertTrue(obj_expled.expled)
5245         tag_class, _, tag_num = tag_decode(tag_expl)
5246         self.assertEqual(obj_expled.tag_order, (tag_class, tag_num))
5247         repr(obj_expled)
5248         list(obj_expled.pps())
5249         pprint(obj_expled, big_blobs=True, with_decode_path=True)
5250         obj_expled_encoded = obj_expled.encode()
5251         ctx_copied = deepcopy(ctx_dummy)
5252         obj_decoded, tail = obj_expled.decode(
5253             obj_expled_encoded + tail_junk,
5254             offset=offset,
5255             ctx=ctx_copied,
5256         )
5257         self.assertDictEqual(ctx_copied, ctx_dummy)
5258         repr(obj_decoded)
5259         list(obj_decoded.pps())
5260         pprint(obj_decoded, big_blobs=True, with_decode_path=True)
5261         self.assertEqual(tail, tail_junk)
5262         self.assertEqual(obj_decoded, obj_expled)
5263         self.assertEqual(obj_decoded.choice, obj_expled.choice)
5264         self.assertEqual(obj_decoded.value, obj_expled.value)
5265         self.assertEqual(obj_decoded.choice, obj.choice)
5266         self.assertEqual(obj_decoded.value, obj.value)
5267         self.assertSequenceEqual(obj_decoded.encode(), obj_expled_encoded)
5268         self.assertSequenceEqual(obj_decoded.expl_tag, tag_expl)
5269         self.assertEqual(obj_decoded.expl_tlen, len(tag_expl))
5270         self.assertEqual(
5271             obj_decoded.expl_llen,
5272             len(len_encode(len(obj_encoded))),
5273         )
5274         self.assertEqual(obj_decoded.tlvlen, len(obj_encoded))
5275         self.assertEqual(obj_decoded.expl_vlen, len(obj_encoded))
5276         self.assertEqual(
5277             obj_decoded.offset,
5278             offset + obj_decoded.expl_tlen + obj_decoded.expl_llen,
5279         )
5280         self.assertEqual(obj_decoded.expl_offset, offset)
5281         self.assertSequenceEqual(
5282             obj_expled_encoded[
5283                 obj_decoded.value.fulloffset - offset:
5284                 obj_decoded.value.fulloffset + obj_decoded.value.fulllen - offset
5285             ],
5286             obj_encoded,
5287         )
5288         assert_exceeding_data(
5289             self,
5290             lambda: obj_expled.decod(obj_expled_encoded + tail_junk),
5291             tail_junk,
5292         )
5293
5294     @given(integers())
5295     def test_set_get(self, value):
5296         class Wahl(Choice):
5297             schema = (
5298                 ("erste", Boolean()),
5299                 ("zweite", Integer()),
5300             )
5301         obj = Wahl()
5302         with self.assertRaises(ObjUnknown) as err:
5303             obj["whatever"] = "whenever"
5304         with self.assertRaises(InvalidValueType) as err:
5305             obj["zweite"] = Boolean(False)
5306         obj["zweite"] = Integer(value)
5307         repr(err.exception)
5308         with self.assertRaises(ObjUnknown) as err:
5309             obj["whatever"]
5310         repr(err.exception)
5311         self.assertIsNone(obj["erste"])
5312         self.assertEqual(obj["zweite"], Integer(value))
5313
5314     def test_tag_mismatch(self):
5315         class Wahl(Choice):
5316             schema = (
5317                 ("erste", Boolean()),
5318             )
5319         int_encoded = Integer(123).encode()
5320         bool_encoded = Boolean(False).encode()
5321         obj = Wahl()
5322         obj.decode(bool_encoded)
5323         with self.assertRaises(TagMismatch):
5324             obj.decode(int_encoded)
5325
5326     def test_tag_mismatch_underlying(self):
5327         class SeqOfBoolean(SequenceOf):
5328             schema = Boolean()
5329
5330         class SeqOfInteger(SequenceOf):
5331             schema = Integer()
5332
5333         class Wahl(Choice):
5334             schema = (
5335                 ("erste", SeqOfBoolean()),
5336             )
5337
5338         int_encoded = SeqOfInteger((Integer(123),)).encode()
5339         bool_encoded = SeqOfBoolean((Boolean(False),)).encode()
5340         obj = Wahl()
5341         obj.decode(bool_encoded)
5342         with self.assertRaises(TagMismatch) as err:
5343             obj.decode(int_encoded)
5344         self.assertEqual(err.exception.decode_path, ("erste", "0"))
5345
5346
5347 @composite
5348 def seq_values_strategy(draw, seq_klass, do_expl=False):
5349     value = None
5350     if draw(booleans()):
5351         value = seq_klass()
5352         value._value = draw(dictionaries(
5353             integers(),
5354             one_of(
5355                 booleans().map(Boolean),
5356                 integers().map(Integer),
5357             ),
5358         ))
5359     schema = None
5360     if draw(booleans()):
5361         schema = list(draw(dictionaries(
5362             integers(),
5363             one_of(
5364                 booleans().map(Boolean),
5365                 integers().map(Integer),
5366             ),
5367         )).items())
5368     impl = None
5369     expl = None
5370     if do_expl:
5371         expl = draw(one_of(none(), integers(min_value=1).map(tag_encode)))
5372     else:
5373         impl = draw(one_of(none(), integers(min_value=1).map(tag_encode)))
5374     default = None
5375     if draw(booleans()):
5376         default = seq_klass()
5377         default._value = draw(dictionaries(
5378             integers(),
5379             one_of(
5380                 booleans().map(Boolean),
5381                 integers().map(Integer),
5382             ),
5383         ))
5384     optional = draw(one_of(none(), booleans()))
5385     _decoded = (
5386         draw(integers(min_value=0)),
5387         draw(integers(min_value=0)),
5388         draw(integers(min_value=0)),
5389     )
5390     return (value, schema, impl, expl, default, optional, _decoded)
5391
5392
5393 @composite
5394 def sequence_strategy(draw, seq_klass):
5395     inputs = draw(lists(
5396         one_of(
5397             tuples(just(Boolean), booleans(), one_of(none(), booleans())),
5398             tuples(just(Integer), integers(), one_of(none(), integers())),
5399         ),
5400         max_size=6,
5401     ))
5402     tags = draw(sets(
5403         integers(min_value=1),
5404         min_size=len(inputs),
5405         max_size=len(inputs),
5406     ))
5407     inits = [
5408         ({"expl": tag_ctxc(tag)} if expled else {"impl": tag_encode(tag)})
5409         for tag, expled in zip(tags, draw(lists(
5410             booleans(),
5411             min_size=len(inputs),
5412             max_size=len(inputs),
5413         )))
5414     ]
5415     empties = []
5416     for i, optional in enumerate(draw(lists(
5417             sampled_from(("required", "optional", "empty")),
5418             min_size=len(inputs),
5419             max_size=len(inputs),
5420     ))):
5421         if optional in ("optional", "empty"):
5422             inits[i]["optional"] = True
5423         if optional == "empty":
5424             empties.append(i)
5425     empties = set(empties)
5426     names = list(draw(sets(
5427         text_printable,
5428         min_size=len(inputs),
5429         max_size=len(inputs),
5430     )))
5431     schema = []
5432     for i, (klass, value, default) in enumerate(inputs):
5433         schema.append((names[i], klass(default=default, **inits[i])))
5434     seq_name = draw(text_letters())
5435     Seq = type(seq_name, (seq_klass,), {"schema": tuple(schema)})
5436     seq = Seq()
5437     expects = []
5438     for i, (klass, value, default) in enumerate(inputs):
5439         name = names[i]
5440         _, spec = schema[i]
5441         expect = {
5442             "name": name,
5443             "optional": False,
5444             "presented": False,
5445             "default_value": None if spec.default is None else default,
5446             "value": None,
5447         }
5448         if i in empties:
5449             expect["optional"] = True
5450         else:
5451             expect["presented"] = True
5452             expect["value"] = value
5453             if spec.optional:
5454                 expect["optional"] = True
5455             if default is not None and default == value:
5456                 expect["presented"] = False
5457             seq[name] = klass(value)
5458         expects.append(expect)
5459     return seq, expects
5460
5461
5462 @composite
5463 def sequences_strategy(draw, seq_klass):
5464     tags = draw(sets(integers(min_value=1), min_size=0, max_size=5))
5465     inits = [
5466         ({"expl": tag_ctxc(tag)} if expled else {"impl": tag_encode(tag)})
5467         for tag, expled in zip(tags, draw(lists(
5468             booleans(),
5469             min_size=len(tags),
5470             max_size=len(tags),
5471         )))
5472     ]
5473     defaulted = set(
5474         i for i, is_default in enumerate(draw(lists(
5475             booleans(),
5476             min_size=len(tags),
5477             max_size=len(tags),
5478         ))) if is_default
5479     )
5480     names = list(draw(sets(
5481         text_printable,
5482         min_size=len(tags),
5483         max_size=len(tags),
5484     )))
5485     seq_expectses = draw(lists(
5486         sequence_strategy(seq_klass=seq_klass),
5487         min_size=len(tags),
5488         max_size=len(tags),
5489     ))
5490     seqs = [seq for seq, _ in seq_expectses]
5491     schema = []
5492     for i, (name, seq) in enumerate(zip(names, seqs)):
5493         schema.append((
5494             name,
5495             seq(default=(seq if i in defaulted else None), **inits[i]),
5496         ))
5497     seq_name = draw(text_letters())
5498     Seq = type(seq_name, (seq_klass,), {"schema": tuple(schema)})
5499     seq_outer = Seq()
5500     expect_outers = []
5501     for name, (seq_inner, expects_inner) in zip(names, seq_expectses):
5502         expect = {
5503             "name": name,
5504             "expects": expects_inner,
5505             "presented": False,
5506         }
5507         seq_outer[name] = seq_inner
5508         if seq_outer.specs[name].default is None:
5509             expect["presented"] = True
5510         expect_outers.append(expect)
5511     return seq_outer, expect_outers
5512
5513
5514 class SeqMixing(object):
5515     def test_invalid_value_type(self):
5516         with self.assertRaises(InvalidValueType) as err:
5517             self.base_klass(123)
5518         repr(err.exception)
5519
5520     def test_invalid_value_type_set(self):
5521         class Seq(self.base_klass):
5522             schema = (("whatever", Boolean()),)
5523         seq = Seq()
5524         with self.assertRaises(InvalidValueType) as err:
5525             seq["whatever"] = Integer(123)
5526         repr(err.exception)
5527
5528     @given(booleans())
5529     def test_optional(self, optional):
5530         obj = self.base_klass(default=self.base_klass(), optional=optional)
5531         self.assertTrue(obj.optional)
5532
5533     @given(data_strategy())
5534     def test_ready(self, d):
5535         ready = {
5536             str(i): v for i, v in enumerate(d.draw(lists(
5537                 booleans(),
5538                 min_size=1,
5539                 max_size=3,
5540             )))
5541         }
5542         non_ready = {
5543             str(i + len(ready)): v for i, v in enumerate(d.draw(lists(
5544                 booleans(),
5545                 min_size=1,
5546                 max_size=3,
5547             )))
5548         }
5549         schema_input = []
5550         for name in d.draw(permutations(
5551                 list(ready.keys()) + list(non_ready.keys()),
5552         )):
5553             schema_input.append((name, Boolean()))
5554
5555         class Seq(self.base_klass):
5556             schema = tuple(schema_input)
5557         seq = Seq()
5558         for name in ready.keys():
5559             seq[name]
5560             seq[name] = Boolean()
5561         self.assertFalse(seq.ready)
5562         repr(seq)
5563         list(seq.pps())
5564         pprint(seq, big_blobs=True, with_decode_path=True)
5565         for name, value in ready.items():
5566             seq[name] = Boolean(value)
5567         self.assertFalse(seq.ready)
5568         repr(seq)
5569         list(seq.pps())
5570         pprint(seq, big_blobs=True, with_decode_path=True)
5571         with self.assertRaises(ObjNotReady) as err:
5572             seq.encode()
5573         repr(err.exception)
5574         for name, value in non_ready.items():
5575             seq[name] = Boolean(value)
5576         self.assertTrue(seq.ready)
5577         repr(seq)
5578         list(seq.pps())
5579         pprint(seq, big_blobs=True, with_decode_path=True)
5580
5581     @given(data_strategy())
5582     def test_call(self, d):
5583         class SeqInherited(self.base_klass):
5584             pass
5585         for klass in (self.base_klass, SeqInherited):
5586             (
5587                 value_initial,
5588                 schema_initial,
5589                 impl_initial,
5590                 expl_initial,
5591                 default_initial,
5592                 optional_initial,
5593                 _decoded_initial,
5594             ) = d.draw(seq_values_strategy(seq_klass=klass))
5595             obj_initial = klass(
5596                 value_initial,
5597                 schema_initial,
5598                 impl_initial,
5599                 expl_initial,
5600                 default_initial,
5601                 optional_initial or False,
5602                 _decoded_initial,
5603             )
5604             (
5605                 value,
5606                 _,
5607                 impl,
5608                 expl,
5609                 default,
5610                 optional,
5611                 _decoded,
5612             ) = d.draw(seq_values_strategy(
5613                 seq_klass=klass,
5614                 do_expl=impl_initial is None,
5615             ))
5616             obj = obj_initial(value, impl, expl, default, optional)
5617             value_expected = default if value is None else value
5618             value_expected = (
5619                 default_initial if value_expected is None
5620                 else value_expected
5621             )
5622             self.assertEqual(obj._value, getattr(value_expected, "_value", {}))
5623             self.assertEqual(obj.tag, impl or impl_initial or obj.tag_default)
5624             self.assertEqual(obj.expl_tag, expl or expl_initial)
5625             self.assertEqual(
5626                 {} if obj.default is None else obj.default._value,
5627                 getattr(default_initial if default is None else default, "_value", {}),
5628             )
5629             if obj.default is None:
5630                 optional = optional_initial if optional is None else optional
5631                 optional = False if optional is None else optional
5632             else:
5633                 optional = True
5634             self.assertEqual(list(obj.specs.items()), schema_initial or [])
5635             self.assertEqual(obj.optional, optional)
5636
5637     @given(data_strategy())
5638     def test_copy(self, d):
5639         class SeqInherited(self.base_klass):
5640             pass
5641         register_class(SeqInherited)
5642         for klass in (self.base_klass, SeqInherited):
5643             values = d.draw(seq_values_strategy(seq_klass=klass))
5644             obj = klass(*values)
5645             for copy_func in copy_funcs:
5646                 obj_copied = copy_func(obj)
5647                 self.assert_copied_basic_fields(obj, obj_copied)
5648                 self.assertEqual(obj.specs, obj_copied.specs)
5649                 self.assertEqual(obj._value, obj_copied._value)
5650
5651     @given(data_strategy())
5652     def test_stripped(self, d):
5653         value = d.draw(integers())
5654         tag_impl = tag_encode(d.draw(integers(min_value=1)))
5655
5656         class Seq(self.base_klass):
5657             impl = tag_impl
5658             schema = (("whatever", Integer()),)
5659         seq = Seq()
5660         seq["whatever"] = Integer(value)
5661         with self.assertRaises(NotEnoughData):
5662             seq.decode(seq.encode()[:-1])
5663
5664     @given(data_strategy())
5665     def test_stripped_expl(self, d):
5666         value = d.draw(integers())
5667         tag_expl = tag_ctxc(d.draw(integers(min_value=1)))
5668
5669         class Seq(self.base_klass):
5670             expl = tag_expl
5671             schema = (("whatever", Integer()),)
5672         seq = Seq()
5673         seq["whatever"] = Integer(value)
5674         with self.assertRaises(NotEnoughData):
5675             seq.decode(seq.encode()[:-1])
5676
5677     @given(integers(min_value=3), binary(min_size=2))
5678     def test_non_tag_mismatch_raised(self, junk_tag_num, junk):
5679         junk = tag_encode(junk_tag_num) + junk
5680         try:
5681             _, _, len_encoded = tag_strip(memoryview(junk))
5682             len_decode(len_encoded)
5683         except Exception:
5684             assume(True)
5685         else:
5686             assume(False)
5687
5688         class Seq(self.base_klass):
5689             schema = (
5690                 ("whatever", Integer()),
5691                 ("junk", Any()),
5692                 ("whenever", Integer()),
5693             )
5694         seq = Seq()
5695         seq["whatever"] = Integer(123)
5696         seq["junk"] = Any(junk)
5697         seq["whenever"] = Integer(123)
5698         with self.assertRaises(DecodeError):
5699             seq.decode(seq.encode())
5700
5701     @given(
5702         integers(min_value=31),
5703         integers(min_value=0),
5704         decode_path_strat,
5705     )
5706     def test_bad_tag(self, tag, offset, decode_path):
5707         with self.assertRaises(DecodeError) as err:
5708             self.base_klass().decode(
5709                 tag_encode(tag)[:-1],
5710                 offset=offset,
5711                 decode_path=decode_path,
5712             )
5713         repr(err.exception)
5714         self.assertEqual(err.exception.offset, offset)
5715         self.assertEqual(err.exception.decode_path, decode_path)
5716
5717     @given(
5718         integers(min_value=128),
5719         integers(min_value=0),
5720         decode_path_strat,
5721     )
5722     def test_bad_len(self, l, offset, decode_path):
5723         with self.assertRaises(DecodeError) as err:
5724             self.base_klass().decode(
5725                 self.base_klass.tag_default + len_encode(l)[:-1],
5726                 offset=offset,
5727                 decode_path=decode_path,
5728             )
5729         repr(err.exception)
5730         self.assertEqual(err.exception.offset, offset)
5731         self.assertEqual(err.exception.decode_path, decode_path)
5732
5733     def _assert_expects(self, seq, expects):
5734         for expect in expects:
5735             self.assertEqual(
5736                 seq.specs[expect["name"]].optional,
5737                 expect["optional"],
5738             )
5739             if expect["default_value"] is not None:
5740                 self.assertEqual(
5741                     seq.specs[expect["name"]].default,
5742                     expect["default_value"],
5743                 )
5744             if expect["presented"]:
5745                 self.assertIn(expect["name"], seq)
5746                 self.assertEqual(seq[expect["name"]], expect["value"])
5747
5748     @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
5749     @given(data_strategy())
5750     def test_symmetric(self, d):
5751         seq, expects = d.draw(sequence_strategy(seq_klass=self.base_klass))
5752         tail_junk = d.draw(binary(max_size=5))
5753         self.assertTrue(seq.ready)
5754         self.assertFalse(seq.decoded)
5755         self._assert_expects(seq, expects)
5756         repr(seq)
5757         list(seq.pps())
5758         pprint(seq, big_blobs=True, with_decode_path=True)
5759         self.assertTrue(seq.ready)
5760         seq_encoded = seq.encode()
5761         seq_decoded, tail = seq.decode(seq_encoded + tail_junk)
5762         self.assertFalse(seq_decoded.lenindef)
5763         self.assertFalse(seq_decoded.ber_encoded)
5764         self.assertFalse(seq_decoded.bered)
5765
5766         t, _, lv = tag_strip(seq_encoded)
5767         _, _, v = len_decode(lv)
5768         seq_encoded_lenindef = t + LENINDEF + v + EOC
5769         with self.assertRaises(DecodeError):
5770             seq.decode(seq_encoded_lenindef)
5771         ctx_copied = deepcopy(ctx_dummy)
5772         ctx_copied["bered"] = True
5773         seq_decoded_lenindef, tail_lenindef = seq.decode(
5774             seq_encoded_lenindef + tail_junk,
5775             ctx=ctx_copied,
5776         )
5777         del ctx_copied["bered"]
5778         self.assertDictEqual(ctx_copied, ctx_dummy)
5779         self.assertTrue(seq_decoded_lenindef.lenindef)
5780         self.assertTrue(seq_decoded_lenindef.bered)
5781         seq_decoded_lenindef = copy(seq_decoded_lenindef)
5782         self.assertTrue(seq_decoded_lenindef.lenindef)
5783         self.assertTrue(seq_decoded_lenindef.bered)
5784         with self.assertRaises(DecodeError):
5785             seq.decode(seq_encoded_lenindef[:-1], ctx={"bered": True})
5786         with self.assertRaises(DecodeError):
5787             seq.decode(seq_encoded_lenindef[:-2], ctx={"bered": True})
5788         repr(seq_decoded_lenindef)
5789         list(seq_decoded_lenindef.pps())
5790         pprint(seq_decoded_lenindef, big_blobs=True, with_decode_path=True)
5791         self.assertTrue(seq_decoded_lenindef.ready)
5792
5793         for decoded, decoded_tail, encoded in (
5794                 (seq_decoded, tail, seq_encoded),
5795                 (seq_decoded_lenindef, tail_lenindef, seq_encoded_lenindef),
5796         ):
5797             self.assertEqual(decoded_tail, tail_junk)
5798             self._assert_expects(decoded, expects)
5799             self.assertEqual(seq, decoded)
5800             self.assertEqual(decoded.encode(), seq_encoded)
5801             self.assertEqual(decoded.tlvlen, len(encoded))
5802             for expect in expects:
5803                 if not expect["presented"]:
5804                     self.assertNotIn(expect["name"], decoded)
5805                     continue
5806                 self.assertIn(expect["name"], decoded)
5807                 obj = decoded[expect["name"]]
5808                 self.assertTrue(obj.decoded)
5809                 offset = obj.expl_offset if obj.expled else obj.offset
5810                 tlvlen = obj.expl_tlvlen if obj.expled else obj.tlvlen
5811                 self.assertSequenceEqual(
5812                     seq_encoded[offset:offset + tlvlen],
5813                     obj.encode(),
5814                 )
5815
5816         assert_exceeding_data(
5817             self,
5818             lambda: seq.decod(seq_encoded_lenindef + tail_junk, ctx={"bered": True}),
5819             tail_junk,
5820         )
5821
5822     @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
5823     @given(data_strategy())
5824     def test_symmetric_with_seq(self, d):
5825         seq, expect_outers = d.draw(sequences_strategy(seq_klass=self.base_klass))
5826         self.assertTrue(seq.ready)
5827         seq_encoded = seq.encode()
5828         seq_decoded, tail = seq.decode(seq_encoded)
5829         self.assertEqual(tail, b"")
5830         self.assertTrue(seq.ready)
5831         self.assertEqual(seq, seq_decoded)
5832         self.assertEqual(seq_decoded.encode(), seq_encoded)
5833         for expect_outer in expect_outers:
5834             if not expect_outer["presented"]:
5835                 self.assertNotIn(expect_outer["name"], seq_decoded)
5836                 continue
5837             self.assertIn(expect_outer["name"], seq_decoded)
5838             obj = seq_decoded[expect_outer["name"]]
5839             self.assertTrue(obj.decoded)
5840             offset = obj.expl_offset if obj.expled else obj.offset
5841             tlvlen = obj.expl_tlvlen if obj.expled else obj.tlvlen
5842             self.assertSequenceEqual(
5843                 seq_encoded[offset:offset + tlvlen],
5844                 obj.encode(),
5845             )
5846             self._assert_expects(obj, expect_outer["expects"])
5847
5848     @given(data_strategy())
5849     def test_default_disappears(self, d):
5850         _schema = list(d.draw(dictionaries(
5851             text_letters(),
5852             sets(integers(), min_size=2, max_size=2),
5853             min_size=1,
5854         )).items())
5855
5856         class Seq(self.base_klass):
5857             schema = [
5858                 (n, Integer(default=d))
5859                 for n, (_, d) in _schema
5860             ]
5861         seq = Seq()
5862         for name, (value, _) in _schema:
5863             seq[name] = Integer(value)
5864         self.assertEqual(len(seq._value), len(_schema))
5865         empty_seq = b"".join((self.base_klass.tag_default, len_encode(0)))
5866         self.assertGreater(len(seq.encode()), len(empty_seq))
5867         for name, (_, default) in _schema:
5868             seq[name] = Integer(default)
5869         self.assertEqual(len(seq._value), 0)
5870         self.assertSequenceEqual(seq.encode(), empty_seq)
5871
5872     @given(data_strategy())
5873     def test_encoded_default_not_accepted(self, d):
5874         _schema = list(d.draw(dictionaries(
5875             text_letters(),
5876             integers(),
5877             min_size=1,
5878         )).items())
5879         tags = [tag_encode(tag) for tag in d.draw(sets(
5880             integers(min_value=1),
5881             min_size=len(_schema),
5882             max_size=len(_schema),
5883         ))]
5884
5885         class SeqWithoutDefault(self.base_klass):
5886             schema = [
5887                 (n, Integer(impl=t))
5888                 for (n, _), t in zip(_schema, tags)
5889             ]
5890         seq_without_default = SeqWithoutDefault()
5891         for name, value in _schema:
5892             seq_without_default[name] = Integer(value)
5893         seq_encoded = seq_without_default.encode()
5894
5895         class SeqWithDefault(self.base_klass):
5896             schema = [
5897                 (n, Integer(default=v, impl=t))
5898                 for (n, v), t in zip(_schema, tags)
5899             ]
5900         seq_with_default = SeqWithDefault()
5901         with assertRaisesRegex(self, DecodeError, "DEFAULT value met"):
5902             seq_with_default.decode(seq_encoded)
5903         for ctx in ({"bered": True}, {"allow_default_values": True}):
5904             seq_decoded, _ = seq_with_default.decode(seq_encoded, ctx=ctx)
5905             self.assertTrue(seq_decoded.ber_encoded)
5906             self.assertTrue(seq_decoded.bered)
5907             seq_decoded = copy(seq_decoded)
5908             self.assertTrue(seq_decoded.ber_encoded)
5909             self.assertTrue(seq_decoded.bered)
5910             for name, value in _schema:
5911                 self.assertEqual(seq_decoded[name], seq_with_default[name])
5912                 self.assertEqual(seq_decoded[name], value)
5913
5914     @given(data_strategy())
5915     def test_missing_from_spec(self, d):
5916         names = list(d.draw(sets(text_letters(), min_size=2)))
5917         tags = [tag_encode(tag) for tag in d.draw(sets(
5918             integers(min_value=1),
5919             min_size=len(names),
5920             max_size=len(names),
5921         ))]
5922         names_tags = [(name, tag) for tag, name in sorted(zip(tags, names))]
5923
5924         class SeqFull(self.base_klass):
5925             schema = [(n, Integer(impl=t)) for n, t in names_tags]
5926         seq_full = SeqFull()
5927         for i, name in enumerate(names):
5928             seq_full[name] = Integer(i)
5929         seq_encoded = seq_full.encode()
5930         altered = names_tags[:-2] + names_tags[-1:]
5931
5932         class SeqMissing(self.base_klass):
5933             schema = [(n, Integer(impl=t)) for n, t in altered]
5934         seq_missing = SeqMissing()
5935         with self.assertRaises(TagMismatch):
5936             seq_missing.decode(seq_encoded)
5937
5938     def test_bered(self):
5939         class Seq(self.base_klass):
5940             schema = (("underlying", Boolean()),)
5941         encoded = Boolean.tag_default + len_encode(1) + b"\x01"
5942         encoded = Seq.tag_default + len_encode(len(encoded)) + encoded
5943         decoded, _ = Seq().decode(encoded, ctx={"bered": True})
5944         self.assertFalse(decoded.ber_encoded)
5945         self.assertFalse(decoded.lenindef)
5946         self.assertTrue(decoded.bered)
5947         decoded = copy(decoded)
5948         self.assertFalse(decoded.ber_encoded)
5949         self.assertFalse(decoded.lenindef)
5950         self.assertTrue(decoded.bered)
5951
5952         class Seq(self.base_klass):
5953             schema = (("underlying", OctetString()),)
5954         encoded = (
5955             tag_encode(form=TagFormConstructed, num=4) +
5956             LENINDEF +
5957             OctetString(b"whatever").encode() +
5958             EOC
5959         )
5960         encoded = Seq.tag_default + len_encode(len(encoded)) + encoded
5961         with self.assertRaises(DecodeError):
5962             Seq().decode(encoded)
5963         decoded, _ = Seq().decode(encoded, ctx={"bered": True})
5964         self.assertFalse(decoded.ber_encoded)
5965         self.assertFalse(decoded.lenindef)
5966         self.assertTrue(decoded.bered)
5967         decoded = copy(decoded)
5968         self.assertFalse(decoded.ber_encoded)
5969         self.assertFalse(decoded.lenindef)
5970         self.assertTrue(decoded.bered)
5971
5972
5973 class TestSequence(SeqMixing, CommonMixin, TestCase):
5974     base_klass = Sequence
5975
5976     @given(
5977         integers(),
5978         binary(min_size=1),
5979     )
5980     def test_remaining(self, value, junk):
5981         class Seq(Sequence):
5982             schema = (
5983                 ("whatever", Integer()),
5984             )
5985         int_encoded = Integer(value).encode()
5986         junked = b"".join((
5987             Sequence.tag_default,
5988             len_encode(len(int_encoded + junk)),
5989             int_encoded + junk,
5990         ))
5991         with assertRaisesRegex(self, DecodeError, "remaining"):
5992             Seq().decode(junked)
5993
5994     @given(sets(text_letters(), min_size=2))
5995     def test_obj_unknown(self, names):
5996         missing = names.pop()
5997
5998         class Seq(Sequence):
5999             schema = [(n, Boolean()) for n in names]
6000         seq = Seq()
6001         with self.assertRaises(ObjUnknown) as err:
6002             seq[missing]
6003         repr(err.exception)
6004         with self.assertRaises(ObjUnknown) as err:
6005             seq[missing] = Boolean()
6006         repr(err.exception)
6007
6008     def test_x690_vector(self):
6009         class Seq(Sequence):
6010             schema = (
6011                 ("name", IA5String()),
6012                 ("ok", Boolean()),
6013             )
6014         seq = Seq().decode(hexdec("300A1605536d6974680101FF"))[0]
6015         self.assertEqual(seq["name"], "Smith")
6016         self.assertEqual(seq["ok"], True)
6017
6018
6019 class TestSet(SeqMixing, CommonMixin, TestCase):
6020     base_klass = Set
6021
6022     @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
6023     @given(data_strategy())
6024     def test_sorted(self, d):
6025         class DummySeq(Sequence):
6026             schema = (("null", Null()),)
6027
6028         tag_nums = d.draw(sets(integers(min_value=1), min_size=1, max_size=50))
6029         _, _, dummy_seq_tag_num = tag_decode(DummySeq.tag_default)
6030         assume(any(i > dummy_seq_tag_num for i in tag_nums))
6031         tag_nums -= set([dummy_seq_tag_num])
6032         _schema = [(str(i), OctetString(impl=tag_encode(i))) for i in tag_nums]
6033         _schema.append(("seq", DummySeq()))
6034
6035         class Seq(Set):
6036             schema = d.draw(permutations(_schema))
6037         seq = Seq()
6038         for name, _ in _schema:
6039             if name != "seq":
6040                 seq[name] = OctetString(name.encode("ascii"))
6041         seq["seq"] = DummySeq((("null", Null()),))
6042
6043         seq_encoded = seq.encode()
6044         seq_decoded, _ = seq.decode(seq_encoded)
6045         seq_encoded_expected = []
6046         for tag_num in sorted(tag_nums | set([dummy_seq_tag_num])):
6047             if tag_num == dummy_seq_tag_num:
6048                 seq_encoded_expected.append(seq["seq"].encode())
6049             else:
6050                 seq_encoded_expected.append(seq[str(tag_num)].encode())
6051         self.assertSequenceEqual(
6052             seq_encoded[seq_decoded.tlen + seq_decoded.llen:],
6053             b"".join(seq_encoded_expected),
6054         )
6055
6056         encoded = b"".join(seq[str(i)].encode() for i in tag_nums)
6057         encoded += seq["seq"].encode()
6058         seq_encoded = b"".join((
6059             Set.tag_default,
6060             len_encode(len(encoded)),
6061             encoded,
6062         ))
6063         with assertRaisesRegex(self, DecodeError, "unordered SET"):
6064             seq.decode(seq_encoded)
6065         for ctx in ({"bered": True}, {"allow_unordered_set": True}):
6066             seq_decoded, _ = Seq().decode(seq_encoded, ctx=ctx)
6067             self.assertTrue(seq_decoded.ber_encoded)
6068             self.assertTrue(seq_decoded.bered)
6069             seq_decoded = copy(seq_decoded)
6070             self.assertTrue(seq_decoded.ber_encoded)
6071             self.assertTrue(seq_decoded.bered)
6072
6073     def test_same_value_twice(self):
6074         class Seq(Set):
6075             schema = (
6076                 ("bool", Boolean()),
6077                 ("int", Integer()),
6078             )
6079
6080         encoded = b"".join((
6081             Integer(123).encode(),
6082             Integer(234).encode(),
6083             Boolean(True).encode(),
6084         ))
6085         encoded = Seq.tag_default + len_encode(len(encoded)) + encoded
6086         with self.assertRaises(TagMismatch):
6087             Seq().decod(encoded, ctx={"allow_unordered_set": True})
6088
6089
6090 @composite
6091 def seqof_values_strategy(draw, schema=None, do_expl=False):
6092     if schema is None:
6093         schema = draw(sampled_from((Boolean(), Integer())))
6094     bound_min, bound_max = sorted(draw(sets(
6095         integers(min_value=0, max_value=10),
6096         min_size=2,
6097         max_size=2,
6098     )))
6099     if isinstance(schema, Boolean):
6100         values_generator = booleans().map(Boolean)
6101     elif isinstance(schema, Integer):
6102         values_generator = integers().map(Integer)
6103     values_generator = lists(
6104         values_generator,
6105         min_size=bound_min,
6106         max_size=bound_max,
6107     )
6108     values = draw(one_of(none(), values_generator))
6109     impl = None
6110     expl = None
6111     if do_expl:
6112         expl = draw(one_of(none(), integers(min_value=1).map(tag_encode)))
6113     else:
6114         impl = draw(one_of(none(), integers(min_value=1).map(tag_encode)))
6115     default = draw(one_of(none(), values_generator))
6116     optional = draw(one_of(none(), booleans()))
6117     _decoded = (
6118         draw(integers(min_value=0)),
6119         draw(integers(min_value=0)),
6120         draw(integers(min_value=0)),
6121     )
6122     return (
6123         schema,
6124         values,
6125         (bound_min, bound_max),
6126         impl,
6127         expl,
6128         default,
6129         optional,
6130         _decoded,
6131     )
6132
6133
6134 class SeqOfMixing(object):
6135     def test_invalid_value_type(self):
6136         with self.assertRaises(InvalidValueType) as err:
6137             self.base_klass(123)
6138         repr(err.exception)
6139
6140     def test_invalid_values_type(self):
6141         class SeqOf(self.base_klass):
6142             schema = Integer()
6143         with self.assertRaises(InvalidValueType) as err:
6144             SeqOf([Integer(123), Boolean(False), Integer(234)])
6145         repr(err.exception)
6146
6147     def test_schema_required(self):
6148         with assertRaisesRegex(self, ValueError, "schema must be specified"):
6149             self.base_klass.__mro__[1]()
6150
6151     @given(booleans(), booleans(), binary(min_size=1), binary(min_size=1))
6152     def test_comparison(self, value1, value2, tag1, tag2):
6153         class SeqOf(self.base_klass):
6154             schema = Boolean()
6155         obj1 = SeqOf([Boolean(value1)])
6156         obj2 = SeqOf([Boolean(value2)])
6157         self.assertEqual(obj1 == obj2, value1 == value2)
6158         self.assertEqual(obj1 != obj2, value1 != value2)
6159         self.assertEqual(obj1 == list(obj2), value1 == value2)
6160         self.assertEqual(obj1 == tuple(obj2), value1 == value2)
6161         obj1 = SeqOf([Boolean(value1)], impl=tag1)
6162         obj2 = SeqOf([Boolean(value1)], impl=tag2)
6163         self.assertEqual(obj1 == obj2, tag1 == tag2)
6164         self.assertEqual(obj1 != obj2, tag1 != tag2)
6165
6166     @given(lists(booleans()))
6167     def test_iter(self, values):
6168         class SeqOf(self.base_klass):
6169             schema = Boolean()
6170         obj = SeqOf([Boolean(value) for value in values])
6171         self.assertEqual(len(obj), len(values))
6172         for i, value in enumerate(obj):
6173             self.assertEqual(value, values[i])
6174
6175     @given(data_strategy())
6176     def test_ready(self, d):
6177         ready = [Integer(v) for v in d.draw(lists(
6178             integers(),
6179             min_size=1,
6180             max_size=3,
6181         ))]
6182         non_ready = [
6183             Integer() for _ in
6184             range(d.draw(integers(min_value=1, max_value=5)))
6185         ]
6186
6187         class SeqOf(self.base_klass):
6188             schema = Integer()
6189         values = d.draw(permutations(ready + non_ready))
6190         seqof = SeqOf()
6191         for value in values:
6192             seqof.append(value)
6193         self.assertFalse(seqof.ready)
6194         repr(seqof)
6195         list(seqof.pps())
6196         pprint(seqof, big_blobs=True, with_decode_path=True)
6197         with self.assertRaises(ObjNotReady) as err:
6198             seqof.encode()
6199         repr(err.exception)
6200         for i, value in enumerate(values):
6201             self.assertEqual(seqof[i], value)
6202             if not seqof[i].ready:
6203                 seqof[i] = Integer(i)
6204         self.assertTrue(seqof.ready)
6205         repr(seqof)
6206         list(seqof.pps())
6207         pprint(seqof, big_blobs=True, with_decode_path=True)
6208
6209     def test_spec_mismatch(self):
6210         class SeqOf(self.base_klass):
6211             schema = Integer()
6212         seqof = SeqOf()
6213         seqof.append(Integer(123))
6214         with self.assertRaises(ValueError):
6215             seqof.append(Boolean(False))
6216         with self.assertRaises(ValueError):
6217             seqof[0] = Boolean(False)
6218
6219     @given(data_strategy())
6220     def test_bounds_satisfied(self, d):
6221         class SeqOf(self.base_klass):
6222             schema = Boolean()
6223         bound_min = d.draw(integers(min_value=0, max_value=1 << 7))
6224         bound_max = d.draw(integers(min_value=bound_min, max_value=1 << 7))
6225         value = [Boolean()] * d.draw(integers(min_value=bound_min, max_value=bound_max))
6226         SeqOf(value=value, bounds=(bound_min, bound_max))
6227
6228     @given(data_strategy())
6229     def test_bounds_unsatisfied(self, d):
6230         class SeqOf(self.base_klass):
6231             schema = Boolean()
6232         bound_min = d.draw(integers(min_value=1, max_value=1 << 7))
6233         bound_max = d.draw(integers(min_value=bound_min, max_value=1 << 7))
6234         value = [Boolean(False)] * d.draw(integers(max_value=bound_min - 1))
6235         with self.assertRaises(BoundsError) as err:
6236             SeqOf(value=value, bounds=(bound_min, bound_max))
6237         repr(err.exception)
6238         with assertRaisesRegex(self, DecodeError, "bounds") as err:
6239             SeqOf(bounds=(bound_min, bound_max)).decode(
6240                 SeqOf(value).encode()
6241             )
6242         repr(err.exception)
6243         value = [Boolean(True)] * d.draw(integers(
6244             min_value=bound_max + 1,
6245             max_value=bound_max + 10,
6246         ))
6247         with self.assertRaises(BoundsError) as err:
6248             SeqOf(value=value, bounds=(bound_min, bound_max))
6249         repr(err.exception)
6250         with assertRaisesRegex(self, DecodeError, "bounds") as err:
6251             SeqOf(bounds=(bound_min, bound_max)).decode(
6252                 SeqOf(value).encode()
6253             )
6254         repr(err.exception)
6255
6256     @given(integers(min_value=1, max_value=10))
6257     def test_out_of_bounds(self, bound_max):
6258         class SeqOf(self.base_klass):
6259             schema = Integer()
6260             bounds = (0, bound_max)
6261         seqof = SeqOf()
6262         for _ in range(bound_max):
6263             seqof.append(Integer(123))
6264         with self.assertRaises(BoundsError):
6265             seqof.append(Integer(123))
6266
6267     @given(data_strategy())
6268     def test_call(self, d):
6269         (
6270             schema_initial,
6271             value_initial,
6272             bounds_initial,
6273             impl_initial,
6274             expl_initial,
6275             default_initial,
6276             optional_initial,
6277             _decoded_initial,
6278         ) = d.draw(seqof_values_strategy())
6279
6280         class SeqOf(self.base_klass):
6281             schema = schema_initial
6282         obj_initial = SeqOf(
6283             value=value_initial,
6284             bounds=bounds_initial,
6285             impl=impl_initial,
6286             expl=expl_initial,
6287             default=default_initial,
6288             optional=optional_initial or False,
6289             _decoded=_decoded_initial,
6290         )
6291         (
6292             _,
6293             value,
6294             bounds,
6295             impl,
6296             expl,
6297             default,
6298             optional,
6299             _decoded,
6300         ) = d.draw(seqof_values_strategy(
6301             schema=schema_initial,
6302             do_expl=impl_initial is None,
6303         ))
6304         if (default is None) and (obj_initial.default is not None):
6305             bounds = None
6306         if (
6307                 (bounds is None) and
6308                 (value is not None) and
6309                 (bounds_initial is not None) and
6310                 not (bounds_initial[0] <= len(value) <= bounds_initial[1])
6311         ):
6312             value = None
6313         if (
6314                 (bounds is None) and
6315                 (default is not None) and
6316                 (bounds_initial is not None) and
6317                 not (bounds_initial[0] <= len(default) <= bounds_initial[1])
6318         ):
6319             default = None
6320         obj = obj_initial(
6321             value=value,
6322             bounds=bounds,
6323             impl=impl,
6324             expl=expl,
6325             default=default,
6326             optional=optional,
6327         )
6328         if obj.ready:
6329             value_expected = default if value is None else value
6330             value_expected = (
6331                 default_initial if value_expected is None
6332                 else value_expected
6333             )
6334             value_expected = () if value_expected is None else value_expected
6335             self.assertEqual(obj, value_expected)
6336         self.assertEqual(obj.tag, impl or impl_initial or obj.tag_default)
6337         self.assertEqual(obj.expl_tag, expl or expl_initial)
6338         self.assertEqual(
6339             obj.default,
6340             default_initial if default is None else default,
6341         )
6342         if obj.default is None:
6343             optional = optional_initial if optional is None else optional
6344             optional = False if optional is None else optional
6345         else:
6346             optional = True
6347         self.assertEqual(obj.optional, optional)
6348         self.assertEqual(
6349             (obj._bound_min, obj._bound_max),
6350             bounds or bounds_initial or (0, float("+inf")),
6351         )
6352
6353     @given(seqof_values_strategy())
6354     def test_copy(self, values):
6355         _schema, value, bounds, impl, expl, default, optional, _decoded = values
6356
6357         class SeqOf(self.base_klass):
6358             schema = _schema
6359         register_class(SeqOf)
6360         obj = SeqOf(
6361             value=value,
6362             bounds=bounds,
6363             impl=impl,
6364             expl=expl,
6365             default=default,
6366             optional=optional or False,
6367             _decoded=_decoded,
6368         )
6369         for copy_func in copy_funcs:
6370             obj_copied = copy_func(obj)
6371             self.assert_copied_basic_fields(obj, obj_copied)
6372             self.assertEqual(obj._bound_min, obj_copied._bound_min)
6373             self.assertEqual(obj._bound_max, obj_copied._bound_max)
6374             self.assertEqual(obj._value, obj_copied._value)
6375
6376     @given(
6377         lists(binary()),
6378         integers(min_value=1).map(tag_encode),
6379     )
6380     def test_stripped(self, values, tag_impl):
6381         class SeqOf(self.base_klass):
6382             schema = OctetString()
6383         obj = SeqOf([OctetString(v) for v in values], impl=tag_impl)
6384         with self.assertRaises(NotEnoughData):
6385             obj.decode(obj.encode()[:-1])
6386
6387     @given(
6388         lists(binary()),
6389         integers(min_value=1).map(tag_ctxc),
6390     )
6391     def test_stripped_expl(self, values, tag_expl):
6392         class SeqOf(self.base_klass):
6393             schema = OctetString()
6394         obj = SeqOf([OctetString(v) for v in values], expl=tag_expl)
6395         with self.assertRaises(NotEnoughData):
6396             obj.decode(obj.encode()[:-1])
6397
6398     @given(
6399         integers(min_value=31),
6400         integers(min_value=0),
6401         decode_path_strat,
6402     )
6403     def test_bad_tag(self, tag, offset, decode_path):
6404         with self.assertRaises(DecodeError) as err:
6405             self.base_klass().decode(
6406                 tag_encode(tag)[:-1],
6407                 offset=offset,
6408                 decode_path=decode_path,
6409             )
6410         repr(err.exception)
6411         self.assertEqual(err.exception.offset, offset)
6412         self.assertEqual(err.exception.decode_path, decode_path)
6413
6414     @given(
6415         integers(min_value=128),
6416         integers(min_value=0),
6417         decode_path_strat,
6418     )
6419     def test_bad_len(self, l, offset, decode_path):
6420         with self.assertRaises(DecodeError) as err:
6421             self.base_klass().decode(
6422                 self.base_klass.tag_default + len_encode(l)[:-1],
6423                 offset=offset,
6424                 decode_path=decode_path,
6425             )
6426         repr(err.exception)
6427         self.assertEqual(err.exception.offset, offset)
6428         self.assertEqual(err.exception.decode_path, decode_path)
6429
6430     @given(binary(min_size=1))
6431     def test_tag_mismatch(self, impl):
6432         assume(impl != self.base_klass.tag_default)
6433         with self.assertRaises(TagMismatch):
6434             self.base_klass(impl=impl).decode(self.base_klass().encode())
6435
6436     @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
6437     @given(
6438         seqof_values_strategy(schema=Integer()),
6439         lists(integers().map(Integer)),
6440         integers(min_value=1).map(tag_ctxc),
6441         integers(min_value=0),
6442         binary(max_size=5),
6443     )
6444     def test_symmetric(self, values, value, tag_expl, offset, tail_junk):
6445         _, _, _, _, _, default, optional, _decoded = values
6446
6447         class SeqOf(self.base_klass):
6448             schema = Integer()
6449         obj = SeqOf(
6450             value=value,
6451             default=default,
6452             optional=optional,
6453             _decoded=_decoded,
6454         )
6455         repr(obj)
6456         list(obj.pps())
6457         pprint(obj, big_blobs=True, with_decode_path=True)
6458         self.assertFalse(obj.expled)
6459         obj_encoded = obj.encode()
6460         obj_expled = obj(value, expl=tag_expl)
6461         self.assertTrue(obj_expled.expled)
6462         repr(obj_expled)
6463         list(obj_expled.pps())
6464         pprint(obj_expled, big_blobs=True, with_decode_path=True)
6465         obj_expled_encoded = obj_expled.encode()
6466         ctx_copied = deepcopy(ctx_dummy)
6467         obj_decoded, tail = obj_expled.decode(
6468             obj_expled_encoded + tail_junk,
6469             offset=offset,
6470             ctx=ctx_copied,
6471         )
6472         self.assertDictEqual(ctx_copied, ctx_dummy)
6473         repr(obj_decoded)
6474         list(obj_decoded.pps())
6475         pprint(obj_decoded, big_blobs=True, with_decode_path=True)
6476         self.assertEqual(tail, tail_junk)
6477         self._test_symmetric_compare_objs(obj_decoded, obj_expled)
6478         self.assertSequenceEqual(obj_decoded.encode(), obj_expled_encoded)
6479         self.assertSequenceEqual(obj_decoded.expl_tag, tag_expl)
6480         self.assertEqual(obj_decoded.expl_tlen, len(tag_expl))
6481         self.assertEqual(
6482             obj_decoded.expl_llen,
6483             len(len_encode(len(obj_encoded))),
6484         )
6485         self.assertEqual(obj_decoded.tlvlen, len(obj_encoded))
6486         self.assertEqual(obj_decoded.expl_vlen, len(obj_encoded))
6487         self.assertEqual(
6488             obj_decoded.offset,
6489             offset + obj_decoded.expl_tlen + obj_decoded.expl_llen,
6490         )
6491         self.assertEqual(obj_decoded.expl_offset, offset)
6492         for obj_inner in obj_decoded:
6493             self.assertIn(obj_inner, obj_decoded)
6494             self.assertSequenceEqual(
6495                 obj_inner.encode(),
6496                 obj_expled_encoded[
6497                     obj_inner.offset - offset:
6498                     obj_inner.offset + obj_inner.tlvlen - offset
6499                 ],
6500             )
6501
6502         t, _, lv = tag_strip(obj_encoded)
6503         _, _, v = len_decode(lv)
6504         obj_encoded_lenindef = t + LENINDEF + v + EOC
6505         with self.assertRaises(DecodeError):
6506             obj.decode(obj_encoded_lenindef)
6507         obj_decoded_lenindef, tail_lenindef = obj.decode(
6508             obj_encoded_lenindef + tail_junk,
6509             ctx={"bered": True},
6510         )
6511         self.assertTrue(obj_decoded_lenindef.lenindef)
6512         self.assertTrue(obj_decoded_lenindef.bered)
6513         obj_decoded_lenindef = copy(obj_decoded_lenindef)
6514         self.assertTrue(obj_decoded_lenindef.lenindef)
6515         self.assertTrue(obj_decoded_lenindef.bered)
6516         repr(obj_decoded_lenindef)
6517         list(obj_decoded_lenindef.pps())
6518         pprint(obj_decoded_lenindef, big_blobs=True, with_decode_path=True)
6519         self.assertEqual(tail_lenindef, tail_junk)
6520         self.assertEqual(obj_decoded_lenindef.tlvlen, len(obj_encoded_lenindef))
6521         with self.assertRaises(DecodeError):
6522             obj.decode(obj_encoded_lenindef[:-1], ctx={"bered": True})
6523         with self.assertRaises(DecodeError):
6524             obj.decode(obj_encoded_lenindef[:-2], ctx={"bered": True})
6525
6526         assert_exceeding_data(
6527             self,
6528             lambda: obj_expled.decod(obj_expled_encoded + tail_junk),
6529             tail_junk,
6530         )
6531
6532     def test_bered(self):
6533         class SeqOf(self.base_klass):
6534             schema = Boolean()
6535         encoded = Boolean(False).encode()
6536         encoded += Boolean.tag_default + len_encode(1) + b"\x01"
6537         encoded = SeqOf.tag_default + len_encode(len(encoded)) + encoded
6538         with self.assertRaises(DecodeError):
6539             SeqOf().decode(encoded)
6540         decoded, _ = SeqOf().decode(encoded, ctx={"bered": True})
6541         self.assertFalse(decoded.ber_encoded)
6542         self.assertFalse(decoded.lenindef)
6543         self.assertTrue(decoded.bered)
6544         decoded = copy(decoded)
6545         self.assertFalse(decoded.ber_encoded)
6546         self.assertFalse(decoded.lenindef)
6547         self.assertTrue(decoded.bered)
6548
6549         class SeqOf(self.base_klass):
6550             schema = OctetString()
6551         encoded = OctetString(b"whatever").encode()
6552         encoded += (
6553             tag_encode(form=TagFormConstructed, num=4) +
6554             LENINDEF +
6555             OctetString(b"whatever").encode() +
6556             EOC
6557         )
6558         encoded = SeqOf.tag_default + len_encode(len(encoded)) + encoded
6559         with self.assertRaises(DecodeError):
6560             SeqOf().decode(encoded)
6561         decoded, _ = SeqOf().decode(encoded, ctx={"bered": True})
6562         self.assertFalse(decoded.ber_encoded)
6563         self.assertFalse(decoded.lenindef)
6564         self.assertTrue(decoded.bered)
6565         decoded = copy(decoded)
6566         self.assertFalse(decoded.ber_encoded)
6567         self.assertFalse(decoded.lenindef)
6568         self.assertTrue(decoded.bered)
6569
6570
6571 class TestSequenceOf(SeqOfMixing, CommonMixin, TestCase):
6572     class SeqOf(SequenceOf):
6573         schema = "whatever"
6574     base_klass = SeqOf
6575
6576     def _test_symmetric_compare_objs(self, obj1, obj2):
6577         self.assertEqual(obj1, obj2)
6578         self.assertSequenceEqual(list(obj1), list(obj2))
6579
6580
6581 class TestSetOf(SeqOfMixing, CommonMixin, TestCase):
6582     class SeqOf(SetOf):
6583         schema = "whatever"
6584     base_klass = SeqOf
6585
6586     def _test_symmetric_compare_objs(self, obj1, obj2):
6587         self.assertSetEqual(
6588             set(int(v) for v in obj1),
6589             set(int(v) for v in obj2),
6590         )
6591
6592     @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
6593     @given(data_strategy())
6594     def test_sorted(self, d):
6595         values = [OctetString(v) for v in d.draw(lists(binary()))]
6596
6597         class Seq(SetOf):
6598             schema = OctetString()
6599         seq = Seq(values)
6600         seq_encoded = seq.encode()
6601         seq_decoded, _ = seq.decode(seq_encoded)
6602         self.assertSequenceEqual(
6603             seq_encoded[seq_decoded.tlen + seq_decoded.llen:],
6604             b"".join(sorted([v.encode() for v in values])),
6605         )
6606
6607     @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
6608     @given(data_strategy())
6609     def test_unsorted(self, d):
6610         values = [OctetString(v).encode() for v in d.draw(sets(
6611             binary(min_size=1, max_size=5),
6612             min_size=2,
6613             max_size=5,
6614         ))]
6615         values = d.draw(permutations(values))
6616         assume(values != sorted(values))
6617         encoded = b"".join(values)
6618         seq_encoded = b"".join((
6619             SetOf.tag_default,
6620             len_encode(len(encoded)),
6621             encoded,
6622         ))
6623
6624         class Seq(SetOf):
6625             schema = OctetString()
6626         seq = Seq()
6627         with assertRaisesRegex(self, DecodeError, "unordered SET OF"):
6628             seq.decode(seq_encoded)
6629
6630         for ctx in ({"bered": True}, {"allow_unordered_set": True}):
6631             seq_decoded, _ = Seq().decode(seq_encoded, ctx=ctx)
6632             self.assertTrue(seq_decoded.ber_encoded)
6633             self.assertTrue(seq_decoded.bered)
6634             seq_decoded = copy(seq_decoded)
6635             self.assertTrue(seq_decoded.ber_encoded)
6636             self.assertTrue(seq_decoded.bered)
6637             self.assertSequenceEqual(
6638                 [obj.encode() for obj in seq_decoded],
6639                 values,
6640             )
6641
6642
6643 class TestGoMarshalVectors(TestCase):
6644     def runTest(self):
6645         self.assertSequenceEqual(Integer(10).encode(), hexdec("02010a"))
6646         self.assertSequenceEqual(Integer(127).encode(), hexdec("02017f"))
6647         self.assertSequenceEqual(Integer(128).encode(), hexdec("02020080"))
6648         self.assertSequenceEqual(Integer(-128).encode(), hexdec("020180"))
6649         self.assertSequenceEqual(Integer(-129).encode(), hexdec("0202ff7f"))
6650
6651         class Seq(Sequence):
6652             schema = (
6653                 ("erste", Integer()),
6654                 ("zweite", Integer(optional=True))
6655             )
6656         seq = Seq()
6657         seq["erste"] = Integer(64)
6658         self.assertSequenceEqual(seq.encode(), hexdec("3003020140"))
6659         seq["erste"] = Integer(0x123456)
6660         self.assertSequenceEqual(seq.encode(), hexdec("30050203123456"))
6661         seq["erste"] = Integer(64)
6662         seq["zweite"] = Integer(65)
6663         self.assertSequenceEqual(seq.encode(), hexdec("3006020140020141"))
6664
6665         class NestedSeq(Sequence):
6666             schema = (
6667                 ("nest", Seq()),
6668             )
6669         seq["erste"] = Integer(127)
6670         seq["zweite"] = None
6671         nested = NestedSeq()
6672         nested["nest"] = seq
6673         self.assertSequenceEqual(nested.encode(), hexdec("3005300302017f"))
6674
6675         self.assertSequenceEqual(
6676             OctetString(b"\x01\x02\x03").encode(),
6677             hexdec("0403010203"),
6678         )
6679
6680         class Seq(Sequence):
6681             schema = (
6682                 ("erste", Integer(impl=tag_encode(5, klass=TagClassContext))),
6683             )
6684         seq = Seq()
6685         seq["erste"] = Integer(64)
6686         self.assertSequenceEqual(seq.encode(), hexdec("3003850140"))
6687
6688         class Seq(Sequence):
6689             schema = (
6690                 ("erste", Integer(expl=tag_ctxc(5))),
6691             )
6692         seq = Seq()
6693         seq["erste"] = Integer(64)
6694         self.assertSequenceEqual(seq.encode(), hexdec("3005a503020140"))
6695
6696         class Seq(Sequence):
6697             schema = (
6698                 ("erste", Null(
6699                     impl=tag_encode(0, klass=TagClassContext),
6700                     optional=True,
6701                 )),
6702             )
6703         seq = Seq()
6704         seq["erste"] = Null()
6705         self.assertSequenceEqual(seq.encode(), hexdec("30028000"))
6706         seq["erste"] = None
6707         self.assertSequenceEqual(seq.encode(), hexdec("3000"))
6708
6709         self.assertSequenceEqual(
6710             UTCTime(datetime(1970, 1, 1, 0, 0)).encode(),
6711             hexdec("170d3730303130313030303030305a"),
6712         )
6713         self.assertSequenceEqual(
6714             UTCTime(datetime(2009, 11, 15, 22, 56, 16)).encode(),
6715             hexdec("170d3039313131353232353631365a"),
6716         )
6717         self.assertSequenceEqual(
6718             GeneralizedTime(datetime(2100, 4, 5, 12, 1, 1)).encode(),
6719             hexdec("180f32313030303430353132303130315a"),
6720         )
6721
6722         class Seq(Sequence):
6723             schema = (
6724                 ("erste", GeneralizedTime()),
6725             )
6726         seq = Seq()
6727         seq["erste"] = GeneralizedTime(datetime(2009, 11, 15, 22, 56, 16))
6728         self.assertSequenceEqual(
6729             seq.encode(),
6730             hexdec("3011180f32303039313131353232353631365a"),
6731         )
6732
6733         self.assertSequenceEqual(
6734             BitString((1, b"\x80")).encode(),
6735             hexdec("03020780"),
6736         )
6737         self.assertSequenceEqual(
6738             BitString((12, b"\x81\xF0")).encode(),
6739             hexdec("03030481f0"),
6740         )
6741
6742         self.assertSequenceEqual(
6743             ObjectIdentifier("1.2.3.4").encode(),
6744             hexdec("06032a0304"),
6745         )
6746         self.assertSequenceEqual(
6747             ObjectIdentifier("1.2.840.133549.1.1.5").encode(),
6748             hexdec("06092a864888932d010105"),
6749         )
6750         self.assertSequenceEqual(
6751             ObjectIdentifier("2.100.3").encode(),
6752             hexdec("0603813403"),
6753         )
6754
6755         self.assertSequenceEqual(
6756             PrintableString("test").encode(),
6757             hexdec("130474657374"),
6758         )
6759         self.assertSequenceEqual(
6760             PrintableString("x" * 127).encode(),
6761             hexdec("137F" + "78" * 127),
6762         )
6763         self.assertSequenceEqual(
6764             PrintableString("x" * 128).encode(),
6765             hexdec("138180" + "78" * 128),
6766         )
6767         self.assertSequenceEqual(UTF8String("Σ").encode(), hexdec("0c02cea3"))
6768
6769         class Seq(Sequence):
6770             schema = (
6771                 ("erste", IA5String()),
6772             )
6773         seq = Seq()
6774         seq["erste"] = IA5String("test")
6775         self.assertSequenceEqual(seq.encode(), hexdec("3006160474657374"))
6776
6777         class Seq(Sequence):
6778             schema = (
6779                 ("erste", PrintableString()),
6780             )
6781         seq = Seq()
6782         seq["erste"] = PrintableString("test")
6783         self.assertSequenceEqual(seq.encode(), hexdec("3006130474657374"))
6784         # Asterisk is actually not allowable
6785         PrintableString._allowable_chars |= set(b"*")
6786         seq["erste"] = PrintableString("test*")
6787         self.assertSequenceEqual(seq.encode(), hexdec("30071305746573742a"))
6788         PrintableString._allowable_chars -= set(b"*")
6789
6790         class Seq(Sequence):
6791             schema = (
6792                 ("erste", Any(optional=True)),
6793                 ("zweite", Integer()),
6794             )
6795         seq = Seq()
6796         seq["zweite"] = Integer(64)
6797         self.assertSequenceEqual(seq.encode(), hexdec("3003020140"))
6798
6799         class Seq(SetOf):
6800             schema = Integer()
6801         seq = Seq()
6802         seq.append(Integer(10))
6803         self.assertSequenceEqual(seq.encode(), hexdec("310302010a"))
6804
6805         class _SeqOf(SequenceOf):
6806             schema = PrintableString()
6807
6808         class SeqOf(SequenceOf):
6809             schema = _SeqOf()
6810         _seqof = _SeqOf()
6811         _seqof.append(PrintableString("1"))
6812         seqof = SeqOf()
6813         seqof.append(_seqof)
6814         self.assertSequenceEqual(seqof.encode(), hexdec("30053003130131"))
6815
6816         class Seq(Sequence):
6817             schema = (
6818                 ("erste", Integer(default=1)),
6819             )
6820         seq = Seq()
6821         seq["erste"] = Integer(0)
6822         self.assertSequenceEqual(seq.encode(), hexdec("3003020100"))
6823         seq["erste"] = Integer(1)
6824         self.assertSequenceEqual(seq.encode(), hexdec("3000"))
6825         seq["erste"] = Integer(2)
6826         self.assertSequenceEqual(seq.encode(), hexdec("3003020102"))
6827
6828
6829 class TestPP(TestCase):
6830     @given(data_strategy())
6831     def test_oid_printing(self, d):
6832         oids = {
6833             str(ObjectIdentifier(k)): v * 2
6834             for k, v in d.draw(dictionaries(oid_strategy(), text_letters())).items()
6835         }
6836         chosen = d.draw(sampled_from(sorted(oids)))
6837         chosen_id = oids[chosen]
6838         pp = _pp(asn1_type_name=ObjectIdentifier.asn1_type_name, value=chosen)
6839         self.assertNotIn(chosen_id, pp_console_row(pp))
6840         self.assertIn(
6841             chosen_id,
6842             pp_console_row(pp, oid_maps=[{'whatever': 'whenever'}, oids]),
6843         )
6844
6845
6846 class TestAutoAddSlots(TestCase):
6847     def runTest(self):
6848         class Inher(Integer):
6849             pass
6850
6851         with self.assertRaises(AttributeError):
6852             inher = Inher()
6853             inher.unexistent = "whatever"
6854
6855
6856 class TestOIDDefines(TestCase):
6857     @given(data_strategy())
6858     def runTest(self, d):
6859         value_names = list(d.draw(sets(text_letters(), min_size=1, max_size=10)))
6860         value_name_chosen = d.draw(sampled_from(value_names))
6861         oids = [
6862             ObjectIdentifier(oid)
6863             for oid in d.draw(sets(oid_strategy(), min_size=2, max_size=10))
6864         ]
6865         oid_chosen = d.draw(sampled_from(oids))
6866         values = d.draw(lists(
6867             integers(),
6868             min_size=len(value_names),
6869             max_size=len(value_names),
6870         ))
6871         for definable_class in (Any, OctetString, BitString):
6872             _schema = [
6873                 ("type", ObjectIdentifier(defines=(((value_name_chosen,), {
6874                     oid: Integer() for oid in oids[:-1]
6875                 }),))),
6876             ]
6877             for i, value_name in enumerate(value_names):
6878                 _schema.append((value_name, definable_class(expl=tag_ctxp(i))))
6879
6880             class Seq(Sequence):
6881                 schema = _schema
6882             seq = Seq()
6883             for value_name, value in zip(value_names, values):
6884                 seq[value_name] = definable_class(Integer(value).encode())
6885             seq["type"] = oid_chosen
6886             seq, _ = Seq().decode(seq.encode())
6887             for value_name in value_names:
6888                 if value_name == value_name_chosen:
6889                     continue
6890                 self.assertIsNone(seq[value_name].defined)
6891             if value_name_chosen in oids[:-1]:
6892                 self.assertIsNotNone(seq[value_name_chosen].defined)
6893                 self.assertEqual(seq[value_name_chosen].defined[0], oid_chosen)
6894                 self.assertIsInstance(seq[value_name_chosen].defined[1], Integer)
6895             repr(seq)
6896             list(seq.pps())
6897             pprint(seq, big_blobs=True, with_decode_path=True)
6898
6899
6900 class TestDefinesByPath(TestCase):
6901     def test_generated(self):
6902         class Seq(Sequence):
6903             schema = (
6904                 ("type", ObjectIdentifier()),
6905                 ("value", OctetString(expl=tag_ctxc(123))),
6906             )
6907
6908         class SeqInner(Sequence):
6909             schema = (
6910                 ("typeInner", ObjectIdentifier()),
6911                 ("valueInner", Any()),
6912             )
6913
6914         class PairValue(SetOf):
6915             schema = Any()
6916
6917         class Pair(Sequence):
6918             schema = (
6919                 ("type", ObjectIdentifier()),
6920                 ("value", PairValue()),
6921             )
6922
6923         class Pairs(SequenceOf):
6924             schema = Pair()
6925
6926         (
6927             type_integered,
6928             type_sequenced,
6929             type_innered,
6930             type_octet_stringed,
6931         ) = [
6932             ObjectIdentifier(oid)
6933             for oid in sets(oid_strategy(), min_size=4, max_size=4).example()
6934         ]
6935         seq_integered = Seq()
6936         seq_integered["type"] = type_integered
6937         seq_integered["value"] = OctetString(Integer(123).encode())
6938         seq_integered_raw = seq_integered.encode()
6939
6940         pairs = Pairs()
6941         pairs_input = (
6942             (type_octet_stringed, OctetString(b"whatever")),
6943             (type_integered, Integer(123)),
6944             (type_octet_stringed, OctetString(b"whenever")),
6945             (type_integered, Integer(234)),
6946         )
6947         for t, v in pairs_input:
6948             pairs.append(Pair((
6949                 ("type", t),
6950                 ("value", PairValue((Any(v),))),
6951             )))
6952         seq_inner = SeqInner()
6953         seq_inner["typeInner"] = type_innered
6954         seq_inner["valueInner"] = Any(pairs)
6955         seq_sequenced = Seq()
6956         seq_sequenced["type"] = type_sequenced
6957         seq_sequenced["value"] = OctetString(seq_inner.encode())
6958         seq_sequenced_raw = seq_sequenced.encode()
6959         repr(seq_sequenced)
6960         list(seq_sequenced.pps())
6961         pprint(seq_sequenced, big_blobs=True, with_decode_path=True)
6962
6963         defines_by_path = []
6964         ctx_copied = deepcopy(ctx_dummy)
6965         seq_integered, _ = Seq().decode(
6966             seq_integered_raw,
6967             ctx=ctx_copied,
6968         )
6969         self.assertDictEqual(ctx_copied, ctx_dummy)
6970         self.assertIsNone(seq_integered["value"].defined)
6971         defines_by_path.append(
6972             (("type",), ((("value",), {
6973                 type_integered: Integer(),
6974                 type_sequenced: SeqInner(),
6975             }),))
6976         )
6977         ctx_copied["defines_by_path"] = defines_by_path
6978         seq_integered, _ = Seq().decode(
6979             seq_integered_raw,
6980             ctx=ctx_copied,
6981         )
6982         del ctx_copied["defines_by_path"]
6983         self.assertDictEqual(ctx_copied, ctx_dummy)
6984         self.assertIsNotNone(seq_integered["value"].defined)
6985         self.assertEqual(seq_integered["value"].defined[0], type_integered)
6986         self.assertEqual(seq_integered["value"].defined[1], Integer(123))
6987         self.assertTrue(seq_integered_raw[
6988             seq_integered["value"].defined[1].offset:
6989         ].startswith(Integer(123).encode()))
6990         repr(seq_integered)
6991         list(seq_integered.pps())
6992         pprint(seq_integered, big_blobs=True, with_decode_path=True)
6993
6994         ctx_copied["defines_by_path"] = defines_by_path
6995         seq_sequenced, _ = Seq().decode(
6996             seq_sequenced_raw,
6997             ctx=ctx_copied,
6998         )
6999         del ctx_copied["defines_by_path"]
7000         self.assertDictEqual(ctx_copied, ctx_dummy)
7001         self.assertIsNotNone(seq_sequenced["value"].defined)
7002         self.assertEqual(seq_sequenced["value"].defined[0], type_sequenced)
7003         seq_inner = seq_sequenced["value"].defined[1]
7004         self.assertIsNone(seq_inner["valueInner"].defined)
7005         repr(seq_sequenced)
7006         list(seq_sequenced.pps())
7007         pprint(seq_sequenced, big_blobs=True, with_decode_path=True)
7008
7009         defines_by_path.append((
7010             ("value", DecodePathDefBy(type_sequenced), "typeInner"),
7011             ((("valueInner",), {type_innered: Pairs()}),),
7012         ))
7013         ctx_copied["defines_by_path"] = defines_by_path
7014         seq_sequenced, _ = Seq().decode(
7015             seq_sequenced_raw,
7016             ctx=ctx_copied,
7017         )
7018         del ctx_copied["defines_by_path"]
7019         self.assertDictEqual(ctx_copied, ctx_dummy)
7020         self.assertIsNotNone(seq_sequenced["value"].defined)
7021         self.assertEqual(seq_sequenced["value"].defined[0], type_sequenced)
7022         seq_inner = seq_sequenced["value"].defined[1]
7023         self.assertIsNotNone(seq_inner["valueInner"].defined)
7024         self.assertEqual(seq_inner["valueInner"].defined[0], type_innered)
7025         pairs = seq_inner["valueInner"].defined[1]
7026         for pair in pairs:
7027             self.assertIsNone(pair["value"][0].defined)
7028         repr(seq_sequenced)
7029         list(seq_sequenced.pps())
7030         pprint(seq_sequenced, big_blobs=True, with_decode_path=True)
7031
7032         defines_by_path.append((
7033             (
7034                 "value",
7035                 DecodePathDefBy(type_sequenced),
7036                 "valueInner",
7037                 DecodePathDefBy(type_innered),
7038                 any,
7039                 "type",
7040             ),
7041             ((("value",), {
7042                 type_integered: Integer(),
7043                 type_octet_stringed: OctetString(),
7044             }),),
7045         ))
7046         ctx_copied["defines_by_path"] = defines_by_path
7047         seq_sequenced, _ = Seq().decode(
7048             seq_sequenced_raw,
7049             ctx=ctx_copied,
7050         )
7051         del ctx_copied["defines_by_path"]
7052         self.assertDictEqual(ctx_copied, ctx_dummy)
7053         self.assertIsNotNone(seq_sequenced["value"].defined)
7054         self.assertEqual(seq_sequenced["value"].defined[0], type_sequenced)
7055         seq_inner = seq_sequenced["value"].defined[1]
7056         self.assertIsNotNone(seq_inner["valueInner"].defined)
7057         self.assertEqual(seq_inner["valueInner"].defined[0], type_innered)
7058         pairs_got = seq_inner["valueInner"].defined[1]
7059         for pair_input, pair_got in zip(pairs_input, pairs_got):
7060             self.assertEqual(pair_got["value"][0].defined[0], pair_input[0])
7061             self.assertEqual(pair_got["value"][0].defined[1], pair_input[1])
7062         repr(seq_sequenced)
7063         list(seq_sequenced.pps())
7064         pprint(seq_sequenced, big_blobs=True, with_decode_path=True)
7065
7066     @given(oid_strategy(), integers())
7067     def test_simple(self, oid, tgt):
7068         class Inner(Sequence):
7069             schema = (
7070                 ("oid", ObjectIdentifier(defines=((("..", "tgt"), {
7071                     ObjectIdentifier(oid): Integer(),
7072                 }),))),
7073             )
7074
7075         class Outer(Sequence):
7076             schema = (
7077                 ("inner", Inner()),
7078                 ("tgt", OctetString()),
7079             )
7080
7081         inner = Inner()
7082         inner["oid"] = ObjectIdentifier(oid)
7083         outer = Outer()
7084         outer["inner"] = inner
7085         outer["tgt"] = OctetString(Integer(tgt).encode())
7086         decoded, _ = Outer().decode(outer.encode())
7087         self.assertEqual(decoded["tgt"].defined[1], Integer(tgt))
7088
7089     def test_remaining_data(self):
7090         oid = ObjectIdentifier("1.2.3")
7091         class Seq(Sequence):
7092             schema = (
7093                 ("oid", ObjectIdentifier(defines=((("tgt",), {
7094                     oid: Integer(),
7095                 }),))),
7096                 ("tgt", OctetString()),
7097             )
7098
7099         seq = Seq((
7100             ("oid", oid),
7101             ("tgt", OctetString(Integer(123).encode() + b"junk")),
7102         ))
7103         with assertRaisesRegex(self, DecodeError, "remaining data"):
7104             Seq().decode(seq.encode())
7105
7106     def test_remaining_data_seqof(self):
7107         oid = ObjectIdentifier("1.2.3")
7108         class SeqOf(SetOf):
7109             schema = OctetString()
7110
7111         class Seq(Sequence):
7112             schema = (
7113                 ("oid", ObjectIdentifier(defines=((("tgt",), {
7114                     oid: Integer(),
7115                 }),))),
7116                 ("tgt", SeqOf()),
7117             )
7118
7119         seq = Seq((
7120             ("oid", oid),
7121             ("tgt", SeqOf([OctetString(Integer(123).encode() + b"junk")])),
7122         ))
7123         with assertRaisesRegex(self, DecodeError, "remaining data"):
7124             Seq().decode(seq.encode())
7125
7126
7127 class TestAbsDecodePath(TestCase):
7128     @given(
7129         lists(text(alphabet=ascii_letters, min_size=1)).map(tuple),
7130         lists(text(alphabet=ascii_letters, min_size=1), min_size=1).map(tuple),
7131     )
7132     def test_concat(self, decode_path, rel_path):
7133         dp = abs_decode_path(decode_path, rel_path)
7134         self.assertSequenceEqual(dp, decode_path + rel_path)
7135         repr(dp)
7136
7137     @given(
7138         lists(text(alphabet=ascii_letters, min_size=1)).map(tuple),
7139         lists(text(alphabet=ascii_letters, min_size=1), min_size=1).map(tuple),
7140     )
7141     def test_abs(self, decode_path, rel_path):
7142         self.assertSequenceEqual(
7143             abs_decode_path(decode_path, ("/",) + rel_path),
7144             rel_path,
7145         )
7146
7147     @given(
7148         lists(text(alphabet=ascii_letters, min_size=1), min_size=5).map(tuple),
7149         integers(min_value=1, max_value=3),
7150         lists(text(alphabet=ascii_letters, min_size=1), min_size=1).map(tuple),
7151     )
7152     def test_dots(self, decode_path, number_of_dots, rel_path):
7153         self.assertSequenceEqual(
7154             abs_decode_path(decode_path, tuple([".."] * number_of_dots) + rel_path),
7155             decode_path[:-number_of_dots] + rel_path,
7156         )
7157
7158
7159 class TestStrictDefaultExistence(TestCase):
7160     @given(data_strategy())
7161     def runTest(self, d):
7162         count = d.draw(integers(min_value=1, max_value=10))
7163         chosen = d.draw(integers(min_value=0, max_value=count - 1))
7164         _schema = [
7165             ("int%d" % i, Integer(expl=tag_ctxc(i + 1)))
7166             for i in range(count)
7167         ]
7168         for klass in (Sequence, Set):
7169             class Seq(klass):
7170                 schema = _schema
7171             seq = Seq()
7172             for i in range(count):
7173                 seq["int%d" % i] = Integer(123)
7174             raw = seq.encode()
7175             chosen_choice = "int%d" % chosen
7176             seq.specs[chosen_choice] = seq.specs[chosen_choice](default=123)
7177             with assertRaisesRegex(self, DecodeError, "DEFAULT value met"):
7178                 seq.decode(raw)
7179             decoded, _ = seq.decode(raw, ctx={"allow_default_values": True})
7180             self.assertTrue(decoded.ber_encoded)
7181             self.assertTrue(decoded.bered)
7182             decoded = copy(decoded)
7183             self.assertTrue(decoded.ber_encoded)
7184             self.assertTrue(decoded.bered)
7185             decoded, _ = seq.decode(raw, ctx={"bered": True})
7186             self.assertTrue(decoded.ber_encoded)
7187             self.assertTrue(decoded.bered)
7188             decoded = copy(decoded)
7189             self.assertTrue(decoded.ber_encoded)
7190             self.assertTrue(decoded.bered)
7191
7192
7193 class TestX690PrefixedType(TestCase):
7194     def runTest(self):
7195         self.assertSequenceEqual(
7196             VisibleString("Jones").encode(),
7197             hexdec("1A054A6F6E6573"),
7198         )
7199         self.assertSequenceEqual(
7200             VisibleString(
7201                 "Jones",
7202                 impl=tag_encode(3, klass=TagClassApplication),
7203             ).encode(),
7204             hexdec("43054A6F6E6573"),
7205         )
7206         self.assertSequenceEqual(
7207             Any(
7208                 VisibleString(
7209                     "Jones",
7210                     impl=tag_encode(3, klass=TagClassApplication),
7211                 ),
7212                 expl=tag_ctxc(2),
7213             ).encode(),
7214             hexdec("A20743054A6F6E6573"),
7215         )
7216         self.assertSequenceEqual(
7217             OctetString(
7218                 VisibleString(
7219                     "Jones",
7220                     impl=tag_encode(3, klass=TagClassApplication),
7221                 ).encode(),
7222                 impl=tag_encode(7, form=TagFormConstructed, klass=TagClassApplication),
7223             ).encode(),
7224             hexdec("670743054A6F6E6573"),
7225         )
7226         self.assertSequenceEqual(
7227             VisibleString("Jones", impl=tag_ctxp(2)).encode(),
7228             hexdec("82054A6F6E6573"),
7229         )
7230
7231
7232 class TestExplOOB(TestCase):
7233     def runTest(self):
7234         expl = tag_ctxc(123)
7235         raw = Integer(123).encode() + Integer(234).encode()
7236         raw = b"".join((expl, len_encode(len(raw)), raw))
7237         with assertRaisesRegex(self, DecodeError, "explicit tag out-of-bound"):
7238             Integer(expl=expl).decode(raw)
7239         Integer(expl=expl).decode(raw, ctx={"allow_expl_oob": True})
7240
7241
7242 class TestPickleDifferentVersion(TestCase):
7243     def runTest(self):
7244         pickled = pickle_dumps(Integer(123), pickle_proto)
7245         import pyderasn
7246         version_orig = pyderasn.__version__
7247         pyderasn.__version__ += "different"
7248         with assertRaisesRegex(self, ValueError, "different PyDERASN version"):
7249             pickle_loads(pickled)
7250         pyderasn.__version__ = version_orig
7251         pickle_loads(pickled)