Skip to content

Commit

Permalink
Tests for VarInt
Browse files Browse the repository at this point in the history
Removed unused import (SerializationError)

We now check if Datatype.SIZE is a number using ABC.

Added 2 decorators for raising serialized and deserialized data
exceptions.

Datatype.SIZE can now be either a number or a sequence. If sequence,
first value is MIN_SIZE and second value is MAX_SIZE

VarInt.serialize now raises ValueError instead of SerializationError
when number is too big to serialize.
  • Loading branch information
JeppeKlitgaard committed Apr 16, 2015
1 parent e1f8f02 commit 55ff270
Show file tree
Hide file tree
Showing 2 changed files with 65 additions and 10 deletions.
69 changes: 59 additions & 10 deletions minecraft/networking/datatypes.py
Expand Up @@ -18,19 +18,52 @@
"VarInt", "VarLong",
"String"]

from minecraft.exceptions import DeserializationError, SerializationError
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
Expand Down Expand Up @@ -65,9 +98,8 @@ def read(cls, fileobject):
return cls.deserialize(bin_data)

@classmethod
@raise_deserialization_data
def deserialize(cls, data):
cls.raise_deserialization_data(data)

deserialized_data = struct.unpack(ENDIANNESS + cls.FORMAT, data)[0]
return deserialized_data

Expand All @@ -76,9 +108,8 @@ def write(cls, fileobject, data):
return fileobject.write(cls.serialize(data))

@classmethod
@raise_serialization_data
def serialize(cls, data):
cls.raise_serialization_data(data)

serialized_data = struct.pack(ENDIANNESS + cls.FORMAT, data)
return serialized_data

Expand Down Expand Up @@ -137,11 +168,23 @@ def raise_deserialization_data(cls, data):

raise TypeError(err)

if cls.SIZE != len(data):
err = "'data' must have a length of {}, not {}"
err = err.format(str(cls.SIZE), str(len(data)))
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)
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

Expand Down Expand Up @@ -304,6 +347,8 @@ class VarInt(NumberDatatype):
# 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
Expand All @@ -329,16 +374,18 @@ def read(cls, fileobject):
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 SerializationError(e)
raise ValueError(e)

result = bytes() # Where we store the serialized number

Expand Down Expand Up @@ -370,6 +417,8 @@ class VarLong(VarInt):

MAX_SIZE = list(SIZE_TABLE.items())[-1][-1]

SIZE = (1, MAX_SIZE)


class String(Datatype):
FORMAT = "utf-8"
Expand Down
6 changes: 6 additions & 0 deletions tests/test_datatypes.py
Expand Up @@ -308,6 +308,12 @@ class DoubleTest(FloatTest):
(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
Expand Down

0 comments on commit 55ff270

Please sign in to comment.