Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New Serializable (no NBT) #273

Closed

Conversation

LiteApplication
Copy link
Contributor

@LiteApplication LiteApplication commented Apr 29, 2024

Changed the way Serializable classes are handled:

Here is how a basic Serializable class looks like:

@final
@dataclass
class ToyClass(Serializable):
    """Toy class for testing demonstrating the use of gen_serializable_test on `Serializable`."""


    # Attributes can be of any type
    a: int
    b: str

    # dataclasses.field() can be used to specify additional metadata

    def serialize_to(self, buf: Buffer):
        """Write the object to a buffer."""
        buf.write_varint(self.a)
        buf.write_utf(self.b)

    @classmethod
    def deserialize(cls, buf: Buffer) -> ToyClass:
        """Deserialize the object from a buffer."""
        a = buf.read_varint()
        if a == 0:
            raise ZeroDivisionError("a must be non-zero")
        b = buf.read_utf()
        return cls(a, b)

    def validate(self) -> None:
        """Validate the object's attributes."""
        if self.a == 0:
            raise ZeroDivisionError("a must be non-zero")
        if len(self.b) > 10:
            raise ValueError("b must be less than 10 characters")

The Serializable class must implement the following methods:

  • serialize_to(buf: Buffer) -> None: Serializes the object to a buffer.
  • deserialize(buf: Buffer) -> Serializable: Deserializes the object from a buffer.
  • validate() -> None: Validates the object's attributes, raising an exception if they are invalid.

Added a test generator for Serializable classes:

The gen_serializable_test function generates tests for Serializable classes. It takes the following arguments:

  • context: The dictionary containing the context in which the generated test class will be placed (e.g. globals()).

    Dictionary updates must reflect in the context. This is the case for globals() but implementation-specific for locals().

  • cls: The Serializable class to generate tests for.

  • fields: A list of fields where the test values will be placed.

    In the example above, the ToyClass class has two fields: a and b.

  • test_data: A list of tuples containing either:

    • ((field1_value, field2_value, ...), expected_bytes): The values of the fields and the expected serialized bytes. This needs to work both ways, i.e. cls(field1_value, field2_value, ...) == cls.deserialize(expected_bytes).
    • ((field1_value, field2_value, ...), exception): The values of the fields and the expected exception when validating the object.
    • (exception, bytes): The expected exception when deserializing the bytes and the bytes to deserialize.

The gen_serializable_test function generates a test class with the following tests:

gen_serializable_test(
    context=globals(),
    cls=ToyClass,
    fields=[("a", int), ("b", str)],
    test_data=[
        ((1, "hello"), b"\x01\x05hello"),
        ((2, "world"), b"\x02\x05world"),
        ((0, "hello"), ZeroDivisionError),
        ((1, "hello world"), ValueError),
        (ZeroDivisionError, b"\x00"),
        (IOError, b"\x01"),
    ],
)

The generated test class will have the following tests:

class TestGenToyClass:
    def test_serialization(self):
        # 2 subtests for the cases 1 and 2
    
    def test_deserialization(self):
        # 2 subtests for the cases 1 and 2

    def test_validation(self):
        # 2 subtests for the cases 3 and 4

    def test_exceptions(self):
        # 2 subtests for the cases 5 and 6

Also this has 100% test coverage for all data types and packets.

@LiteApplication LiteApplication mentioned this pull request Apr 30, 2024
@ItsDrike ItsDrike added p: 2 - normal Normal priority a: API Related to exposed core API of the project t: optimization Optimizations to the code (performance improvements, code cleanup, or other general optimizations) labels Apr 30, 2024
Provide a way to test serialization, deserialization, validation and deserialization errors easily.
This fixes Avoid repetition in tests for serialize+deserialize tests py-mine#64 and makes it easier to add new data types
@LiteApplication
Copy link
Contributor Author

Merge this pull request with #285

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
a: API Related to exposed core API of the project p: 2 - normal Normal priority t: optimization Optimizations to the code (performance improvements, code cleanup, or other general optimizations)
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants