conflicts

This commit is contained in:
Brian Merriam 2023-03-21 02:53:33 +00:00
commit 07f0ca8abd
9 changed files with 924 additions and 7 deletions

12
minecraft/compat.py Normal file
View File

@ -0,0 +1,12 @@
"""
This module stores code used for making pyCraft compatible with
both Python2 and Python3 while using the same codebase.
"""
import six
# ### LONG ###
if six.PY3:
long = int
else: # pragma: no cover
long = long

View File

@ -81,4 +81,19 @@ class IgnorePacket(Exception):
`PacketReactor.react' or a packet listener added with
`Connection.register_packet_listener', to stop any subsequent handlers
from being called on that particular packet.
Base ``Exception`` for the Yggdrasil authentication service.
"""
class DeserializationError(Exception):
"""
``Exception`` raised when something went wrong during the deserialization
process.
"""
class SerializationError(Exception):
"""
``Exception`` raised when something went wrong during the serialization
process.
"""

View File

@ -0,0 +1,443 @@
"""
Contains the datatypes used by the networking part of `pyminecraft`.
The types are described at http://wiki.vg/Protocol#Data_types
These datatypes are used by the packet definitions.
"""
__all__ = ["ENDIANNESS",
"Datatype", "NumberDatatype", "StringDatatype",
"Boolean",
"Byte", "UnsignedByte",
"Short", "UnsignedShort",
"Integer", "UnsignedInteger",
"Long", "UnsignedLong",
"LongLong", "UnsignedLongLong",
"Float",
"Double",
"VarInt", "VarLong",
"String"]
from minecraft.exceptions import DeserializationError
from minecraft.compat import long
from io import BytesIO
import struct
import collections
import numbers
ENDIANNESS = "!" # Network, big-endian
def raise_serialization_data(func):
"""
A decorator to be used on a ``Datatype``.serialize definition.
Must be placed before a classmethod decorator.
"""
def wrapped(cls, data):
cls.raise_serialization_data(data)
return func(cls, data)
return wrapped
def raise_deserialization_data(func):
"""
A decorator to be used on a ``Datatype``.serialize definition.
Must be placed before a classmethod decorator.
"""
def wrapped(cls, data):
cls.raise_deserialization_data(data)
return func(cls, data)
return wrapped
class Datatype(object):
"""
Base object for all `pyminecraft` networking datatypes.
``Datatype``.SIZE can be either a number, specifying an exact required size
of data to be deserialized, or it can be a tuple like this:
``(MIN_SIZE, MAX_SIZE)``
.. note::
If ``ALLOWED_SERIALIZATION_TYPES`` is not empty, only the types found
in ``ALLOWED_SERIALIZATION_TYPES`` are allowed as serialization
``data``. This does somewhat go against the Duck-typing principle.
The same applies for ``ALLOWED_DESERIALIZATION_TYPES``.
.. note::
If ``DISALLOWED_SERIALIZATION_TYPES`` is not empty, only the types
found in ``DISALLOWED_SERIALIZATION_TYPES`` are allowed as
serialization ``data``. This does somewhat go against the
Duck-typing principle.
``DISALLOWED_SERIALIZATION_TYPES`` exists as a way to exclude certain
subclasses of a given type.
The same applies for ``DISALLOWED_DESERIALIZATION_TYPES``.
"""
FORMAT = ""
SIZE = 0
ALLOWED_SERIALIZATION_TYPES = tuple()
ALLOWED_DESERIALIZATION_TYPES = tuple()
DISALLOWED_SERIALIZATION_TYPES = tuple()
DISALLOWED_SERIALIZATION_TYPES = tuple()
@classmethod
def read(cls, fileobject):
bin_data = fileobject.read(cls.SIZE)
return cls.deserialize(bin_data)
@classmethod
@raise_deserialization_data
def deserialize(cls, data):
deserialized_data = struct.unpack(ENDIANNESS + cls.FORMAT, data)[0]
return deserialized_data
@classmethod
def write(cls, fileobject, data):
return fileobject.write(cls.serialize(data))
@classmethod
@raise_serialization_data
def serialize(cls, data):
serialized_data = struct.pack(ENDIANNESS + cls.FORMAT, data)
return serialized_data
@classmethod
def raise_serialization_data(cls, data):
"""
Raises an appropriate ``Exception`` if ``data`` is not valid.
:return: ``None``
:rtype: ``None``
:raises: ``TypeError``, ``ValueError``
"""
error_message = "'data's type ('{}') is not an allowed type."
error_message = error_message.format(type(data).__name__)
if (cls.ALLOWED_SERIALIZATION_TYPES and
not any([isinstance(data, type_) for type_
in cls.ALLOWED_SERIALIZATION_TYPES])):
raise TypeError(error_message)
for type_ in cls.DISALLOWED_SERIALIZATION_TYPES:
if isinstance(data, type_):
raise TypeError(error_message)
cls._raise_serialization_value_error_data(data)
return None
@classmethod
def _raise_serialization_value_error_data(cls, data):
"""
Raises a ValueError if ``data`` is not valid.
:return: ``None``
:rtype: ``None``
:raises: ``ValueError``
"""
return None
@classmethod
def raise_deserialization_data(cls, data):
"""
Raises an appropriate ``Exception`` if ``data`` is not valid.
:return: ``None``
:rtype: ``None``
:raises: ``TypeError``, ``ValueError``
"""
if (cls.ALLOWED_DESERIALIZATION_TYPES and
not any([isinstance(data, type_) for type_
in cls.ALLOWED_DESERIALIZATION_TYPES])):
err = "'data's type ('{}') is not an allowed type."
err = err.format(type(data).__name__)
raise TypeError(err)
if isinstance(cls.SIZE, numbers.Number):
if cls.SIZE != len(data):
err = "'data' must have a length of {}, not {}"
err = err.format(str(cls.SIZE), str(len(data)))
raise ValueError(err)
elif isinstance(cls.SIZE, collections.Sequence):
if not cls.SIZE[0] <= len(data) <= cls.SIZE[1]:
err = "'data' must have a length between {} and {}, not {}"
err = err.format(str(cls.SIZE[0]), str(cls.SIZE[1]),
str(len(data)))
raise ValueError(err)
else:
raise TypeError("'cls.SIZE' must be a number or a sequence.")
return None
class NumberDatatype(Datatype):
"""
Base abstract class for all number-like minecraft networking datatypes.
.. note::
Numbers to be serialized must be between this classes
``MIN_NUMBER_VALUE`` and ``MAX_NUMBER_VALUE``, or a ``ValueError`` will
be raised.
If ``MIN_NUMBER_VALUE`` or ``MAX_NUMBER_VALUE`` are ``None``
(as in the case of float), checking is left to the ``struct`` module.
"""
MIN_NUMBER_VALUE = None
MAX_NUMBER_VALUE = None
ALLOWED_SERIALIZATION_TYPES = (int, long)
DISALLOWED_SERIALIZATION_TYPES = (bool,)
@classmethod
def _raise_serialization_value_error_data(cls, data):
if (cls.MIN_NUMBER_VALUE is not None
and cls.MAX_NUMBER_VALUE is not None):
if not cls.MIN_NUMBER_VALUE <= data <= cls.MAX_NUMBER_VALUE:
err = "'data' must be an integer with value between {} and {}."
err = err.format(str(cls.MIN_NUMBER_VALUE),
str(cls.MAX_NUMBER_VALUE))
raise ValueError(err)
return None
class StringDatatype(Datatype):
pass
class Boolean(Datatype):
FORMAT = "?"
SIZE = 1
ALLOWED_SERIALIZATION_TYPES = (bool,)
ALLOWED_DESERIALIZATION_TYPES = (collections.Sequence,)
class Byte(NumberDatatype):
FORMAT = "b"
SIZE = 1
MIN_NUMBER_VALUE = -128
MAX_NUMBER_VALUE = 127
class UnsignedByte(NumberDatatype):
FORMAT = "B"
SIZE = 1
MIN_NUMBER_VALUE = 0
MAX_NUMBER_VALUE = 255
class Short(NumberDatatype):
FORMAT = "h"
SIZE = 2
MIN_NUMBER_VALUE = -32768
MAX_NUMBER_VALUE = 32767
class UnsignedShort(NumberDatatype):
FORMAT = "H"
SIZE = 2
MIN_NUMBER_VALUE = 0
MAX_NUMBER_VALUE = 65535
class Integer(NumberDatatype):
FORMAT = "i"
SIZE = 4
MIN_NUMBER_VALUE = -2147483648
MAX_NUMBER_VALUE = 2147483647
class UnsignedInteger(NumberDatatype):
FORMAT = "I"
SIZE = 4
MIN_NUMBER_VALUE = 0
MAX_NUMBER_VALUE = 4294967295
class Long(NumberDatatype):
FORMAT = "l"
SIZE = 4
MIN_NUMBER_VALUE = -2147483648
MAX_NUMBER_VALUE = 2147483647
class UnsignedLong(NumberDatatype):
FORMAT = "L"
SIZE = 4
MIN_NUMBER_VALUE = 0
MAX_NUMBER_VALUE = 4294967295
class LongLong(NumberDatatype):
FORMAT = "q"
SIZE = 8
MIN_NUMBER_VALUE = -9223372036854775808
MAX_NUMBER_VALUE = 9223372036854775807
class UnsignedLongLong(NumberDatatype):
FORMAT = "Q"
SIZE = 8
MIN_NUMBER_VALUE = 0
MAX_NUMBER_VALUE = 18446744073709551615
class Float(NumberDatatype):
FORMAT = "f"
SIZE = 4
ALLOWED_SERIALIZATION_TYPES = (int, long, float)
DISALLOWED_SERIALIZATION_TYPES = (bool,)
class Double(Float):
FORMAT = "d"
SIZE = 8
class VarInt(NumberDatatype):
# See: https://developers.google.com/protocol-buffers/docs/encoding#varints
# See: https://github.com/ammaraskar/pyCraft/blob/7e8df473520d57ca22fb57888681f51705128cdc/network/types.py#l123 # noqa
# See: https://github.com/google/protobuf/blob/0c59f2e6fc0a2cb8e8e3b4c7327f650e8586880a/python/google/protobuf/internal/decoder.py#l107 # noqa
# According to http://wiki.vg/Protocol#Data_types,
# MineCraftian VarInts can be at most 5 bytes.
# Maximum integer value: size of serialized VarInt in bytes
SIZE_TABLE = {
2**7: 1,
2**14: 2,
2**21: 3,
2**28: 4,
2**35: 5,
}
# Largest element in SIZE_TABLE, assuming largest element is last.
MAX_SIZE = list(SIZE_TABLE.items())[-1][-1]
SIZE = (1, MAX_SIZE)
@classmethod
def read(cls, fileobject):
number = 0 # The decoded number
i = 0 # Incrementor
while True:
if i > cls.MAX_SIZE: # Check if we have exceeded max-size
name_of_self = str(type(cls))
e = "Data too large to be a {}".format(name_of_self)
raise DeserializationError(e)
try:
byte = ord(fileobject.read(1)) # Read a byte as integer
except TypeError:
e = "Fileobject ran out of data. Socket closed?"
raise DeserializationError(e)
number |= ((byte & 0x7f) << (i * 7))
if not (byte & 0x80):
break
i += 1
return number
@classmethod
@raise_deserialization_data
def deserialize(cls, data):
data_fileobject = BytesIO(bytes(data))
return cls.read(data_fileobject)
@classmethod
@raise_serialization_data
def serialize(cls, data):
if data > cls.SIZE_TABLE[-1][0]:
name_of_self = str(type(cls))
e = "Number too big to serialize as {}".format(name_of_self)
raise ValueError(e)
result = bytes() # Where we store the serialized number
while True:
byte = data & 0x7f
data >>= 7
result += UnsignedByte.serialize(byte | (0x80 if data > 0 else 0))
if not data:
break
return result
class VarLong(VarInt):
# According to http://wiki.vg/Protocol#Data_types,
# MineCraftian VarInts can be at most 10 bytes.
SIZE_TABLE = VarInt.SIZE_TABLE
SIZE_TABLE.update(
{
2**42: 6,
2**49: 7,
2**56: 8,
2**63: 9,
2**70: 10,
}
)
MAX_SIZE = list(SIZE_TABLE.items())[-1][-1]
SIZE = (1, MAX_SIZE)
class String(Datatype):
FORMAT = "utf-8"
@classmethod
def read(cls, fileobject):
str_size = VarInt.read(fileobject)
string = fileobject.read(str_size).decode(cls.FORMAT)
return string
@classmethod
def deserialize(cls, data):
data_fileobject = BytesIO(bytes(data))
return cls.read(data_fileobject)
@classmethod
def serialize(cls, data):
data = data.encode(cls.FORMAT)
len_data = VarInt.serialize(len(data))
return len_data + data

View File

@ -1,3 +1,3 @@
# Package dependencies are stored in setup.py.
# For more information, see <https://github.com/ammaraskar/pyCraft/pull/156>.
-e .
cryptography
requests
six

79
start.py Normal file
View File

@ -0,0 +1,79 @@
import getpass
import sys
from optparse import OptionParser
from minecraft import authentication
from minecraft.exceptions import YggdrasilError
from minecraft.networking.connection import Connection
from minecraft.networking.packets import ChatMessagePacket, ChatPacket
from six.moves import input
def get_options():
parser = OptionParser()
parser.add_option("-u", "--username", dest="username", default=None,
help="username to log in with")
parser.add_option("-p", "--password", dest="password", default=None,
help="password to log in with")
parser.add_option("-s", "--server", dest="server", default=None,
help="server to connect to")
(options, args) = parser.parse_args()
if not options.username:
options.username = input("Enter your username: ")
if not options.password:
options.password = getpass.getpass("Enter your password: ")
if not options.server:
options.server = input("Please enter server address"
" (including port): ")
# Try to split out port and address
if ':' in options.server:
server = options.server.split(":")
options.address = server[0]
options.port = int(server[1])
else:
options.address = options.server
options.port = 25565
return options
def main():
options = get_options()
auth_token = authentication.AuthenticationToken()
try:
auth_token.authenticate(options.username, options.password)
except YggdrasilError as e:
print(e)
sys.exit()
print("Logged in as " + auth_token.username)
connection = Connection(options.address, options.port, auth_token)
connection.connect()
def print_chat(chat_packet):
print("Position: " + str(chat_packet.position))
print("Data: " + chat_packet.json_data)
connection.register_packet_listener(print_chat, ChatMessagePacket)
while True:
try:
text = input()
packet = ChatPacket()
packet.message = text
connection.write_packet(packet)
except KeyboardInterrupt:
print("Bye!")
sys.exit()
if __name__ == "__main__":
main()

11
tests/test_compat.py Normal file
View File

@ -0,0 +1,11 @@
from minecraft import compat # noqa unused-import
import unittest # noqa unused-import
class TestCompatLong(unittest.TestCase):
def test_import_long(self):
from minecraft.compat import long # noqa unused-import
def test_long(self):
compat.long(45)

331
tests/test_datatypes.py Normal file
View File

@ -0,0 +1,331 @@
from minecraft.networking.datatypes import * # noqa undefined-names
import unittest
class BaseDatatypeTester(unittest.TestCase):
DATATYPE_CLS = Datatype # We use Datatype as a an example here.
# VALID_VALUES should have the following format:
# [(DESERIALIZED_VALUE, SERIALIZED_VALUE), ...]
#
# So that DESERIALIZED_VALUE is SERIALIZED_VALUE when serialized
# and vice versa.
VALID_VALUES = []
# INVALID_SERIALIZATION_VALUES should be a list of tuples
# containing the value and the expected exception.
INVALID_SERIALIZATION_VALUES = []
# INVALID_DESERIALIZATION_VALUES should be a list of tuples
# containing the value and the expected exception.
INVALID_DESERIALIZATION_VALUES = []
def dynamic_assert_equal(self, first, second):
"""
Overriden by floating point datatypes in order to handle
the floating point issue.
"""
return self.assertEqual(first, second)
def test_init(self):
d = self.DATATYPE_CLS() # noqa
def test_init_with_arg(self):
# We shouldn't accept any parameters.
with self.assertRaises(TypeError):
d = self.DATATYPE_CLS("This is a positional argument...") # noqa
def test_valid_data_serialization_values(self):
for deserialized_val, serialized_val in self.VALID_VALUES:
self.dynamic_assert_equal(
self.DATATYPE_CLS.serialize(deserialized_val),
serialized_val)
def test_valid_data_deserialization_values(self):
for deserialized_val, serialized_val in self.VALID_VALUES:
self.dynamic_assert_equal(
self.DATATYPE_CLS.deserialize(serialized_val),
deserialized_val)
def test_invalid_data_serialization_values(self):
for value, exception in self.INVALID_SERIALIZATION_VALUES:
with self.assertRaises(exception):
self.DATATYPE_CLS.serialize(value)
def test_invalid_data_deserialization_values(self):
for value, exception in self.INVALID_DESERIALIZATION_VALUES:
with self.assertRaises(exception):
self.DATATYPE_CLS.deserialize(value)
class BaseNumberDatatypeTester(BaseDatatypeTester):
BASE_NUMBER_INVALID_SERIALIZATION_VALUES = [
("", TypeError),
("Test", TypeError),
(b"\x00", TypeError),
(b"\x80", TypeError),
(True, TypeError),
(False, TypeError)
]
def base_number_invalid_data_serialization_values(self):
values_to_test = BASE_INVALID_SERIALIZATION_VALUES
if (cls.MIN_NUMBER_VALUE is not None
and cls.MAX_NUMBER_VALUE is not None):
values_to_test.extend([
(self.DATATYPE_CLS.MIN_NUMBER_VALUE - 1, ValueError),
(self.DATATYPE_CLS.MAX_NUMBER_VALUE + 1, ValueError)
])
for value, exception in values_to_test:
with self.assertRaises(exception):
self.DATATYPE_CLS.serialize(value)
class BaseStringDatatypeTester(BaseDatatypeTester):
pass
BASE_INVALID_DESERIALIZATION_VALUES = [
(-1, TypeError),
(0, TypeError),
(1, TypeError),
("", ValueError),
(True, TypeError),
(False, TypeError)
]
class DatatypeTest(BaseDatatypeTester):
DATATYPE_CLS = Datatype
class NumberDatatypeTest(BaseNumberDatatypeTester):
DATATYPE_CLS = NumberDatatype
class StringDatatypeTest(BaseStringDatatypeTester):
DATATYPE_CLS = StringDatatype
class BooleanTest(BaseDatatypeTester):
DATATYPE_CLS = Boolean
VALID_VALUES = [
(True, b"\x01"),
(False, b"\x00")
]
INVALID_SERIALIZATION_VALUES = [
("\x00", TypeError),
("\x01", TypeError),
("\x02", TypeError),
(-1, TypeError),
(0, TypeError),
(1, TypeError),
("", TypeError),
("Test", TypeError)
]
# Use list(BASE_INVALID_DESERIALIZATION_VALUES) instead of
# just = BASE_INVALID_DESERIALIZATION_VALUES, cause we want a COPY
# of the list, NOT a reference (that we'll later extend!)
INVALID_DESERIALIZATION_VALUES = list(BASE_INVALID_DESERIALIZATION_VALUES)
INVALID_DESERIALIZATION_VALUES.extend([
(b"\x00\x01", ValueError)
])
class ByteTest(BaseNumberDatatypeTester):
DATATYPE_CLS = Byte
VALID_VALUES = [
(-128, b"\x80"),
(-22, b"\xea"),
(0, b"\x00"),
(22, b"\x16"),
(127, b"\x7f")
]
INVALID_DESERIALIZATION_VALUES = list(BASE_INVALID_DESERIALIZATION_VALUES)
INVALID_DESERIALIZATION_VALUES.extend([
(b"\x01\x20", ValueError),
])
class UnsignedByteTest(BaseNumberDatatypeTester):
DATATYPE_CLS = UnsignedByte
VALID_VALUES = [
(0, b"\x00"),
(127, b"\x7f"),
(255, b"\xff")
]
INVALID_DESERIALIZATION_VALUES = ByteTest.INVALID_DESERIALIZATION_VALUES
class ShortTest(BaseNumberDatatypeTester):
DATATYPE_CLS = Short
VALID_VALUES = [
(-32768, b"\x80\x00"),
(-10000, b"\xd8\xf0"),
(0, b"\x00\x00"),
(5000, b"\x13\x88"),
(32767, b"\x7f\xff")
]
INVALID_DESERIALIZATION_VALUES = list(BASE_INVALID_DESERIALIZATION_VALUES)
INVALID_DESERIALIZATION_VALUES.extend([
(b"\xff", ValueError),
(b"\xff\x01\x6e", ValueError)
])
class UnsignedShortTest(BaseNumberDatatypeTester):
DATATYPE_CLS = UnsignedShort
VALID_VALUES = [
(0, b"\x00\x00"),
(10000, b"'\x10"),
(32767, b"\x7f\xff"),
(65535, b"\xff\xff")
]
INVALID_DESERIALIZATION_VALUES = ShortTest.INVALID_DESERIALIZATION_VALUES
class IntegerTest(BaseNumberDatatypeTester):
DATATYPE_CLS = Integer
VALID_VALUES = [
(-2147483648, b"\x80\x00\x00\x00"),
(-1000000, b"\xff\xf0\xbd\xc0"),
(0, b"\x00\x00\x00\x00"),
(10000000, b"\x00\x98\x96\x80"),
(2147483647, b"\x7f\xff\xff\xff")
]
INVALID_DESERIALIZATION_VALUES = list(BASE_INVALID_DESERIALIZATION_VALUES)
INVALID_DESERIALIZATION_VALUES.extend([
(b"\xff", ValueError),
(b"\x00\x01", ValueError),
(b"\x76\x80\x80\x10\xff", ValueError)
])
class UnsignedIntegerTest(BaseNumberDatatypeTester):
DATATYPE_CLS = UnsignedInteger
VALID_VALUES = [
(0, b"\x00\x00\x00\x00"),
(10000000, b"\x00\x98\x96\x80"),
(2147483647, b"\x7f\xff\xff\xff"),
(4294967295, b"\xff\xff\xff\xff")
]
INVALID_DESERIALIZATION_VALUES = IntegerTest.INVALID_DESERIALIZATION_VALUES
class LongTest(IntegerTest):
DATATYPE_CLS = Long
class UnsignedLongTest(UnsignedInteger):
DATATYPE_CLS = UnsignedLong
class LongLongTest(BaseNumberDatatypeTester):
DATATYPE_CLS = LongLong
VALID_VALUES = [
(-9223372036854775808, b"\x80\x00\x00\x00\x00\x00\x00\x00"),
(-1000000, b"\xff\xff\xff\xff\xff\xf0\xbd\xc0"),
(0, b"\x00\x00\x00\x00\x00\x00\x00\x00"),
(10000000, b"\x00\x00\x00\x00\x00\x98\x96\x80"),
(9223372036854775807, b"\x7f\xff\xff\xff\xff\xff\xff\xff")
]
INVALID_DESERIALIZATION_VALUES = list(BASE_INVALID_DESERIALIZATION_VALUES)
INVALID_DESERIALIZATION_VALUES.extend([
(b"\xff", ValueError),
(b"\x00\x01", ValueError),
(b"\x76\x80\x80\x10\xff", ValueError),
(b"\x55\x44\x33\x22\x11\x66\x77\x88\x99", ValueError)
])
class UnsignedLongLongTest(BaseNumberDatatypeTester):
DATATYPE_CLS = UnsignedLongLong
VALID_VALUES = [
(0, b"\x00\x00\x00\x00\x00\x00\x00\x00"),
(10000000, b"\x00\x00\x00\x00\x00\x98\x96\x80"),
(9223372036854775807, b"\x7f\xff\xff\xff\xff\xff\xff\xff"),
(18446744073709551615, b"\xff\xff\xff\xff\xff\xff\xff\xff")
]
INVALID_DESERIALIZATION_VALUES = \
LongLongTest.INVALID_DESERIALIZATION_VALUES
class FloatTest(BaseNumberDatatypeTester):
DATATYPE_CLS = Float
VALID_VALUES = [
(-100.5467, b"\xc2\xc9\x17\xe9"),
(0.00000, b"\x00\x00\x00\x00"),
(5000.72, b"E\x9cE\xc3"),
(65.123565787856342347, b"B\x82?D"),
]
INVALID_DESERIALIZATION_VALUES = list(BASE_INVALID_DESERIALIZATION_VALUES)
INVALID_DESERIALIZATION_VALUES.extend([
(b"\xff", ValueError),
(b"\x00\x01", ValueError),
(b"\x76\x80\x80\x10\xff", ValueError),
(b"\x55\x44\x33\x22\x11\x66\x77\x88\x99", ValueError)
])
def dynamic_assert_equal(self, first, second):
return self.assertAlmostEqual(first, second, places=3)
class DoubleTest(FloatTest):
DATATYPE_CLS = Double
VALID_VALUES = [
(-10000560.86432, b"\xc1c\x13\x16\x1b\xa8\x82k"),
(-56.672345756870345754623, b"\xc0LV\x0fl\xfe\xaef"),
(0.00000, b"\x00\x00\x00\x00\x00\x00\x00\x00"),
(5000.72, b"@\xb3\x88\xb8Q\xeb\x85\x1f"),
(65.123565787856342347, b"@PG\xe8\x80zo\xd6"),
(5324342541.72123, b"A\xf3\xd5\xb0P\xdb\x8a(")
]
class VarIntTest(BaseNumberDatatypeTester):
DATATYPE_CLS = VarInt
INVALID_DESERIALIZATION_VALUES = BASE_INVALID_DESERIALIZATION_VALUES
# def _bin(binstr):
# """
# Accepts a pretty looking string of binary numbers and
# returns the binary number.
# Parameters:
# binstr - a string with this format: `'1010 0010 0100'`.
# Returns:
# Int
# """
# binstr = binstr.replace(" ", "") # Remove all spaces.
# num = int("0b" + binstr, 2)
# return num

View File

@ -1,15 +1,38 @@
from minecraft.exceptions import YggdrasilError
from minecraft.exceptions import DeserializationError, SerializationError
import unittest
class RaiseYggdrasilError(unittest.TestCase):
def test_raise_yggdrasil_error(self):
with self.assertRaises(YggdrasilError):
raise YggdrasilError
class BaseRaiseExceptionTest(unittest.TestCase):
EXCEPTION_TO_TEST = Exception
<<<<<<< HEAD
def test_raise_yggdrasil_error_message(self):
with self.assertRaises(YggdrasilError) as cm:
raise YggdrasilError("Error!")
self.assertEqual(str(cm.exception), "Error!")
=======
def test_raise_error(self):
with self.assertRaises(self.EXCEPTION_TO_TEST):
raise self.EXCEPTION_TO_TEST
def test_raise_error_message(self):
with self.assertRaises(self.EXCEPTION_TO_TEST) as e:
raise self.EXCEPTION_TO_TEST("Error!")
self.assertEqual(str(e.exception), "Error!")
class RaiseYggdrasilError(BaseRaiseExceptionTest):
EXCEPTION_TO_TEST = YggdrasilError
class RaiseDeserializationError(BaseRaiseExceptionTest):
EXCEPTION_TO_TEST = DeserializationError
class RaiseSerializationError(BaseRaiseExceptionTest):
EXCEPTION_TO_TEST = SerializationError
>>>>>>> 55ff270f167d36cd67c637332d7db9ad1b5c68ce

View File

@ -15,6 +15,9 @@ deps =
nose
nose-timer
-r{toxinidir}/requirements.txt
requests
cryptography
six
[testenv:cover]
deps =