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