Updating support for the BigInteger primitive

This change fixes various bugs with the original BigInteger
implementation, adding in a working version of the primitive. A full
unit test suite is included.
This commit is contained in:
Peter Hamilton 2015-08-27 09:00:00 -04:00
parent 9c7edd65d2
commit 89a6e21a06
2 changed files with 359 additions and 271 deletions

View File

@ -15,6 +15,7 @@
import logging import logging
import six import six
import struct
import sys import sys
from struct import pack, unpack from struct import pack, unpack
@ -357,105 +358,131 @@ class LongInteger(Base):
class BigInteger(Base): class BigInteger(Base):
BLOCK_SIZE = 8 """
SHIFT_SIZE = 64 An encodeable object representing a big integer value.
def __init__(self, value=None, tag=Tags.DEFAULT): A BigInteger is one of the KMIP primitive object types. It is encoded as
a signed, big-endian, integer of arbitrary size. For more information, see
Section 9.1 of the KMIP 1.1 specification.
"""
def __init__(self, value=0, tag=Tags.DEFAULT):
super(BigInteger, self).__init__(tag, type=Types.BIG_INTEGER) super(BigInteger, self).__init__(tag, type=Types.BIG_INTEGER)
self.value = value self.value = value
if self.value is not None:
self.real_length = utils.count_bytes(self.value)
self.padding_length = self.BLOCK_SIZE - (self.length %
self.BLOCK_SIZE)
if self.padding_length == self.BLOCK_SIZE:
self.padding_length = 0
else:
self.length = None
self.padding_length = None
self.validate()
def read_value(self, istream):
if (self.length < self.BLOCK_SIZE) or (self.length % self.BLOCK_SIZE):
raise errors.InvalidLengthError(BigInteger.__name__,
('multiple'
'of {0}'.format(self.BLOCK_SIZE)),
self.length)
self.value = 0
num_blocks = self.length / self.BLOCK_SIZE
# Read first block as signed data
self.value = unpack('!q', str(istream.read(self.BLOCK_SIZE)))[0]
# Shift current value and add on next unsigned block
for _ in range(num_blocks - 1):
self.value = self.value << self.SHIFT_SIZE
stream_data = istream.read(self.BLOCK_SIZE)
self.value += unpack('!Q', stream_data)[0]
self.validate() self.validate()
def read(self, istream): def read(self, istream):
"""
Read the encoding of the BigInteger from the input stream.
Args:
istream (stream): A buffer containing the encoded bytes of the
value of a BigInteger. Usually a BytearrayStream object.
Required.
Raises:
InvalidPrimitiveLength: if the big integer encoding read in has
an invalid encoded length.
"""
super(BigInteger, self).read(istream) super(BigInteger, self).read(istream)
self.read_value(istream)
def write_value(self, ostream): # Check for a valid length before even trying to parse the value.
# 1. Determine the sign of the value (+/-); save it. if self.length % 8:
# 2. Extend hex of value with 0s until encoding is right size (8x). raise exceptions.InvalidPrimitiveLength(
# 3. Write out each block of the encoding as signed, 2s complement: "invalid big integer length read; "
# pack('!q', sign * block) "expected: multiple of 8, observed: {0}".format(self.length))
# Determine sign for padding sign = 1
pad_byte = 0x00 binary = ''
pad_nybl = 0x0
if self.value < 0: # Read the value byte by byte and convert it into binary, padding each
pad_byte = 0xff # byte as needed.
pad_nybl = 0xf for _ in range(self.length):
byte = struct.unpack('!B', istream.read(1))[0]
bits = "{0:b}".format(byte)
pad = len(bits) % 8
if pad:
bits = ('0' * (8 - pad)) + bits
binary += bits
# Compose padding bytes # If the value is negative, convert via two's complement.
pad = '' if binary[0] == '1':
for _ in range(self.padding_length): sign = -1
pad += hex(pad_byte)[2:] binary = binary.replace('1', 'i')
binary = binary.replace('0', '1')
binary = binary.replace('i', '0')
str_rep = hex(self.value).rstrip("Ll")[2:] pivot = binary.rfind('0')
if len(str_rep) % 2: binary = binary[0:pivot] + '1' + ('0' * len(binary[pivot + 1:]))
pad += hex(pad_nybl)[2]
# Compose value for block-based write # Convert the value back to an integer and reapply the sign.
str_rep = pad + str_rep self.value = int(binary, 2) * sign
num_blocks = len(str_rep) / self.BLOCK_SIZE
# Write first block as signed data
block = int(str_rep[0:self.BLOCK_SIZE], 16)
ostream.write(pack('!q', block))
# Write remaining blocks as unsigned data
for i in range(1, num_blocks):
block = str_rep[(self.BLOCK_SIZE * i):(self.BLOCK_SIZE * (i + 1))]
block = int(block, 16)
ostream.write(pack('!Q', block))
def write(self, ostream): def write(self, ostream):
"""
Write the encoding of the BigInteger to the output stream.
Args:
ostream (Stream): A buffer to contain the encoded bytes of a
BigInteger object. Usually a BytearrayStream object.
Required.
"""
# Convert the value to binary and pad it as needed.
binary = "{0:b}".format(abs(self.value))
binary = ("0" * (64 - (len(binary) % 64))) + binary
# If the value is negative, convert via two's complement.
if self.value < 0:
binary = binary.replace('1', 'i')
binary = binary.replace('0', '1')
binary = binary.replace('i', '0')
pivot = binary.rfind('0')
binary = binary[0:pivot] + '1' + ('0' * len(binary[pivot + 1:]))
# Convert each byte to hex and build the hex string for the value.
hexadecimal = b''
for i in range(0, len(binary), 8):
byte = binary[i:i + 8]
byte = int(byte, 2)
hexadecimal += struct.pack('!B', byte)
self.length = len(hexadecimal)
super(BigInteger, self).write(ostream) super(BigInteger, self).write(ostream)
self.write_value(ostream) ostream.write(hexadecimal)
def validate(self): def validate(self):
self.__validate() """
Verify that the value of the BigInteger is valid.
def __validate(self): Raises:
TypeError: if the value is not of type int or long
"""
if self.value is not None: if self.value is not None:
data_type = type(self.value) if not isinstance(self.value, six.integer_types):
if data_type not in six.integer_types: raise TypeError('expected (one of): {0}, observed: {1}'.format(
raise errors.StateTypeError( six.integer_types, type(self.value)))
BigInteger.__name__, "{0}".format(six.integer_types),
data_type) def __repr__(self):
num_bytes = utils.count_bytes(self.length) return "BigInteger(value={0}, tag={1})".format(self.value, self.tag)
if num_bytes > self.LENGTH_SIZE:
raise errors.StateOverflowError( def __str__(self):
BigInteger.__name__, 'length', self.LENGTH_SIZE, return str(self.value)
num_bytes)
def __eq__(self, other):
if isinstance(other, BigInteger):
if self.value == other.value:
return True
else:
return False
else:
return NotImplemented
def __ne__(self, other):
if isinstance(other, BigInteger):
return not self.__eq__(other)
else:
return NotImplemented
class Enumeration(Integer): class Enumeration(Integer):

View File

@ -15,8 +15,7 @@
import testtools import testtools
from kmip.core import enums from kmip.core import exceptions
from kmip.core import errors
from kmip.core import primitives from kmip.core import primitives
from kmip.core import utils from kmip.core import utils
@ -25,208 +24,270 @@ class TestBigInteger(testtools.TestCase):
def setUp(self): def setUp(self):
super(TestBigInteger, self).setUp() super(TestBigInteger, self).setUp()
self.stream = utils.BytearrayStream()
self.max_byte_long = 18446744073709551615 # Encodings and values taken from Sections 5.1, 13.3 and 18.2 of the
self.max_long = 9223372036854775807 # KMIP 1.1 testing documentation.
self.bad_value = ( self.value_positive = int(
'Bad primitives.BigInteger.{0} after init: expected {1}, ' '74570697368583857894612671217453076717255131155396275504564761583'
'received {2}') '15899148268876158582639566401239193216235126746176682996459367959'
self.bad_write = ( '36793366865165780165066709295778050045731105353780121783233185565'
'Bad primitives.BigInteger write: expected {0} bytes, ' '36420486996200625818559496541368747791032257508332162004121562017'
'received {1} bytes') '72772159096834586599791505043949123930975157363117571140205992199'
self.bad_encoding = ( '59827555693853730430222361950476764952992840295849053634702315874'
'Bad primitives.BigInteger write: encoding mismatch') '87536235568284292445148693873502200712082861995083783995720224553'
self.bad_read = ( '38838078028390162249415071016709848797960500969432640102143437177'
'Bad primitives.BigInteger.value read: expected {0}, ' '60785867099769472998343832254180691121895373077720157164352949735'
'received {1}') '8482684822484513735382434823977')
self.value_negative = -1000
self.encoding_zero = utils.BytearrayStream(
b'\x42\x00\x00\x04\x00\x00\x00\x08\x00\x00\x00\x00\x00\x00\x00'
b'\x00')
self.encoding_positive = utils.BytearrayStream(
b'\x42\x00\x00\x04\x00\x00\x01\x00\x3B\x12\x45\x5D\x53\xC1\x81\x65'
b'\x16\xC5\x18\x49\x3F\x63\x98\xAA\xFA\x72\xB1\x7D\xFA\x89\x4D\xB8'
b'\x88\xA7\xD4\x8C\x0A\x47\xF6\x25\x79\xA4\xE6\x44\xF8\x6D\xA7\x11'
b'\xFE\xC8\x50\xCD\xD9\xDB\xBD\x17\xF6\x9A\x44\x3D\x2E\xC1\xDD\x60'
b'\xD3\xC6\x18\xFA\x74\xCD\xE5\xFD\xAF\xAB\xD6\xBA\xA2\x6E\xB0\xA3'
b'\xAD\xB4\xDE\xF6\x48\x0F\xB1\x21\x8C\xD3\xB0\x83\xE2\x52\xE8\x85'
b'\xB6\xF0\x72\x9F\x98\xB2\x14\x4D\x2B\x72\x29\x3E\x1B\x11\xD7\x33'
b'\x93\xBC\x41\xF7\x5B\x15\xEE\x3D\x75\x69\xB4\x99\x5E\xD1\xA1\x44'
b'\x25\xDA\x43\x19\xB7\xB2\x6B\x0E\x8F\xEF\x17\xC3\x75\x42\xAE\x5C'
b'\x6D\x58\x49\xF8\x72\x09\x56\x7F\x39\x25\xA4\x7B\x01\x6D\x56\x48'
b'\x59\x71\x7B\xC5\x7F\xCB\x45\x22\xD0\xAA\x49\xCE\x81\x6E\x5B\xE7'
b'\xB3\x08\x81\x93\x23\x6E\xC9\xEF\xFF\x14\x08\x58\x04\x5B\x73\xC5'
b'\xD7\x9B\xAF\x38\xF7\xC6\x7F\x04\xC5\xDC\xF0\xE3\x80\x6A\xD9\x82'
b'\xD1\x25\x90\x58\xC3\x47\x3E\x84\x71\x79\xA8\x78\xF2\xC6\xB3\xBD'
b'\x96\x8F\xB9\x9E\xA4\x6E\x91\x85\x89\x2F\x36\x76\xE7\x89\x65\xC2'
b'\xAE\xD4\x87\x7B\xA3\x91\x7D\xF0\x7C\x5E\x92\x74\x74\xF1\x9E\x76'
b'\x4B\xA6\x1D\xC3\x8D\x63\xBF\x29')
self.encoding_negative = utils.BytearrayStream(
b'\x42\x00\x00\x04\x00\x00\x00\x08\xFF\xFF\xFF\xFF\xFF\xFF\xFC'
b'\x18')
self.encoding_bad_length = utils.BytearrayStream(
b'\x42\x00\x00\x04\x00\x00\x01\x01\x3B\x12\x45\x5D\x53\xC1\x81\x65'
b'\x16\xC5\x18\x49\x3F\x63\x98\xAA\xFA\x72\xB1\x7D\xFA\x89\x4D\xB8'
b'\x88\xA7\xD4\x8C\x0A\x47\xF6\x25\x79\xA4\xE6\x44\xF8\x6D\xA7\x11'
b'\xFE\xC8\x50\xCD\xD9\xDB\xBD\x17\xF6\x9A\x44\x3D\x2E\xC1\xDD\x60'
b'\xD3\xC6\x18\xFA\x74\xCD\xE5\xFD\xAF\xAB\xD6\xBA\xA2\x6E\xB0\xA3'
b'\xAD\xB4\xDE\xF6\x48\x0F\xB1\x21\x8C\xD3\xB0\x83\xE2\x52\xE8\x85'
b'\xB6\xF0\x72\x9F\x98\xB2\x14\x4D\x2B\x72\x29\x3E\x1B\x11\xD7\x33'
b'\x93\xBC\x41\xF7\x5B\x15\xEE\x3D\x75\x69\xB4\x99\x5E\xD1\xA1\x44'
b'\x25\xDA\x43\x19\xB7\xB2\x6B\x0E\x8F\xEF\x17\xC3\x75\x42\xAE\x5C'
b'\x6D\x58\x49\xF8\x72\x09\x56\x7F\x39\x25\xA4\x7B\x01\x6D\x56\x48'
b'\x59\x71\x7B\xC5\x7F\xCB\x45\x22\xD0\xAA\x49\xCE\x81\x6E\x5B\xE7'
b'\xB3\x08\x81\x93\x23\x6E\xC9\xEF\xFF\x14\x08\x58\x04\x5B\x73\xC5'
b'\xD7\x9B\xAF\x38\xF7\xC6\x7F\x04\xC5\xDC\xF0\xE3\x80\x6A\xD9\x82'
b'\xD1\x25\x90\x58\xC3\x47\x3E\x84\x71\x79\xA8\x78\xF2\xC6\xB3\xBD'
b'\x96\x8F\xB9\x9E\xA4\x6E\x91\x85\x89\x2F\x36\x76\xE7\x89\x65\xC2'
b'\xAE\xD4\x87\x7B\xA3\x91\x7D\xF0\x7C\x5E\x92\x74\x74\xF1\x9E\x76'
b'\x4B\xA6\x1D\xC3\x8D\x63\xBF\x29')
def tearDown(self): def tearDown(self):
super(TestBigInteger, self).tearDown() super(TestBigInteger, self).tearDown()
def test_big_integer(self): def test_init(self):
self.skip('primitives.BigInteger implementation incomplete') """
i = primitives.BigInteger(0) Test that a BigInteger can be instantiated.
"""
big_int = primitives.BigInteger(1)
self.assertEqual(1, big_int.value)
self.assertEqual(0, i.value, def test_init_unset(self):
self.bad_value.format('value', 0, i.value)) """
self.assertEqual(1, i.length, Test that a BigInteger can be instantiated with no input.
self.bad_value.format('length', 1, i.length)) """
self.assertEqual(i.BLOCK_SIZE - 1, i.padding_length, big_int = primitives.BigInteger()
self.bad_value.format('padding_length', self.assertEqual(0, big_int.value)
i.BLOCK_SIZE - 1,
i.padding_length))
def test_big_integer_unset(self): def test_init_big_positive(self):
self.skip('primitives.BigInteger implementation incomplete') """
i = primitives.BigInteger() Test that a BigInteger can be instantiated with large positive input.
"""
big_int = primitives.BigInteger(self.value_positive)
self.assertEqual(self.value_positive, big_int.value)
self.assertEqual(None, i.value, def test_init_negative(self):
self.bad_value.format('value', None, i.value)) """
self.assertEqual(None, i.length, Test that a BigInteger can be instantiated with negative input.
self.bad_value.format('length', None, i.length)) """
self.assertEqual(None, i.padding_length, big_int = primitives.BigInteger(self.value_negative)
self.bad_value.format('padding_length', None, self.assertEqual(self.value_negative, big_int.value)
i.padding_length))
def test_validate_on_valid(self): def test_validate_on_invalid(self):
self.skip('primitives.BigInteger implementation incomplete') """
i = primitives.BigInteger() Test that a TypeError is thrown on input of invalid type (e.g., str).
i.value = 0 """
i.length = i.BLOCK_SIZE self.assertRaises(TypeError, primitives.BigInteger, 'invalid')
i.padding_length = 0
# Check no exception thrown
i.validate()
def test_validate_on_valid_long(self):
self.skip('primitives.BigInteger implementation incomplete')
i = primitives.BigInteger()
i.value = self.max_long + 1
i.length = i.BLOCK_SIZE
i.padding_length = 0
# Check no exception thrown
i.validate()
def test_validate_on_valid_unset(self):
self.skip('primitives.BigInteger implementation incomplete')
i = primitives.BigInteger()
# Check no exception thrown
i.validate()
def test_validate_on_invalid_type(self):
self.skip('primitives.BigInteger implementation incomplete')
i = primitives.BigInteger()
i.value = 'test'
self.assertRaises(errors.StateTypeError, i.validate)
def test_write(self):
self.skip('primitives.BigInteger implementation incomplete')
encoding = (
b'\x42\x00\x01\x04\x00\x00\x00\x08\x00\x00\x00\x00\x00\x00\x00'
b'\x01')
i = primitives.BigInteger(1)
i.TAG = enums.Tags.ACTIVATION_DATE
i.write(self.stream)
result = self.stream.read()
len_exp = len(encoding)
len_rcv = len(result)
self.assertEqual(len_exp, len_rcv, self.bad_write.format(len_exp,
len_rcv))
self.assertEqual(encoding, result, self.bad_encoding)
def test_write_zero(self):
self.skip('primitives.BigInteger implementation incomplete')
encoding = (
b'\x42\x00\x01\x04\x00\x00\x00\x08\x00\x00\x00\x00\x00\x00\x00'
b'\x00')
i = primitives.BigInteger(0)
i.TAG = enums.Tags.ACTIVATION_DATE
i.write(self.stream)
result = self.stream.read()
len_exp = len(encoding)
len_rcv = len(result)
self.assertEqual(len_exp, len_rcv, self.bad_write.format(len_exp,
len_rcv))
self.assertEqual(encoding, result, self.bad_encoding)
def test_write_max_positive_value(self):
self.skip('primitives.BigInteger implementation incomplete')
encoding = (
b'\x42\x00\x01\x04\x00\x00\x00\x08\x7f\xff\xff\xff\xff\xff\xff'
b'\xff')
i = primitives.BigInteger(self.max_long)
i.TAG = enums.Tags.ACTIVATION_DATE
i.write(self.stream)
result = self.stream.read()
len_exp = len(encoding)
len_rcv = len(result)
self.assertEqual(len_exp, len_rcv, self.bad_write.format(len_exp,
len_rcv))
self.assertEqual(encoding, result, self.bad_encoding)
def test_write_min_negative_value(self):
self.skip('primitives.BigInteger implementation incomplete')
encoding = (
b'\x42\x00\x01\x04\x00\x00\x00\x08\xff\xff\xff\xff\xff\xff\xff'
b'\xff')
i = primitives.BigInteger(-1)
i.TAG = enums.Tags.ACTIVATION_DATE
i.write(self.stream)
result = self.stream.read()
len_exp = len(encoding)
len_rcv = len(result)
self.assertEqual(len_exp, len_rcv, self.bad_write.format(len_exp,
len_rcv))
self.assertEqual(encoding, result, self.bad_encoding)
def test_read(self):
self.skip('primitives.BigInteger implementation incomplete')
encoding = (
b'\x42\x00\x01\x04\x00\x00\x00\x08\x00\x00\x00\x00\x00\x00\x00'
b'\x01')
self.stream = utils.BytearrayStream(encoding)
i = primitives.BigInteger()
i.TAG = enums.Tags.ACTIVATION_DATE
i.read(self.stream)
self.assertEqual(1, i.value, self.bad_read.format(1, i.value))
def test_read_zero(self): def test_read_zero(self):
self.skip('primitives.BigInteger implementation incomplete') """
encoding = ( Test that a BigInteger representing the value 0 can be read from a
b'\x42\x00\x01\x04\x00\x00\x00\x08\x00\x00\x00\x00\x00\x00\x00' byte stream.
b'\x00') """
self.stream = utils.BytearrayStream(encoding) big_int = primitives.BigInteger()
i = primitives.BigInteger() big_int.read(self.encoding_zero)
i.TAG = enums.Tags.ACTIVATION_DATE self.assertEqual(0, big_int.value)
i.read(self.stream)
self.assertEqual(0, i.value, self.bad_read.format(0, i.value)) def test_read_positive(self):
"""
Test that a BigInteger representing a big positive value can be read
from a byte stream.
"""
big_int = primitives.BigInteger()
big_int.read(self.encoding_positive)
self.assertEqual(self.value_positive, big_int.value)
def test_read_max_positive_value(self): def test_read_negative(self):
self.skip('primitives.BigInteger implementation incomplete') """
encoding = ( Test that a BigInteger representing a negative value can be read from
b'\x42\x00\x01\x04\x00\x00\x00\x08\x7f\xff\xff\xff\xff\xff\xff' a byte stream.
b'\xff') """
self.stream = utils.BytearrayStream(encoding) big_int = primitives.BigInteger()
i = primitives.BigInteger() big_int.read(self.encoding_negative)
i.TAG = enums.Tags.ACTIVATION_DATE self.assertEqual(self.value_negative, big_int.value)
i.read(self.stream)
self.assertEqual(self.max_long, i.value,
self.bad_read.format(1, i.value))
def test_read_min_negative_value(self):
self.skip('primitives.BigInteger implementation incomplete')
encoding = (
b'\x42\x00\x01\x04\x00\x00\x00\x08\xff\xff\xff\xff\xff\xff\xff'
b'\xff')
self.stream = utils.BytearrayStream(encoding)
i = primitives.BigInteger()
i.TAG = enums.Tags.ACTIVATION_DATE
i.read(self.stream)
self.assertEqual(-1, i.value,
self.bad_read.format(1, i.value))
def test_read_on_invalid_length(self): def test_read_on_invalid_length(self):
self.skip('primitives.BigInteger implementation incomplete') """
encoding = ( Test that an InvalidPrimitiveLength exception is thrown when attempting
b'\x42\x00\x01\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' to decode a BigInteger with an invalid length.
b'\x00') """
self.stream = utils.BytearrayStream(encoding) big_int = primitives.BigInteger()
i = primitives.BigInteger() self.assertRaises(
i.TAG = enums.Tags.ACTIVATION_DATE exceptions.InvalidPrimitiveLength, big_int.read,
self.encoding_bad_length)
self.assertRaises(errors.InvalidLengthError, i.read, self.stream) def test_write_zero(self):
"""
Test that a BigInteger representing the value 0 can be read written to
a byte stream.
"""
stream = utils.BytearrayStream()
big_int = primitives.BigInteger()
big_int.write(stream)
self.assertEqual(self.encoding_zero, stream)
def test_write_positive(self):
"""
Test that a BigInteger representing a big positive value can be written
to a byte stream.
"""
stream = utils.BytearrayStream()
big_int = primitives.BigInteger(self.value_positive)
big_int.write(stream)
self.assertEqual(self.encoding_positive, stream)
def test_write_negative(self):
"""
Test that a BigInteger representing a negative value can be written to
a byte stream.
"""
stream = utils.BytearrayStream()
big_int = primitives.BigInteger(self.value_negative)
big_int.write(stream)
self.assertEqual(self.encoding_negative, stream)
def test_repr(self):
"""
Test that the representation of a BigInteger is formatted properly.
"""
long_int = primitives.BigInteger()
value = "value={0}".format(long_int.value)
tag = "tag={0}".format(long_int.tag)
self.assertEqual(
"BigInteger({0}, {1})".format(value, tag), repr(long_int))
def test_str(self):
"""
Test that the string representation of a BigInteger is formatted
properly.
"""
self.assertEqual("0", str(primitives.BigInteger()))
def test_equal_on_equal(self):
"""
Test that the equality operator returns True when comparing two
BigIntegers.
"""
a = primitives.BigInteger(1)
b = primitives.BigInteger(1)
self.assertTrue(a == b)
self.assertTrue(b == a)
def test_equal_on_equal_and_empty(self):
"""
Test that the equality operator returns True when comparing two
BigIntegers.
"""
a = primitives.BigInteger()
b = primitives.BigInteger()
self.assertTrue(a == b)
self.assertTrue(b == a)
def test_equal_on_not_equal(self):
"""
Test that the equality operator returns False when comparing two
BigIntegers with different values.
"""
a = primitives.BigInteger(1)
b = primitives.BigInteger(2)
self.assertFalse(a == b)
self.assertFalse(b == a)
def test_equal_on_type_mismatch(self):
"""
Test that the equality operator returns False when comparing a
BigInteger to a non-BigInteger object.
"""
a = primitives.BigInteger()
b = 'invalid'
self.assertFalse(a == b)
self.assertFalse(b == a)
def test_not_equal_on_equal(self):
"""
Test that the inequality operator returns False when comparing
two BigIntegers with the same values.
"""
a = primitives.BigInteger(1)
b = primitives.BigInteger(1)
self.assertFalse(a != b)
self.assertFalse(b != a)
def test_not_equal_on_equal_and_empty(self):
"""
Test that the inequality operator returns False when comparing
two BigIntegers.
"""
a = primitives.BigInteger()
b = primitives.BigInteger()
self.assertFalse(a != b)
self.assertFalse(b != a)
def test_not_equal_on_not_equal(self):
"""
Test that the inequality operator returns True when comparing two
BigIntegers with different values.
"""
a = primitives.BigInteger(1)
b = primitives.BigInteger(2)
self.assertTrue(a != b)
self.assertTrue(b != a)
def test_not_equal_on_type_mismatch(self):
"""
Test that the inequality operator returns True when comparing a
BigInteger to a non-BigInteger object.
"""
a = primitives.BigInteger()
b = 'invalid'
self.assertTrue(a != b)
self.assertTrue(b != a)