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