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