def test_string_ascii(self): """ Test basic put_string and get_string behavior, using ASCII strings. """ bitstream = BitStream() string1 = "Test string " # 12 chars/bytes string2 = "using only ASCII characters (0-126):\n\t" # 38 chars/bytes string3 = "Hello World!" # 12 chars/bytes # Store our message in 3 writes bitstream.put_string(string1) bitstream.put_string(string2) bitstream.put_string(string3) # Sanity check: self.assertEquals(bitstream.get_length(), (12 + 38 + 12) * 8) self.assertEquals(bitstream.get_current_pos(), (12 + 38 + 12) * 8) # Retrieve our message in 2 reads bitstream.seek(0) self.assertEquals( bitstream.get_string(29 * 8), # read 29 bytes "Test string using only ASCII ") self.assertEquals( bitstream.get_string(33 * 8), # read 33 bytes "characters (0-126):\n\tHello World!")
def test_get_num_beyond_eos(self): """ Test than trying to read beyond the end of the stream raises an exception when calling get_num(...). """ bitstream = BitStream() # Store a 64-bit integer in the stream. num = int( '00000000000000110101011001010011' '00101100001000101000100110101111', 2) bitstream.put_num(num, 64) # Check that trying to read 65 bits from the beginning of the stream # raises NotEnoughBitsInStreamError bitstream.seek(0) self.assertRaises(NotEnoughBitsInStreamError, bitstream.get_num, 65) # An invalid read call should not move the position indicator. self.assertEquals(bitstream.get_current_pos(), 0) # Check that trying to read 33 bits from the middle of the stream # (pos = 32) raises NotEnoughBitsInStreamError bitstream.seek(32) self.assertRaises(NotEnoughBitsInStreamError, bitstream.get_num, 33) # An invalid read call should not move the position indicator. self.assertEquals(bitstream.get_current_pos(), 32) # Check that trying to read a single bit while at the end of the stream # raises NotEnoughBitsInStreamError bitstream.seek(64) self.assertRaises(NotEnoughBitsInStreamError, bitstream.get_num, 1) # An invalid read call should not move the position indicator. self.assertEquals(bitstream.get_current_pos(), 64)
def test_seek_basic(self): """ Use seek to read data at different points in the bitstream. """ bitstream = BitStream() # Store a 64-bit integer in the stream. num = int( '00000000000000110101011001010011' '00101100001000101000100110101111', 2) bitstream.put_num(num, 64) # Get the 0th, 3rd, 5th and 6th bytes of the stream # First in forwards order bitstream.seek(0) self.assertEqual(bitstream.get_num(8), int('00000000', 2)) bitstream.seek(3 * 8) self.assertEqual(bitstream.get_num(8), int('01010011', 2)) bitstream.seek(5 * 8) self.assertEqual(bitstream.get_num(8), int('00100010', 2)) self.assertEqual(bitstream.get_num(8), int('10001001', 2)) # Then in backwards order bitstream.seek(6 * 8) self.assertEqual(bitstream.get_num(8), int('10001001', 2)) bitstream.seek(5 * 8) self.assertEqual(bitstream.get_num(8), int('00100010', 2)) bitstream.seek(3 * 8) self.assertEqual(bitstream.get_num(8), int('01010011', 2)) bitstream.seek(0) self.assertEqual(bitstream.get_num(8), int('00000000', 2))
def test_stress(self): """ Stress test BitStream by writings lots of numbers of different bit sizes and reading them back. """ # num_writes = 10000 num_writes = 1000 min_bit_length = 1 max_bit_length = 8192 # Generate the numbers nums = [] total_bit_length = 0 for i in range(0, num_writes): bit_length = random.randint(min_bit_length, max_bit_length) total_bit_length += bit_length val = random.randint(0, 2**bit_length - 1) nums.append({"bit_length": bit_length, "value": val}) # Write them to a BitStream object bitstream = BitStream() for num in nums: bitstream.put_num(num["value"], num["bit_length"]) # Check the BitStream length and current position self.assertEquals(bitstream.get_length(), total_bit_length) self.assertEquals(bitstream.get_current_pos(), total_bit_length) # Read all numbers back bitstream.seek(0) for num in nums: self.assertEquals(bitstream.get_num(num["bit_length"]), num["value"])
def test_seek_negative_position(self): """ Test that seeking to a negative position raises an error. """ bitstream = BitStream() self.assertRaises(SeekOutOfRangeError, bitstream.seek, -1) # An invalid seek(...) call should not move the position indicator. self.assertEqual(bitstream.get_current_pos(), 0)
def test_put_num_negative_length(self): """ Test that put_num raises an exception when passed negative integer values as its bit_length argument. """ bitstream = BitStream() val = random.randint(-128, -1) self.assertRaises(ValueError, bitstream.put_num, 23, val)
def test_put_byte_negative(self): """ Test that put_byte raises an exception when passed negative integer values as its argument. """ bitstream = BitStream() val = random.randint(-2**8 + 1, -1) self.assertRaises(ValueError, bitstream.put_byte, val)
def test_put_byte_to_big(self): """ Test that put_byte raises an exception when passed any integer value > 255 as its argument. """ bitstream = BitStream() self.assertRaises(ValueError, bitstream.put_byte, 256) val = random.randint(2**8 + 1, 2**32) self.assertRaises(ValueError, bitstream.put_byte, val)
def test_put_num_wrong_bit_length(self): """ Test that put_num raises an exception when given incompatible num and bit_length arguments (ie. num >= 2**bit_length) """ bitstream = BitStream() self.assertRaises(ValueError, bitstream.put_num, 2**16, 16) val = random.randint(2**64 + 1, 2**128) self.assertRaises(ValueError, bitstream.put_num, val, 64)
def test_bitstream_constructor(self): """ Test that creating an empty bitstream succeeds. """ bitstream = BitStream() # An empty bitstream has length 0 ... self.assertEqual(bitstream.get_length(), 0) # ...and has its current position marker at position 0. self.assertEqual(bitstream.get_current_pos(), 0)
def test_put_base64_wrong_type(self): """ Test that put_base64 raises an exception when passed non string values as its argument. """ bitstream = BitStream() self.assertRaises(TypeError, bitstream.put_base64, 23234) self.assertRaises(TypeError, bitstream.put_base64, 0.1) self.assertRaises(TypeError, bitstream.put_base64, object())
def test_put_bitstream_copy(self): """ Test the basic functionality of the put_bitstream_copy method. """ bitstream1 = BitStream() bitstream1.put_string("This is bitstream1") bitstream2 = BitStream() bitstream2.put_string("This is bitstream2") bitstream3 = BitStream() # bitstream3 remains empty bitstream4 = BitStream() bitstream4.put_string("This is bitstream4") # copy the full contents of bitstream2 to the end of bitstream1 bitstream2.seek(0) bitstream1.put_bitstream_copy(bitstream2) self.assertEquals(bitstream2.get_current_pos(), bitstream2.get_length()) # check the contents of bitstream1 bitstream1.seek(0) self.assertEquals(bitstream1.get_string(bitstream1.get_length()), "This is bitstream1This is bitstream2") # copy the full contents of bitstream3 (aka. nothing) to the end of # bitstream4 bitstream3.seek(0) bitstream4.put_bitstream_copy(bitstream3) # check the contents of bitstream4 bitstream4.seek(0) self.assertEquals(bitstream4.get_string(bitstream4.get_length()), "This is bitstream4") # copy the contents of bitstream4 from the position 8 onwards bitstream4.seek(8 * 8) bitstream1.put_bitstream_copy(bitstream4) self.assertEquals(bitstream4.get_current_pos(), bitstream4.get_length()) # check the contents of bitstream1 bitstream1.seek(0) self.assertEquals(bitstream1.get_string(bitstream1.get_length()), "This is bitstream1This is bitstream2bitstream4")
def test_get_hex_invalid_length(self): """ Test that trying to read a number of bits that is not a multiple of 4 as hex data raises an exception. """ bitstream = BitStream() bitstream.put_hex("DfF7CE69fF5478A") # 15 digits, 60 bits bitstream.seek(0) self.assertRaises(ValueError, bitstream.get_hex, 47) # read 11.75 hex digits?
def test_string_character_zero(self): """ Test that we are handling character zero ('\\0') correctly. """ bitstream = BitStream() bitstream.put_string("Evil \0String.") # 13 chars/bytes self.assertEquals(bitstream.get_length(), 13 * 8) bitstream.seek(0) self.assertEquals(bitstream.get_string(bitstream.get_length()), "Evil \0String.")
def test_put_num_non_integer_length(self): """ Test that put_num raises an exception when passed non integer values as its bit_length argument. """ bitstream = BitStream() self.assertRaises(TypeError, bitstream.put_num, 23, "gato") self.assertRaises(TypeError, bitstream.put_num, 23, 12.1) self.assertRaises(TypeError, bitstream.put_num, 23, [1, 'a', 3]) self.assertRaises(TypeError, bitstream.put_num, 23, object())
def test_get_base64_invalid_length(self): """ Test that trying to read a number of bits that is not a multiple of 8 as base64 data raises an exception. """ bitstream = BitStream() bitstream.put_base64("Zm9vYmE=") # 40 bits bitstream.seek(0) self.assertRaises(ValueError, bitstream.get_base64, 26) # read 3.25 bytes?
def test_put_num_non_integer(self): """ Test that put_num raises an exception when passed non integer values as its num argument. """ bitstream = BitStream() self.assertRaises(TypeError, bitstream.put_num, "gato", 16) self.assertRaises(TypeError, bitstream.put_num, 0.1, 16) self.assertRaises(TypeError, bitstream.put_num, [1, 'a', 3], 16) self.assertRaises(TypeError, bitstream.put_num, object(), 16)
def test_put_byte_non_integer(self): """ Test that put_byte raises an exception when passed non integer values as its argument. """ bitstream = BitStream() self.assertRaises(TypeError, bitstream.put_byte, "gato") self.assertRaises(TypeError, bitstream.put_byte, 0.1) self.assertRaises(TypeError, bitstream.put_byte, [1, 'a', 3]) self.assertRaises(TypeError, bitstream.put_byte, object())
def test_string_unicode(self): """ Test basic put_string and get_string support for python unicode objects. """ bitstream = BitStream() unicode_string = u"ÄäÜüßЯБГДЖЙŁĄŻĘĆŃŚŹてすとアイウエオカキクケコサシスセソタチツテ" bitstream.put_string(unicode_string) bitstream.seek(0) # Note: the string is read back as a "normal" UTF-8 string, unicode # type information is not stored in the bitstream. self.assertEquals(bitstream.get_string(bitstream.get_length()), "ÄäÜüßЯБГДЖЙŁĄŻĘĆŃŚŹてすとアイウエオカキクケコサシスセソタチツテ")
def test_multiformat_write_multiformat_read(self): """ This test writes numeric, byte and string data to a stream and then reads the whole stream as binary, hex and base64 data, ensuring that the output is the expected one in each case. """ # Write a number, 2 bytes and a string bitstream = BitStream() bitstream.put_num(10438341575639894917, 64) bitstream.put_byte(230) bitstream.put_byte(191) bitstream.put_string("ÄäÜüßTestЯБГДЖЙŁĄŻStringĘĆŃŚŹてす" \ "とアイウエオカキク4234ケコサシスセソタチツテ") # Read in binary, hex and base64 formats expected_bits = \ "1001000011011100011100000101101110111100001101010000101110000101" \ "1110011010111111110000111000010011000011101001001100001110011100" \ "1100001110111100110000111001111101010100011001010111001101110100" \ "1101000010101111110100001001000111010000100100111101000010010100" \ "1101000010010110110100001001100111000101100000011100010010000100" \ "1100010110111011010100110111010001110010011010010110111001100111" \ "1100010010011000110001001000011011000101100000111100010110011010" \ "1100010110111001111000111000000110100110111000111000000110011001" \ "1110001110000001101010001110111110111101101100011110111110111101" \ "1011001011101111101111011011001111101111101111011011010011101111" \ "1011110110110101111011111011110110110110111011111011110110110111" \ "1110111110111101101110000011010000110010001100110011010011101111" \ "1011110110111001111011111011110110111010111011111011110110111011" \ "1110111110111101101111001110111110111101101111011110111110111101" \ "1011111011101111101111011011111111101111101111101000000011101111" \ "1011111010000001111011111011111010000010111011111011111010000011" expected_hex = \ "90dc705bbc350b85e6bfc384c3a4c39cc3bcc39f54657374d0afd091d093d094" \ "d096d099c581c484c5bb537472696e67c498c486c583c59ac5b9e381a6e38199" \ "e381a8efbdb1efbdb2efbdb3efbdb4efbdb5efbdb6efbdb7efbdb834323334ef" \ "bdb9efbdbaefbdbbefbdbcefbdbdefbdbeefbdbfefbe80efbe81efbe82efbe83" expected_base64 = \ "kNxwW7w1C4Xmv8OEw6TDnMO8w59UZXN00K/QkdCT0JTQltCZxYHEhMW7U3RyaW5n" \ "xJjEhsWDxZrFueOBpuOBmeOBqO+9se+9su+9s++9tO+9te+9tu+9t++9uDQyMzTv" \ "vbnvvbrvvbvvvbzvvb3vvb7vvb/vvoDvvoHvvoLvvoM=" bitstream.seek(0) self.assertEquals( \ bitstream.get_bit_dump_string(bitstream.get_length()), expected_bits) bitstream.seek(0) self.assertEquals( bitstream.get_hex(bitstream.get_length()).lower(), expected_hex) bitstream.seek(0) self.assertEquals(bitstream.get_base64(bitstream.get_length()), expected_base64)
def test_get_bit_dump_string_zero_bits(self): """ Test that reading zero bits from the stream as a bit dump string results in getting the empty string: \"\". """ bitstream = BitStream() self.assertEquals(bitstream.get_bit_dump_string(0), "") # Store some bits in the stream. bitstream.put_bit_dump_string("0001110101") # 10 bits bitstream.seek(0) self.assertEquals(bitstream.get_bit_dump_string(0), "")
def test_get_base64_zero_bits(self): """ Test that reading zero bits from the stream as base64 data results in getting the empty string: \"\". """ bitstream = BitStream() self.assertEquals(bitstream.get_base64(0), "") # Store some base64 data in the stream. bitstream.put_base64("Zm9vYmE=") # 40 bits bitstream.seek(0) self.assertEquals(bitstream.get_base64(0), "")
def test_get_hex_zero_bits(self): """ Test that reading zero bits from the stream as hex data results in getting the empty string: \"\". """ bitstream = BitStream() self.assertEquals(bitstream.get_hex(0), "") # Store some hex data in the stream. bitstream.put_hex("DfF7CE69fF5478A") # 15 digits, 60 bits bitstream.seek(0) self.assertEquals(bitstream.get_hex(0), "")
def test_get_string_zero_bits(self): """ Test that reading zero bits from the stream as a string results in getting the empty string: \"\". """ bitstream = BitStream() self.assertEquals(bitstream.get_string(0), "") # Store a string in the stream. bitstream.put_string("Hello World!") bitstream.seek(0) self.assertEquals(bitstream.get_string(0), "")
def test_get_string_non_bytable_size(self): """ Test that calling get_string with a bit_length that is not a multiple of 8 raises an exception. That is, we cannot read partial bytes as string data. """ bitstream = BitStream() bitstream.put_string("Hello World!") bitstream.seek(0) self.assertRaises(ValueError, bitstream.get_string, 18) # Current position should not have been changed self.assertEquals(bitstream.get_current_pos(), 0)
def test_get_num_zero_bits(self): """ Test that reading zero bits from the stream as a number results in getting the number 0. """ bitstream = BitStream() self.assertEquals(bitstream.get_num(0), 0) # Store a 64-bit integer in the stream. num = int( '00000000000000110101011001010011' '00101100001000101000100110101111', 2) bitstream.put_num(num, 64) bitstream.seek(0) self.assertEquals(bitstream.get_num(0), 0)
def test_get_string_utf8_read_partial_characters(self): """ Test that it is possible to read "partial" utf-8 characters from the bitstream and that concatenation restores the full character/glyph. """ bitstream = BitStream() string = "てすとアイウエオカキクケコサシスセソタチツテ" # 22 chars, 66 bytes bitstream.put_string(string) bitstream.seek(0) # First read 32 bytes readString = bitstream.get_string(32 * 8) self.assertEquals(readString, "てすとアイウエオカキ\xef\xbd") # Now read the rest readString += bitstream.get_string(34 * 8) self.assertEquals(readString, "てすとアイウエオカキクケコサシスセソタチツテ")
def test_get_base64_beyond_eos(self): """ Test that trying to read beyond the end of the stream raises an exception when calling get_base64(...). """ bitstream = BitStream() self.assertRaises(NotEnoughBitsInStreamError, bitstream.get_base64, 8) # Current position should not have been changed self.assertEquals(bitstream.get_current_pos(), 0) bitstream.put_base64("Zm9vYmE=") # 40 bits bitstream.seek(0) # Read beyond EOS self.assertRaises(NotEnoughBitsInStreamError, bitstream.get_base64, 48) # Current position should not have been changed self.assertEquals(bitstream.get_current_pos(), 0)
def test_get_byte_beyond_eos(self): """ Test that trying to read beyond the end of the stream raises an exception when calling get_byte(...). """ bitstream = BitStream() self.assertRaises(NotEnoughBitsInStreamError, bitstream.get_byte) # Current position should not have been changed self.assertEquals(bitstream.get_current_pos(), 0) bitstream.put_byte(14) bitstream.seek(1) # Read a single bit beyond EOS self.assertRaises(NotEnoughBitsInStreamError, bitstream.get_byte) # Current position should not have been changed self.assertEquals(bitstream.get_current_pos(), 1)
def test_get_hex_beyond_eos(self): """ Test that trying to read beyond the end of the stream raises an exception when calling get_hex(...). """ bitstream = BitStream() self.assertRaises(NotEnoughBitsInStreamError, bitstream.get_hex, 1) # Current position should not have been changed self.assertEquals(bitstream.get_current_pos(), 0) bitstream.put_hex("DfF7CE69fF5478A") # 15 digits, 60 bits bitstream.seek(0) # Read beyond EOS self.assertRaises(NotEnoughBitsInStreamError, bitstream.get_hex, 61) # Current position should not have been changed self.assertEquals(bitstream.get_current_pos(), 0)