# coding: utf-8
from __future__ import unicode_literals, division, absolute_import, print_function

import unittest
import os
from datetime import datetime, timedelta

from asn1crypto import core, util

from .unittest_data import data_decorator, data
from ._unittest_compat import patch

patch()

tests_root = os.path.dirname(__file__)
fixtures_dir = os.path.join(tests_root, 'fixtures')


class NamedBits(core.BitString):
    _map = {
        0: 'zero',
        1: 'one',
        2: 'two',
        3: 'three',
        4: 'four',
        6: 'six',
        7: 'seven',
    }


class SequenceOfInts(core.SequenceOf):
    _child_spec = core.Integer


class SequenceAny(core.SequenceOf):
    _child_spec = core.Any


class Seq(core.Sequence):
    _fields = [
        ('id', core.ObjectIdentifier),
        ('value', core.Any),
    ]

    _oid_pair = ('id', 'value')
    _oid_specs = {
        '1.2.3': core.Integer,
        '2.3.4': core.OctetString,
    }


class CopySeq(core.Sequence):
    _fields = [
        ('name', core.UTF8String),
        ('pair', Seq),
    ]


class NestSeqAny(core.Sequence):
    _fields = [
        ('id', core.ObjectIdentifier),
        ('value', core.Any),
    ]

    _oid_pair = ('id', 'value')
    _oid_specs = {
        '2.3.4.5': Seq,
    }


class NestSeqExplicit(core.Sequence):
    _fields = [
        ('id', core.ObjectIdentifier),
        ('value', NamedBits),
    ]

    _oid_pair = ('id', 'value')
    _oid_specs = {
        '2.3.4.5': Seq,
    }


class Enum(core.Enumerated):
    _map = {
        0: 'a',
        1: 'b',
    }


class ExplicitFieldDefault(core.Sequence):
    _fields = [
        ('bits', NamedBits),
        ('seq', Seq, {'explicit': 2, 'default': {'id': '1.2.3', 'value': 10}}),
    ]


class NumChoice(core.Choice):
    _alternatives = [
        ('one', core.Integer, {'explicit': 0}),
        ('two', core.Integer, {'implicit': 1}),
        ('three', core.Integer, {'explicit': 2}),
    ]


class NumChoiceOldApi(core.Choice):
    _alternatives = [
        ('one', core.Integer, {'tag_type': 'explicit', 'tag': 0}),
        ('two', core.Integer, {'tag_type': 'implicit', 'tag': 1}),
        ('three', core.Integer, {'tag_type': 'explicit', 'tag': 2}),
    ]


class SeqChoice(core.Choice):
    _alternatives = [
        ('one', CopySeq, {'explicit': 0}),
        ('two', CopySeq, {'implicit': 1}),
    ]


class SeqChoiceOldApi(core.Choice):
    _alternatives = [
        ('one', CopySeq, {'tag_type': 'explicit', 'tag': 0}),
        ('two', CopySeq, {'tag_type': 'implicit', 'tag': 1}),
    ]


class ChoiceChoice(core.Choice):
    _alternatives = [
        ('num', NumChoice, {'explicit': 0}),
        ('seq', SeqChoice, {'explicit': 1}),
    ]


class CCSeq(core.Sequence):
    _fields = [
        ('cc', ChoiceChoice)
    ]


class ExplicitField(core.Sequence):
    _fields = [
        ('field', NumChoice, {'tag_type': 'explicit', 'tag': 0}),
    ]


class ExplicitFieldOldApi(core.Sequence):
    _fields = [
        ('field', NumChoiceOldApi, {'explicit': 0}),
    ]


class SetTest(core.Set):
    _fields = [
        ('two', core.Integer, {'tag_type': 'implicit', 'tag': 2}),
        ('one', core.Integer, {'tag_type': 'implicit', 'tag': 1}),
    ]


class SetTestOldApi(core.Set):
    _fields = [
        ('two', core.Integer, {'implicit': 2}),
        ('one', core.Integer, {'implicit': 1}),
    ]


class SetOfTest(core.SetOf):
    _child_spec = core.Integer


class ConcatTest(core.Concat):
    _child_specs = [Seq, core.Integer]


class IntegerConcats(core.Concat):
    _child_specs = [core.Integer, core.Integer]


class MyOids(core.ObjectIdentifier):
    _map = {
        '1.2.3': 'abc',
        '4.5.6': 'def',
    }


class ApplicationTaggedInteger(core.Integer):
    # This class attribute may be a 2-element tuple of integers,
    # or a tuple of 2-element tuple of integers. The first form
    # will be converted to the second form the first time an
    # object of this type is constructed.
    explicit = ((1, 10), )


class ApplicationTaggedInner(core.Sequence):
    """
    TESTCASE DEFINITIONS EXPLICIT TAGS ::=
    BEGIN

    INNERSEQ ::= SEQUENCE {
        innernumber       [21] INTEGER
    }

    INNER ::= [APPLICATION 20] INNERSEQ
    """

    explicit = (1, 20)

    _fields = [
        ('innernumber', core.Integer, {'explicit': 21}),
    ]


class ApplicationTaggedOuter(core.Sequence):
    """
    OUTERSEQ ::= SEQUENCE {
        outernumber  [11] INTEGER,
        inner        [12] INNER
    }

    OUTER ::= [APPLICATION 10] OUTERSEQ
    END
    """

    explicit = (1, 10)

    _fields = [
        ('outernumber', core.Integer, {'explicit': 11}),
        ('inner', ApplicationTaggedInner, {'explicit': 12}),
    ]


class SpcPeImageFlags(core.BitString):
    _map = {
        0: "includeResources",
        1: "includeDebugInfo",
        2: "includeImportAddressTable",
    }


class SpcSerializedObject(core.Sequence):
    _fields = [
        ("classId", core.OctetString),
        ("serializedData", core.OctetString),
    ]


class SpcString(core.Choice):
    _alternatives = [
        ("unicode", core.BMPString, {"implicit": 0}),
        ("ascii", core.IA5String, {"implicit": 1}),
    ]


class SpcLink(core.Choice):
    _alternatives = [
        ("url", core.IA5String, {"implicit": 0}),
        ("moniker", SpcSerializedObject, {"implicit": 1}),
        ("file", SpcString, {"explicit": 2})
    ]


class SpcPeImageData(core.Sequence):
    _fields = [
        ("flags", SpcPeImageFlags, {"default": "includeResources"}),
        ("file", SpcLink, {"explicit": 0})
    ]


class UTF8Sequence(core.Sequence):
    _fields = [
        ("string", core.UTF8String)
    ]


class NestedUTF8Sequence(core.Sequence):
    _fields = [
        ("seq", UTF8Sequence)
    ]


@data_decorator
class CoreTests(unittest.TestCase):

    def test_large_tag_encode(self):
        # https://misc.daniel-marschall.de/asn.1/oid_facts.html
        v = core.Primitive(tag=31, contents=b'')
        self.assertEqual(b'\x1f\x1f\x00', v.dump())

        v = core.Primitive(tag=36, contents=b'')
        self.assertEqual(b'\x1f\x24\x00', v.dump())

        # One extra byte
        v = core.Primitive(
            class_="application",
            method="constructed",
            tag=73,
            contents=b''
        )
        self.assertEqual(b'\x7f\x49\x00', v.dump())

        # Two extra bytes
        v = core.Primitive(
            class_="application",
            method="constructed",
            tag=201,
            contents=b''
        )
        self.assertEqual(b'\x7f\x81\x49\x00', v.dump())

        # Three extra bytes
        v = core.Primitive(
            class_="application",
            method="constructed",
            tag=16384,
            contents=b''
        )
        self.assertEqual(b'\x7f\x81\x80\x00\x00', v.dump())

    def test_manual_construction(self):
        v = core.Asn1Value(
            class_="application",
            method="constructed",
            tag=1,
            contents=b''
        )
        self.assertEqual(b'\x61\x00', v.dump())

    def test_sequence_spec(self):
        seq = Seq()
        seq['id'] = '1.2.3'
        self.assertEqual(core.Integer, seq.spec('value'))
        seq['id'] = '2.3.4'
        self.assertEqual(core.OctetString, seq.spec('value'))

    def test_sequence_of_spec(self):
        seq = SequenceAny()
        self.assertEqual(core.Any, seq.spec())

    @staticmethod
    def compare_primitive_info():
        return (
            (core.ObjectIdentifier('1.2.3'), core.ObjectIdentifier('1.2.3'), True),
            (core.Integer(1), Enum(1), False),
            (core.Integer(1), core.Integer(1, implicit=5), True),
            (core.Integer(1), core.Integer(1, explicit=5), True),
            (core.Integer(1), core.Integer(2), False),
            (core.OctetString(b''), core.OctetString(b''), True),
            (core.OctetString(b''), core.OctetString(b'1'), False),
            (core.OctetString(b''), core.OctetBitString(b''), False),
            (core.ParsableOctetString(b'12'), core.OctetString(b'12'), True),
            (core.ParsableOctetBitString(b'12'), core.OctetBitString(b'12'), True),
            (core.UTF8String('12'), core.UTF8String('12'), True),
            (core.UTF8String('12'), core.UTF8String('1'), False),
            (core.UTF8String('12'), core.IA5String('12'), False),
        )

    @data('compare_primitive_info')
    def compare_primitive(self, one, two, equal):
        if equal:
            self.assertEqual(one, two)
        else:
            self.assertNotEqual(one, two)

    @staticmethod
    def integer_info():
        return (
            (0, b'\x02\x01\x00'),
            (255, b'\x02\x02\x00\xFF'),
            (128, b'\x02\x02\x00\x80'),
            (127, b'\x02\x01\x7F'),
            (-127, b'\x02\x01\x81'),
            (-127, b'\x02\x01\x81'),
            (32768, b'\x02\x03\x00\x80\x00'),
            (-32768, b'\x02\x02\x80\x00'),
            (-32769, b'\x02\x03\xFF\x7F\xFF'),
        )

    @data('integer_info')
    def integer(self, native, der_bytes):
        i = core.Integer(native)
        self.assertEqual(der_bytes, i.dump())
        self.assertEqual(native, core.Integer.load(der_bytes).native)

    @staticmethod
    def utctime_info():
        return (
            (datetime(2030, 12, 31, 8, 30, 0, tzinfo=util.timezone.utc), b'\x17\x0D301231083000Z'),
            (datetime(2049, 12, 31, 8, 30, 0, tzinfo=util.timezone.utc), b'\x17\x0D491231083000Z'),
            (datetime(1950, 12, 31, 8, 30, 0, tzinfo=util.timezone.utc), b'\x17\x0D501231083000Z'),
            (datetime(2018, 10, 20, 7, 35, 4, tzinfo=util.timezone(timedelta(hours=7, minutes=40))),
             b'\x17\x0D181019235504Z'),
        )

    @data('utctime_info')
    def utctime(self, native, der_bytes):
        u = core.UTCTime(native)
        self.assertEqual(der_bytes, u.dump())
        self.assertEqual(native, core.UTCTime.load(der_bytes).native)

    def test_utctime_errors(self):
        with self.assertRaises(ValueError):
            # is not aware
            core.UTCTime(datetime.fromtimestamp(1234567890))

        with self.assertRaises(ValueError):
            # Is pre 1950
            core.UTCTime(datetime(1910, 6, 22, 11, 33, 44, tzinfo=util.timezone.utc))

        with self.assertRaises(ValueError):
            # Is past 2050
            core.UTCTime(datetime(2106, 2, 7, 6, 28, 16, tzinfo=util.timezone.utc))

    def test_utctime_copy(self):
        a = core.UTCTime(datetime(2019, 11, 11, 17, 45, 18, tzinfo=util.timezone.utc))
        # Ensure _native is set because we want to test copy on the nested timezone object.
        a.native
        b = a.copy()
        self.assertEqual(a.native, b.native)
        self.assertEqual(a.contents, b.contents)
        self.assertEqual(a.dump(), b.dump())

    @staticmethod
    def generalized_time_info():
        def tz(hours, minutes=0):
            return util.create_timezone(timedelta(hours=hours, minutes=minutes))

        return (
            (b'\x18\x1520180405062426.0+0200', datetime(2018, 4, 5, 6, 24, 26, 0, tz(2)), b'\x18\x0f20180405042426Z'),
            (b'\x18\x0f2018062419-1355', datetime(2018, 6, 24, 19, 0, 0, 0, tz(-13, -55)), b'\x18\x0f20180625085500Z'),
            (b'\x18\x0d2018062419-13', datetime(2018, 6, 24, 19, 0, 0, 0, tz(-13)), b'\x18\x0f20180625080000Z'),
            (b'\x18\x0b2018062419Z', datetime(2018, 6, 24, 19, 0, 0, 0, tz(0)), b'\x18\x0f20180624190000Z'),
            (b'\x18\x122018062419.15+0345', datetime(2018, 6, 24, 19, 9, 0, 0, tz(3, 45)), b'\x18\x0f20180624152400Z'),
            (
                b'\x18\x13201806241957,433+02',
                datetime(2018, 6, 24, 19, 57, 25, 980000, tz(2)),
                b'\x18\x1220180624175725.98Z',
            ),
            (
                b'\x18\x1620180624195724.215999Z',
                datetime(2018, 6, 24, 19, 57, 24, 215999, tz(0)),
                b'\x18\x1620180624195724.215999Z',
            ),
            (
                b'\x18\x150000022910.31337-0815',
                util.extended_datetime(0, 2, 29, 10, 18, 48, 132000, tz(-8, -15)),
                b'\x18\x1300000229183348.132Z',
            ),
            (b'\x18\x1520180624195724.215999', datetime(2018, 6, 24, 19, 57, 24, 215999), None),
            (b'\x18\x0a2018062419', datetime(2018, 6, 24, 19, 0, 0, 0), None),
        )

    @data('generalized_time_info')
    def generalized_time(self, ber_bytes, native, der_bytes):
        decoded = core.GeneralizedTime.load(ber_bytes)

        self.assertEqual(decoded.native, native)
        self.assertEqual(decoded.native.tzinfo, native.tzinfo)

        if der_bytes is not None:
            encoded = core.GeneralizedTime(native).dump()
            self.assertEqual(encoded, der_bytes)

            decoded2 = core.GeneralizedTime.load(encoded)
            self.assertEqual(decoded2.native, native)
        else:
            with self.assertRaises(ValueError):
                encoded = core.GeneralizedTime(native).dump()

    @staticmethod
    def type_info():
        return (
            ('universal/object_identifier.der', core.ObjectIdentifier, '1.2.840.113549.1.1.1'),
        )

    @data('type_info')
    def parse_universal_type(self, input_filename, type_class, native):
        with open(os.path.join(fixtures_dir, input_filename), 'rb') as f:
            der = f.read()
            parsed = type_class.load(der)

        self.assertEqual(native, parsed.native)
        self.assertEqual(der, parsed.dump(force=True))

    def test_int_to_bit_tuple(self):
        self.assertEqual((), core._int_to_bit_tuple(0, 0))
        self.assertEqual((0,), core._int_to_bit_tuple(0, 1))
        self.assertEqual((1,), core._int_to_bit_tuple(1, 1))
        self.assertEqual((0, 0), core._int_to_bit_tuple(0, 2))
        self.assertEqual((0, 1), core._int_to_bit_tuple(1, 2))
        self.assertEqual((0, 0, 1), core._int_to_bit_tuple(1, 3))
        self.assertEqual((0, 1, 0), core._int_to_bit_tuple(2, 3))
        self.assertEqual((1, 0, 1), core._int_to_bit_tuple(5, 3))

        with self.assertRaises(ValueError):
            core._int_to_bit_tuple(9, 3)
        with self.assertRaises(ValueError):
            core._int_to_bit_tuple(-9, 5)

    @staticmethod
    def bit_string_info():
        return (
            ((0, 1, 1), b'\x03\x02\x05\x60'),
            ((0, 1, 1, 0, 0, 0, 0, 0), b'\x03\x02\x00\x60'),
            ((0, 0, 0, 0, 0, 0, 0, 0), b'\x03\x02\x00\x00'),
            ((0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1), b'\x03\x03\x00\x00\x01'),
        )

    @data('bit_string_info')
    def bit_string(self, native, der_bytes):
        bs = core.BitString(native)
        self.assertEqual(der_bytes, bs.dump())
        self.assertEqual(native, core.BitString.load(der_bytes).native)

    def test_bit_string_load_dump(self):
        bs = core.BitString.load(b'\x03\x01\x00')
        self.assertEqual(tuple(), bs.native)
        self.assertEqual(b'\x03\x01\x00', bs.dump(True))

    @staticmethod
    def bit_string_error_values():
        return (
            # unused bits in empty bit string
            (b'\x03\x01\x05',),
            # too many unused bits
            (b'\x03\x03\x0e\x0c\x00',),
            # chunk with unused bits is not last chunk
            (b'\x23\x80\x03\x02\x01\xfe\x03\x02\x00\x55\x00\x00',),
        )

    @data('bit_string_error_values')
    def bit_string_errors(self, enc_bytes):
        with self.assertRaises(ValueError):
            core.BitString.load(enc_bytes).native

    def test_cast(self):
        a = core.OctetBitString(b'\x00\x01\x02\x03')
        self.assertEqual(b'\x00\x01\x02\x03', a.native)
        b = a.cast(core.BitString)
        self.assertIsInstance(b, core.BitString)
        self.assertEqual(
            (
                0, 0, 0, 0, 0, 0, 0, 0,
                0, 0, 0, 0, 0, 0, 0, 1,
                0, 0, 0, 0, 0, 0, 1, 0,
                0, 0, 0, 0, 0, 0, 1, 1
            ),
            b.native
        )
        c = a.cast(core.IntegerBitString)
        self.assertIsInstance(c, core.IntegerBitString)
        self.assertEqual(66051, c.native)

    def test_load(self):
        i = core.load(b'\x02\x01\x00')
        self.assertIsInstance(i, core.Integer)
        self.assertEqual(0, i.native)

    def test_load_wrong_type(self):
        with self.assertRaises(TypeError):
            core.load('\x02\x01\x00')

    @staticmethod
    def truncated_der_byte_strings():
        return (
            (b'',),
            (b'\x30',),
            (b'\x30\x03\x02\x00\x02',),
        )

    @data('truncated_der_byte_strings')
    def truncated(self, der_bytes):
        with self.assertRaises(ValueError):
            core.load(der_bytes).native

    def test_strict(self):
        with self.assertRaises(ValueError):
            core.load(b'\x02\x01\x00\x00', strict=True)

    def test_strict_on_class(self):
        with self.assertRaises(ValueError):
            core.Integer.load(b'\x02\x01\x00\x00', strict=True)

    def test_strict_concat(self):
        with self.assertRaises(ValueError):
            IntegerConcats.load(b'\x02\x01\x00\x02\x01\x00\x00', strict=True)

    def test_strict_choice(self):
        with self.assertRaises(ValueError):
            NumChoice.load(b'\xA0\x03\x02\x01\x00\x00', strict=True)
        with self.assertRaises(ValueError):
            NumChoiceOldApi.load(b'\xA0\x03\x02\x01\x00\x00', strict=True)

    def test_choice_parse_return(self):
        nc = NumChoice.load(b'\xA0\x03\x02\x01\x00\x00')
        nc._parsed = None
        self.assertEqual(0, nc.parse().native)

    def test_sequece_choice_choice(self):
        CCSeq({
            'cc': ChoiceChoice(
                'num',
                NumChoice('one', core.Integer(0))
            )
        })

    def test_bit_string_item_access(self):
        named = core.BitString()
        named[0] = True
        self.assertEqual(False, named[2])
        self.assertEqual(False, named[1])
        self.assertEqual(True, named[0])

    @staticmethod
    def mapped_bit_string_info():
        return (
            (
                (0, 1, 1),
                b'\x03\x02\x05\x60',
                set(['one', 'two'])
            ),
            (
                (0,),
                b'\x03\x01\x00',
                set()
            ),
            (
                set(['one', 'two']),
                b'\x03\x02\x05\x60',
                set(['one', 'two'])
            )
        )

    @data('mapped_bit_string_info')
    def mapped_bit_string(self, input_native, der_bytes, native):
        named = NamedBits(input_native)
        self.assertEqual(der_bytes, named.dump())
        self.assertEqual(native, NamedBits.load(der_bytes).native)

    def test_mapped_bit_string_item_access(self):
        named = NamedBits()
        named['one'] = True
        self.assertEqual(False, named['two'])
        self.assertEqual(True, named['one'])
        self.assertEqual(True, 'one' in named.native)

    def test_mapped_bit_string_unset_bit(self):
        named = NamedBits(set(['one', 'two']))
        named['one'] = False
        self.assertEqual(True, named['two'])
        self.assertEqual(set(['two']), named.native)

    def test_mapped_bit_string_sparse(self):
        named = NamedBits((0, 0, 0, 0, 0, 1))
        self.assertEqual(False, named['two'])
        self.assertEqual(True, named[5])
        self.assertEqual(True, 5 in named.native)

    def test_mapped_bit_string_numeric(self):
        named = NamedBits()
        named[1] = True
        self.assertEqual(True, named['one'])
        self.assertEqual(set(['one']), named.native)

    def test_get_sequence_value(self):
        seq = SequenceOfInts([1, 2])
        self.assertEqual(2, seq[1].native)

    def test_replace_sequence_value(self):
        seq = SequenceOfInts([1, 2])
        self.assertEqual([1, 2], seq.native)
        seq[0] = 5
        self.assertEqual([5, 2], seq.native)

    def test_add_to_end_sequence_value(self):
        seq = SequenceOfInts([1, 2])
        self.assertEqual([1, 2], seq.native)
        seq[2] = 5
        self.assertEqual([1, 2, 5], seq.native)
        seq.append(6)
        self.assertEqual([1, 2, 5, 6], seq.native)

    def test_delete_sequence_value(self):
        seq = SequenceOfInts([1, 2])
        self.assertEqual([1, 2], seq.native)
        del seq[0]
        self.assertEqual([2], seq.native)

    def test_sequence_any_asn1value(self):
        seq = SequenceAny()
        seq.append(core.Integer(5))
        self.assertEqual([5], seq.native)

    def test_sequence_any_native_value(self):
        seq = SequenceAny()
        with self.assertRaises(ValueError):
            seq.append(5)

    def test_copy(self):
        a = core.Integer(200)
        b = a.copy()
        self.assertNotEqual(id(a), id(b))
        self.assertEqual(a.contents, b.contents)
        self.assertEqual(a.dump(), b.dump())

    def test_copy_mutable(self):
        a = CopySeq({'name': 'foo', 'pair': {'id': '1.2.3', 'value': 5}})
        # Cache the native representation so it is copied during the copy operation
        a.native
        b = a.copy()
        self.assertNotEqual(id(a), id(b))
        self.assertNotEqual(id(a['pair']), id(b['pair']))
        self.assertEqual(a.contents, b.contents)
        self.assertEqual(a.dump(), b.dump())

        self.assertEqual(a['pair']['value'].native, b['pair']['value'].native)
        a['pair']['value'] = 6
        self.assertNotEqual(a['pair']['value'].native, b['pair']['value'].native)

        a.native['pair']['value'] = 6
        self.assertNotEqual(a.native['pair']['value'], b.native['pair']['value'])

        self.assertNotEqual(a.contents, b.contents)
        self.assertNotEqual(a.dump(), b.dump())

    def test_explicit_tag_header(self):
        val = NumChoice.load(b'\xa0\x03\x02\x01\x00')
        self.assertEqual(b'\xa0\x03\x02\x01', val.chosen._header)
        self.assertEqual(b'\x00', val.chosen.contents)
        val2 = NumChoiceOldApi.load(b'\xa0\x03\x02\x01\x00')
        self.assertEqual(b'\xa0\x03\x02\x01', val2.chosen._header)
        self.assertEqual(b'\x00', val2.chosen.contents)

    def test_explicit_field_default(self):
        val = ExplicitFieldDefault.load(b'\x30\x0f\x03\x02\x06@\xa2\x090\x07\x06\x02*\x03\x02\x01\x01')
        self.assertEqual(set(['one']), val['bits'].native)
        self.assertEqual(
            util.OrderedDict([
                ('id', '1.2.3'),
                ('value', 1)
            ]),
            val['seq'].native
        )

    def test_explicit_header_field_choice(self):
        der = b'\x30\x07\xa0\x05\xa0\x03\x02\x01\x00'
        val = ExplicitField.load(der)
        self.assertEqual(0, val['field'].chosen.native)
        self.assertEqual(der, val.dump(force=True))

        val2 = ExplicitFieldOldApi.load(der)
        self.assertEqual(0, val2['field'].chosen.native)
        self.assertEqual(der, val2.dump(force=True))

    def test_retag(self):
        a = core.Integer(200)
        b = a.retag('explicit', 0)
        self.assertNotEqual(id(a), id(b))
        self.assertEqual(a.contents, b.contents)
        self.assertNotEqual(a.dump(), b.dump())

    def test_untag(self):
        a = core.Integer(200, explicit=0)
        b = a.untag()
        self.assertNotEqual(id(a), id(b))
        self.assertEqual(a.contents, b.contents)
        self.assertNotEqual(a.dump(), b.dump())

    def test_choice_dict_name(self):
        a = CopySeq({'name': 'foo', 'pair': {'id': '1.2.3', 'value': 5}})
        choice = SeqChoice({'one': a})
        self.assertEqual('one', choice.name)

        with self.assertRaises(ValueError):
            SeqChoice({})

        with self.assertRaises(ValueError):
            SeqChoice({'one': a, 'two': a})

        choice2 = SeqChoiceOldApi({'one': a})
        self.assertEqual('one', choice2.name)

        with self.assertRaises(ValueError):
            SeqChoiceOldApi({})

        with self.assertRaises(ValueError):
            SeqChoiceOldApi({'one': a, 'two': a})

    def test_choice_tuple_name(self):
        a = CopySeq({'name': 'foo', 'pair': {'id': '1.2.3', 'value': 5}})
        choice = SeqChoice(('one', a))
        self.assertEqual('one', choice.name)

        with self.assertRaises(ValueError):
            SeqChoice(('one',))

        with self.assertRaises(ValueError):
            SeqChoice(('one', a, None))

        choice2 = SeqChoiceOldApi(('one', a))
        self.assertEqual('one', choice2.name)

        with self.assertRaises(ValueError):
            SeqChoiceOldApi(('one',))

        with self.assertRaises(ValueError):
            SeqChoiceOldApi(('one', a, None))

    def test_load_invalid_choice(self):
        with self.assertRaises(ValueError):
            NumChoice.load(b'\x02\x01\x00')
        with self.assertRaises(ValueError):
            NumChoiceOldApi.load(b'\x02\x01\x00')

    def test_fix_tagging_choice(self):
        correct = core.Integer(200, explicit=2)
        choice = NumChoice(
            name='three',
            value=core.Integer(200, explicit=1)
        )
        self.assertEqual(correct.dump(), choice.dump())
        self.assertEqual(correct.explicit, choice.chosen.explicit)
        choice2 = NumChoiceOldApi(
            name='three',
            value=core.Integer(200, explicit=1)
        )
        self.assertEqual(correct.dump(), choice2.dump())
        self.assertEqual(correct.explicit, choice2.chosen.explicit)

    def test_copy_choice_mutate(self):
        a = CopySeq({'name': 'foo', 'pair': {'id': '1.2.3', 'value': 5}})
        choice = SeqChoice(
            name='one',
            value=a
        )
        choice.dump()
        choice_copy = choice.copy()
        choice.chosen['name'] = 'bar'
        self.assertNotEqual(choice.chosen['name'], choice_copy.chosen['name'])

        choice2 = SeqChoiceOldApi(
            name='one',
            value=a
        )
        choice2.dump()
        choice2_copy = choice2.copy()
        choice2.chosen['name'] = 'bar'
        self.assertNotEqual(choice2.chosen['name'], choice2_copy.chosen['name'])

    def test_dump_ber_indefinite(self):
        # A simple primitive type that is indefinite-length-encoded will be
        # automatically re-encoded to DER encoding
        data = b'\x2C\x80\x0C\x03foo\x00\x00'
        v = core.UTF8String.load(data)
        self.assertEqual(True, v._indefinite)
        self.assertEqual('foo', v.native)
        self.assertEqual(b'\x0C\x03foo', v.dump())

        # In this case the indefinite length items are nested, and the
        # top-level item is fixed-length, so it won't get automatically
        # re-encoded
        data = b'\x30\x0d\x30\x80\x2C\x80\x0C\x03foo\x00\x00\x00\x00'
        v = NestedUTF8Sequence.load(data)
        self.assertEqual(data, v.dump())

        # Here both the top-level and the nested encoding will get fixed since
        # the top-level being indefinitely triggers a full re-encoding
        data = b'\x30\x80\x30\x09\x2C\x80\x0C\x03foo\x00\x00\x00\x00'
        v = NestedUTF8Sequence.load(data)
        self.assertEqual(b'\x30\x07\x30\x05\x0C\x03foo', v.dump())

    def test_copy_indefinite(self):
        v = core.BitString.load(b'\x23\x80\x03\x02\x00\x04\x00\x00')
        self.assertEqual(True, v._indefinite)
        v2 = v.copy()
        self.assertEqual(0, v2.method)
        self.assertEqual(3, v2.tag)
        self.assertEqual(False, v2._indefinite)
        self.assertEqual((0, 0, 0, 0, 0, 1, 0, 0), v2.native)
        self.assertEqual(b'\x03\x02\x00\x04', v2.dump())

        v = core.OctetBitString.load(b'\x23\x80\x03\x02\x00\x04\x00\x00')
        self.assertEqual(True, v._indefinite)
        v2 = v.copy()
        self.assertEqual(0, v2.method)
        self.assertEqual(3, v2.tag)
        self.assertEqual(False, v2._indefinite)
        self.assertEqual(b'\x04', v2.native)
        self.assertEqual(b'\x03\x02\x00\x04', v2.dump())

        v = core.ParsableOctetBitString.load(b'\x23\x80\x03\x04\x00\x02\x01\x04\x00\x00')
        self.assertEqual(4, v.parsed.native)
        self.assertEqual(True, v._indefinite)
        v2 = v.copy()
        self.assertEqual(0, v2.method)
        self.assertEqual(3, v2.tag)
        self.assertEqual(False, v2._indefinite)
        self.assertEqual(4, v2.parsed.native)
        self.assertEqual(b'\x03\x04\x00\x02\x01\x04', v2.dump())

        v = core.IntegerBitString.load(b'\x23\x80\x03\x02\x00\x04\x00\x00')
        self.assertEqual(True, v._indefinite)
        v2 = v.copy()
        self.assertEqual(0, v2.method)
        self.assertEqual(3, v2.tag)
        self.assertEqual(False, v2._indefinite)
        self.assertEqual(4, v2.native)
        self.assertEqual(b'\x03\x02\x00\x04', v2.dump())

        v = core.OctetString.load(b'\x24\x80\x04\x03foo\x00\x00')
        self.assertEqual(True, v._indefinite)
        v2 = v.copy()
        self.assertEqual(0, v2.method)
        self.assertEqual(4, v2.tag)
        self.assertEqual(False, v2._indefinite)
        self.assertEqual(b'foo', v2.native)
        self.assertEqual(b'\x04\x03foo', v2.dump())

        v = core.IntegerOctetString.load(b'\x24\x80\x04\x01\x04\x00\x00')
        self.assertEqual(True, v._indefinite)
        v2 = v.copy()
        self.assertEqual(0, v2.method)
        self.assertEqual(4, v2.tag)
        self.assertEqual(False, v2._indefinite)
        self.assertEqual(4, v2.native)
        self.assertEqual(b'\x04\x01\x04', v2.dump())

        v = core.ParsableOctetString.load(b'\x24\x80\x04\x03\x02\x01\x04\x00\x00')
        self.assertEqual(4, v.parsed.native)
        self.assertEqual(True, v._indefinite)
        v2 = v.copy()
        self.assertEqual(0, v2.method)
        self.assertEqual(4, v2.tag)
        self.assertEqual(False, v2._indefinite)
        self.assertEqual(4, v2.parsed.native)
        self.assertEqual(b'\x02\x01\x04', v2.__bytes__())
        self.assertEqual(b'\x04\x03\x02\x01\x04', v2.dump())

        v = core.UTF8String.load(b'\x2C\x80\x0C\x03foo\x00\x00')
        self.assertEqual(True, v._indefinite)
        v2 = v.copy()
        self.assertEqual(0, v2.method)
        self.assertEqual(12, v2.tag)
        self.assertEqual(False, v2._indefinite)
        self.assertEqual('foo', v2.native)
        self.assertEqual(b'\x0C\x03foo', v2.dump())

    def test_concat(self):
        child1 = Seq({
            'id': '1.2.3',
            'value': 1
        })
        child2 = core.Integer(0)
        parent = ConcatTest([
            child1,
            child2
        ])
        self.assertEqual(child1, parent[0])
        self.assertEqual(child2, parent[1])
        self.assertEqual(child1.dump() + child2.dump(), parent.dump())

    def test_oid_map_unmap(self):
        self.assertEqual('abc', MyOids.map('1.2.3'))
        self.assertEqual('def', MyOids.map('4.5.6'))
        self.assertEqual('7.8.9', MyOids.map('7.8.9'))
        self.assertEqual('1.2.3', MyOids.unmap('abc'))
        self.assertEqual('4.5.6', MyOids.unmap('def'))
        self.assertEqual('7.8.9', MyOids.unmap('7.8.9'))

        with self.assertRaises(ValueError):
            MyOids.unmap('no_such_mapping')

    def test_oid_dotted_native(self):
        self.assertEqual('abc', MyOids('1.2.3').native)
        self.assertEqual('1.2.3', MyOids('1.2.3').dotted)
        self.assertEqual('abc', MyOids('abc').native)
        self.assertEqual('1.2.3', MyOids('abc').dotted)

    def test_dump_set(self):
        st = SetTest({'two': 2, 'one': 1})
        self.assertEqual(b'1\x06\x81\x01\x01\x82\x01\x02', st.dump())

    def test_dump_set_of(self):
        st = SetOfTest([3, 2, 1])
        self.assertEqual(b'1\x09\x02\x01\x01\x02\x01\x02\x02\x01\x03', st.dump())

    def test_indefinite_length_octet_string(self):
        data = b'$\x80\x04\x02\x01\x01\x04\x01\x01\x00\x00'
        a = core.OctetString.load(data)
        self.assertEqual(b'\x01\x01\x01', a.native)
        self.assertEqual(b'\x01\x01\x01', a.__bytes__())
        self.assertEqual(1, a.method)
        # Test copying moves internal state
        self.assertEqual(a._bytes, a.copy()._bytes)

    def test_indefinite_length_octet_string_2(self):
        data = b'$\x80\x04\r\x8d\xff\xf0\x98\x076\xaf\x93nB:\xcf\xcc\x04\x15' \
            b'\x92w\xf7\xf0\xe4y\xff\xc7\xdc3\xb2\xd0={\x1a\x18mDr\xaaI\x00\x00'
        a = core.OctetString.load(data)
        self.assertEqual(
            b'\x8d\xff\xf0\x98\x076\xaf\x93nB:\xcf\xcc\x92w\xf7\xf0\xe4y\xff\xc7\xdc3\xb2\xd0={\x1a\x18mDr\xaaI',
            a.native
        )

    def test_nested_indefinite_length_octet_string(self):
        data = b'\x24\x80\x24\x80\x24\x80\x04\x00\x00\x00\x00\x00\x00\x00'
        a = core.load(data)
        self.assertEqual(b'', a.native)
        self.assertEqual(b'', a.__bytes__())
        self.assertEqual(1, a.method)
        self.assertEqual(b'\x04\x00', a.dump(force=True))
        # Test copying moves internal state
        self.assertEqual(a._bytes, a.copy()._bytes)

    def test_indefinite_length_integer_octet_string(self):
        data = b'$\x80\x04\x02\x01\x01\x04\x01\x01\x00\x00'
        a = core.IntegerOctetString.load(data)
        self.assertEqual(65793, a.native)
        self.assertEqual(1, a.method)
        self.assertEqual(b'\x01\x01\x01', a.cast(core.OctetString).native)

    def test_indefinite_length_parsable_octet_string(self):
        data = b'$\x80\x04\x02\x04\x01\x04\x01\x01\x00\x00'
        a = core.ParsableOctetString.load(data)
        self.assertEqual(b'\x04\x01\x01', a.parsed.dump())
        self.assertEqual(b'\x04\x01\x01', a.__bytes__())
        self.assertEqual(1, a.method)
        self.assertEqual(b'\x01', a.parsed.native)
        self.assertEqual(b'\x01', a.native)
        self.assertEqual(b'\x04\x01\x01', a.cast(core.OctetString).native)
        # Test copying moves internal state
        self.assertEqual(a._bytes, a.copy()._bytes)
        self.assertEqual(a._parsed, a.copy()._parsed)

    def test_indefinite_length_utf8string(self):
        data = b'\x2C\x80\x0C\x02\x61\x62\x0C\x01\x63\x00\x00'
        a = core.UTF8String.load(data)
        self.assertEqual('abc', a.native)
        self.assertEqual('abc', a.__unicode__())
        self.assertEqual(1, a.method)
        # Ensure a forced re-encoding is proper DER
        self.assertEqual(b'\x0C\x03\x61\x62\x63', a.dump(force=True))
        # Test copying moves internal state
        self.assertEqual(a._unicode, a.copy()._unicode)

    def test_indefinite_length_bit_string(self):
        data = b'#\x80\x03\x02\x00\x01\x03\x02\x02\x04\x00\x00'
        a = core.BitString.load(data)
        self.assertEqual((0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1), a.native)
        self.assertEqual((0, 0), a.unused_bits)

        # Example from X.690 ยง8.6.4.2
        prim = core.BitString.load(b'\x03\x07\x04\x0A\x3B\x5F\x29\x1C\xD0')
        self.assertEqual((0, 0, 0, 0), prim.unused_bits)
        indef = core.BitString.load(b'\x23\x80\x03\x03\x00\x0a\x3b\x03\x05\x04\x5f\x29\x1c\xd0\x00\x00')
        self.assertEqual(prim.native, indef.native)
        self.assertEqual(core._int_to_bit_tuple(0x0A3B5F291CD, 44), indef.native)
        self.assertEqual((0, 0, 0, 0), indef.unused_bits)

        unused = core.BitString.load(b'\x23\x80\x03\x03\x00\x0a\x3b\x03\x05\x04\x5f\x29\x1c\xdd\x00\x00')
        self.assertEqual(indef.native, unused.native)
        self.assertEqual((1, 1, 0, 1), unused.unused_bits)

        unused.set(indef.native)
        self.assertEqual(indef.native, unused.native)
        self.assertEqual((0, 0, 0, 0), unused.unused_bits)

    def test_integer_bit_string(self):
        a = core.IntegerBitString.load(b'\x03\x02\x04\xcb')
        self.assertEqual(12, a.native)
        self.assertEqual((1, 0, 1, 1), a.unused_bits)

        b = a.copy()
        self.assertEqual(12, b.native)
        self.assertEqual((1, 0, 1, 1), b.unused_bits)

        a.set(56)
        self.assertEqual((), a.unused_bits)
        self.assertEqual(56, a.native)
        self.assertEqual(b'\x03\x02\x00\x38', a.dump())

        with self.assertRaises(TypeError):
            a.set('badtype')

        with self.assertRaises(ValueError):
            core.IntegerBitString(-1)

    def test_indefinite_length_integer_bit_string(self):
        data = b'#\x80\x03\x02\x00\x01\x03\x02\x00\x04\x00\x00'
        a = core.IntegerBitString.load(data)
        self.assertEqual(260, a.native)
        self.assertEqual((), a.unused_bits)

        a = core.IntegerBitString.load(b'\x23\x80\x00\x00')
        self.assertEqual(0, a.native)
        self.assertEqual((), a.unused_bits)

        a = core.IntegerBitString.load(b'\x23\x80\x03\x01\x00\x03\x03\x03\x03\x03\x00\x00')
        self.assertEqual(96, a.native)
        self.assertEqual((0, 1, 1), a.unused_bits)

        a.set(56)
        self.assertEqual((), a.unused_bits)
        self.assertEqual(56, a.native)
        self.assertEqual(b'\x03\x02\x00\x38', a.dump())

    @data('bit_string_error_values')
    def integer_bit_string_errors(self, enc_bytes):
        with self.assertRaises(ValueError):
            core.IntegerBitString.load(enc_bytes).native

    def test_octet_bit_string(self):
        a = core.OctetBitString.load(b'\x03\x02\x04\xcb')
        self.assertEqual(b'\xc0', a.native)
        self.assertEqual((1, 0, 1, 1), a.unused_bits)

        a.set(b'\x38')
        self.assertEqual((), a.unused_bits)
        self.assertEqual(b'\x38', a.native)
        self.assertEqual(b'\x03\x02\x00\x38', a.dump())

        with self.assertRaises(TypeError):
            a.set('badtype')

    def test_indefinite_length_octet_bit_string(self):
        data = b'#\x80\x03\x02\x00\x01\x03\x02\x00\x04\x00\x00'
        a = core.OctetBitString.load(data)
        self.assertEqual(b'\x01\x04', a.native)
        self.assertEqual(b'\x01\x04', a.__bytes__())
        # Test copying moves internal state
        self.assertEqual(a._bytes, a.copy()._bytes)

        # octet bit string with unused bits
        a = core.OctetBitString.load(b'\x23\x80\x03\x05\x05\x74\x65\x73\x74\x00\x00')
        self.assertEqual(b'\x74\x65\x73\x60', a.native)
        self.assertEqual((1, 0, 1, 0, 0), a.unused_bits)

        a.set(b'\x38')
        self.assertEqual((), a.unused_bits)
        self.assertEqual(b'\x38', a.native)
        self.assertEqual(b'\x03\x02\x00\x38', a.dump())

    @data('bit_string_error_values')
    def octet_bit_string_errors(self, enc_bytes):
        with self.assertRaises(ValueError):
            core.OctetBitString.load(enc_bytes).native

    def test_indefinite_length_parsable_octet_bit_string(self):
        data = b'#\x80\x03\x03\x00\x0C\x02\x03\x03\x00\x61\x62\x00\x00'
        a = core.ParsableOctetBitString.load(data)
        self.assertEqual(b'\x0C\x02\x61\x62', a.parsed.dump())
        self.assertEqual(b'\x0C\x02\x61\x62', a.__bytes__())
        self.assertEqual('ab', a.parsed.native)
        self.assertEqual('ab', a.native)
        # Test copying moves internal state
        self.assertEqual(a._bytes, a.copy()._bytes)
        self.assertEqual(a._parsed, a.copy()._parsed)

        with self.assertRaises(ValueError):
            # parsable octet bit string with unused bits
            core.ParsableOctetBitString.load(b'\x23\x80\x03\x03\x04\x02\x00\x03\x03\x04\x12\xa0\x00\x00').native

    def test_integer_octet_string(self):
        v = core.IntegerOctetString(10)
        self.assertEqual(10, v.native)

        with self.assertRaises(TypeError):
            core.IntegerOctetString('0')

        with self.assertRaises(ValueError):
            core.IntegerOctetString(-1)

    def test_explicit_application_tag(self):
        data = b'\x6a\x81\x03\x02\x01\x00'
        ati = ApplicationTaggedInteger.load(data)

        self.assertEqual(((1, 10),), ati.explicit)
        self.assertEqual(0, ati.class_)
        self.assertEqual(2, ati.tag)
        self.assertEqual(0, ati.native)

        # The output encoding is DER, whereas the input was not, so
        # the length encoding changes from long form to short form
        self.assertEqual(b'\x6a\x03\x02\x01\x00', ati.dump(force=True))

    def test_required_field(self):
        with self.assertRaisesRegex(ValueError, '"id" is missing from structure'):
            Seq({'value': core.Integer(5)}).dump()

    def test_explicit_application_tag_nested(self):
        # tag = [APPLICATION 10] constructed; length = 18
        #   OUTER SEQUENCE: tag = [UNIVERSAL 16] constructed; length = 16
        #     outernumber : tag = [11] constructed; length = 3
        #       INTEGER: tag = [UNIVERSAL 2] primitive; length = 1
        #         23
        #     inner : tag = [12] constructed; length = 9
        #       tag = [APPLICATION 20] constructed; length = 7
        #         INNER SEQUENCE: tag = [UNIVERSAL 16] constructed; length = 5
        #           innernumber : tag = [21] constructed; length = 3
        #             INTEGER: tag = [UNIVERSAL 2] primitive; length = 1
        #               42
        der = (
            b'\x6A\x12\x30\x10\xAB\x03\x02\x01\x17\xAC\x09\x74'
            b'\x07\x30\x05\xB5\x03\x02\x01\x2A'
        )

        ato = ApplicationTaggedOuter.load(der)
        self.assertEqual(((1, 10),), ato.explicit)
        self.assertEqual(0, ato.class_)
        self.assertEqual(16, ato.tag)
        self.assertEqual(1, ato.method)

        onum = ato['outernumber']
        self.assertEqual(((2, 11),), onum.explicit)
        self.assertEqual(0, onum.class_)
        self.assertEqual(2, onum.tag)
        self.assertEqual(0, onum.method)
        self.assertEqual(23, onum.native)

        ati = ato['inner']
        self.assertEqual(((1, 20), (2, 12)), ati.explicit)
        self.assertEqual(0, ati.class_)
        self.assertEqual(16, ati.tag)
        self.assertEqual(util.OrderedDict([('innernumber', 42)]), ati.native)

        inum = ati['innernumber']
        self.assertEqual(((2, 21),), inum.explicit)
        self.assertEqual(0, inum.class_)
        self.assertEqual(2, inum.tag)
        self.assertEqual(0, inum.method)
        self.assertEqual(42, inum.native)

        self.assertEqual(der, ato.dump(force=True))

    def test_sequence_choice_field_by_tuple(self):
        val = ExplicitField({'field': ('one', 32)})
        self.assertEqual('one', val['field'].name)
        self.assertEqual(32, val['field'].chosen.native)

    def test_sequence_choice_field_by_dict(self):
        val = ExplicitField({'field': {'two': 32}})
        self.assertEqual('two', val['field'].name)
        self.assertEqual(32, val['field'].chosen.native)

    def test_nested_explicit_tag_choice(self):
        # Explicitly tagged values have a _header that contains
        # the explicit tag and the header for the contained value.
        # When parsing nested Choice values, it is necessary to not pull
        # up the next Choice value's header, since Choice values
        # themselves don't have their own header and it will result in
        # duplication.
        data = b'\x30\x09\x03\x01\x00\xa0\x04\xa2\x02\x80\x00'
        image_data = SpcPeImageData.load(data, strict=True)
        self.assertEqual(data[2:5],  image_data['flags'].dump())
        self.assertEqual(data[5:11],  image_data['file'].dump())
        self.assertEqual(data[5:7],  image_data['file']._header)
        self.assertEqual(data[7:11],  image_data['file'].chosen.dump())
        self.assertEqual(data[7:9],  image_data['file'].chosen._header)
        self.assertEqual(data[9:11],  image_data['file'].chosen.chosen.dump())
        self.assertEqual(data[9:11],  image_data['file'].chosen.chosen._header)

        image_data2 = SpcPeImageData.load(data, strict=True)
        self.assertEqual(data[2:5],  image_data2['flags'].dump(True))
        self.assertEqual(data[5:11],  image_data2['file'].dump(True))
        self.assertEqual(data[5:7],  image_data2['file']._header)
        self.assertEqual(data[7:11],  image_data2['file'].chosen.dump(True))
        self.assertEqual(data[7:9],  image_data2['file'].chosen._header)
        self.assertEqual(data[9:11],  image_data2['file'].chosen.chosen.dump(True))
        self.assertEqual(data[9:11],  image_data2['file'].chosen.chosen._header)

    def test_choice_dump_header_native(self):
        s = SpcString({'unicode': 'test'})
        self.assertEqual(b'\x80\x08\x00t\x00e\x00s\x00t', s.dump())
        self.assertEqual(b'', s._header)
        self.assertEqual('test', s.native)
        self.assertEqual(b'\x80\x08', s.chosen._header)
        self.assertEqual('test', s.chosen.native)

        link = SpcLink('file', {'unicode': 'test'})
        self.assertEqual(b'\xa2\x0a\x80\x08\x00t\x00e\x00s\x00t', link.dump())
        self.assertEqual(b'', link._header)
        self.assertEqual('test', link.native)
        self.assertEqual(b'\xa2\x0a', link.chosen._header)
        self.assertEqual('test', link.chosen.native)
        self.assertEqual(b'\x80\x08', link.chosen.chosen._header)
        self.assertEqual('test', link.chosen.chosen.native)

    def test_parse_broken_sequence_fields_repeatedly(self):
        s = Seq.load(b'\x30\x06\x88\x00\x00\x00\x00\x00')
        with self.assertRaises(ValueError):
            s.native
        with self.assertRaises(ValueError):
            s.native

    def test_parse_broken_sequenceof_children_repeatedly(self):
        s = SequenceOfInts.load(b'\x30\x06\x88\x00\x00\x00\x00\x00')
        with self.assertRaises(ValueError):
            s.native
        with self.assertRaises(ValueError):
            s.native

    def test_wrong_asn1value(self):
        with self.assertRaises(TypeError):
            Seq({
                'id': core.Integer(1),
                'value': 1
            })

    def test_wrong_asn1value2(self):
        with self.assertRaises(TypeError):
            CopySeq({
                'name': core.UTF8String('Test'),
                'pair': core.Integer(1)
            })

    def test_wrong_asn1value3(self):
        with self.assertRaises(TypeError):
            NestSeqAny({
                'id': '2.3.4.5',
                'value': core.Integer(1)
            })

    def test_wrong_asn1value4(self):
        with self.assertRaises(TypeError):
            NestSeqExplicit({
                'id': '2.3.4.5',
                'value': core.Integer(1)
            })

    def test_integer_octet_string_encoded_width(self):
        a = core.IntegerOctetString(1)
        self.assertEqual(1, a.native)
        self.assertEqual(b'\x04\x01\x01', a.dump())

        b = core.IntegerOctetString(1)
        b.set_encoded_width(4)
        self.assertEqual(1, b.native)
        self.assertEqual(b'\x04\x04\x00\x00\x00\x01', b.dump())

    @staticmethod
    def object_identifier_info():
        return (
            ("0.0", b"\x06\x01\x00"),
            ("0.39", b"\x06\x01\x27"),
            ("1.0", b"\x06\x01\x28"),
            ("1.39", b"\x06\x01\x4f"),
            ("2.0", b"\x06\x01\x50"),
            ("2.39", b"\x06\x01\x77"),
            ("2.100.3", b"\x06\x03\x81\x34\x03"),
            ("2.16.840.1.113730.1.1", b"\x06\x09\x60\x86\x48\x01\x86\xf8\x42\x01\x01"),
        )

    @data('object_identifier_info')
    def object_identifier(self, native, der_bytes):
        oid = core.ObjectIdentifier(native)
        self.assertEqual(der_bytes, oid.dump())
        self.assertEqual(native, core.ObjectIdentifier.load(der_bytes).native)

    def test_broken_object_identifier(self):
        with self.assertRaisesRegex(ValueError, "First arc must be "):
            core.ObjectIdentifier("3.4.5")

        with self.assertRaisesRegex(ValueError, "Second arc must be "):
            core.ObjectIdentifier("1.100.1000")

        with self.assertRaisesRegex(ValueError, "Second arc must be "):
            core.ObjectIdentifier("0.40")