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