conflicts
This commit is contained in:
commit
07f0ca8abd
|
@ -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
|
|
@ -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.
|
||||
"""
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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()
|
|
@ -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)
|
|
@ -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
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue