More refactoring to make the system more extensible.
This commit is contained in:
		
							parent
							
								
									1d45822c67
								
							
						
					
					
						commit
						c25232c3ec
					
				| 
						 | 
				
			
			@ -1,142 +1,35 @@
 | 
			
		|||
package nl.andrewl.record_net;
 | 
			
		||||
 | 
			
		||||
import nl.andrewl.record_net.util.Pair;
 | 
			
		||||
 | 
			
		||||
import java.lang.reflect.Constructor;
 | 
			
		||||
import java.lang.reflect.RecordComponent;
 | 
			
		||||
import java.util.Arrays;
 | 
			
		||||
import java.util.HashMap;
 | 
			
		||||
import java.util.Map;
 | 
			
		||||
import java.util.function.Function;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Record containing the components needed to read and write a given message.
 | 
			
		||||
 * <p>
 | 
			
		||||
 *     Also contains methods for automatically generating message type
 | 
			
		||||
 *     implementations for standard record-based messages.
 | 
			
		||||
 * </p>
 | 
			
		||||
 * @param <T> The type of message.
 | 
			
		||||
 * @param messageClass The class of the message.
 | 
			
		||||
 * @param byteSizeFunction A function that computes the byte size of the message.
 | 
			
		||||
 * @param reader A reader that can read messages from an input stream.
 | 
			
		||||
 * @param writer A writer that write messages from an input stream.
 | 
			
		||||
 * A type serializer provides the basic components needed to read and write
 | 
			
		||||
 * instances of the given message type.
 | 
			
		||||
 * @param <T> The message type.
 | 
			
		||||
 */
 | 
			
		||||
public record MessageTypeSerializer<T extends Message>(
 | 
			
		||||
		Class<T> messageClass,
 | 
			
		||||
		Function<T, Integer> byteSizeFunction,
 | 
			
		||||
		MessageReader<T> reader,
 | 
			
		||||
		MessageWriter<T> writer
 | 
			
		||||
) {
 | 
			
		||||
	/**
 | 
			
		||||
	 * An internal cache for storing generated type serializers.
 | 
			
		||||
	 */
 | 
			
		||||
	private static final Map<Pair<Class<?>, Serializer>, MessageTypeSerializer<?>> generatedMessageTypes = new HashMap<>();
 | 
			
		||||
public interface MessageTypeSerializer<T extends Message> {
 | 
			
		||||
    /**
 | 
			
		||||
     * Gets the class of the message type that this serializer handles.
 | 
			
		||||
     * @return The message class.
 | 
			
		||||
     */
 | 
			
		||||
    Class<T> messageClass();
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Gets the {@link MessageTypeSerializer} instance for a given message class, and
 | 
			
		||||
	 * generates a new implementation if none exists yet.
 | 
			
		||||
	 * @param serializer The serializer context to get a type serializer for.
 | 
			
		||||
	 * @param messageClass The class of the message to get a type for.
 | 
			
		||||
	 * @param <T> The type of the message.
 | 
			
		||||
	 * @return The message type.
 | 
			
		||||
	 */
 | 
			
		||||
	@SuppressWarnings("unchecked")
 | 
			
		||||
	public static <T extends Message> MessageTypeSerializer<T> get(Serializer serializer, Class<T> messageClass) {
 | 
			
		||||
		return (MessageTypeSerializer<T>) generatedMessageTypes.computeIfAbsent(
 | 
			
		||||
				new Pair<>(messageClass, serializer),
 | 
			
		||||
				p -> generateForRecord(serializer, (Class<T>) p.first())
 | 
			
		||||
		);
 | 
			
		||||
	}
 | 
			
		||||
    /**
 | 
			
		||||
     * Gets a function that computes the size, in bytes, of messages of this
 | 
			
		||||
     * serializer's type.
 | 
			
		||||
     * @return A byte size function.
 | 
			
		||||
     */
 | 
			
		||||
    Function<T, Integer> byteSizeFunction();
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Generates a message type instance for a given class, using reflection to
 | 
			
		||||
	 * introspect the fields of the message.
 | 
			
		||||
	 * <p>
 | 
			
		||||
	 *     Note that this only works for record-based messages.
 | 
			
		||||
	 * </p>
 | 
			
		||||
	 * @param serializer The serializer context to get a type serializer for.
 | 
			
		||||
	 * @param messageTypeClass The class of the message type.
 | 
			
		||||
	 * @param <T> The type of the message.
 | 
			
		||||
	 * @return A message type instance.
 | 
			
		||||
	 */
 | 
			
		||||
	public static <T extends Message> MessageTypeSerializer<T> generateForRecord(Serializer serializer, Class<T> messageTypeClass) {
 | 
			
		||||
		RecordComponent[] components = messageTypeClass.getRecordComponents();
 | 
			
		||||
		if (components == null) throw new IllegalArgumentException("Cannot generate a MessageTypeSerializer for non-record class " + messageTypeClass.getSimpleName());
 | 
			
		||||
		Constructor<T> constructor;
 | 
			
		||||
		try {
 | 
			
		||||
			constructor = messageTypeClass.getDeclaredConstructor(Arrays.stream(components)
 | 
			
		||||
					.map(RecordComponent::getType).toArray(Class<?>[]::new));
 | 
			
		||||
		} catch (NoSuchMethodException e) {
 | 
			
		||||
			throw new IllegalArgumentException(e);
 | 
			
		||||
		}
 | 
			
		||||
		return new MessageTypeSerializer<>(
 | 
			
		||||
				messageTypeClass,
 | 
			
		||||
				generateByteSizeFunction(serializer, components),
 | 
			
		||||
				generateReader(constructor),
 | 
			
		||||
				generateWriter(components)
 | 
			
		||||
		);
 | 
			
		||||
	}
 | 
			
		||||
    /**
 | 
			
		||||
     * Gets a component that can read messages from an input stream.
 | 
			
		||||
     * @return The message reader.
 | 
			
		||||
     */
 | 
			
		||||
    MessageReader<T> reader();
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Generates a function implementation that counts the byte size of a
 | 
			
		||||
	 * message based on the message's record component types.
 | 
			
		||||
	 * @param serializer The serializer context to generate a function for.
 | 
			
		||||
	 * @param components The list of components that make up the message.
 | 
			
		||||
	 * @param <T> The message type.
 | 
			
		||||
	 * @return A function that computes the byte size of a message of the given
 | 
			
		||||
	 * type.
 | 
			
		||||
	 */
 | 
			
		||||
	private static <T extends Message> Function<T, Integer> generateByteSizeFunction(Serializer serializer, RecordComponent[] components) {
 | 
			
		||||
		return msg -> {
 | 
			
		||||
			int size = 0;
 | 
			
		||||
			for (var component : components) {
 | 
			
		||||
				try {
 | 
			
		||||
					size += MessageUtils.getByteSize(serializer, component.getAccessor().invoke(msg));
 | 
			
		||||
				} catch (ReflectiveOperationException e) {
 | 
			
		||||
					throw new IllegalStateException(e);
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			return size;
 | 
			
		||||
		};
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Generates a message reader for the given message constructor method. It
 | 
			
		||||
	 * will try to read objects from the input stream according to the
 | 
			
		||||
	 * parameters of the canonical constructor of a message record.
 | 
			
		||||
	 * @param constructor The canonical constructor of the message record.
 | 
			
		||||
	 * @param <T> The message type.
 | 
			
		||||
	 * @return A message reader for the given type.
 | 
			
		||||
	 */
 | 
			
		||||
	private static <T extends Message> MessageReader<T> generateReader(Constructor<T> constructor) {
 | 
			
		||||
		return in -> {
 | 
			
		||||
			Object[] values = new Object[constructor.getParameterCount()];
 | 
			
		||||
			for (int i = 0; i < values.length; i++) {
 | 
			
		||||
				values[i] = in.readObject(constructor.getParameterTypes()[i]);
 | 
			
		||||
			}
 | 
			
		||||
			try {
 | 
			
		||||
				return constructor.newInstance(values);
 | 
			
		||||
			} catch (ReflectiveOperationException e) {
 | 
			
		||||
				throw new IllegalStateException(e);
 | 
			
		||||
			}
 | 
			
		||||
		};
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Generates a message writer for the given message record components.
 | 
			
		||||
	 * @param components The record components to write.
 | 
			
		||||
	 * @param <T> The type of message.
 | 
			
		||||
	 * @return The message writer for the given type.
 | 
			
		||||
	 */
 | 
			
		||||
	private static <T extends Message> MessageWriter<T> generateWriter(RecordComponent[] components) {
 | 
			
		||||
		return (msg, out) -> {
 | 
			
		||||
			for (var component: components) {
 | 
			
		||||
				try {
 | 
			
		||||
					out.writeObject(component.getAccessor().invoke(msg), component.getType());
 | 
			
		||||
				} catch (ReflectiveOperationException e) {
 | 
			
		||||
					throw new IllegalStateException(e);
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		};
 | 
			
		||||
	}
 | 
			
		||||
    /**
 | 
			
		||||
     * Gets a component that can write messages to an output stream.
 | 
			
		||||
     * @return The message writer.
 | 
			
		||||
     */
 | 
			
		||||
    MessageWriter<T> writer();
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,18 @@
 | 
			
		|||
package nl.andrewl.record_net;
 | 
			
		||||
 | 
			
		||||
import java.util.function.Function;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Record containing the components needed to read and write a given message.
 | 
			
		||||
 * @param <T> The type of message.
 | 
			
		||||
 * @param messageClass The class of the message.
 | 
			
		||||
 * @param byteSizeFunction A function that computes the byte size of the message.
 | 
			
		||||
 * @param reader A reader that can read messages from an input stream.
 | 
			
		||||
 * @param writer A writer that write messages from an input stream.
 | 
			
		||||
 */
 | 
			
		||||
public record MessageTypeSerializerImpl<T extends Message>(
 | 
			
		||||
		Class<T> messageClass,
 | 
			
		||||
		Function<T, Integer> byteSizeFunction,
 | 
			
		||||
		MessageReader<T> reader,
 | 
			
		||||
		MessageWriter<T> writer
 | 
			
		||||
) implements MessageTypeSerializer<T> {}
 | 
			
		||||
| 
						 | 
				
			
			@ -40,7 +40,7 @@ public class MessageUtils {
 | 
			
		|||
		if (msg == null) {
 | 
			
		||||
			return 1;
 | 
			
		||||
		} else {
 | 
			
		||||
			MessageTypeSerializer<T> typeSerializer = (MessageTypeSerializer<T>) serializer.getTypeSerializer(msg.getClass());
 | 
			
		||||
			MessageTypeSerializerImpl<T> typeSerializer = (MessageTypeSerializerImpl<T>) serializer.getTypeSerializer(msg.getClass());
 | 
			
		||||
			return 1 + typeSerializer.byteSizeFunction().apply(msg);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -2,6 +2,7 @@ package nl.andrewl.record_net;
 | 
			
		|||
 | 
			
		||||
import nl.andrewl.record_net.util.ExtendedDataInputStream;
 | 
			
		||||
import nl.andrewl.record_net.util.ExtendedDataOutputStream;
 | 
			
		||||
import nl.andrewl.record_net.util.RecordMessageTypeSerializer;
 | 
			
		||||
 | 
			
		||||
import java.io.*;
 | 
			
		||||
import java.util.HashMap;
 | 
			
		||||
| 
						 | 
				
			
			@ -37,21 +38,28 @@ public class Serializer {
 | 
			
		|||
	 * their ids.
 | 
			
		||||
	 * @param messageTypes A map containing message types mapped to their ids.
 | 
			
		||||
	 */
 | 
			
		||||
	public Serializer(Map<Byte, Class<? extends Message>> messageTypes) {
 | 
			
		||||
	public Serializer(Map<Integer, Class<? extends Message>> messageTypes) {
 | 
			
		||||
		messageTypes.forEach(this::registerType);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Helper method which registers a message type to be supported by the
 | 
			
		||||
	 * serializer, by adding it to the normal and inverse mappings.
 | 
			
		||||
	 * Helper method for registering a message type serializer for a record
 | 
			
		||||
	 * class, using {@link RecordMessageTypeSerializer#generateForRecord(Serializer, Class)}.
 | 
			
		||||
	 * @param id The byte which will be used to identify messages of the given
 | 
			
		||||
	 *           class. The value should from 0 to 127.
 | 
			
		||||
	 * @param messageClass The type of message associated with the given id.
 | 
			
		||||
	 */
 | 
			
		||||
	public synchronized <T extends Message> void registerType(int id, Class<T> messageClass) {
 | 
			
		||||
		registerTypeSerializer(id, MessageTypeSerializer.generateForRecord(this, messageClass));
 | 
			
		||||
		registerTypeSerializer(id, RecordMessageTypeSerializer.generateForRecord(this, messageClass));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Registers the given type serializer with the given id.
 | 
			
		||||
	 * @param id The id to use.
 | 
			
		||||
	 * @param typeSerializer The type serializer that will be associated with
 | 
			
		||||
	 *                       the given id.
 | 
			
		||||
	 * @param <T> The message type.
 | 
			
		||||
	 */
 | 
			
		||||
	public synchronized <T extends Message> void registerTypeSerializer(int id, MessageTypeSerializer<T> typeSerializer) {
 | 
			
		||||
		if (id < 0 || id > 127) throw new IllegalArgumentException("Invalid id.");
 | 
			
		||||
		messageTypes.put((byte) id, typeSerializer);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -85,10 +85,10 @@ public class ExtendedDataInputStream extends DataInputStream {
 | 
			
		|||
			int length = this.readInt();
 | 
			
		||||
			return this.readNBytes(length);
 | 
			
		||||
		} else if (type.isArray() && Message.class.isAssignableFrom(type.getComponentType())) {
 | 
			
		||||
			var messageType = MessageTypeSerializer.get(serializer, (Class<? extends Message>) type.getComponentType());
 | 
			
		||||
			var messageType = RecordMessageTypeSerializer.get(serializer, (Class<? extends Message>) type.getComponentType());
 | 
			
		||||
			return this.readArray(messageType);
 | 
			
		||||
		} else if (Message.class.isAssignableFrom(type)) {
 | 
			
		||||
			var messageType = MessageTypeSerializer.get(serializer, (Class<? extends Message>) type);
 | 
			
		||||
			var messageType = RecordMessageTypeSerializer.get(serializer, (Class<? extends Message>) type);
 | 
			
		||||
			return messageType.reader().read(this);
 | 
			
		||||
		} else {
 | 
			
		||||
			throw new IOException("Unsupported object type: " + type.getSimpleName());
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,129 @@
 | 
			
		|||
package nl.andrewl.record_net.util;
 | 
			
		||||
 | 
			
		||||
import nl.andrewl.record_net.*;
 | 
			
		||||
 | 
			
		||||
import java.lang.reflect.Constructor;
 | 
			
		||||
import java.lang.reflect.RecordComponent;
 | 
			
		||||
import java.util.Arrays;
 | 
			
		||||
import java.util.HashMap;
 | 
			
		||||
import java.util.Map;
 | 
			
		||||
import java.util.function.Function;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Helper class that contains logic for generating {@link MessageTypeSerializerImpl}
 | 
			
		||||
 * implementations for record classes.
 | 
			
		||||
 */
 | 
			
		||||
public class RecordMessageTypeSerializer {
 | 
			
		||||
    /**
 | 
			
		||||
     * An internal cache for storing generated type serializers.
 | 
			
		||||
     */
 | 
			
		||||
    private static final Map<Pair<Class<?>, Serializer>, MessageTypeSerializer<?>> generatedMessageTypes = new HashMap<>();
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Gets the {@link MessageTypeSerializer} instance for a given message class, and
 | 
			
		||||
     * generates a new implementation if none exists yet.
 | 
			
		||||
     * @param serializer The serializer context to get a type serializer for.
 | 
			
		||||
     * @param messageClass The class of the message to get a type for.
 | 
			
		||||
     * @param <T> The type of the message.
 | 
			
		||||
     * @return The message type.
 | 
			
		||||
     */
 | 
			
		||||
    @SuppressWarnings("unchecked")
 | 
			
		||||
    public static <T extends Message> MessageTypeSerializer<T> get(Serializer serializer, Class<T> messageClass) {
 | 
			
		||||
        return (MessageTypeSerializer<T>) generatedMessageTypes.computeIfAbsent(
 | 
			
		||||
                new Pair<>(messageClass, serializer),
 | 
			
		||||
                p -> generateForRecord(serializer, (Class<T>) p.first())
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Generates a message type instance for a given class, using reflection to
 | 
			
		||||
     * introspect the fields of the message.
 | 
			
		||||
     * <p>
 | 
			
		||||
     *     Note that this only works for record-based messages.
 | 
			
		||||
     * </p>
 | 
			
		||||
     * @param serializer The serializer context to get a type serializer for.
 | 
			
		||||
     * @param messageTypeClass The class of the message type.
 | 
			
		||||
     * @param <T> The type of the message.
 | 
			
		||||
     * @return A message type instance.
 | 
			
		||||
     */
 | 
			
		||||
    public static <T extends Message> MessageTypeSerializerImpl<T> generateForRecord(Serializer serializer, Class<T> messageTypeClass) {
 | 
			
		||||
        RecordComponent[] components = messageTypeClass.getRecordComponents();
 | 
			
		||||
        if (components == null) throw new IllegalArgumentException("Cannot generate a MessageTypeSerializer for non-record class " + messageTypeClass.getSimpleName());
 | 
			
		||||
        Constructor<T> constructor;
 | 
			
		||||
        try {
 | 
			
		||||
            constructor = messageTypeClass.getDeclaredConstructor(Arrays.stream(components)
 | 
			
		||||
                    .map(RecordComponent::getType).toArray(Class<?>[]::new));
 | 
			
		||||
        } catch (NoSuchMethodException e) {
 | 
			
		||||
            throw new IllegalArgumentException(e);
 | 
			
		||||
        }
 | 
			
		||||
        return new MessageTypeSerializerImpl<>(
 | 
			
		||||
                messageTypeClass,
 | 
			
		||||
                generateByteSizeFunction(serializer, components),
 | 
			
		||||
                generateReader(constructor),
 | 
			
		||||
                generateWriter(components)
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Generates a function implementation that counts the byte size of a
 | 
			
		||||
     * message based on the message's record component types.
 | 
			
		||||
     * @param serializer The serializer context to generate a function for.
 | 
			
		||||
     * @param components The list of components that make up the message.
 | 
			
		||||
     * @param <T> The message type.
 | 
			
		||||
     * @return A function that computes the byte size of a message of the given
 | 
			
		||||
     * type.
 | 
			
		||||
     */
 | 
			
		||||
    private static <T extends Message> Function<T, Integer> generateByteSizeFunction(Serializer serializer, RecordComponent[] components) {
 | 
			
		||||
        return msg -> {
 | 
			
		||||
            int size = 0;
 | 
			
		||||
            for (var component : components) {
 | 
			
		||||
                try {
 | 
			
		||||
                    size += MessageUtils.getByteSize(serializer, component.getAccessor().invoke(msg));
 | 
			
		||||
                } catch (ReflectiveOperationException e) {
 | 
			
		||||
                    throw new IllegalStateException(e);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            return size;
 | 
			
		||||
        };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Generates a message reader for the given message constructor method. It
 | 
			
		||||
     * will try to read objects from the input stream according to the
 | 
			
		||||
     * parameters of the canonical constructor of a message record.
 | 
			
		||||
     * @param constructor The canonical constructor of the message record.
 | 
			
		||||
     * @param <T> The message type.
 | 
			
		||||
     * @return A message reader for the given type.
 | 
			
		||||
     */
 | 
			
		||||
    private static <T extends Message> MessageReader<T> generateReader(Constructor<T> constructor) {
 | 
			
		||||
        return in -> {
 | 
			
		||||
            Object[] values = new Object[constructor.getParameterCount()];
 | 
			
		||||
            for (int i = 0; i < values.length; i++) {
 | 
			
		||||
                values[i] = in.readObject(constructor.getParameterTypes()[i]);
 | 
			
		||||
            }
 | 
			
		||||
            try {
 | 
			
		||||
                return constructor.newInstance(values);
 | 
			
		||||
            } catch (ReflectiveOperationException e) {
 | 
			
		||||
                throw new IllegalStateException(e);
 | 
			
		||||
            }
 | 
			
		||||
        };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Generates a message writer for the given message record components.
 | 
			
		||||
     * @param components The record components to write.
 | 
			
		||||
     * @param <T> The type of message.
 | 
			
		||||
     * @return The message writer for the given type.
 | 
			
		||||
     */
 | 
			
		||||
    private static <T extends Message> MessageWriter<T> generateWriter(RecordComponent[] components) {
 | 
			
		||||
        return (msg, out) -> {
 | 
			
		||||
            for (var component: components) {
 | 
			
		||||
                try {
 | 
			
		||||
                    out.writeObject(component.getAccessor().invoke(msg), component.getType());
 | 
			
		||||
                } catch (ReflectiveOperationException e) {
 | 
			
		||||
                    throw new IllegalStateException(e);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        };
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -3,6 +3,7 @@ package nl.andrewl.record_net;
 | 
			
		|||
import nl.andrewl.record_net.msg.ChatMessage;
 | 
			
		||||
import nl.andrewl.record_net.util.ExtendedDataInputStream;
 | 
			
		||||
import nl.andrewl.record_net.util.ExtendedDataOutputStream;
 | 
			
		||||
import nl.andrewl.record_net.util.RecordMessageTypeSerializer;
 | 
			
		||||
import org.junit.jupiter.api.Test;
 | 
			
		||||
 | 
			
		||||
import java.io.ByteArrayOutputStream;
 | 
			
		||||
| 
						 | 
				
			
			@ -11,11 +12,11 @@ import java.io.IOException;
 | 
			
		|||
import static org.junit.jupiter.api.Assertions.assertEquals;
 | 
			
		||||
import static org.junit.jupiter.api.Assertions.assertThrows;
 | 
			
		||||
 | 
			
		||||
public class MessageTypeSerializerTest {
 | 
			
		||||
public class RecordMessageTypeSerializerTest {
 | 
			
		||||
    @Test
 | 
			
		||||
    public void testGenerateForRecord() throws IOException {
 | 
			
		||||
        Serializer serializer = new Serializer();
 | 
			
		||||
        var s1 = MessageTypeSerializer.get(serializer, ChatMessage.class);
 | 
			
		||||
        var s1 = RecordMessageTypeSerializer.get(serializer, ChatMessage.class);
 | 
			
		||||
        ChatMessage msg = new ChatMessage("andrew", 123, "Hello world!");
 | 
			
		||||
        int expectedByteSize = 4 + msg.username().length() + 8 + 4 + msg.message().length();
 | 
			
		||||
        assertEquals(expectedByteSize, s1.byteSizeFunction().apply(msg));
 | 
			
		||||
| 
						 | 
				
			
			@ -30,6 +31,6 @@ public class MessageTypeSerializerTest {
 | 
			
		|||
 | 
			
		||||
        // Only record classes can be generated.
 | 
			
		||||
        class NonRecordMessage implements Message {}
 | 
			
		||||
        assertThrows(IllegalArgumentException.class, () -> MessageTypeSerializer.get(serializer, NonRecordMessage.class));
 | 
			
		||||
        assertThrows(IllegalArgumentException.class, () -> RecordMessageTypeSerializer.get(serializer, NonRecordMessage.class));
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
		Loading…
	
		Reference in New Issue