def test__parse_num(self): """ Tests that the _parse_num handles integers correctly. """ data = b"e" decoder = bencode.Decoder(data) with self.subTest(msg="Emtpy integer and only delim."): with self.assertRaises(bencode.DecodeError): decoder._parse_num(bencode.BencodeDelimiters.END) data = b"1^" with self.subTest(msg="Non-traditional delimiters."): decoder._set_data(data) self.assertEqual(decoder._parse_num(b"^"), 1) data = b"n12e" with self.subTest(msg="Not a digit or '-'."): with self.assertRaises(bencode.DecodeError): decoder._set_data(data) decoder._parse_num(bencode.BencodeDelimiters.END) data = b"01e" with self.subTest(msg="Leading zero."): with self.assertRaises(bencode.DecodeError): decoder._set_data(data) decoder._parse_num(bencode.BencodeDelimiters.END) data = b"-0e" with self.subTest(msg="Negative zero."): with self.assertRaises(bencode.DecodeError): decoder._set_data(data) decoder._parse_num(bencode.BencodeDelimiters.END)
def test__decode_dict(self): """ Tests that Decoder._decode_dict functions properly we leave the dictionary start delimiter b'd' off as we call into _decode_dict directly. """ data = b"e" decoder = bencode.Decoder(data) with self.subTest(msg="Decode with no key in the Decoder."): self.assertEqual(decoder._decode_dict(), OrderedDict()) data = b"i14e4:datae" with self.subTest(msg="Invalid dictionary key type."): with self.assertRaises(bencode.DecodeError): decoder._set_data(data) decoder._decode_dict() data = b"5:b key3:val5:a key3:vale" with self.subTest(msg="Unordered keys."): with self.assertRaises(bencode.DecodeError): decoder._set_data(data) decoder._decode_dict() data = b"3:key3:vale" with self.subTest(msg="Valid dictionary."): decoder._set_data(data) self.assertEqual(decoder._decode_dict(), OrderedDict({"key": b"val"})) data = b"3:key3:valeee" with self.subTest(msg="Extra end delimiters."): decoder._set_data(data) self.assertEqual(decoder._decode_dict(), OrderedDict({"key": b"val"}))
def test_valid_creation(self): """ Ensure __init__ works properly. """ data = b"4:data" decoder = bencode.Decoder(data) self.assertIsInstance(decoder, bencode.Decoder) self.assertEqual(decoder._recursion_limit, 99999) self.assertEqual(decoder._current_iter, 0) self.assertEqual(decoder._data.read(), data)
def test_invalid_creation(self): """ Ensure __init__ rejects invalid arguments. """ with self.subTest(msg="Invalid recursion limit."): with self.assertRaises(bencode.DecodeError): for invalid_limit in [0, -1]: bencode.Decoder(bytes(), recursion_limit=invalid_limit) with self.subTest(msg="No data."): with self.assertRaises(bencode.DecodeError): bencode.Decoder(bytes()) with self.subTest(msg="Invalid data types passed to Decoder."): with self.assertRaises(bencode.DecodeError): for invalid_type in [[1, 2], "string", { "1": "a" }, (1, 2), {1, 2, 3}]: # noinspection PyTypeChecker bencode.Decoder(invalid_type)
def test__decode(self): """ Ensure bencode.Decoder._decode works properly. """ eof = b"!" # eof marker used in the Decoder with self.subTest(msg="Testing _recursion_limit is reached."): with self.assertRaises(bencode.BencodeRecursionError): decoder = bencode.Decoder(b"l") decoder._current_iter = 5 decoder._recursion_limit = 4 decoder._decode() decoder = bencode.Decoder(b"l") with self.subTest(msg="Testing _decode with nothing in the buffer."): decoder._data.read(1) # exhaust the buffer self.assertEqual(decoder._decode(), eof) with self.subTest( msg= "Testing _decode with only the end of a dictionary (or list, or int)." ): decoder._set_data(bencode.BencodeDelimiters.END ) # empty dictionary also ends recursion self.assertEqual(decoder._decode(), eof) with self.subTest(msg="Testing _decode with an empty dictionary."): decoder._set_data(b"de") self.assertEqual(decoder._decode(), OrderedDict()) with self.subTest(msg="Testing _decode with an empty list."): decoder._set_data(b"le") self.assertEqual(decoder._decode(), []) with self.subTest(msg="Testing with an invalid bencoding key."): decoder._set_data(b"?") with self.assertRaises(bencode.DecodeError): decoder._decode()
def test__decode_list(self): """ Tests that Decoder._decode_list functions properly we leave the list start delimiter b'l' off as we call into _decode_list directly. """ data = b"e" # le decoder = bencode.Decoder(data) with self.subTest(msg="Empty list."): self.assertEqual(decoder._decode_list(), []) data = b"lee" # llee with self.subTest(msg="Nested empty lists."): decoder._set_data(data) self.assertEqual(decoder._decode_list(), [[]]) data = b"l3:valee" # ll3:valee with self.subTest(msg="Populated inner list."): decoder._set_data(data) self.assertEqual(decoder._decode_list(), [[b"val"]]) data = b"3:vall3:val3:val3:valel3:valedee" # 3:vall3:val3:val3:valel3:valedee with self.subTest(msg="Populated with many types."): decoder._set_data(data) self.assertEqual( decoder._decode_list(), [b"val", [b"val", b"val", b"val"], [b"val"], OrderedDict()]) data = b"3:valeee" with self.subTest(msg="Extra end delimiters."): decoder._set_data(data) self.assertEqual(bencode.Decoder(data)._decode_list(), [b"val"]) data = b"?e" with self.subTest(msg="Invalid list item."): decoder._set_data(data) with self.assertRaises(bencode.DecodeError): bencode.Decoder(data)._decode_list()
def test__set_data(self): """ Ensure _set_data works properly. _set_data with invalid types is already tested in test_invalid_creation """ old_data = b"8:old data" new_data = b"8:new data" empty_data = b"" decoder = bencode.Decoder(old_data) self.assertEqual(decoder._data.read(), old_data) decoder._set_data(new_data) self.assertEqual(decoder._data.read(), new_data) decoder._set_data(empty_data) self.assertEqual(decoder._data.read(), empty_data) decoder._set_data(empty_data) self.assertIsNone(decoder.decode())
def test_decode_recode_compare(self): """ This should probably live in test_bencode.py, but resides here now since this class creates a .torrent metainfo file with an external program. TODO: move this test to a more proper location """ file_copy = os.path.abspath( os.path.join(os.path.dirname(__file__), "copy.torrent")) with open(self.external_torrent_path, 'rb') as f: data = f.read() unencoded_data = bencode.Decoder(data).decode() with open(file_copy, 'wb+') as ff: encoded_data = bencode.Encoder(unencoded_data).encode() ff.write(encoded_data) self.assertTrue(cmp(self.external_torrent_path, file_copy)) os.remove(file_copy)
def test__decode_bytestr(self): """ Ensures that Decoder._decode_bytestr handles properly and improperly formatted data """ data = b"13:nope" decoder = bencode.Decoder(data) with self.subTest(msg="Invalid string length."): with self.assertRaises(bencode.DecodeError): decoder._data.read(1) decoder._decode_bytestr() data = b"3-val" with self.subTest(msg="Invalid delimiter."): with self.assertRaises(bencode.DecodeError): decoder._set_data(data) decoder._data.read(1) decoder._decode_bytestr() data = b"34:string with spaces and bytes \x00 \x12 \x24" with self.subTest(msg="Valid string."): decoder._set_data(data) decoder._data.read(1) self.assertEqual(decoder._decode_bytestr(), data[3:])