]> Cypherpunks.ru repositories - pyderasn.git/blob - tests/test_pyderasn.py
90d50e9597a0cab8f7f101b9fa5f93abd3ae647a
[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         elif not PY2:
4499             self.assertEqual(obj.todatetime().timestamp(), dt.timestamp())
4500         self.assertEqual(obj.ber_encoded, not dered)
4501         self.assertEqual(obj.bered, not dered)
4502         self.assertEqual(obj.ber_raw, None if dered else data)
4503         self.assertEqual(obj.encode() == data_der, dered)
4504         repr(obj)
4505         bytes(obj)
4506         str(obj)
4507
4508     def test_invalid_ber(self):
4509         for data in ((
4510                 # "00010203040506.07",
4511                 "-0010203040506.07",
4512                 "0001-203040506.07",
4513                 "000102-3040506.07",
4514                 "00010203-40506.07",
4515                 "0001020304-506.07",
4516                 "000102030405-6.07",
4517                 "00010203040506.-7",
4518                 "+0010203040506.07",
4519                 "0001+203040506.07",
4520                 "000102+3040506.07",
4521                 "00010203+40506.07",
4522                 "0001020304+506.07",
4523                 "000102030405+6.07",
4524                 "00010203040506.+7",
4525                 " 0010203040506.07",
4526                 "0001 203040506.07",
4527                 "000102 3040506.07",
4528                 "00010203 40506.07",
4529                 "0001020304 506.07",
4530                 "000102030405 6.07",
4531                 "00010203040506. 7",
4532                 "001 0203040506.07",
4533                 "00012 03040506.07",
4534                 "0001023 040506.07",
4535                 "000102034 0506.07",
4536                 "00010203045 06.07",
4537                 "0001020304056 .07",
4538                 "00010203040506.7 ",
4539                 "00010203040506.",
4540                 "0001020304050607",
4541
4542                 "-0010203040506",
4543                 "0001-203040506",
4544                 "000102-3040506",
4545                 "00010203-40506",
4546                 "0001020304-506",
4547                 "000102030405-6",
4548                 "0001+203040506",
4549                 "000102+3040506",
4550                 "00010203+40506",
4551                 "0001020304+506",
4552                 "000102030405+6",
4553                 " 0010203040506",
4554                 "0001 203040506",
4555                 "000102 3040506",
4556                 "00010203 40506",
4557                 "0001020304 506",
4558                 "000102030405 6",
4559                 "001 0203040506",
4560                 "00012 03040506",
4561                 "0001023 040506",
4562                 "000102034 0506",
4563                 "00010203045 06",
4564                 "0001020304056 ",
4565
4566                 "-00102030405.07",
4567                 "0001-2030405.07",
4568                 "000102-30405.07",
4569                 "00010203-405.07",
4570                 "0001020304-5.07",
4571                 "000102030405.-7",
4572                 "+00102030405.07",
4573                 "0001+2030405.07",
4574                 "00010203+405.07",
4575                 "0001020304+5.07",
4576                 "000102030405.+7",
4577                 " 00102030405.07",
4578                 "0001 2030405.07",
4579                 "000102 30405.07",
4580                 "00010203 405.07",
4581                 "0001020304 5.07",
4582                 "000102030405. 7",
4583                 "001 02030405.07",
4584                 "00012 030405.07",
4585                 "0001023 0405.07",
4586                 "000102034 05.07",
4587                 "00010203045 .07",
4588                 "000102030405.7 ",
4589                 "000102030405.",
4590
4591                 "-001020304.07",
4592                 "0001-20304.07",
4593                 "000102-304.07",
4594                 "00010203-4.07",
4595                 "0001020304.-7",
4596                 "+001020304.07",
4597                 "0001+20304.07",
4598                 "00010203+4.07",
4599                 "0001020304.+7",
4600                 " 001020304.07",
4601                 "0001 20304.07",
4602                 "000102 304.07",
4603                 "00010203 4.07",
4604                 "0001020304. 7",
4605                 "001 020304.07",
4606                 "00012 0304.07",
4607                 "0001023 04.07",
4608                 "000102034 .07",
4609                 "0001020304.7 ",
4610                 "0001020304.",
4611
4612                 "00010203",
4613                 "00010203040506Y",
4614                 "0001010100+0001",
4615                 "0001010100+00:01",
4616                 "0001010100+01",
4617
4618                 "00010203040506.07+15",
4619                 "00010203040506.07-15",
4620                 "00010203040506.07+14:60",
4621                 "00010203040506.07+1460",
4622                 "00010203040506.07-1460",
4623                 "00010203040506.07+00:60",
4624                 "00010203040506.07-00:60",
4625
4626                 "00010203040506+15",
4627                 "00010203040506-15",
4628                 "00010203040506+14:60",
4629                 "00010203040506+1460",
4630                 "00010203040506-1460",
4631                 "00010203040506+00:60",
4632                 "00010203040506-00:60",
4633
4634                 "0001020304050.07",
4635                 "00010203040.07",
4636                 "000102030.07",
4637                 "0001020304050",
4638                 "00010203040",
4639                 "000102030",
4640         )):
4641             with self.assertRaises(DecodeError):
4642                 GeneralizedTime(data.encode("ascii"), ctx={"bered": True})
4643             data = data.replace(".", ",")
4644             with self.assertRaises(DecodeError):
4645                 GeneralizedTime(data.encode("ascii"), ctx={"bered": True})
4646
4647     @given(
4648         binary(
4649             min_size=(LEN_YYYYMMDDHHMMSSZ - 1) // 2,
4650             max_size=(LEN_YYYYMMDDHHMMSSZ - 1) // 2,
4651         ),
4652         binary(min_size=1, max_size=1),
4653         binary(
4654             min_size=(LEN_YYYYMMDDHHMMSSZ - 1) // 2,
4655             max_size=(LEN_YYYYMMDDHHMMSSZ - 1) // 2,
4656         ),
4657     )
4658     def test_junk(self, part0, part1, part2):
4659         junk = part0 + part1 + part2
4660         assume(not (set(junk) <= set(digits.encode("ascii"))))
4661         with self.assertRaises(DecodeError):
4662             GeneralizedTime().decode(
4663                 GeneralizedTime.tag_default +
4664                 len_encode(len(junk)) +
4665                 junk
4666             )
4667
4668     @given(
4669         binary(
4670             min_size=(LEN_YYYYMMDDHHMMSSDMZ - 1) // 2,
4671             max_size=(LEN_YYYYMMDDHHMMSSDMZ - 1) // 2,
4672         ),
4673         binary(min_size=1, max_size=1),
4674         binary(
4675             min_size=(LEN_YYYYMMDDHHMMSSDMZ - 1) // 2,
4676             max_size=(LEN_YYYYMMDDHHMMSSDMZ - 1) // 2,
4677         ),
4678     )
4679     def test_junk_dm(self, part0, part1, part2):
4680         junk = part0 + part1 + part2
4681         assume(not (set(junk) <= set(digits.encode("ascii"))))
4682         with self.assertRaises(DecodeError):
4683             GeneralizedTime().decode(
4684                 GeneralizedTime.tag_default +
4685                 len_encode(len(junk)) +
4686                 junk
4687             )
4688
4689     def test_ns_fractions(self):
4690         GeneralizedTime(b"20010101000000.000001Z")
4691         with assertRaisesRegex(self, DecodeError, "only microsecond fractions"):
4692             GeneralizedTime(b"20010101000000.0000001Z")
4693
4694     def test_non_pure_integers(self):
4695         for data in ((
4696                 # b"20000102030405Z,
4697                 b"+2000102030405Z",
4698                 b"2000+102030405Z",
4699                 b"200001+2030405Z",
4700                 b"20000102+30405Z",
4701                 b"2000010203+405Z",
4702                 b"200001020304+5Z",
4703                 b"20000102030405.+6Z",
4704                 b"20000102030405.-6Z",
4705                 b"_2000102030405Z",
4706                 b"2000_102030405Z",
4707                 b"200001_2030405Z",
4708                 b"20000102_30405Z",
4709                 b"2000010203_405Z",
4710                 b"200001020304_5Z",
4711                 b"20000102030405._6Z",
4712                 b"20000102030405.6_Z",
4713                 b" 2000102030405Z",
4714                 b"2000 102030405Z",
4715                 b"200001 2030405Z",
4716                 b"20000102 30405Z",
4717                 b"2000010203 405Z",
4718                 b"200001020304 5Z",
4719                 b"20000102030405. 6Z",
4720                 b"200 0102030405Z",
4721                 b"20001 02030405Z",
4722                 b"2000012 030405Z",
4723                 b"200001023 0405Z",
4724                 b"20000102034 05Z",
4725                 b"2000010203045 Z",
4726                 b"20000102030405.6 Z",
4727         )):
4728             with self.assertRaises(DecodeError):
4729                 GeneralizedTime(data)
4730
4731     def test_aware(self):
4732         with assertRaisesRegex(self, ValueError, "only naive"):
4733             GeneralizedTime(datetime(2000, 1, 1, 1, tzinfo=UTC))
4734
4735
4736 class TestUTCTime(TimeMixin, CommonMixin, TestCase):
4737     base_klass = UTCTime
4738     omit_ms = True
4739     min_datetime = datetime(2000, 1, 1)
4740     max_datetime = datetime(2049, 12, 31)
4741     evgen_mode_skip_value = False
4742
4743     def additional_symmetric_check(self, value, obj_encoded):
4744         pass
4745
4746     def test_repr_not_ready(self):
4747         unicode(GeneralizedTime()) if PY2 else str(GeneralizedTime())
4748         repr(UTCTime())
4749
4750     def test_x690_vector_valid(self):
4751         for data in ((
4752                 b"920521000000Z",
4753                 b"920622123421Z",
4754                 b"920722132100Z",
4755         )):
4756             UTCTime(data)
4757
4758     def test_x690_vector_invalid(self):
4759         for data in ((
4760                 b"920520240000Z",
4761                 b"9207221321Z",
4762         )):
4763             with self.assertRaises(DecodeError) as err:
4764                 UTCTime(data)
4765             repr(err.exception)
4766
4767     def test_go_vectors_invalid(self):
4768         for data in ((
4769                 b"a10506234540Z",
4770                 b"91a506234540Z",
4771                 b"9105a6234540Z",
4772                 b"910506a34540Z",
4773                 b"910506334a40Z",
4774                 b"91050633444aZ",
4775                 b"910506334461Z",
4776                 b"910506334400Za",
4777                 b"000100000000Z",
4778                 b"101302030405Z",
4779                 b"100002030405Z",
4780                 b"100100030405Z",
4781                 b"100132030405Z",
4782                 b"100231030405Z",
4783                 b"100102240405Z",
4784                 b"100102036005Z",
4785                 b"100102030460Z",
4786                 b"-100102030410Z",
4787                 b"10-0102030410Z",
4788                 b"10-0002030410Z",
4789                 b"1001-02030410Z",
4790                 b"100102-030410Z",
4791                 b"10010203-0410Z",
4792                 b"1001020304-10Z",
4793                 # These ones are INVALID in *DER*, but accepted
4794                 # by Go's encoding/asn1
4795                 b"910506164540-0700",
4796                 b"910506164540+0730",
4797                 b"9105062345Z",
4798                 b"5105062345Z",
4799         )):
4800             with self.assertRaises(DecodeError) as err:
4801                 UTCTime(data)
4802             repr(err.exception)
4803
4804     def test_go_vectors_valid(self):
4805         self.assertEqual(
4806             UTCTime(b"910506234540Z").todatetime(),
4807             datetime(1991, 5, 6, 23, 45, 40, 0),
4808         )
4809
4810     def test_non_pure_integers(self):
4811         for data in ((
4812                 # b"000102030405Z",
4813                 b"+10102030405Z",
4814                 b"00+102030405Z",
4815                 b"0001+2030405Z",
4816                 b"000102+30405Z",
4817                 b"00010203+405Z",
4818                 b"0001020304+5Z",
4819                 b"_10102030405Z",
4820                 b"00_102030405Z",
4821                 b"0001_2030405Z",
4822                 b"000102_30405Z",
4823                 b"00010203_405Z",
4824                 b"0001020304_5Z",
4825                 b"00010203045_Z",
4826                 b" 10102030405Z",
4827                 b"00 102030405Z",
4828                 b"0001 2030405Z",
4829                 b"000102 30405Z",
4830                 b"00010203 405Z",
4831                 b"0001020304 5Z",
4832                 b"1 0102030405Z",
4833                 b"001 02030405Z",
4834                 b"00012 030405Z",
4835                 b"0001023 0405Z",
4836                 b"000102034 05Z",
4837                 b"00010203045 Z",
4838         )):
4839             with self.assertRaises(DecodeError):
4840                 UTCTime(data)
4841
4842     def test_x680_vector_valid_ber(self):
4843         for data, dt in ((
4844                 (b"8201021200Z", datetime(1982, 1, 2, 12)),
4845                 (b"8201020700-0500", datetime(1982, 1, 2, 12)),
4846                 (b"0101021200Z", datetime(2001, 1, 2, 12)),
4847                 (b"0101020700-0500", datetime(2001, 1, 2, 12)),
4848         )):
4849             data_der = UTCTime.tag_default + len_encode(len(data)) + data
4850             obj = UTCTime().decod(data_der, ctx={"bered": True})
4851             self.assertEqual(obj, dt)
4852             self.assertEqual(obj.todatetime(), dt)
4853             self.assertTrue(obj.ber_encoded)
4854             self.assertTrue(obj.bered)
4855             self.assertEqual(obj.ber_raw, data)
4856             self.assertNotEqual(obj.encode(), data_der)
4857             repr(obj)
4858
4859     def test_go_vectors_valid_ber(self):
4860         for data in ((
4861                 b"910506164540-0700",
4862                 b"910506164540+0730",
4863                 b"9105062345Z",
4864                 b"5105062345Z",
4865         )):
4866             data = UTCTime.tag_default + len_encode(len(data)) + data
4867             obj = UTCTime().decod(data, ctx={"bered": True})
4868             self.assertTrue(obj.ber_encoded)
4869             self.assertTrue(obj.bered)
4870             self.assertNotEqual(obj.encode(), data)
4871             repr(obj)
4872
4873     @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
4874     @given(data_strategy())
4875     def test_valid_ber(self, d):
4876         year = d.draw(integers(min_value=0, max_value=99))
4877         month = d.draw(integers(min_value=1, max_value=12))
4878         day = d.draw(integers(min_value=1, max_value=28))
4879         hours = d.draw(integers(min_value=0, max_value=23))
4880         minute = d.draw(integers(min_value=0, max_value=59))
4881         data = "%02d%02d%02d%02d%02d" % (year, month, day, hours, minute)
4882         dt = datetime(
4883             year + (2000 if year < 50 else 1900),
4884             month,
4885             day,
4886             hours,
4887             minute,
4888         )
4889         dered = False
4890         if d.draw(booleans()):
4891             dered = True
4892             seconds = d.draw(integers(min_value=0, max_value=59))
4893             data += "%02d" % seconds
4894             dt += timedelta(seconds=seconds)
4895         if d.draw(booleans()):
4896             data += "Z"
4897         else:
4898             dered = False
4899             offset_hour = d.draw(integers(min_value=0, max_value=13))
4900             offset_minute = d.draw(integers(min_value=0, max_value=59))
4901             offset = timedelta(seconds=offset_hour * 3600 + offset_minute * 60)
4902             if d.draw(booleans()):
4903                 dt += offset
4904                 data += "-"
4905             else:
4906                 dt -= offset
4907                 data += "+"
4908             data += "%02d%02d" % (offset_hour, offset_minute)
4909         data = data.encode("ascii")
4910         data_der = UTCTime.tag_default + len_encode(len(data)) + data
4911         obj = UTCTime().decod(data_der, ctx={"bered": True})
4912         self.assertEqual(obj, dt)
4913         self.assertEqual(obj.todatetime(), dt)
4914         self.assertEqual(obj.ber_encoded, not dered)
4915         self.assertEqual(obj.bered, not dered)
4916         self.assertEqual(obj.ber_raw, None if dered else data)
4917         self.assertEqual(obj.encode() == data_der, dered)
4918         repr(obj)
4919         bytes(obj)
4920         str(obj)
4921
4922     def test_invalid_ber(self):
4923         for data in ((
4924                 # b"0001020304Z",
4925                 b"-101020304Z",
4926                 b"00-1020304Z",
4927                 b"0001-20304Z",
4928                 b"000102-304Z",
4929                 b"000102-104Z",
4930                 b"00000203-4Z",
4931                 b"+101020304Z",
4932                 b"00+1020304Z",
4933                 b"0001+20304Z",
4934                 b"000102+304Z",
4935                 b"000102+104Z",
4936                 b"00000203+4Z",
4937                 b" 101020304Z",
4938                 b"00 1020304Z",
4939                 b"0001 20304Z",
4940                 b"000102 304Z",
4941                 b"000102 104Z",
4942                 b"00000203 4Z",
4943                 b"1 01020304Z",
4944                 b"001 020304Z",
4945                 b"00012 0304Z",
4946                 b"0001023 04Z",
4947                 b"0001021 04Z",
4948                 b"000002034 Z",
4949                 b"0013020304Z",
4950                 b"0001000304Z",
4951                 b"0001320304Z",
4952                 b"0001022404Z",
4953                 b"0001020360Z",
4954                 b"0002300304Z",
4955                 b"0001020304",
4956                 b"0001020304T",
4957                 b"0001020304+",
4958                 b"0001020304-",
4959                 b"0001020304+0",
4960                 b"0001020304+00",
4961                 b"0001020304+000",
4962                 b"0001020304+000Z",
4963                 b"0001020304+0000Z",
4964                 b"0001020304+-101",
4965                 b"0001020304+01-1",
4966                 b"0001020304+0060",
4967                 b"0001020304+1401",
4968                 b"5001010000+0001",
4969                 b"000102030Z",
4970                 b"0001020Z",
4971         )):
4972             with self.assertRaises(DecodeError):
4973                 UTCTime(data, ctx={"bered": True})
4974             data = data[:8] + data[8+2:]
4975             with self.assertRaises(DecodeError):
4976                 UTCTime(data, ctx={"bered": True})
4977
4978         for data in ((
4979                 # b"000102030405Z",
4980                 b"-10102030405Z",
4981                 b"00-102030405Z",
4982                 b"0001-2030405Z",
4983                 b"000102-30405Z",
4984                 b"000102-10405Z",
4985                 b"00000203-405Z",
4986                 b"0000020304-5Z",
4987                 b"+10102030405Z",
4988                 b"00+102030405Z",
4989                 b"0001+2030405Z",
4990                 b"000102+30405Z",
4991                 b"000102+10405Z",
4992                 b"00000203+405Z",
4993                 b"0000020304+5Z",
4994                 b" 10102030405Z",
4995                 b"00 102030405Z",
4996                 b"0001 2030405Z",
4997                 b"000102 30405Z",
4998                 b"000102 10405Z",
4999                 b"00000203 405Z",
5000                 b"0000020304 5Z",
5001                 b"1 0102030405Z",
5002                 b"001 02030405Z",
5003                 b"00012 030405Z",
5004                 b"0001023 0405Z",
5005                 b"0001021 0405Z",
5006                 b"000002034 05Z",
5007                 b"00000203045 Z",
5008                 b"001302030405Z",
5009                 b"000100030405Z",
5010                 b"000132030405Z",
5011                 b"000102240405Z",
5012                 b"000102036005Z",
5013                 b"000230030405Z",
5014                 b"000102030460Z",
5015                 b"000102030405",
5016                 b"000102030405T",
5017                 b"000102030405+",
5018                 b"000102030405-",
5019                 b"000102030405+0",
5020                 b"000102030405+00",
5021                 b"000102030405+000",
5022                 b"000102030405+000Z",
5023                 b"000102030405+0000Z",
5024                 b"000102030405+-101",
5025                 b"000102030405+01-1",
5026                 b"000102030405+0060",
5027                 b"000102030405+1401",
5028                 b"500101000002+0003",
5029         )):
5030             with self.assertRaises(DecodeError):
5031                 UTCTime(data, ctx={"bered": True})
5032
5033     @given(integers(min_value=0, max_value=49))
5034     def test_pre50(self, year):
5035         self.assertEqual(
5036             UTCTime(("%02d1231235959Z" % year).encode("ascii")).todatetime().year,
5037             2000 + year,
5038         )
5039
5040     @given(integers(min_value=50, max_value=99))
5041     def test_post50(self, year):
5042         self.assertEqual(
5043             UTCTime(("%02d1231235959Z" % year).encode("ascii")).todatetime().year,
5044             1900 + year,
5045         )
5046
5047     @given(
5048         binary(
5049             min_size=(LEN_YYMMDDHHMMSSZ - 1) // 2,
5050             max_size=(LEN_YYMMDDHHMMSSZ - 1) // 2,
5051         ),
5052         binary(min_size=1, max_size=1),
5053         binary(
5054             min_size=(LEN_YYMMDDHHMMSSZ - 1) // 2,
5055             max_size=(LEN_YYMMDDHHMMSSZ - 1) // 2,
5056         ),
5057     )
5058     def test_junk(self, part0, part1, part2):
5059         junk = part0 + part1 + part2
5060         assume(not (set(junk) <= set(digits.encode("ascii"))))
5061         with self.assertRaises(DecodeError):
5062             UTCTime().decode(
5063                 UTCTime.tag_default +
5064                 len_encode(len(junk)) +
5065                 junk
5066             )
5067
5068     def test_aware(self):
5069         with assertRaisesRegex(self, ValueError, "only naive"):
5070             UTCTime(datetime(2000, 1, 1, 1, tzinfo=UTC))
5071
5072
5073 @composite
5074 def tlv_value_strategy(draw):
5075     tag_num = draw(integers(min_value=1))
5076     data = draw(binary())
5077     return b"".join((tag_encode(tag_num), len_encode(len(data)), data))
5078
5079
5080 @composite
5081 def any_values_strategy(draw, do_expl=False):
5082     value = draw(one_of(none(), tlv_value_strategy()))
5083     expl = None
5084     if do_expl:
5085         expl = draw(one_of(none(), integers(min_value=1).map(tag_encode)))
5086     optional = draw(one_of(none(), booleans()))
5087     _decoded = (
5088         draw(integers(min_value=0)),
5089         draw(integers(min_value=0)),
5090         draw(integers(min_value=0)),
5091     )
5092     return (value, expl, optional, _decoded)
5093
5094
5095 class AnyInherited(Any):
5096     pass
5097
5098
5099 class TestAny(CommonMixin, TestCase):
5100     base_klass = Any
5101
5102     def test_invalid_value_type(self):
5103         with self.assertRaises(InvalidValueType) as err:
5104             Any(123)
5105         repr(err.exception)
5106
5107     @given(booleans())
5108     def test_optional(self, optional):
5109         obj = Any(optional=optional)
5110         self.assertEqual(obj.optional, optional)
5111
5112     @given(tlv_value_strategy())
5113     def test_ready(self, value):
5114         obj = Any()
5115         self.assertFalse(obj.ready)
5116         repr(obj)
5117         list(obj.pps())
5118         pprint(obj, big_blobs=True, with_decode_path=True)
5119         with self.assertRaises(ObjNotReady) as err:
5120             obj.encode()
5121         repr(err.exception)
5122         with self.assertRaises(ObjNotReady) as err:
5123             encode2pass(obj)
5124         obj = Any(value)
5125         self.assertTrue(obj.ready)
5126         repr(obj)
5127         list(obj.pps())
5128         pprint(obj, big_blobs=True, with_decode_path=True)
5129
5130     @given(integers())
5131     def test_basic(self, value):
5132         integer_encoded = Integer(value).encode()
5133         for obj in (
5134                 Any(integer_encoded),
5135                 Any(Integer(value)),
5136                 Any(Any(Integer(value))),
5137         ):
5138             self.assertSequenceEqual(bytes(obj), integer_encoded)
5139             self.assertEqual(
5140                 obj.decode(obj.encode())[0].vlen,
5141                 len(integer_encoded),
5142             )
5143             repr(obj)
5144             list(obj.pps())
5145             pprint(obj, big_blobs=True, with_decode_path=True)
5146             self.assertSequenceEqual(obj.encode(), integer_encoded)
5147
5148     @given(tlv_value_strategy(), tlv_value_strategy())
5149     def test_comparison(self, value1, value2):
5150         for klass in (Any, AnyInherited):
5151             obj1 = klass(value1)
5152             obj2 = klass(value2)
5153             self.assertEqual(obj1 == obj2, value1 == value2)
5154             self.assertEqual(obj1 != obj2, value1 != value2)
5155             self.assertEqual(obj1 == bytes(obj2), value1 == value2)
5156
5157     @given(data_strategy())
5158     def test_call(self, d):
5159         for klass in (Any, AnyInherited):
5160             (
5161                 value_initial,
5162                 expl_initial,
5163                 optional_initial,
5164                 _decoded_initial,
5165             ) = d.draw(any_values_strategy())
5166             obj_initial = klass(
5167                 value_initial,
5168                 expl_initial,
5169                 optional_initial or False,
5170                 _decoded_initial,
5171             )
5172             (
5173                 value,
5174                 expl,
5175                 optional,
5176                 _decoded,
5177             ) = d.draw(any_values_strategy(do_expl=True))
5178             obj = obj_initial(value, expl, optional)
5179             if obj.ready:
5180                 value_expected = None if value is None else value
5181                 self.assertEqual(obj, value_expected)
5182             self.assertEqual(obj.expl_tag, expl or expl_initial)
5183             if obj.default is None:
5184                 optional = optional_initial if optional is None else optional
5185                 optional = False if optional is None else optional
5186             self.assertEqual(obj.optional, optional)
5187
5188     def test_simultaneous_impl_expl(self):
5189         # override it, as Any does not have implicit tag
5190         pass
5191
5192     def test_decoded(self):
5193         # override it, as Any does not have implicit tag
5194         pass
5195
5196     @given(any_values_strategy())
5197     def test_copy(self, values):
5198         for klass in (Any, AnyInherited):
5199             obj = klass(*values)
5200             for copy_func in copy_funcs:
5201                 obj_copied = copy_func(obj)
5202                 self.assert_copied_basic_fields(obj, obj_copied)
5203                 self.assertEqual(obj._value, obj_copied._value)
5204
5205     @given(binary().map(OctetString))
5206     def test_stripped(self, value):
5207         obj = Any(value)
5208         with self.assertRaises(NotEnoughData):
5209             obj.decode(obj.encode()[:-1])
5210
5211     @given(
5212         tlv_value_strategy(),
5213         integers(min_value=1).map(tag_ctxc),
5214     )
5215     def test_stripped_expl(self, value, tag_expl):
5216         obj = Any(value, expl=tag_expl)
5217         with self.assertRaises(NotEnoughData):
5218             obj.decode(obj.encode()[:-1])
5219
5220     @given(
5221         integers(min_value=31),
5222         integers(min_value=0),
5223         decode_path_strat,
5224     )
5225     def test_bad_tag(self, tag, offset, decode_path):
5226         with self.assertRaises(DecodeError) as err:
5227             Any().decode(
5228                 tag_encode(tag)[:-1],
5229                 offset=offset,
5230                 decode_path=decode_path,
5231             )
5232         repr(err.exception)
5233         self.assertEqual(err.exception.offset, offset)
5234         self.assertEqual(err.exception.decode_path, decode_path)
5235
5236     @given(
5237         integers(min_value=128),
5238         integers(min_value=0),
5239         decode_path_strat,
5240     )
5241     def test_bad_len(self, l, offset, decode_path):
5242         with self.assertRaises(DecodeError) as err:
5243             Any().decode(
5244                 Any.tag_default + len_encode(l)[:-1],
5245                 offset=offset,
5246                 decode_path=decode_path,
5247             )
5248         repr(err.exception)
5249         self.assertEqual(err.exception.offset, offset)
5250         self.assertEqual(err.exception.decode_path, decode_path)
5251
5252     @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
5253     @given(
5254         any_values_strategy(),
5255         integers().map(lambda x: Integer(x).encode()),
5256         integers(min_value=1).map(tag_ctxc),
5257         integers(min_value=0),
5258         binary(max_size=5),
5259         decode_path_strat,
5260     )
5261     def test_symmetric(self, values, value, tag_expl, offset, tail_junk, decode_path):
5262         for klass in (Any, AnyInherited):
5263             _, _, optional, _decoded = values
5264             obj = klass(value=value, optional=optional, _decoded=_decoded)
5265             repr(obj)
5266             list(obj.pps())
5267             pprint(obj, big_blobs=True, with_decode_path=True)
5268             self.assertFalse(obj.expled)
5269             tag_class, _, tag_num = tag_decode(tag_strip(value)[0])
5270             self.assertEqual(obj.tag_order, (tag_class, tag_num))
5271             obj_encoded = obj.encode()
5272             self.assertEqual(encode2pass(obj), obj_encoded)
5273             obj_expled = obj(value, expl=tag_expl)
5274             self.assertTrue(obj_expled.expled)
5275             tag_class, _, tag_num = tag_decode(tag_expl)
5276             self.assertEqual(obj_expled.tag_order, (tag_class, tag_num))
5277             repr(obj_expled)
5278             list(obj_expled.pps())
5279             pprint(obj_expled, big_blobs=True, with_decode_path=True)
5280             obj_expled_encoded = obj_expled.encode()
5281             ctx_copied = deepcopy(ctx_dummy)
5282             obj_decoded, tail = obj_expled.decode(
5283                 obj_expled_encoded + tail_junk,
5284                 offset=offset,
5285                 ctx=ctx_copied,
5286             )
5287             self.assertDictEqual(ctx_copied, ctx_dummy)
5288             repr(obj_decoded)
5289             list(obj_decoded.pps())
5290             pprint(obj_decoded, big_blobs=True, with_decode_path=True)
5291             self.assertEqual(tail, tail_junk)
5292             self.assertEqual(obj_decoded, obj_expled)
5293             self.assertEqual(bytes(obj_decoded), bytes(obj_expled))
5294             self.assertEqual(bytes(obj_decoded), bytes(obj))
5295             self.assertSequenceEqual(obj_decoded.encode(), obj_expled_encoded)
5296             self.assertSequenceEqual(obj_decoded.expl_tag, tag_expl)
5297             self.assertEqual(obj_decoded.expl_tlen, len(tag_expl))
5298             self.assertEqual(
5299                 obj_decoded.expl_llen,
5300                 len(len_encode(len(obj_encoded))),
5301             )
5302             self.assertEqual(obj_decoded.tlvlen, len(obj_encoded))
5303             self.assertEqual(obj_decoded.expl_vlen, len(obj_encoded))
5304             self.assertEqual(
5305                 obj_decoded.offset,
5306                 offset + obj_decoded.expl_tlen + obj_decoded.expl_llen,
5307             )
5308             self.assertEqual(obj_decoded.expl_offset, offset)
5309             self.assertEqual(obj_decoded.tlen, 0)
5310             self.assertEqual(obj_decoded.llen, 0)
5311             self.assertEqual(obj_decoded.vlen, len(value))
5312             assert_exceeding_data(
5313                 self,
5314                 lambda: obj_expled.decod(obj_expled_encoded + tail_junk),
5315                 tail_junk,
5316             )
5317
5318             evgens = list(obj_expled.decode_evgen(
5319                 obj_expled_encoded + tail_junk,
5320                 offset=offset,
5321                 decode_path=decode_path,
5322                 ctx=ctx_copied,
5323             ))
5324             self.assertEqual(len(evgens), 1)
5325             _decode_path, obj, tail = evgens[0]
5326             self.assertSequenceEqual(tail, tail_junk)
5327             self.assertEqual(_decode_path, decode_path)
5328             self.assertEqual(obj.expl_offset, offset)
5329             repr(obj)
5330             list(obj.pps())
5331
5332     @given(
5333         integers(min_value=1).map(tag_ctxc),
5334         integers(min_value=0, max_value=3),
5335         integers(min_value=0),
5336         decode_path_strat,
5337         binary(),
5338     )
5339     def test_indefinite(self, expl, chunks, offset, decode_path, junk):
5340         chunk = Boolean(False, expl=expl).encode()
5341         encoded = (
5342             OctetString.tag_default +
5343             LENINDEF +
5344             b"".join([chunk] * chunks) +
5345             EOC
5346         )
5347         with self.assertRaises(LenIndefForm):
5348             Any().decode(
5349                 encoded + junk,
5350                 offset=offset,
5351                 decode_path=decode_path,
5352             )
5353         obj, tail = Any().decode(
5354             encoded + junk,
5355             offset=offset,
5356             decode_path=decode_path,
5357             ctx={"bered": True},
5358         )
5359         self.assertSequenceEqual(tail, junk)
5360         self.assertEqual(obj.offset, offset)
5361         self.assertEqual(obj.tlvlen, len(encoded))
5362         self.assertTrue(obj.lenindef)
5363         self.assertFalse(obj.ber_encoded)
5364         self.assertTrue(obj.bered)
5365         obj = copy(obj)
5366         self.assertTrue(obj.lenindef)
5367         self.assertFalse(obj.ber_encoded)
5368         self.assertTrue(obj.bered)
5369         repr(obj)
5370         list(obj.pps())
5371         pprint(obj, big_blobs=True, with_decode_path=True)
5372         with self.assertRaises(NotEnoughData) as err:
5373             Any().decode(
5374                 encoded[:-1],
5375                 offset=offset,
5376                 decode_path=decode_path,
5377                 ctx={"bered": True},
5378             )
5379         self.assertEqual(err.exception.offset, offset + 1 + 1 + len(chunk) * chunks)
5380         self.assertEqual(err.exception.decode_path, decode_path + (str(chunks),))
5381
5382         class SeqOf(SequenceOf):
5383             schema = Boolean(expl=expl)
5384
5385         class Seq(Sequence):
5386             schema = (
5387                 ("type", ObjectIdentifier(defines=((("value",), {
5388                     ObjectIdentifier("1.2.3"): SeqOf(impl=OctetString.tag_default),
5389                 }),))),
5390                 ("value", Any()),
5391             )
5392         seq = Seq((
5393             ("type", ObjectIdentifier("1.2.3")),
5394             ("value", Any(encoded)),
5395         ))
5396         seq_encoded = seq.encode()
5397         seq_decoded, _ = Seq().decode(seq_encoded, ctx={"bered": True})
5398         self.assertIsNotNone(seq_decoded["value"].defined)
5399         repr(seq_decoded)
5400         list(seq_decoded.pps())
5401         pprint(seq_decoded, big_blobs=True, with_decode_path=True)
5402         self.assertTrue(seq_decoded.bered)
5403         self.assertFalse(seq_decoded["type"].bered)
5404         self.assertTrue(seq_decoded["value"].bered)
5405
5406         chunk = chunk[:-1] + b"\x01"
5407         chunks = b"".join([chunk] * (chunks + 1))
5408         encoded = OctetString.tag_default + len_encode(len(chunks)) + chunks
5409         seq = Seq((
5410             ("type", ObjectIdentifier("1.2.3")),
5411             ("value", Any(encoded)),
5412         ))
5413         seq_encoded = seq.encode()
5414         seq_decoded, _ = Seq().decode(seq_encoded, ctx={"bered": True})
5415         self.assertIsNotNone(seq_decoded["value"].defined)
5416         repr(seq_decoded)
5417         list(seq_decoded.pps())
5418         pprint(seq_decoded, big_blobs=True, with_decode_path=True)
5419         self.assertTrue(seq_decoded.bered)
5420         self.assertFalse(seq_decoded["type"].bered)
5421         self.assertTrue(seq_decoded["value"].bered)
5422
5423
5424 @composite
5425 def choice_values_strategy(draw, value_required=False, schema=None, do_expl=False):
5426     if schema is None:
5427         names = list(draw(sets(text_letters(), min_size=1, max_size=5)))
5428         tags = [{tag_type: tag_value} for tag_type, tag_value in draw(sets(
5429             one_of(
5430                 tuples(just("impl"), integers(min_value=0).map(tag_encode)),
5431                 tuples(just("expl"), integers(min_value=0).map(tag_ctxp)),
5432             ),
5433             min_size=len(names),
5434             max_size=len(names),
5435         ))]
5436         schema = [
5437             (name, Integer(**tag_kwargs))
5438             for name, tag_kwargs in zip(names, tags)
5439         ]
5440     value = None
5441     if value_required or draw(booleans()):
5442         value = draw(tuples(
5443             sampled_from([name for name, _ in schema]),
5444             integers().map(Integer),
5445         ))
5446     expl = None
5447     if do_expl:
5448         expl = draw(one_of(none(), integers(min_value=1).map(tag_encode)))
5449     default = draw(one_of(
5450         none(),
5451         tuples(sampled_from([name for name, _ in schema]), integers().map(Integer)),
5452     ))
5453     optional = draw(one_of(none(), booleans()))
5454     _decoded = (
5455         draw(integers(min_value=0)),
5456         draw(integers(min_value=0)),
5457         draw(integers(min_value=0)),
5458     )
5459     return (schema, value, expl, default, optional, _decoded)
5460
5461
5462 class ChoiceInherited(Choice):
5463     pass
5464
5465
5466 class TestChoice(CommonMixin, TestCase):
5467     class Wahl(Choice):
5468         schema = (("whatever", Boolean()),)
5469     base_klass = Wahl
5470
5471     def test_schema_required(self):
5472         with assertRaisesRegex(self, ValueError, "schema must be specified"):
5473             Choice()
5474
5475     def test_impl_forbidden(self):
5476         with assertRaisesRegex(self, ValueError, "no implicit tag allowed"):
5477             Choice(impl=b"whatever")
5478
5479     def test_invalid_value_type(self):
5480         with self.assertRaises(InvalidValueType) as err:
5481             self.base_klass(123)
5482         repr(err.exception)
5483         with self.assertRaises(ObjUnknown) as err:
5484             self.base_klass(("whenever", Boolean(False)))
5485         repr(err.exception)
5486         with self.assertRaises(InvalidValueType) as err:
5487             self.base_klass(("whatever", Integer(123)))
5488         repr(err.exception)
5489
5490     @given(booleans())
5491     def test_optional(self, optional):
5492         obj = self.base_klass(
5493             default=self.base_klass(("whatever", Boolean(False))),
5494             optional=optional,
5495         )
5496         self.assertTrue(obj.optional)
5497
5498     @given(booleans())
5499     def test_ready(self, value):
5500         obj = self.base_klass()
5501         self.assertFalse(obj.ready)
5502         repr(obj)
5503         list(obj.pps())
5504         pprint(obj, big_blobs=True, with_decode_path=True)
5505         self.assertIsNone(obj["whatever"])
5506         with self.assertRaises(ObjNotReady) as err:
5507             obj.encode()
5508         repr(err.exception)
5509         with self.assertRaises(ObjNotReady) as err:
5510             encode2pass(obj)
5511         obj["whatever"] = Boolean()
5512         self.assertFalse(obj.ready)
5513         repr(obj)
5514         list(obj.pps())
5515         pprint(obj, big_blobs=True, with_decode_path=True)
5516         obj["whatever"] = Boolean(value)
5517         self.assertTrue(obj.ready)
5518         repr(obj)
5519         list(obj.pps())
5520         pprint(obj, big_blobs=True, with_decode_path=True)
5521
5522     @given(booleans(), booleans())
5523     def test_comparison(self, value1, value2):
5524         class WahlInherited(self.base_klass):
5525             pass
5526         for klass in (self.base_klass, WahlInherited):
5527             obj1 = klass(("whatever", Boolean(value1)))
5528             obj2 = klass(("whatever", Boolean(value2)))
5529             self.assertEqual(obj1 == obj2, value1 == value2)
5530             self.assertEqual(obj1 != obj2, value1 != value2)
5531             self.assertEqual(obj1 == obj2._value, value1 == value2)
5532             self.assertFalse(obj1 == obj2._value[1])
5533
5534     @given(data_strategy())
5535     def test_call(self, d):
5536         for klass in (Choice, ChoiceInherited):
5537             (
5538                 schema_initial,
5539                 value_initial,
5540                 expl_initial,
5541                 default_initial,
5542                 optional_initial,
5543                 _decoded_initial,
5544             ) = d.draw(choice_values_strategy())
5545
5546             class Wahl(klass):
5547                 schema = schema_initial
5548             obj_initial = Wahl(
5549                 value=value_initial,
5550                 expl=expl_initial,
5551                 default=default_initial,
5552                 optional=optional_initial or False,
5553                 _decoded=_decoded_initial,
5554             )
5555             (
5556                 _,
5557                 value,
5558                 expl,
5559                 default,
5560                 optional,
5561                 _decoded,
5562             ) = d.draw(choice_values_strategy(schema=schema_initial, do_expl=True))
5563             obj = obj_initial(value, expl, default, optional)
5564             if obj.ready:
5565                 value_expected = default if value is None else value
5566                 value_expected = (
5567                     default_initial if value_expected is None
5568                     else value_expected
5569                 )
5570                 self.assertEqual(obj.choice, value_expected[0])
5571                 self.assertEqual(obj.value, int(value_expected[1]))
5572             self.assertEqual(obj.expl_tag, expl or expl_initial)
5573             default_expect = default_initial if default is None else default
5574             if default_expect is not None:
5575                 self.assertEqual(obj.default.choice, default_expect[0])
5576                 self.assertEqual(obj.default.value, int(default_expect[1]))
5577             if obj.default is None:
5578                 optional = optional_initial if optional is None else optional
5579                 optional = False if optional is None else optional
5580             else:
5581                 optional = True
5582             self.assertEqual(obj.optional, optional)
5583             self.assertEqual(obj.specs, obj_initial.specs)
5584
5585     def test_simultaneous_impl_expl(self):
5586         # override it, as Any does not have implicit tag
5587         pass
5588
5589     def test_decoded(self):
5590         # override it, as Any does not have implicit tag
5591         pass
5592
5593     @given(choice_values_strategy())
5594     def test_copy(self, values):
5595         _schema, value, expl, default, optional, _decoded = values
5596
5597         class Wahl(self.base_klass):
5598             schema = _schema
5599         register_class(Wahl)
5600         obj = Wahl(
5601             value=value,
5602             expl=expl,
5603             default=default,
5604             optional=optional or False,
5605             _decoded=_decoded,
5606         )
5607         for copy_func in copy_funcs:
5608             obj_copied = copy_func(obj)
5609             self.assertIsNone(obj.tag)
5610             self.assertIsNone(obj_copied.tag)
5611             # hack for assert_copied_basic_fields
5612             obj.tag = "whatever"
5613             obj_copied.tag = "whatever"
5614             self.assert_copied_basic_fields(obj, obj_copied)
5615             obj.tag = None
5616             self.assertEqual(obj._value, obj_copied._value)
5617             self.assertEqual(obj.specs, obj_copied.specs)
5618
5619     @given(booleans())
5620     def test_stripped(self, value):
5621         obj = self.base_klass(("whatever", Boolean(value)))
5622         with self.assertRaises(NotEnoughData):
5623             obj.decode(obj.encode()[:-1])
5624
5625     @given(
5626         booleans(),
5627         integers(min_value=1).map(tag_ctxc),
5628     )
5629     def test_stripped_expl(self, value, tag_expl):
5630         obj = self.base_klass(("whatever", Boolean(value)), expl=tag_expl)
5631         with self.assertRaises(NotEnoughData):
5632             obj.decode(obj.encode()[:-1])
5633
5634     @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
5635     @given(data_strategy())
5636     def test_symmetric(self, d):
5637         _schema, value, _, default, optional, _decoded = d.draw(
5638             choice_values_strategy(value_required=True)
5639         )
5640         tag_expl = tag_ctxc(d.draw(integers(min_value=1)))
5641         offset = d.draw(integers(min_value=0))
5642         tail_junk = d.draw(binary(max_size=5))
5643         decode_path = d.draw(decode_path_strat)
5644
5645         class Wahl(self.base_klass):
5646             schema = _schema
5647         obj = Wahl(
5648             value=value,
5649             default=default,
5650             optional=optional,
5651             _decoded=_decoded,
5652         )
5653         repr(obj)
5654         list(obj.pps())
5655         pprint(obj, big_blobs=True, with_decode_path=True)
5656         self.assertFalse(obj.expled)
5657         self.assertEqual(obj.tag_order, obj.value.tag_order)
5658         obj_encoded = obj.encode()
5659         self.assertEqual(encode2pass(obj), obj_encoded)
5660         obj_expled = obj(value, expl=tag_expl)
5661         self.assertTrue(obj_expled.expled)
5662         tag_class, _, tag_num = tag_decode(tag_expl)
5663         self.assertEqual(obj_expled.tag_order, (tag_class, tag_num))
5664         repr(obj_expled)
5665         list(obj_expled.pps())
5666         pprint(obj_expled, big_blobs=True, with_decode_path=True)
5667         obj_expled_encoded = obj_expled.encode()
5668         ctx_copied = deepcopy(ctx_dummy)
5669         obj_decoded, tail = obj_expled.decode(
5670             obj_expled_encoded + tail_junk,
5671             offset=offset,
5672             ctx=ctx_copied,
5673         )
5674         self.assertDictEqual(ctx_copied, ctx_dummy)
5675         repr(obj_decoded)
5676         list(obj_decoded.pps())
5677         pprint(obj_decoded, big_blobs=True, with_decode_path=True)
5678         self.assertEqual(tail, tail_junk)
5679         self.assertEqual(obj_decoded, obj_expled)
5680         self.assertEqual(obj_decoded.choice, obj_expled.choice)
5681         self.assertEqual(obj_decoded.value, obj_expled.value)
5682         self.assertEqual(obj_decoded.choice, obj.choice)
5683         self.assertEqual(obj_decoded.value, obj.value)
5684         self.assertSequenceEqual(obj_decoded.encode(), obj_expled_encoded)
5685         self.assertSequenceEqual(obj_decoded.expl_tag, tag_expl)
5686         self.assertEqual(obj_decoded.expl_tlen, len(tag_expl))
5687         self.assertEqual(
5688             obj_decoded.expl_llen,
5689             len(len_encode(len(obj_encoded))),
5690         )
5691         self.assertEqual(obj_decoded.tlvlen, len(obj_encoded))
5692         self.assertEqual(obj_decoded.expl_vlen, len(obj_encoded))
5693         self.assertEqual(
5694             obj_decoded.offset,
5695             offset + obj_decoded.expl_tlen + obj_decoded.expl_llen,
5696         )
5697         self.assertEqual(obj_decoded.expl_offset, offset)
5698         self.assertSequenceEqual(
5699             obj_expled_encoded[
5700                 obj_decoded.value.fulloffset - offset:
5701                 obj_decoded.value.fulloffset + obj_decoded.value.fulllen - offset
5702             ],
5703             obj_encoded,
5704         )
5705         assert_exceeding_data(
5706             self,
5707             lambda: obj_expled.decod(obj_expled_encoded + tail_junk),
5708             tail_junk,
5709         )
5710
5711         evgens = list(obj_expled.decode_evgen(
5712             obj_expled_encoded + tail_junk,
5713             offset=offset,
5714             decode_path=decode_path,
5715             ctx=ctx_copied,
5716         ))
5717         self.assertEqual(len(evgens), 2)
5718         _decode_path, obj, tail = evgens[0]
5719         self.assertEqual(_decode_path, decode_path + (obj_decoded.choice,))
5720         _decode_path, obj, tail = evgens[1]
5721         self.assertSequenceEqual(tail, tail_junk)
5722         self.assertEqual(_decode_path, decode_path)
5723         self.assertEqual(obj.expl_offset, offset)
5724         repr(obj)
5725         list(obj.pps())
5726
5727     @given(integers())
5728     def test_set_get(self, value):
5729         class Wahl(Choice):
5730             schema = (
5731                 ("erste", Boolean()),
5732                 ("zweite", Integer()),
5733             )
5734         obj = Wahl()
5735         with self.assertRaises(ObjUnknown) as err:
5736             obj["whatever"] = "whenever"
5737         with self.assertRaises(InvalidValueType) as err:
5738             obj["zweite"] = Boolean(False)
5739         obj["zweite"] = Integer(value)
5740         repr(err.exception)
5741         with self.assertRaises(ObjUnknown) as err:
5742             obj["whatever"]
5743         repr(err.exception)
5744         self.assertIsNone(obj["erste"])
5745         self.assertEqual(obj["zweite"], Integer(value))
5746
5747     def test_tag_mismatch(self):
5748         class Wahl(Choice):
5749             schema = (
5750                 ("erste", Boolean()),
5751             )
5752         int_encoded = Integer(123).encode()
5753         bool_encoded = Boolean(False).encode()
5754         obj = Wahl()
5755         obj.decode(bool_encoded)
5756         with self.assertRaises(TagMismatch):
5757             obj.decode(int_encoded)
5758
5759     def test_tag_mismatch_underlying(self):
5760         class SeqOfBoolean(SequenceOf):
5761             schema = Boolean()
5762
5763         class SeqOfInteger(SequenceOf):
5764             schema = Integer()
5765
5766         class Wahl(Choice):
5767             schema = (
5768                 ("erste", SeqOfBoolean()),
5769             )
5770
5771         int_encoded = SeqOfInteger((Integer(123),)).encode()
5772         bool_encoded = SeqOfBoolean((Boolean(False),)).encode()
5773         obj = Wahl()
5774         obj.decode(bool_encoded)
5775         with self.assertRaises(TagMismatch) as err:
5776             obj.decode(int_encoded)
5777         self.assertEqual(err.exception.decode_path, ("erste", "0"))
5778
5779
5780 @composite
5781 def seq_values_strategy(draw, seq_klass, do_expl=False):
5782     value = None
5783     if draw(booleans()):
5784         value = seq_klass()
5785         value._value = draw(dictionaries(
5786             integers(),
5787             one_of(
5788                 booleans().map(Boolean),
5789                 integers().map(Integer),
5790             ),
5791         ))
5792     schema = None
5793     if draw(booleans()):
5794         schema = list(draw(dictionaries(
5795             integers(),
5796             one_of(
5797                 booleans().map(Boolean),
5798                 integers().map(Integer),
5799             ),
5800         )).items())
5801     impl = None
5802     expl = None
5803     if do_expl:
5804         expl = draw(one_of(none(), integers(min_value=1).map(tag_encode)))
5805     else:
5806         impl = draw(one_of(none(), integers(min_value=1).map(tag_encode)))
5807     default = None
5808     if draw(booleans()):
5809         default = seq_klass()
5810         default._value = draw(dictionaries(
5811             integers(),
5812             one_of(
5813                 booleans().map(Boolean),
5814                 integers().map(Integer),
5815             ),
5816         ))
5817     optional = draw(one_of(none(), booleans()))
5818     _decoded = (
5819         draw(integers(min_value=0)),
5820         draw(integers(min_value=0)),
5821         draw(integers(min_value=0)),
5822     )
5823     return (value, schema, impl, expl, default, optional, _decoded)
5824
5825
5826 @composite
5827 def sequence_strategy(draw, seq_klass):
5828     inputs = draw(lists(
5829         one_of(
5830             tuples(just(Boolean), booleans(), one_of(none(), booleans())),
5831             tuples(just(Integer), integers(), one_of(none(), integers())),
5832         ),
5833         max_size=6,
5834     ))
5835     tags = draw(sets(
5836         integers(min_value=1),
5837         min_size=len(inputs),
5838         max_size=len(inputs),
5839     ))
5840     inits = [
5841         ({"expl": tag_ctxc(tag)} if expled else {"impl": tag_encode(tag)})
5842         for tag, expled in zip(tags, draw(lists(
5843             booleans(),
5844             min_size=len(inputs),
5845             max_size=len(inputs),
5846         )))
5847     ]
5848     empties = []
5849     for i, optional in enumerate(draw(lists(
5850             sampled_from(("required", "optional", "empty")),
5851             min_size=len(inputs),
5852             max_size=len(inputs),
5853     ))):
5854         if optional in ("optional", "empty"):
5855             inits[i]["optional"] = True
5856         if optional == "empty":
5857             empties.append(i)
5858     empties = set(empties)
5859     names = list(draw(sets(
5860         text_printable,
5861         min_size=len(inputs),
5862         max_size=len(inputs),
5863     )))
5864     schema = []
5865     for i, (klass, value, default) in enumerate(inputs):
5866         schema.append((names[i], klass(default=default, **inits[i])))
5867     seq_name = draw(text_letters())
5868     Seq = type(seq_name, (seq_klass,), {"schema": tuple(schema)})
5869     seq = Seq()
5870     expects = []
5871     for i, (klass, value, default) in enumerate(inputs):
5872         name = names[i]
5873         _, spec = schema[i]
5874         expect = {
5875             "name": name,
5876             "optional": False,
5877             "presented": False,
5878             "default_value": None if spec.default is None else default,
5879             "value": None,
5880         }
5881         if i in empties:
5882             expect["optional"] = True
5883         else:
5884             expect["presented"] = True
5885             expect["value"] = value
5886             if spec.optional:
5887                 expect["optional"] = True
5888             if default is not None and default == value:
5889                 expect["presented"] = False
5890             seq[name] = klass(value)
5891         expects.append(expect)
5892     return seq, expects
5893
5894
5895 @composite
5896 def sequences_strategy(draw, seq_klass):
5897     tags = draw(sets(integers(min_value=1), min_size=0, max_size=5))
5898     inits = [
5899         ({"expl": tag_ctxc(tag)} if expled else {"impl": tag_encode(tag)})
5900         for tag, expled in zip(tags, draw(lists(
5901             booleans(),
5902             min_size=len(tags),
5903             max_size=len(tags),
5904         )))
5905     ]
5906     defaulted = set(
5907         i for i, is_default in enumerate(draw(lists(
5908             booleans(),
5909             min_size=len(tags),
5910             max_size=len(tags),
5911         ))) if is_default
5912     )
5913     names = list(draw(sets(
5914         text_printable,
5915         min_size=len(tags),
5916         max_size=len(tags),
5917     )))
5918     seq_expectses = draw(lists(
5919         sequence_strategy(seq_klass=seq_klass),
5920         min_size=len(tags),
5921         max_size=len(tags),
5922     ))
5923     seqs = [seq for seq, _ in seq_expectses]
5924     schema = []
5925     for i, (name, seq) in enumerate(zip(names, seqs)):
5926         schema.append((
5927             name,
5928             seq(default=(seq if i in defaulted else None), **inits[i]),
5929         ))
5930     seq_name = draw(text_letters())
5931     Seq = type(seq_name, (seq_klass,), {"schema": tuple(schema)})
5932     seq_outer = Seq()
5933     expect_outers = []
5934     for name, (seq_inner, expects_inner) in zip(names, seq_expectses):
5935         expect = {
5936             "name": name,
5937             "expects": expects_inner,
5938             "presented": False,
5939         }
5940         seq_outer[name] = seq_inner
5941         if seq_outer.specs[name].default is None:
5942             expect["presented"] = True
5943         expect_outers.append(expect)
5944     return seq_outer, expect_outers
5945
5946
5947 class SeqMixing(object):
5948     def test_invalid_value_type(self):
5949         with self.assertRaises(InvalidValueType) as err:
5950             self.base_klass(123)
5951         repr(err.exception)
5952
5953     def test_invalid_value_type_set(self):
5954         class Seq(self.base_klass):
5955             schema = (("whatever", Boolean()),)
5956         seq = Seq()
5957         with self.assertRaises(InvalidValueType) as err:
5958             seq["whatever"] = Integer(123)
5959         repr(err.exception)
5960
5961     @given(booleans())
5962     def test_optional(self, optional):
5963         obj = self.base_klass(default=self.base_klass(), optional=optional)
5964         self.assertTrue(obj.optional)
5965
5966     @given(data_strategy())
5967     def test_ready(self, d):
5968         ready = {
5969             str(i): v for i, v in enumerate(d.draw(lists(
5970                 booleans(),
5971                 min_size=1,
5972                 max_size=3,
5973             )))
5974         }
5975         non_ready = {
5976             str(i + len(ready)): v for i, v in enumerate(d.draw(lists(
5977                 booleans(),
5978                 min_size=1,
5979                 max_size=3,
5980             )))
5981         }
5982         schema_input = []
5983         for name in d.draw(permutations(
5984                 list(ready.keys()) + list(non_ready.keys()),
5985         )):
5986             schema_input.append((name, Boolean()))
5987
5988         class Seq(self.base_klass):
5989             schema = tuple(schema_input)
5990         seq = Seq()
5991         for name in ready.keys():
5992             seq[name]
5993             seq[name] = Boolean()
5994         self.assertFalse(seq.ready)
5995         repr(seq)
5996         list(seq.pps())
5997         pprint(seq, big_blobs=True, with_decode_path=True)
5998         for name, value in ready.items():
5999             seq[name] = Boolean(value)
6000         self.assertFalse(seq.ready)
6001         repr(seq)
6002         list(seq.pps())
6003         pprint(seq, big_blobs=True, with_decode_path=True)
6004         with self.assertRaises(ObjNotReady) as err:
6005             seq.encode()
6006         repr(err.exception)
6007         with self.assertRaises(ObjNotReady) as err:
6008             encode2pass(seq)
6009         for name, value in non_ready.items():
6010             seq[name] = Boolean(value)
6011         self.assertTrue(seq.ready)
6012         repr(seq)
6013         list(seq.pps())
6014         pprint(seq, big_blobs=True, with_decode_path=True)
6015
6016     @given(data_strategy())
6017     def test_call(self, d):
6018         class SeqInherited(self.base_klass):
6019             pass
6020         for klass in (self.base_klass, SeqInherited):
6021             (
6022                 value_initial,
6023                 schema_initial,
6024                 impl_initial,
6025                 expl_initial,
6026                 default_initial,
6027                 optional_initial,
6028                 _decoded_initial,
6029             ) = d.draw(seq_values_strategy(seq_klass=klass))
6030             obj_initial = klass(
6031                 value_initial,
6032                 schema_initial,
6033                 impl_initial,
6034                 expl_initial,
6035                 default_initial,
6036                 optional_initial or False,
6037                 _decoded_initial,
6038             )
6039             (
6040                 value,
6041                 _,
6042                 impl,
6043                 expl,
6044                 default,
6045                 optional,
6046                 _decoded,
6047             ) = d.draw(seq_values_strategy(
6048                 seq_klass=klass,
6049                 do_expl=impl_initial is None,
6050             ))
6051             obj = obj_initial(value, impl, expl, default, optional)
6052             value_expected = default if value is None else value
6053             value_expected = (
6054                 default_initial if value_expected is None
6055                 else value_expected
6056             )
6057             self.assertEqual(obj._value, getattr(value_expected, "_value", {}))
6058             self.assertEqual(obj.tag, impl or impl_initial or obj.tag_default)
6059             self.assertEqual(obj.expl_tag, expl or expl_initial)
6060             self.assertEqual(
6061                 {} if obj.default is None else obj.default._value,
6062                 getattr(default_initial if default is None else default, "_value", {}),
6063             )
6064             if obj.default is None:
6065                 optional = optional_initial if optional is None else optional
6066                 optional = False if optional is None else optional
6067             else:
6068                 optional = True
6069             self.assertEqual(list(obj.specs.items()), schema_initial or [])
6070             self.assertEqual(obj.optional, optional)
6071
6072     @given(data_strategy())
6073     def test_copy(self, d):
6074         class SeqInherited(self.base_klass):
6075             pass
6076         register_class(SeqInherited)
6077         for klass in (self.base_klass, SeqInherited):
6078             values = d.draw(seq_values_strategy(seq_klass=klass))
6079             obj = klass(*values)
6080             for copy_func in copy_funcs:
6081                 obj_copied = copy_func(obj)
6082                 self.assert_copied_basic_fields(obj, obj_copied)
6083                 self.assertEqual(obj.specs, obj_copied.specs)
6084                 self.assertEqual(obj._value, obj_copied._value)
6085
6086     @given(data_strategy())
6087     def test_stripped(self, d):
6088         value = d.draw(integers())
6089         tag_impl = tag_encode(d.draw(integers(min_value=1)))
6090
6091         class Seq(self.base_klass):
6092             impl = tag_impl
6093             schema = (("whatever", Integer()),)
6094         seq = Seq()
6095         seq["whatever"] = Integer(value)
6096         with self.assertRaises(NotEnoughData):
6097             seq.decode(seq.encode()[:-1])
6098
6099     @given(data_strategy())
6100     def test_stripped_expl(self, d):
6101         value = d.draw(integers())
6102         tag_expl = tag_ctxc(d.draw(integers(min_value=1)))
6103
6104         class Seq(self.base_klass):
6105             expl = tag_expl
6106             schema = (("whatever", Integer()),)
6107         seq = Seq()
6108         seq["whatever"] = Integer(value)
6109         with self.assertRaises(NotEnoughData):
6110             seq.decode(seq.encode()[:-1])
6111
6112     @given(integers(min_value=3), binary(min_size=2))
6113     def test_non_tag_mismatch_raised(self, junk_tag_num, junk):
6114         junk = tag_encode(junk_tag_num) + junk
6115         try:
6116             _, _, len_encoded = tag_strip(memoryview(junk))
6117             len_decode(len_encoded)
6118         except Exception:
6119             assume(True)
6120         else:
6121             assume(False)
6122
6123         class Seq(self.base_klass):
6124             schema = (
6125                 ("whatever", Integer()),
6126                 ("junk", Any()),
6127                 ("whenever", Integer()),
6128             )
6129         seq = Seq()
6130         seq["whatever"] = Integer(123)
6131         seq["junk"] = Any(junk)
6132         seq["whenever"] = Integer(123)
6133         with self.assertRaises(DecodeError):
6134             seq.decode(seq.encode())
6135
6136     @given(
6137         integers(min_value=31),
6138         integers(min_value=0),
6139         decode_path_strat,
6140     )
6141     def test_bad_tag(self, tag, offset, decode_path):
6142         with self.assertRaises(DecodeError) as err:
6143             self.base_klass().decode(
6144                 tag_encode(tag)[:-1],
6145                 offset=offset,
6146                 decode_path=decode_path,
6147             )
6148         repr(err.exception)
6149         self.assertEqual(err.exception.offset, offset)
6150         self.assertEqual(err.exception.decode_path, decode_path)
6151
6152     @given(
6153         integers(min_value=128),
6154         integers(min_value=0),
6155         decode_path_strat,
6156     )
6157     def test_bad_len(self, l, offset, decode_path):
6158         with self.assertRaises(DecodeError) as err:
6159             self.base_klass().decode(
6160                 self.base_klass.tag_default + len_encode(l)[:-1],
6161                 offset=offset,
6162                 decode_path=decode_path,
6163             )
6164         repr(err.exception)
6165         self.assertEqual(err.exception.offset, offset)
6166         self.assertEqual(err.exception.decode_path, decode_path)
6167
6168     def _assert_expects(self, seq, expects):
6169         for expect in expects:
6170             self.assertEqual(
6171                 seq.specs[expect["name"]].optional,
6172                 expect["optional"],
6173             )
6174             if expect["default_value"] is not None:
6175                 self.assertEqual(
6176                     seq.specs[expect["name"]].default,
6177                     expect["default_value"],
6178                 )
6179             if expect["presented"]:
6180                 self.assertIn(expect["name"], seq)
6181                 self.assertEqual(seq[expect["name"]], expect["value"])
6182
6183     @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
6184     @given(data_strategy())
6185     def test_symmetric(self, d):
6186         seq, expects = d.draw(sequence_strategy(seq_klass=self.base_klass))
6187         tail_junk = d.draw(binary(max_size=5))
6188         decode_path = d.draw(decode_path_strat)
6189         self.assertTrue(seq.ready)
6190         self.assertFalse(seq.decoded)
6191         self._assert_expects(seq, expects)
6192         repr(seq)
6193         list(seq.pps())
6194         pprint(seq, big_blobs=True, with_decode_path=True)
6195         self.assertTrue(seq.ready)
6196         seq_encoded = seq.encode()
6197         self.assertEqual(encode2pass(seq), seq_encoded)
6198         seq_encoded_cer = encode_cer(seq)
6199         self.assertNotEqual(seq_encoded_cer, seq_encoded)
6200         self.assertSequenceEqual(
6201             seq.decod(seq_encoded_cer, ctx={"bered": True}).encode(),
6202             seq_encoded,
6203         )
6204         seq_decoded, tail = seq.decode(seq_encoded + tail_junk)
6205         self.assertFalse(seq_decoded.lenindef)
6206         self.assertFalse(seq_decoded.ber_encoded)
6207         self.assertFalse(seq_decoded.bered)
6208
6209         t, _, lv = tag_strip(seq_encoded)
6210         _, _, v = len_decode(lv)
6211         seq_encoded_lenindef = t + LENINDEF + v + EOC
6212         with self.assertRaises(DecodeError):
6213             seq.decode(seq_encoded_lenindef)
6214         ctx_copied = deepcopy(ctx_dummy)
6215         ctx_copied["bered"] = True
6216         seq_decoded_lenindef, tail_lenindef = seq.decode(
6217             seq_encoded_lenindef + tail_junk,
6218             ctx=ctx_copied,
6219         )
6220         del ctx_copied["bered"]
6221         self.assertDictEqual(ctx_copied, ctx_dummy)
6222         self.assertTrue(seq_decoded_lenindef.lenindef)
6223         self.assertTrue(seq_decoded_lenindef.bered)
6224         seq_decoded_lenindef = copy(seq_decoded_lenindef)
6225         self.assertTrue(seq_decoded_lenindef.lenindef)
6226         self.assertTrue(seq_decoded_lenindef.bered)
6227         with self.assertRaises(DecodeError):
6228             seq.decode(seq_encoded_lenindef[:-1], ctx={"bered": True})
6229         with self.assertRaises(DecodeError):
6230             seq.decode(seq_encoded_lenindef[:-2], ctx={"bered": True})
6231         repr(seq_decoded_lenindef)
6232         list(seq_decoded_lenindef.pps())
6233         pprint(seq_decoded_lenindef, big_blobs=True, with_decode_path=True)
6234         self.assertTrue(seq_decoded_lenindef.ready)
6235
6236         for decoded, decoded_tail, encoded in (
6237                 (seq_decoded, tail, seq_encoded),
6238                 (seq_decoded_lenindef, tail_lenindef, seq_encoded_lenindef),
6239         ):
6240             self.assertEqual(decoded_tail, tail_junk)
6241             self._assert_expects(decoded, expects)
6242             self.assertEqual(seq, decoded)
6243             self.assertEqual(decoded.encode(), seq_encoded)
6244             self.assertEqual(decoded.tlvlen, len(encoded))
6245             for expect in expects:
6246                 if not expect["presented"]:
6247                     self.assertNotIn(expect["name"], decoded)
6248                     continue
6249                 self.assertIn(expect["name"], decoded)
6250                 obj = decoded[expect["name"]]
6251                 self.assertTrue(obj.decoded)
6252                 offset = obj.expl_offset if obj.expled else obj.offset
6253                 tlvlen = obj.expl_tlvlen if obj.expled else obj.tlvlen
6254                 self.assertSequenceEqual(
6255                     seq_encoded[offset:offset + tlvlen],
6256                     obj.encode(),
6257                 )
6258
6259             evgens = list(seq.decode_evgen(
6260                 encoded + decoded_tail,
6261                 decode_path=decode_path,
6262                 ctx={"bered": True},
6263             ))
6264             self.assertEqual(len(evgens), len(list(decoded._values_for_encoding())) + 1)
6265             for _decode_path, obj, _ in evgens[:-1]:
6266                 self.assertEqual(_decode_path[:-1], decode_path)
6267                 repr(obj)
6268                 list(obj.pps())
6269             _decode_path, obj, tail = evgens[-1]
6270             self.assertEqual(_decode_path, decode_path)
6271             repr(obj)
6272             list(obj.pps())
6273
6274         assert_exceeding_data(
6275             self,
6276             lambda: seq.decod(seq_encoded_lenindef + tail_junk, ctx={"bered": True}),
6277             tail_junk,
6278         )
6279
6280     @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
6281     @given(data_strategy())
6282     def test_symmetric_with_seq(self, d):
6283         seq, expect_outers = d.draw(sequences_strategy(seq_klass=self.base_klass))
6284         self.assertTrue(seq.ready)
6285         seq_encoded = seq.encode()
6286         self.assertEqual(encode2pass(seq), seq_encoded)
6287         seq_decoded, tail = seq.decode(seq_encoded)
6288         self.assertEqual(tail, b"")
6289         self.assertTrue(seq.ready)
6290         self.assertEqual(seq, seq_decoded)
6291         self.assertEqual(seq_decoded.encode(), seq_encoded)
6292         for expect_outer in expect_outers:
6293             if not expect_outer["presented"]:
6294                 self.assertNotIn(expect_outer["name"], seq_decoded)
6295                 continue
6296             self.assertIn(expect_outer["name"], seq_decoded)
6297             obj = seq_decoded[expect_outer["name"]]
6298             self.assertTrue(obj.decoded)
6299             offset = obj.expl_offset if obj.expled else obj.offset
6300             tlvlen = obj.expl_tlvlen if obj.expled else obj.tlvlen
6301             self.assertSequenceEqual(
6302                 seq_encoded[offset:offset + tlvlen],
6303                 obj.encode(),
6304             )
6305             self._assert_expects(obj, expect_outer["expects"])
6306
6307     @given(data_strategy())
6308     def test_default_disappears(self, d):
6309         _schema = list(d.draw(dictionaries(
6310             text_letters(),
6311             sets(integers(), min_size=2, max_size=2),
6312             min_size=1,
6313         )).items())
6314
6315         class Seq(self.base_klass):
6316             schema = [
6317                 (n, Integer(default=d))
6318                 for n, (_, d) in _schema
6319             ]
6320         seq = Seq()
6321         for name, (value, _) in _schema:
6322             seq[name] = Integer(value)
6323         self.assertEqual(len(seq._value), len(_schema))
6324         empty_seq = b"".join((self.base_klass.tag_default, len_encode(0)))
6325         self.assertGreater(len(seq.encode()), len(empty_seq))
6326         for name, (_, default) in _schema:
6327             seq[name] = Integer(default)
6328         self.assertEqual(len(seq._value), 0)
6329         self.assertSequenceEqual(seq.encode(), empty_seq)
6330
6331     @given(data_strategy())
6332     def test_encoded_default_not_accepted(self, d):
6333         _schema = list(d.draw(dictionaries(
6334             text_letters(),
6335             integers(),
6336             min_size=1,
6337         )).items())
6338         tags = [tag_encode(tag) for tag in d.draw(sets(
6339             integers(min_value=1),
6340             min_size=len(_schema),
6341             max_size=len(_schema),
6342         ))]
6343
6344         class Wahl(Choice):
6345             schema = (("int", Integer()),)
6346
6347         class SeqWithoutDefault(self.base_klass):
6348             schema = [
6349                 (n, Wahl(expl=t))
6350                 for (n, _), t in zip(_schema, tags)
6351             ]
6352         seq_without_default = SeqWithoutDefault()
6353         for name, value in _schema:
6354             seq_without_default[name] = Wahl(("int", Integer(value)))
6355         seq_encoded = seq_without_default.encode()
6356         seq_without_default.decode(seq_encoded)
6357         self.assertEqual(
6358             len(list(seq_without_default.decode_evgen(seq_encoded))),
6359             len(_schema) * 2 + 1,
6360         )
6361
6362         class SeqWithDefault(self.base_klass):
6363             schema = [
6364                 (n, Wahl(default=Wahl(("int", Integer(v))), expl=t))
6365                 for (n, v), t in zip(_schema, tags)
6366             ]
6367         seq_with_default = SeqWithDefault()
6368         with assertRaisesRegex(self, DecodeError, "DEFAULT value met"):
6369             seq_with_default.decode(seq_encoded)
6370         with assertRaisesRegex(self, DecodeError, "DEFAULT value met"):
6371             list(seq_with_default.decode_evgen(seq_encoded))
6372         for ctx in ({"bered": True}, {"allow_default_values": True}):
6373             seq_decoded, _ = seq_with_default.decode(seq_encoded, ctx=ctx)
6374             self.assertTrue(seq_decoded.ber_encoded)
6375             self.assertTrue(seq_decoded.bered)
6376             seq_decoded = copy(seq_decoded)
6377             self.assertTrue(seq_decoded.ber_encoded)
6378             self.assertTrue(seq_decoded.bered)
6379             for name, value in _schema:
6380                 self.assertEqual(seq_decoded[name], seq_with_default[name])
6381                 self.assertEqual(seq_decoded[name].value, value)
6382             self.assertEqual(
6383                 len(list(seq_with_default.decode_evgen(seq_encoded, ctx=ctx))),
6384                 len(_schema) + 1,
6385             )
6386
6387         seq_without_default = SeqWithoutDefault()
6388         for name, value in _schema:
6389             seq_without_default[name] = Wahl(("int", Integer(value + 1)))
6390         seq_encoded = seq_without_default.encode()
6391         seq_with_default.decode(seq_encoded)
6392         self.assertEqual(
6393             len(list(seq_with_default.decode_evgen(seq_encoded))),
6394             len(_schema) + 1,
6395         )
6396
6397     @given(data_strategy())
6398     def test_missing_from_spec(self, d):
6399         names = list(d.draw(sets(text_letters(), min_size=2)))
6400         tags = [tag_encode(tag) for tag in d.draw(sets(
6401             integers(min_value=1),
6402             min_size=len(names),
6403             max_size=len(names),
6404         ))]
6405         names_tags = [(name, tag) for tag, name in sorted(zip(tags, names))]
6406
6407         class SeqFull(self.base_klass):
6408             schema = [(n, Integer(impl=t)) for n, t in names_tags]
6409         seq_full = SeqFull()
6410         for i, name in enumerate(names):
6411             seq_full[name] = Integer(i)
6412         seq_encoded = seq_full.encode()
6413         altered = names_tags[:-2] + names_tags[-1:]
6414
6415         class SeqMissing(self.base_klass):
6416             schema = [(n, Integer(impl=t)) for n, t in altered]
6417         seq_missing = SeqMissing()
6418         with self.assertRaises(TagMismatch):
6419             seq_missing.decode(seq_encoded)
6420         with self.assertRaises(TagMismatch):
6421             list(seq_missing.decode_evgen(seq_encoded))
6422
6423     def test_bered(self):
6424         class Seq(self.base_klass):
6425             schema = (("underlying", Boolean()),)
6426         encoded = Boolean.tag_default + len_encode(1) + b"\x01"
6427         encoded = Seq.tag_default + len_encode(len(encoded)) + encoded
6428         decoded, _ = Seq().decode(encoded, ctx={"bered": True})
6429         self.assertFalse(decoded.ber_encoded)
6430         self.assertFalse(decoded.lenindef)
6431         self.assertTrue(decoded.bered)
6432         decoded = copy(decoded)
6433         self.assertFalse(decoded.ber_encoded)
6434         self.assertFalse(decoded.lenindef)
6435         self.assertTrue(decoded.bered)
6436
6437         class Seq(self.base_klass):
6438             schema = (("underlying", OctetString()),)
6439         encoded = (
6440             tag_encode(form=TagFormConstructed, num=4) +
6441             LENINDEF +
6442             OctetString(b"whatever").encode() +
6443             EOC
6444         )
6445         encoded = Seq.tag_default + len_encode(len(encoded)) + encoded
6446         with self.assertRaises(DecodeError):
6447             Seq().decode(encoded)
6448         with self.assertRaises(DecodeError):
6449             list(Seq().decode_evgen(encoded))
6450         list(Seq().decode_evgen(encoded, ctx={"bered": True}))
6451         decoded, _ = Seq().decode(encoded, ctx={"bered": True})
6452         self.assertFalse(decoded.ber_encoded)
6453         self.assertFalse(decoded.lenindef)
6454         self.assertTrue(decoded.bered)
6455         decoded = copy(decoded)
6456         self.assertFalse(decoded.ber_encoded)
6457         self.assertFalse(decoded.lenindef)
6458         self.assertTrue(decoded.bered)
6459
6460
6461 class TestSequence(SeqMixing, CommonMixin, TestCase):
6462     base_klass = Sequence
6463
6464     @given(
6465         integers(),
6466         binary(min_size=1),
6467     )
6468     def test_remaining(self, value, junk):
6469         class Seq(Sequence):
6470             schema = (
6471                 ("whatever", Integer()),
6472             )
6473         int_encoded = Integer(value).encode()
6474         junked = b"".join((
6475             Sequence.tag_default,
6476             len_encode(len(int_encoded + junk)),
6477             int_encoded + junk,
6478         ))
6479         with assertRaisesRegex(self, DecodeError, "remaining"):
6480             Seq().decode(junked)
6481
6482     @given(sets(text_letters(), min_size=2))
6483     def test_obj_unknown(self, names):
6484         missing = names.pop()
6485
6486         class Seq(Sequence):
6487             schema = [(n, Boolean()) for n in names]
6488         seq = Seq()
6489         with self.assertRaises(ObjUnknown) as err:
6490             seq[missing]
6491         repr(err.exception)
6492         with self.assertRaises(ObjUnknown) as err:
6493             seq[missing] = Boolean()
6494         repr(err.exception)
6495
6496     def test_x690_vector(self):
6497         class Seq(Sequence):
6498             schema = (
6499                 ("name", IA5String()),
6500                 ("ok", Boolean()),
6501             )
6502         seq = Seq().decode(hexdec("300A1605536d6974680101FF"))[0]
6503         self.assertEqual(seq["name"], "Smith")
6504         self.assertEqual(seq["ok"], True)
6505
6506
6507 class TestSet(SeqMixing, CommonMixin, TestCase):
6508     base_klass = Set
6509
6510     @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
6511     @given(data_strategy())
6512     def test_sorted(self, d):
6513         class DummySeq(Sequence):
6514             schema = (("null", Null()),)
6515
6516         tag_nums = d.draw(sets(integers(min_value=1), min_size=1, max_size=50))
6517         _, _, dummy_seq_tag_num = tag_decode(DummySeq.tag_default)
6518         assume(any(i > dummy_seq_tag_num for i in tag_nums))
6519         tag_nums -= set([dummy_seq_tag_num])
6520         _schema = [(str(i), OctetString(impl=tag_encode(i))) for i in tag_nums]
6521         _schema.append(("seq", DummySeq()))
6522
6523         class Seq(Set):
6524             schema = d.draw(permutations(_schema))
6525         seq = Seq()
6526         for name, _ in _schema:
6527             if name != "seq":
6528                 seq[name] = OctetString(name.encode("ascii"))
6529         seq["seq"] = DummySeq((("null", Null()),))
6530
6531         seq_encoded = seq.encode()
6532         seq_decoded, _ = seq.decode(seq_encoded)
6533         seq_encoded_expected = []
6534         for tag_num in sorted(tag_nums | set([dummy_seq_tag_num])):
6535             if tag_num == dummy_seq_tag_num:
6536                 seq_encoded_expected.append(seq["seq"].encode())
6537             else:
6538                 seq_encoded_expected.append(seq[str(tag_num)].encode())
6539         self.assertSequenceEqual(
6540             seq_encoded[seq_decoded.tlen + seq_decoded.llen:],
6541             b"".join(seq_encoded_expected),
6542         )
6543
6544         encoded = b"".join(seq[str(i)].encode() for i in tag_nums)
6545         encoded += seq["seq"].encode()
6546         seq_encoded = b"".join((
6547             Set.tag_default,
6548             len_encode(len(encoded)),
6549             encoded,
6550         ))
6551         with assertRaisesRegex(self, DecodeError, "unordered SET"):
6552             seq.decode(seq_encoded)
6553         for ctx in ({"bered": True}, {"allow_unordered_set": True}):
6554             seq_decoded, _ = Seq().decode(seq_encoded, ctx=ctx)
6555             self.assertTrue(seq_decoded.ber_encoded)
6556             self.assertTrue(seq_decoded.bered)
6557             seq_decoded = copy(seq_decoded)
6558             self.assertTrue(seq_decoded.ber_encoded)
6559             self.assertTrue(seq_decoded.bered)
6560
6561     def test_same_value_twice(self):
6562         class Seq(Set):
6563             schema = (
6564                 ("bool", Boolean()),
6565                 ("int", Integer()),
6566             )
6567
6568         encoded = b"".join((
6569             Integer(123).encode(),
6570             Integer(234).encode(),
6571             Boolean(True).encode(),
6572         ))
6573         encoded = Seq.tag_default + len_encode(len(encoded)) + encoded
6574         with self.assertRaises(TagMismatch):
6575             Seq().decod(encoded, ctx={"allow_unordered_set": True})
6576
6577
6578 @composite
6579 def seqof_values_strategy(draw, schema=None, do_expl=False):
6580     if schema is None:
6581         schema = draw(sampled_from((Boolean(), Integer())))
6582     bound_min, bound_max = sorted(draw(sets(
6583         integers(min_value=0, max_value=10),
6584         min_size=2,
6585         max_size=2,
6586     )))
6587     if isinstance(schema, Boolean):
6588         values_generator = booleans().map(Boolean)
6589     elif isinstance(schema, Integer):
6590         values_generator = integers().map(Integer)
6591     values_generator = lists(
6592         values_generator,
6593         min_size=bound_min,
6594         max_size=bound_max,
6595     )
6596     values = draw(one_of(none(), values_generator))
6597     impl = None
6598     expl = None
6599     if do_expl:
6600         expl = draw(one_of(none(), integers(min_value=1).map(tag_encode)))
6601     else:
6602         impl = draw(one_of(none(), integers(min_value=1).map(tag_encode)))
6603     default = draw(one_of(none(), values_generator))
6604     optional = draw(one_of(none(), booleans()))
6605     _decoded = (
6606         draw(integers(min_value=0)),
6607         draw(integers(min_value=0)),
6608         draw(integers(min_value=0)),
6609     )
6610     return (
6611         schema,
6612         values,
6613         (bound_min, bound_max),
6614         impl,
6615         expl,
6616         default,
6617         optional,
6618         _decoded,
6619     )
6620
6621
6622 class SeqOfMixing(object):
6623     def test_invalid_value_type(self):
6624         with self.assertRaises(InvalidValueType) as err:
6625             self.base_klass(123)
6626         repr(err.exception)
6627
6628     def test_invalid_values_type(self):
6629         class SeqOf(self.base_klass):
6630             schema = Integer()
6631         with self.assertRaises(InvalidValueType) as err:
6632             SeqOf([Integer(123), Boolean(False), Integer(234)])
6633         repr(err.exception)
6634
6635     def test_schema_required(self):
6636         with assertRaisesRegex(self, ValueError, "schema must be specified"):
6637             self.base_klass.__mro__[1]()
6638
6639     @given(booleans(), booleans(), binary(min_size=1), binary(min_size=1))
6640     def test_comparison(self, value1, value2, tag1, tag2):
6641         class SeqOf(self.base_klass):
6642             schema = Boolean()
6643         obj1 = SeqOf([Boolean(value1)])
6644         obj2 = SeqOf([Boolean(value2)])
6645         self.assertEqual(obj1 == obj2, value1 == value2)
6646         self.assertEqual(obj1 != obj2, value1 != value2)
6647         self.assertEqual(obj1 == list(obj2), value1 == value2)
6648         self.assertEqual(obj1 == tuple(obj2), value1 == value2)
6649         obj1 = SeqOf([Boolean(value1)], impl=tag1)
6650         obj2 = SeqOf([Boolean(value1)], impl=tag2)
6651         self.assertEqual(obj1 == obj2, tag1 == tag2)
6652         self.assertEqual(obj1 != obj2, tag1 != tag2)
6653
6654     @given(lists(booleans()))
6655     def test_iter(self, values):
6656         class SeqOf(self.base_klass):
6657             schema = Boolean()
6658         obj = SeqOf([Boolean(value) for value in values])
6659         self.assertEqual(len(obj), len(values))
6660         for i, value in enumerate(obj):
6661             self.assertEqual(value, values[i])
6662
6663     @given(data_strategy())
6664     def test_ready(self, d):
6665         ready = [Integer(v) for v in d.draw(lists(
6666             integers(),
6667             min_size=1,
6668             max_size=3,
6669         ))]
6670         non_ready = [
6671             Integer() for _ in
6672             range(d.draw(integers(min_value=1, max_value=5)))
6673         ]
6674
6675         class SeqOf(self.base_klass):
6676             schema = Integer()
6677         values = d.draw(permutations(ready + non_ready))
6678         seqof = SeqOf()
6679         for value in values:
6680             seqof.append(value)
6681         self.assertFalse(seqof.ready)
6682         repr(seqof)
6683         list(seqof.pps())
6684         pprint(seqof, big_blobs=True, with_decode_path=True)
6685         with self.assertRaises(ObjNotReady) as err:
6686             seqof.encode()
6687         repr(err.exception)
6688         with self.assertRaises(ObjNotReady) as err:
6689             encode2pass(seqof)
6690         for i, value in enumerate(values):
6691             self.assertEqual(seqof[i], value)
6692             if not seqof[i].ready:
6693                 seqof[i] = Integer(i)
6694         self.assertTrue(seqof.ready)
6695         repr(seqof)
6696         list(seqof.pps())
6697         pprint(seqof, big_blobs=True, with_decode_path=True)
6698
6699     def test_spec_mismatch(self):
6700         class SeqOf(self.base_klass):
6701             schema = Integer()
6702         seqof = SeqOf()
6703         seqof.append(Integer(123))
6704         with self.assertRaises(ValueError):
6705             seqof.append(Boolean(False))
6706         with self.assertRaises(ValueError):
6707             seqof[0] = Boolean(False)
6708
6709     @given(data_strategy())
6710     def test_bounds_satisfied(self, d):
6711         class SeqOf(self.base_klass):
6712             schema = Boolean()
6713         bound_min = d.draw(integers(min_value=0, max_value=1 << 7))
6714         bound_max = d.draw(integers(min_value=bound_min, max_value=1 << 7))
6715         value = [Boolean()] * d.draw(integers(min_value=bound_min, max_value=bound_max))
6716         SeqOf(value=value, bounds=(bound_min, bound_max))
6717
6718     @given(data_strategy())
6719     def test_bounds_unsatisfied(self, d):
6720         class SeqOf(self.base_klass):
6721             schema = Boolean()
6722         bound_min = d.draw(integers(min_value=1, max_value=1 << 7))
6723         bound_max = d.draw(integers(min_value=bound_min, max_value=1 << 7))
6724         value = [Boolean(False)] * d.draw(integers(max_value=bound_min - 1))
6725         with self.assertRaises(BoundsError) as err:
6726             SeqOf(value=value, bounds=(bound_min, bound_max))
6727         repr(err.exception)
6728         with assertRaisesRegex(self, DecodeError, "bounds") as err:
6729             SeqOf(bounds=(bound_min, bound_max)).decode(
6730                 SeqOf(value).encode()
6731             )
6732         repr(err.exception)
6733         with assertRaisesRegex(self, DecodeError, "bounds") as err:
6734             SeqOf(bounds=(bound_min, bound_max)).decode(
6735                 encode2pass(SeqOf(value))
6736             )
6737         value = [Boolean(True)] * d.draw(integers(
6738             min_value=bound_max + 1,
6739             max_value=bound_max + 10,
6740         ))
6741         with self.assertRaises(BoundsError) as err:
6742             SeqOf(value=value, bounds=(bound_min, bound_max))
6743         repr(err.exception)
6744         with assertRaisesRegex(self, DecodeError, "bounds") as err:
6745             SeqOf(bounds=(bound_min, bound_max)).decode(
6746                 SeqOf(value).encode()
6747             )
6748         repr(err.exception)
6749         with assertRaisesRegex(self, DecodeError, "bounds") as err:
6750             SeqOf(bounds=(bound_min, bound_max)).decode(
6751                 encode2pass(SeqOf(value))
6752             )
6753
6754     @given(integers(min_value=1, max_value=10))
6755     def test_out_of_bounds(self, bound_max):
6756         class SeqOf(self.base_klass):
6757             schema = Integer()
6758             bounds = (0, bound_max)
6759         seqof = SeqOf()
6760         for _ in range(bound_max):
6761             seqof.append(Integer(123))
6762         with self.assertRaises(BoundsError):
6763             seqof.append(Integer(123))
6764
6765     @given(data_strategy())
6766     def test_call(self, d):
6767         (
6768             schema_initial,
6769             value_initial,
6770             bounds_initial,
6771             impl_initial,
6772             expl_initial,
6773             default_initial,
6774             optional_initial,
6775             _decoded_initial,
6776         ) = d.draw(seqof_values_strategy())
6777
6778         class SeqOf(self.base_klass):
6779             schema = schema_initial
6780         obj_initial = SeqOf(
6781             value=value_initial,
6782             bounds=bounds_initial,
6783             impl=impl_initial,
6784             expl=expl_initial,
6785             default=default_initial,
6786             optional=optional_initial or False,
6787             _decoded=_decoded_initial,
6788         )
6789         (
6790             _,
6791             value,
6792             bounds,
6793             impl,
6794             expl,
6795             default,
6796             optional,
6797             _decoded,
6798         ) = d.draw(seqof_values_strategy(
6799             schema=schema_initial,
6800             do_expl=impl_initial is None,
6801         ))
6802         if (default is None) and (obj_initial.default is not None):
6803             bounds = None
6804         if (
6805                 (bounds is None) and
6806                 (value is not None) and
6807                 (bounds_initial is not None) and
6808                 not (bounds_initial[0] <= len(value) <= bounds_initial[1])
6809         ):
6810             value = None
6811         if (
6812                 (bounds is None) and
6813                 (default is not None) and
6814                 (bounds_initial is not None) and
6815                 not (bounds_initial[0] <= len(default) <= bounds_initial[1])
6816         ):
6817             default = None
6818         obj = obj_initial(
6819             value=value,
6820             bounds=bounds,
6821             impl=impl,
6822             expl=expl,
6823             default=default,
6824             optional=optional,
6825         )
6826         if obj.ready:
6827             value_expected = default if value is None else value
6828             value_expected = (
6829                 default_initial if value_expected is None
6830                 else value_expected
6831             )
6832             value_expected = () if value_expected is None else value_expected
6833             self.assertEqual(obj, value_expected)
6834         self.assertEqual(obj.tag, impl or impl_initial or obj.tag_default)
6835         self.assertEqual(obj.expl_tag, expl or expl_initial)
6836         self.assertEqual(
6837             obj.default,
6838             default_initial if default is None else default,
6839         )
6840         if obj.default is None:
6841             optional = optional_initial if optional is None else optional
6842             optional = False if optional is None else optional
6843         else:
6844             optional = True
6845         self.assertEqual(obj.optional, optional)
6846         self.assertEqual(
6847             (obj._bound_min, obj._bound_max),
6848             bounds or bounds_initial or (0, float("+inf")),
6849         )
6850
6851     @given(seqof_values_strategy())
6852     def test_copy(self, values):
6853         _schema, value, bounds, impl, expl, default, optional, _decoded = values
6854
6855         class SeqOf(self.base_klass):
6856             schema = _schema
6857         register_class(SeqOf)
6858         obj = SeqOf(
6859             value=value,
6860             bounds=bounds,
6861             impl=impl,
6862             expl=expl,
6863             default=default,
6864             optional=optional or False,
6865             _decoded=_decoded,
6866         )
6867         for copy_func in copy_funcs:
6868             obj_copied = copy_func(obj)
6869             self.assert_copied_basic_fields(obj, obj_copied)
6870             self.assertEqual(obj._bound_min, obj_copied._bound_min)
6871             self.assertEqual(obj._bound_max, obj_copied._bound_max)
6872             self.assertEqual(obj._value, obj_copied._value)
6873
6874     @given(
6875         lists(binary()),
6876         integers(min_value=1).map(tag_encode),
6877     )
6878     def test_stripped(self, values, tag_impl):
6879         class SeqOf(self.base_klass):
6880             schema = OctetString()
6881         obj = SeqOf([OctetString(v) for v in values], impl=tag_impl)
6882         with self.assertRaises(NotEnoughData):
6883             obj.decode(obj.encode()[:-1])
6884
6885     @given(
6886         lists(binary()),
6887         integers(min_value=1).map(tag_ctxc),
6888     )
6889     def test_stripped_expl(self, values, tag_expl):
6890         class SeqOf(self.base_klass):
6891             schema = OctetString()
6892         obj = SeqOf([OctetString(v) for v in values], expl=tag_expl)
6893         with self.assertRaises(NotEnoughData):
6894             obj.decode(obj.encode()[:-1])
6895
6896     @given(
6897         integers(min_value=31),
6898         integers(min_value=0),
6899         decode_path_strat,
6900     )
6901     def test_bad_tag(self, tag, offset, decode_path):
6902         with self.assertRaises(DecodeError) as err:
6903             self.base_klass().decode(
6904                 tag_encode(tag)[:-1],
6905                 offset=offset,
6906                 decode_path=decode_path,
6907             )
6908         repr(err.exception)
6909         self.assertEqual(err.exception.offset, offset)
6910         self.assertEqual(err.exception.decode_path, decode_path)
6911
6912     @given(
6913         integers(min_value=128),
6914         integers(min_value=0),
6915         decode_path_strat,
6916     )
6917     def test_bad_len(self, l, offset, decode_path):
6918         with self.assertRaises(DecodeError) as err:
6919             self.base_klass().decode(
6920                 self.base_klass.tag_default + len_encode(l)[:-1],
6921                 offset=offset,
6922                 decode_path=decode_path,
6923             )
6924         repr(err.exception)
6925         self.assertEqual(err.exception.offset, offset)
6926         self.assertEqual(err.exception.decode_path, decode_path)
6927
6928     @given(binary(min_size=1))
6929     def test_tag_mismatch(self, impl):
6930         assume(impl != self.base_klass.tag_default)
6931         with self.assertRaises(TagMismatch):
6932             self.base_klass(impl=impl).decode(self.base_klass().encode())
6933
6934     @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
6935     @given(
6936         seqof_values_strategy(schema=Integer()),
6937         lists(integers().map(Integer)),
6938         integers(min_value=1).map(tag_ctxc),
6939         integers(min_value=0),
6940         binary(max_size=5),
6941         decode_path_strat,
6942     )
6943     def test_symmetric(self, values, value, tag_expl, offset, tail_junk, decode_path):
6944         _, _, _, _, _, default, optional, _decoded = values
6945
6946         class SeqOf(self.base_klass):
6947             schema = Integer()
6948         obj = SeqOf(
6949             value=value,
6950             default=default,
6951             optional=optional,
6952             _decoded=_decoded,
6953         )
6954         repr(obj)
6955         list(obj.pps())
6956         pprint(obj, big_blobs=True, with_decode_path=True)
6957         self.assertFalse(obj.expled)
6958         obj_encoded = obj.encode()
6959         self.assertEqual(encode2pass(obj), obj_encoded)
6960         obj_encoded_cer = encode_cer(obj)
6961         self.assertNotEqual(obj_encoded_cer, obj_encoded)
6962         self.assertSequenceEqual(
6963             obj.decod(obj_encoded_cer, ctx={"bered": True}).encode(),
6964             obj_encoded,
6965         )
6966         obj_expled = obj(value, expl=tag_expl)
6967         self.assertTrue(obj_expled.expled)
6968         repr(obj_expled)
6969         list(obj_expled.pps())
6970         pprint(obj_expled, big_blobs=True, with_decode_path=True)
6971         obj_expled_encoded = obj_expled.encode()
6972         ctx_copied = deepcopy(ctx_dummy)
6973         obj_decoded, tail = obj_expled.decode(
6974             obj_expled_encoded + tail_junk,
6975             offset=offset,
6976             ctx=ctx_copied,
6977         )
6978         self.assertDictEqual(ctx_copied, ctx_dummy)
6979         repr(obj_decoded)
6980         list(obj_decoded.pps())
6981         pprint(obj_decoded, big_blobs=True, with_decode_path=True)
6982         self.assertEqual(tail, tail_junk)
6983         self._test_symmetric_compare_objs(obj_decoded, obj_expled)
6984         self.assertSequenceEqual(obj_decoded.encode(), obj_expled_encoded)
6985         self.assertSequenceEqual(obj_decoded.expl_tag, tag_expl)
6986         self.assertEqual(obj_decoded.expl_tlen, len(tag_expl))
6987         self.assertEqual(
6988             obj_decoded.expl_llen,
6989             len(len_encode(len(obj_encoded))),
6990         )
6991         self.assertEqual(obj_decoded.tlvlen, len(obj_encoded))
6992         self.assertEqual(obj_decoded.expl_vlen, len(obj_encoded))
6993         self.assertEqual(
6994             obj_decoded.offset,
6995             offset + obj_decoded.expl_tlen + obj_decoded.expl_llen,
6996         )
6997         self.assertEqual(obj_decoded.expl_offset, offset)
6998         for obj_inner in obj_decoded:
6999             self.assertIn(obj_inner, obj_decoded)
7000             self.assertSequenceEqual(
7001                 obj_inner.encode(),
7002                 obj_expled_encoded[
7003                     obj_inner.offset - offset:
7004                     obj_inner.offset + obj_inner.tlvlen - offset
7005                 ],
7006             )
7007
7008         t, _, lv = tag_strip(obj_encoded)
7009         _, _, v = len_decode(lv)
7010         obj_encoded_lenindef = t + LENINDEF + v + EOC
7011         with self.assertRaises(DecodeError):
7012             obj.decode(obj_encoded_lenindef)
7013         obj_decoded_lenindef, tail_lenindef = obj.decode(
7014             obj_encoded_lenindef + tail_junk,
7015             ctx={"bered": True},
7016         )
7017         self.assertTrue(obj_decoded_lenindef.lenindef)
7018         self.assertTrue(obj_decoded_lenindef.bered)
7019         obj_decoded_lenindef = copy(obj_decoded_lenindef)
7020         self.assertTrue(obj_decoded_lenindef.lenindef)
7021         self.assertTrue(obj_decoded_lenindef.bered)
7022         repr(obj_decoded_lenindef)
7023         list(obj_decoded_lenindef.pps())
7024         pprint(obj_decoded_lenindef, big_blobs=True, with_decode_path=True)
7025         self.assertEqual(tail_lenindef, tail_junk)
7026         self.assertEqual(obj_decoded_lenindef.tlvlen, len(obj_encoded_lenindef))
7027         with self.assertRaises(DecodeError):
7028             obj.decode(obj_encoded_lenindef[:-1], ctx={"bered": True})
7029         with self.assertRaises(DecodeError):
7030             obj.decode(obj_encoded_lenindef[:-2], ctx={"bered": True})
7031
7032         evgens = list(obj.decode_evgen(
7033             obj_encoded_lenindef + tail_junk,
7034             decode_path=decode_path,
7035             ctx={"bered": True},
7036         ))
7037         self.assertEqual(len(evgens), len(obj_decoded_lenindef) + 1)
7038         for i, (_decode_path, obj, _) in enumerate(evgens[:-1]):
7039             self.assertEqual(_decode_path, decode_path + (str(i),))
7040             repr(obj)
7041             list(obj.pps())
7042         _decode_path, obj, tail = evgens[-1]
7043         self.assertEqual(_decode_path, decode_path)
7044         repr(obj)
7045         list(obj.pps())
7046
7047         assert_exceeding_data(
7048             self,
7049             lambda: obj_expled.decod(obj_expled_encoded + tail_junk),
7050             tail_junk,
7051         )
7052
7053     def test_bered(self):
7054         class SeqOf(self.base_klass):
7055             schema = Boolean()
7056         encoded = Boolean(False).encode()
7057         encoded += Boolean.tag_default + len_encode(1) + b"\x01"
7058         encoded = SeqOf.tag_default + len_encode(len(encoded)) + encoded
7059         with self.assertRaises(DecodeError):
7060             SeqOf().decode(encoded)
7061         decoded, _ = SeqOf().decode(encoded, ctx={"bered": True})
7062         self.assertFalse(decoded.ber_encoded)
7063         self.assertFalse(decoded.lenindef)
7064         self.assertTrue(decoded.bered)
7065         decoded = copy(decoded)
7066         self.assertFalse(decoded.ber_encoded)
7067         self.assertFalse(decoded.lenindef)
7068         self.assertTrue(decoded.bered)
7069
7070         class SeqOf(self.base_klass):
7071             schema = OctetString()
7072         encoded = OctetString(b"whatever").encode()
7073         encoded += (
7074             tag_encode(form=TagFormConstructed, num=4) +
7075             LENINDEF +
7076             OctetString(b"whatever").encode() +
7077             EOC
7078         )
7079         encoded = SeqOf.tag_default + len_encode(len(encoded)) + encoded
7080         with self.assertRaises(DecodeError):
7081             SeqOf().decode(encoded)
7082         decoded, _ = SeqOf().decode(encoded, ctx={"bered": True})
7083         self.assertFalse(decoded.ber_encoded)
7084         self.assertFalse(decoded.lenindef)
7085         self.assertTrue(decoded.bered)
7086         decoded = copy(decoded)
7087         self.assertFalse(decoded.ber_encoded)
7088         self.assertFalse(decoded.lenindef)
7089         self.assertTrue(decoded.bered)
7090
7091
7092 class TestSequenceOf(SeqOfMixing, CommonMixin, TestCase):
7093     class SeqOf(SequenceOf):
7094         schema = "whatever"
7095     base_klass = SeqOf
7096
7097     def _test_symmetric_compare_objs(self, obj1, obj2):
7098         self.assertEqual(obj1, obj2)
7099         self.assertSequenceEqual(list(obj1), list(obj2))
7100
7101     def test_iterator_pickling(self):
7102         class SeqOf(SequenceOf):
7103             schema = Integer()
7104         register_class(SeqOf)
7105         seqof = SeqOf()
7106         pickle_dumps(seqof)
7107         seqof = seqof(iter(six_xrange(10)))
7108         with assertRaisesRegex(self, ValueError, "iterator"):
7109             pickle_dumps(seqof)
7110
7111     def test_iterator_bounds(self):
7112         class SeqOf(SequenceOf):
7113             schema = Integer()
7114             bounds = (10, 20)
7115         seqof = None
7116
7117         def gen(n):
7118             for i in six_xrange(n):
7119                 yield Integer(i)
7120         for n in (9, 21):
7121             seqof = SeqOf(gen(n))
7122             self.assertTrue(seqof.ready)
7123             with self.assertRaises(BoundsError):
7124                 seqof.encode()
7125             self.assertFalse(seqof.ready)
7126             seqof = seqof(gen(n))
7127             self.assertTrue(seqof.ready)
7128             with self.assertRaises(BoundsError):
7129                 encode_cer(seqof)
7130             self.assertFalse(seqof.ready)
7131
7132     def test_iterator_twice(self):
7133         class SeqOf(SequenceOf):
7134             schema = Integer()
7135             bounds = (1, float("+inf"))
7136
7137         def gen():
7138             for i in six_xrange(10):
7139                 yield Integer(i)
7140         seqof = SeqOf(gen())
7141         self.assertTrue(seqof.ready)
7142         seqof.encode()
7143         self.assertFalse(seqof.ready)
7144         register_class(SeqOf)
7145         pickle_dumps(seqof)
7146
7147     def test_iterator_2pass(self):
7148         class SeqOf(SequenceOf):
7149             schema = Integer()
7150             bounds = (1, float("+inf"))
7151
7152         def gen():
7153             for i in six_xrange(10):
7154                 yield Integer(i)
7155         seqof = SeqOf(gen())
7156         self.assertTrue(seqof.ready)
7157         _, state = seqof.encode1st()
7158         self.assertFalse(seqof.ready)
7159         seqof = seqof(gen())
7160         self.assertTrue(seqof.ready)
7161         buf = BytesIO()
7162         seqof.encode2nd(buf.write, iter(state))
7163         self.assertSequenceEqual(
7164             [int(i) for i in seqof.decod(buf.getvalue())],
7165             list(gen()),
7166         )
7167
7168     def test_non_ready_bound_min(self):
7169         class SeqOf(SequenceOf):
7170             schema = Integer()
7171             bounds = (1, float("+inf"))
7172         seqof = SeqOf()
7173         self.assertFalse(seqof.ready)
7174
7175
7176 class TestSetOf(SeqOfMixing, CommonMixin, TestCase):
7177     class SeqOf(SetOf):
7178         schema = "whatever"
7179     base_klass = SeqOf
7180
7181     def _test_symmetric_compare_objs(self, obj1, obj2):
7182         self.assertSetEqual(
7183             set(int(v) for v in obj1),
7184             set(int(v) for v in obj2),
7185         )
7186
7187     @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
7188     @given(data_strategy())
7189     def test_sorted(self, d):
7190         values = [OctetString(v) for v in d.draw(lists(binary()))]
7191
7192         class Seq(SetOf):
7193             schema = OctetString()
7194         seq = Seq(values)
7195         seq_encoded = seq.encode()
7196         seq_decoded, _ = seq.decode(seq_encoded)
7197         self.assertSequenceEqual(
7198             seq_encoded[seq_decoded.tlen + seq_decoded.llen:],
7199             b"".join(sorted([v.encode() for v in values])),
7200         )
7201
7202     @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
7203     @given(data_strategy())
7204     def test_unsorted(self, d):
7205         values = [OctetString(v).encode() for v in d.draw(sets(
7206             binary(min_size=1, max_size=5),
7207             min_size=2,
7208             max_size=5,
7209         ))]
7210         values = d.draw(permutations(values))
7211         assume(values != sorted(values))
7212         encoded = b"".join(values)
7213         seq_encoded = b"".join((
7214             SetOf.tag_default,
7215             len_encode(len(encoded)),
7216             encoded,
7217         ))
7218
7219         class Seq(SetOf):
7220             schema = OctetString()
7221         seq = Seq()
7222         with assertRaisesRegex(self, DecodeError, "unordered SET OF"):
7223             seq.decode(seq_encoded)
7224
7225         for ctx in ({"bered": True}, {"allow_unordered_set": True}):
7226             seq_decoded, _ = Seq().decode(seq_encoded, ctx=ctx)
7227             self.assertTrue(seq_decoded.ber_encoded)
7228             self.assertTrue(seq_decoded.bered)
7229             seq_decoded = copy(seq_decoded)
7230             self.assertTrue(seq_decoded.ber_encoded)
7231             self.assertTrue(seq_decoded.bered)
7232             self.assertSequenceEqual(
7233                 [obj.encode() for obj in seq_decoded],
7234                 values,
7235             )
7236
7237
7238 class TestGoMarshalVectors(TestCase):
7239     def runTest(self):
7240         self.assertSequenceEqual(Integer(10).encode(), hexdec("02010a"))
7241         self.assertSequenceEqual(Integer(127).encode(), hexdec("02017f"))
7242         self.assertSequenceEqual(Integer(128).encode(), hexdec("02020080"))
7243         self.assertSequenceEqual(Integer(-128).encode(), hexdec("020180"))
7244         self.assertSequenceEqual(Integer(-129).encode(), hexdec("0202ff7f"))
7245
7246         class Seq(Sequence):
7247             schema = (
7248                 ("erste", Integer()),
7249                 ("zweite", Integer(optional=True))
7250             )
7251         seq = Seq()
7252         seq["erste"] = Integer(64)
7253         self.assertSequenceEqual(seq.encode(), hexdec("3003020140"))
7254         seq["erste"] = Integer(0x123456)
7255         self.assertSequenceEqual(seq.encode(), hexdec("30050203123456"))
7256         seq["erste"] = Integer(64)
7257         seq["zweite"] = Integer(65)
7258         self.assertSequenceEqual(seq.encode(), hexdec("3006020140020141"))
7259
7260         class NestedSeq(Sequence):
7261             schema = (
7262                 ("nest", Seq()),
7263             )
7264         seq["erste"] = Integer(127)
7265         seq["zweite"] = None
7266         nested = NestedSeq()
7267         nested["nest"] = seq
7268         self.assertSequenceEqual(nested.encode(), hexdec("3005300302017f"))
7269
7270         self.assertSequenceEqual(
7271             OctetString(b"\x01\x02\x03").encode(),
7272             hexdec("0403010203"),
7273         )
7274
7275         class Seq(Sequence):
7276             schema = (
7277                 ("erste", Integer(impl=tag_encode(5, klass=TagClassContext))),
7278             )
7279         seq = Seq()
7280         seq["erste"] = Integer(64)
7281         self.assertSequenceEqual(seq.encode(), hexdec("3003850140"))
7282
7283         class Seq(Sequence):
7284             schema = (
7285                 ("erste", Integer(expl=tag_ctxc(5))),
7286             )
7287         seq = Seq()
7288         seq["erste"] = Integer(64)
7289         self.assertSequenceEqual(seq.encode(), hexdec("3005a503020140"))
7290
7291         class Seq(Sequence):
7292             schema = (
7293                 ("erste", Null(
7294                     impl=tag_encode(0, klass=TagClassContext),
7295                     optional=True,
7296                 )),
7297             )
7298         seq = Seq()
7299         seq["erste"] = Null()
7300         self.assertSequenceEqual(seq.encode(), hexdec("30028000"))
7301         seq["erste"] = None
7302         self.assertSequenceEqual(seq.encode(), hexdec("3000"))
7303
7304         self.assertSequenceEqual(
7305             UTCTime(datetime(1970, 1, 1, 0, 0)).encode(),
7306             hexdec("170d3730303130313030303030305a"),
7307         )
7308         self.assertSequenceEqual(
7309             UTCTime(datetime(2009, 11, 15, 22, 56, 16)).encode(),
7310             hexdec("170d3039313131353232353631365a"),
7311         )
7312         self.assertSequenceEqual(
7313             GeneralizedTime(datetime(2100, 4, 5, 12, 1, 1)).encode(),
7314             hexdec("180f32313030303430353132303130315a"),
7315         )
7316
7317         class Seq(Sequence):
7318             schema = (
7319                 ("erste", GeneralizedTime()),
7320             )
7321         seq = Seq()
7322         seq["erste"] = GeneralizedTime(datetime(2009, 11, 15, 22, 56, 16))
7323         self.assertSequenceEqual(
7324             seq.encode(),
7325             hexdec("3011180f32303039313131353232353631365a"),
7326         )
7327
7328         self.assertSequenceEqual(
7329             BitString((1, b"\x80")).encode(),
7330             hexdec("03020780"),
7331         )
7332         self.assertSequenceEqual(
7333             BitString((12, b"\x81\xF0")).encode(),
7334             hexdec("03030481f0"),
7335         )
7336
7337         self.assertSequenceEqual(
7338             ObjectIdentifier("1.2.3.4").encode(),
7339             hexdec("06032a0304"),
7340         )
7341         self.assertSequenceEqual(
7342             ObjectIdentifier("1.2.840.133549.1.1.5").encode(),
7343             hexdec("06092a864888932d010105"),
7344         )
7345         self.assertSequenceEqual(
7346             ObjectIdentifier("2.100.3").encode(),
7347             hexdec("0603813403"),
7348         )
7349
7350         self.assertSequenceEqual(
7351             PrintableString("test").encode(),
7352             hexdec("130474657374"),
7353         )
7354         self.assertSequenceEqual(
7355             PrintableString("x" * 127).encode(),
7356             hexdec("137F" + "78" * 127),
7357         )
7358         self.assertSequenceEqual(
7359             PrintableString("x" * 128).encode(),
7360             hexdec("138180" + "78" * 128),
7361         )
7362         self.assertSequenceEqual(UTF8String("Σ").encode(), hexdec("0c02cea3"))
7363
7364         class Seq(Sequence):
7365             schema = (
7366                 ("erste", IA5String()),
7367             )
7368         seq = Seq()
7369         seq["erste"] = IA5String("test")
7370         self.assertSequenceEqual(seq.encode(), hexdec("3006160474657374"))
7371
7372         class Seq(Sequence):
7373             schema = (
7374                 ("erste", PrintableString()),
7375             )
7376         seq = Seq()
7377         seq["erste"] = PrintableString("test")
7378         self.assertSequenceEqual(seq.encode(), hexdec("3006130474657374"))
7379         # Asterisk is actually not allowable
7380         PrintableString._allowable_chars |= set(b"*")
7381         seq["erste"] = PrintableString("test*")
7382         self.assertSequenceEqual(seq.encode(), hexdec("30071305746573742a"))
7383         PrintableString._allowable_chars -= set(b"*")
7384
7385         class Seq(Sequence):
7386             schema = (
7387                 ("erste", Any(optional=True)),
7388                 ("zweite", Integer()),
7389             )
7390         seq = Seq()
7391         seq["zweite"] = Integer(64)
7392         self.assertSequenceEqual(seq.encode(), hexdec("3003020140"))
7393
7394         class Seq(SetOf):
7395             schema = Integer()
7396         seq = Seq()
7397         seq.append(Integer(10))
7398         self.assertSequenceEqual(seq.encode(), hexdec("310302010a"))
7399
7400         class _SeqOf(SequenceOf):
7401             schema = PrintableString()
7402
7403         class SeqOf(SequenceOf):
7404             schema = _SeqOf()
7405         _seqof = _SeqOf()
7406         _seqof.append(PrintableString("1"))
7407         seqof = SeqOf()
7408         seqof.append(_seqof)
7409         self.assertSequenceEqual(seqof.encode(), hexdec("30053003130131"))
7410
7411         class Seq(Sequence):
7412             schema = (
7413                 ("erste", Integer(default=1)),
7414             )
7415         seq = Seq()
7416         seq["erste"] = Integer(0)
7417         self.assertSequenceEqual(seq.encode(), hexdec("3003020100"))
7418         seq["erste"] = Integer(1)
7419         self.assertSequenceEqual(seq.encode(), hexdec("3000"))
7420         seq["erste"] = Integer(2)
7421         self.assertSequenceEqual(seq.encode(), hexdec("3003020102"))
7422
7423
7424 class TestPP(TestCase):
7425     @given(data_strategy())
7426     def test_oid_printing(self, d):
7427         oids = {
7428             str(ObjectIdentifier(k)): v * 2
7429             for k, v in d.draw(dictionaries(oid_strategy(), text_letters())).items()
7430         }
7431         chosen = d.draw(sampled_from(sorted(oids)))
7432         chosen_id = oids[chosen]
7433         pp = _pp(asn1_type_name=ObjectIdentifier.asn1_type_name, value=chosen)
7434         self.assertNotIn(chosen_id, pp_console_row(pp))
7435         self.assertIn(
7436             chosen_id,
7437             pp_console_row(pp, oid_maps=[{'whatever': 'whenever'}, oids]),
7438         )
7439
7440
7441 class TestAutoAddSlots(TestCase):
7442     def runTest(self):
7443         class Inher(Integer):
7444             pass
7445
7446         with self.assertRaises(AttributeError):
7447             inher = Inher()
7448             inher.unexistent = "whatever"
7449
7450
7451 class TestOIDDefines(TestCase):
7452     @given(data_strategy())
7453     def runTest(self, d):
7454         value_names = list(d.draw(sets(text_letters(), min_size=1, max_size=10)))
7455         value_name_chosen = d.draw(sampled_from(value_names))
7456         oids = [
7457             ObjectIdentifier(oid)
7458             for oid in d.draw(sets(oid_strategy(), min_size=2, max_size=10))
7459         ]
7460         oid_chosen = d.draw(sampled_from(oids))
7461         values = d.draw(lists(
7462             integers(),
7463             min_size=len(value_names),
7464             max_size=len(value_names),
7465         ))
7466         for definable_class in (Any, OctetString, BitString):
7467             _schema = [
7468                 ("type", ObjectIdentifier(defines=(((value_name_chosen,), {
7469                     oid: Integer() for oid in oids[:-1]
7470                 }),))),
7471             ]
7472             for i, value_name in enumerate(value_names):
7473                 _schema.append((value_name, definable_class(expl=tag_ctxp(i))))
7474
7475             class Seq(Sequence):
7476                 schema = _schema
7477             seq = Seq()
7478             for value_name, value in zip(value_names, values):
7479                 seq[value_name] = definable_class(Integer(value).encode())
7480             seq["type"] = oid_chosen
7481             seq, _ = Seq().decode(seq.encode())
7482             for value_name in value_names:
7483                 if value_name == value_name_chosen:
7484                     continue
7485                 self.assertIsNone(seq[value_name].defined)
7486             if value_name_chosen in oids[:-1]:
7487                 self.assertIsNotNone(seq[value_name_chosen].defined)
7488                 self.assertEqual(seq[value_name_chosen].defined[0], oid_chosen)
7489                 self.assertIsInstance(seq[value_name_chosen].defined[1], Integer)
7490             repr(seq)
7491             list(seq.pps())
7492             pprint(seq, big_blobs=True, with_decode_path=True)
7493
7494
7495 class TestDefinesByPath(TestCase):
7496     def test_generated(self):
7497         class Seq(Sequence):
7498             schema = (
7499                 ("type", ObjectIdentifier()),
7500                 ("value", OctetString(expl=tag_ctxc(123))),
7501             )
7502
7503         class SeqInner(Sequence):
7504             schema = (
7505                 ("typeInner", ObjectIdentifier()),
7506                 ("valueInner", Any()),
7507             )
7508
7509         class PairValue(SetOf):
7510             schema = Any()
7511
7512         class Pair(Sequence):
7513             schema = (
7514                 ("type", ObjectIdentifier()),
7515                 ("value", PairValue()),
7516             )
7517
7518         class Pairs(SequenceOf):
7519             schema = Pair()
7520
7521         (
7522             type_integered,
7523             type_sequenced,
7524             type_innered,
7525             type_octet_stringed,
7526         ) = [
7527             ObjectIdentifier(oid)
7528             for oid in sets(oid_strategy(), min_size=4, max_size=4).example()
7529         ]
7530         seq_integered = Seq()
7531         seq_integered["type"] = type_integered
7532         seq_integered["value"] = OctetString(Integer(123).encode())
7533         seq_integered_raw = seq_integered.encode()
7534
7535         pairs = Pairs()
7536         pairs_input = (
7537             (type_octet_stringed, OctetString(b"whatever")),
7538             (type_integered, Integer(123)),
7539             (type_octet_stringed, OctetString(b"whenever")),
7540             (type_integered, Integer(234)),
7541         )
7542         for t, v in pairs_input:
7543             pairs.append(Pair((
7544                 ("type", t),
7545                 ("value", PairValue((Any(v),))),
7546             )))
7547         seq_inner = SeqInner()
7548         seq_inner["typeInner"] = type_innered
7549         seq_inner["valueInner"] = Any(pairs)
7550         seq_sequenced = Seq()
7551         seq_sequenced["type"] = type_sequenced
7552         seq_sequenced["value"] = OctetString(seq_inner.encode())
7553         seq_sequenced_raw = seq_sequenced.encode()
7554         repr(seq_sequenced)
7555         list(seq_sequenced.pps())
7556         pprint(seq_sequenced, big_blobs=True, with_decode_path=True)
7557
7558         defines_by_path = []
7559         ctx_copied = deepcopy(ctx_dummy)
7560         seq_integered, _ = Seq().decode(
7561             seq_integered_raw,
7562             ctx=ctx_copied,
7563         )
7564         self.assertDictEqual(ctx_copied, ctx_dummy)
7565         self.assertIsNone(seq_integered["value"].defined)
7566         defines_by_path.append(
7567             (("type",), ((("value",), {
7568                 type_integered: Integer(),
7569                 type_sequenced: SeqInner(),
7570             }),))
7571         )
7572         ctx_copied["defines_by_path"] = defines_by_path
7573         seq_integered, _ = Seq().decode(
7574             seq_integered_raw,
7575             ctx=ctx_copied,
7576         )
7577         del ctx_copied["defines_by_path"]
7578         self.assertDictEqual(ctx_copied, ctx_dummy)
7579         self.assertIsNotNone(seq_integered["value"].defined)
7580         self.assertEqual(seq_integered["value"].defined[0], type_integered)
7581         self.assertEqual(seq_integered["value"].defined[1], Integer(123))
7582         self.assertTrue(seq_integered_raw[
7583             seq_integered["value"].defined[1].offset:
7584         ].startswith(Integer(123).encode()))
7585         repr(seq_integered)
7586         list(seq_integered.pps())
7587         pprint(seq_integered, big_blobs=True, with_decode_path=True)
7588
7589         ctx_copied["defines_by_path"] = defines_by_path
7590         seq_sequenced, _ = Seq().decode(
7591             seq_sequenced_raw,
7592             ctx=ctx_copied,
7593         )
7594         del ctx_copied["defines_by_path"]
7595         self.assertDictEqual(ctx_copied, ctx_dummy)
7596         self.assertIsNotNone(seq_sequenced["value"].defined)
7597         self.assertEqual(seq_sequenced["value"].defined[0], type_sequenced)
7598         seq_inner = seq_sequenced["value"].defined[1]
7599         self.assertIsNone(seq_inner["valueInner"].defined)
7600         repr(seq_sequenced)
7601         list(seq_sequenced.pps())
7602         pprint(seq_sequenced, big_blobs=True, with_decode_path=True)
7603
7604         defines_by_path.append((
7605             ("value", DecodePathDefBy(type_sequenced), "typeInner"),
7606             ((("valueInner",), {type_innered: Pairs()}),),
7607         ))
7608         ctx_copied["defines_by_path"] = defines_by_path
7609         seq_sequenced, _ = Seq().decode(
7610             seq_sequenced_raw,
7611             ctx=ctx_copied,
7612         )
7613         del ctx_copied["defines_by_path"]
7614         self.assertDictEqual(ctx_copied, ctx_dummy)
7615         self.assertIsNotNone(seq_sequenced["value"].defined)
7616         self.assertEqual(seq_sequenced["value"].defined[0], type_sequenced)
7617         seq_inner = seq_sequenced["value"].defined[1]
7618         self.assertIsNotNone(seq_inner["valueInner"].defined)
7619         self.assertEqual(seq_inner["valueInner"].defined[0], type_innered)
7620         pairs = seq_inner["valueInner"].defined[1]
7621         for pair in pairs:
7622             self.assertIsNone(pair["value"][0].defined)
7623         repr(seq_sequenced)
7624         list(seq_sequenced.pps())
7625         pprint(seq_sequenced, big_blobs=True, with_decode_path=True)
7626
7627         defines_by_path.append((
7628             (
7629                 "value",
7630                 DecodePathDefBy(type_sequenced),
7631                 "valueInner",
7632                 DecodePathDefBy(type_innered),
7633                 any,
7634                 "type",
7635             ),
7636             ((("value",), {
7637                 type_integered: Integer(),
7638                 type_octet_stringed: OctetString(),
7639             }),),
7640         ))
7641         ctx_copied["defines_by_path"] = defines_by_path
7642         seq_sequenced, _ = Seq().decode(
7643             seq_sequenced_raw,
7644             ctx=ctx_copied,
7645         )
7646         del ctx_copied["defines_by_path"]
7647         self.assertDictEqual(ctx_copied, ctx_dummy)
7648         self.assertIsNotNone(seq_sequenced["value"].defined)
7649         self.assertEqual(seq_sequenced["value"].defined[0], type_sequenced)
7650         seq_inner = seq_sequenced["value"].defined[1]
7651         self.assertIsNotNone(seq_inner["valueInner"].defined)
7652         self.assertEqual(seq_inner["valueInner"].defined[0], type_innered)
7653         pairs_got = seq_inner["valueInner"].defined[1]
7654         for pair_input, pair_got in zip(pairs_input, pairs_got):
7655             self.assertEqual(pair_got["value"][0].defined[0], pair_input[0])
7656             self.assertEqual(pair_got["value"][0].defined[1], pair_input[1])
7657         repr(seq_sequenced)
7658         list(seq_sequenced.pps())
7659         pprint(seq_sequenced, big_blobs=True, with_decode_path=True)
7660
7661     @given(oid_strategy(), integers())
7662     def test_simple(self, oid, tgt):
7663         class Inner(Sequence):
7664             schema = (
7665                 ("oid", ObjectIdentifier(defines=((("..", "tgt"), {
7666                     ObjectIdentifier(oid): Integer(),
7667                 }),))),
7668             )
7669
7670         class Outer(Sequence):
7671             schema = (
7672                 ("inner", Inner()),
7673                 ("tgt", OctetString()),
7674             )
7675
7676         inner = Inner()
7677         inner["oid"] = ObjectIdentifier(oid)
7678         outer = Outer()
7679         outer["inner"] = inner
7680         outer["tgt"] = OctetString(Integer(tgt).encode())
7681         decoded, _ = Outer().decode(outer.encode())
7682         self.assertEqual(decoded["tgt"].defined[1], Integer(tgt))
7683
7684     def test_remaining_data(self):
7685         oid = ObjectIdentifier("1.2.3")
7686
7687         class Seq(Sequence):
7688             schema = (
7689                 ("oid", ObjectIdentifier(defines=((("tgt",), {
7690                     oid: Integer(),
7691                 }),))),
7692                 ("tgt", OctetString()),
7693             )
7694
7695         seq = Seq((
7696             ("oid", oid),
7697             ("tgt", OctetString(Integer(123).encode() + b"junk")),
7698         ))
7699         with assertRaisesRegex(self, DecodeError, "remaining data"):
7700             Seq().decode(seq.encode())
7701
7702     def test_remaining_data_seqof(self):
7703         oid = ObjectIdentifier("1.2.3")
7704
7705         class SeqOf(SetOf):
7706             schema = OctetString()
7707
7708         class Seq(Sequence):
7709             schema = (
7710                 ("oid", ObjectIdentifier(defines=((("tgt",), {
7711                     oid: Integer(),
7712                 }),))),
7713                 ("tgt", SeqOf()),
7714             )
7715
7716         seq = Seq((
7717             ("oid", oid),
7718             ("tgt", SeqOf([OctetString(Integer(123).encode() + b"junk")])),
7719         ))
7720         with assertRaisesRegex(self, DecodeError, "remaining data"):
7721             Seq().decode(seq.encode())
7722
7723
7724 class TestAbsDecodePath(TestCase):
7725     @given(
7726         lists(text(alphabet=ascii_letters, min_size=1)).map(tuple),
7727         lists(text(alphabet=ascii_letters, min_size=1), min_size=1).map(tuple),
7728     )
7729     def test_concat(self, decode_path, rel_path):
7730         dp = abs_decode_path(decode_path, rel_path)
7731         self.assertSequenceEqual(dp, decode_path + rel_path)
7732         repr(dp)
7733
7734     @given(
7735         lists(text(alphabet=ascii_letters, min_size=1)).map(tuple),
7736         lists(text(alphabet=ascii_letters, min_size=1), min_size=1).map(tuple),
7737     )
7738     def test_abs(self, decode_path, rel_path):
7739         self.assertSequenceEqual(
7740             abs_decode_path(decode_path, ("/",) + rel_path),
7741             rel_path,
7742         )
7743
7744     @given(
7745         lists(text(alphabet=ascii_letters, min_size=1), min_size=5).map(tuple),
7746         integers(min_value=1, max_value=3),
7747         lists(text(alphabet=ascii_letters, min_size=1), min_size=1).map(tuple),
7748     )
7749     def test_dots(self, decode_path, number_of_dots, rel_path):
7750         self.assertSequenceEqual(
7751             abs_decode_path(decode_path, tuple([".."] * number_of_dots) + rel_path),
7752             decode_path[:-number_of_dots] + rel_path,
7753         )
7754
7755
7756 class TestStrictDefaultExistence(TestCase):
7757     @given(data_strategy())
7758     def runTest(self, d):
7759         count = d.draw(integers(min_value=1, max_value=10))
7760         chosen = d.draw(integers(min_value=0, max_value=count - 1))
7761         _schema = [
7762             ("int%d" % i, Integer(expl=tag_ctxc(i + 1)))
7763             for i in range(count)
7764         ]
7765         for klass in (Sequence, Set):
7766             class Seq(klass):
7767                 schema = _schema
7768             seq = Seq()
7769             for i in range(count):
7770                 seq["int%d" % i] = Integer(123)
7771             raw = seq.encode()
7772             chosen_choice = "int%d" % chosen
7773             seq.specs[chosen_choice] = seq.specs[chosen_choice](default=123)
7774             with assertRaisesRegex(self, DecodeError, "DEFAULT value met"):
7775                 seq.decode(raw)
7776             decoded, _ = seq.decode(raw, ctx={"allow_default_values": True})
7777             self.assertTrue(decoded.ber_encoded)
7778             self.assertTrue(decoded.bered)
7779             decoded = copy(decoded)
7780             self.assertTrue(decoded.ber_encoded)
7781             self.assertTrue(decoded.bered)
7782             decoded, _ = seq.decode(raw, ctx={"bered": True})
7783             self.assertTrue(decoded.ber_encoded)
7784             self.assertTrue(decoded.bered)
7785             decoded = copy(decoded)
7786             self.assertTrue(decoded.ber_encoded)
7787             self.assertTrue(decoded.bered)
7788
7789
7790 class TestX690PrefixedType(TestCase):
7791     def test_1(self):
7792         self.assertSequenceEqual(
7793             VisibleString("Jones").encode(),
7794             hexdec("1A054A6F6E6573"),
7795         )
7796
7797     def test_2(self):
7798         self.assertSequenceEqual(
7799             VisibleString(
7800                 "Jones",
7801                 impl=tag_encode(3, klass=TagClassApplication),
7802             ).encode(),
7803             hexdec("43054A6F6E6573"),
7804         )
7805
7806     def test_3(self):
7807         self.assertSequenceEqual(
7808             Any(
7809                 VisibleString(
7810                     "Jones",
7811                     impl=tag_encode(3, klass=TagClassApplication),
7812                 ),
7813                 expl=tag_ctxc(2),
7814             ).encode(),
7815             hexdec("A20743054A6F6E6573"),
7816         )
7817
7818     def test_4(self):
7819         self.assertSequenceEqual(
7820             OctetString(
7821                 VisibleString(
7822                     "Jones",
7823                     impl=tag_encode(3, klass=TagClassApplication),
7824                 ).encode(),
7825                 impl=tag_encode(7, form=TagFormConstructed, klass=TagClassApplication),
7826             ).encode(),
7827             hexdec("670743054A6F6E6573"),
7828         )
7829
7830     def test_5(self):
7831         self.assertSequenceEqual(
7832             VisibleString("Jones", impl=tag_ctxp(2)).encode(),
7833             hexdec("82054A6F6E6573"),
7834         )
7835
7836
7837 class TestExplOOB(TestCase):
7838     def runTest(self):
7839         expl = tag_ctxc(123)
7840         raw = Integer(123).encode() + Integer(234).encode()
7841         raw = b"".join((expl, len_encode(len(raw)), raw))
7842         with assertRaisesRegex(self, DecodeError, "explicit tag out-of-bound"):
7843             Integer(expl=expl).decode(raw)
7844         Integer(expl=expl).decode(raw, ctx={"allow_expl_oob": True})
7845
7846
7847 class TestPickleDifferentVersion(TestCase):
7848     def runTest(self):
7849         pickled = pickle_dumps(Integer(123), pickle_proto)
7850         import pyderasn
7851         version_orig = pyderasn.__version__
7852         pyderasn.__version__ += "different"
7853         with assertRaisesRegex(self, ValueError, "different PyDERASN version"):
7854             pickle_loads(pickled)
7855         pyderasn.__version__ = version_orig
7856         pickle_loads(pickled)
7857
7858
7859 class TestCERSetOrdering(TestCase):
7860     def test_vectors(self):
7861         """Taken from X.690-201508
7862         """
7863         class B(Choice):
7864             schema = (
7865                 ("c", Integer(impl=tag_ctxp(2))),
7866                 ("d", Integer(impl=tag_ctxp(4))),
7867             )
7868
7869         class F(Choice):
7870             schema = (
7871                 ("g", Integer(impl=tag_ctxp(5))),
7872                 ("h", Integer(impl=tag_ctxp(6))),
7873             )
7874
7875         class I(Choice):
7876             schema = (
7877                 ("j", Integer(impl=tag_ctxp(0))),
7878             )
7879
7880         class E(Choice):
7881             schema = (
7882                 ("f", F()),
7883                 ("i", I()),
7884             )
7885
7886         class A(Set):
7887             schema = (
7888                 ("a", Integer(impl=tag_ctxp(3))),
7889                 ("b", B(expl=tag_ctxc(1))),
7890                 ("e", E()),
7891             )
7892
7893         a = A((
7894             ("a", Integer(123)),
7895             ("b", B(("d", Integer(234)))),
7896             ("e", E(("f", F(("g", Integer(345)))))),
7897         ))
7898         order = sorted(a._values_for_encoding(), key=attrgetter("tag_order_cer"))
7899         self.assertSequenceEqual(
7900             [i.__class__.__name__ for i in order],
7901             ("E", "B", "Integer"),
7902         )