package mc.protocol.io; import mc.protocol.io.coder.ByteArrayNetInputStream; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; import java.io.ByteArrayOutputStream; import java.io.DataOutputStream; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.util.Random; import java.util.UUID; import java.util.stream.Stream; import static org.junit.jupiter.api.Assertions.*; class NetInputStreamTest { private Random random; private ByteArrayOutputStream baos; @BeforeEach void setUp() { random = new Random(System.currentTimeMillis()); baos = new ByteArrayOutputStream(); } @ParameterizedTest @MethodSource("paramsReadBoolean") void readBoolean(byte sourceByte, boolean expectedValue) { baos.write(sourceByte); NetInputStream netInputStream = createNetInputStream(baos.toByteArray()); assertEquals(expectedValue, netInputStream.readBoolean()); } @Test void readByte() { byte b = Integer.valueOf(random.nextInt()).byteValue(); baos.write(b); assertEquals(b, createNetInputStream(baos.toByteArray()).readByte()); assertEquals(b, createNetInputStream(baos.toByteArray()).read()); baos.reset(); baos.write(128); assertEquals(-128, createNetInputStream(baos.toByteArray()).readByte()); } @Test void readUnsignedByte() { int value = 128; baos.write(value); assertEquals(value, createNetInputStream(baos.toByteArray()).readUnsignedByte()); } @Test void readShort() throws IOException { int value = Integer.valueOf(random.nextInt()).shortValue(); createDataOutputStream().writeShort(value); assertEquals(value, createNetInputStream(baos.toByteArray()).readShort()); baos.reset(); createDataOutputStream().writeShort(32768); assertEquals(-32768, createNetInputStream(baos.toByteArray()).readShort()); } @Test void readUnsignedShort() throws IOException { int value = 32768; createDataOutputStream().writeShort(value); assertEquals(value, createNetInputStream(baos.toByteArray()).readUnsignedShort()); } @ParameterizedTest @MethodSource("paramsReadUnsignedByte") void readUnsignedByte(byte sourceByte, int expectedValue) { baos.write(sourceByte); assertEquals(expectedValue, createNetInputStream(baos.toByteArray()).readUnsignedByte()); } @ParameterizedTest @MethodSource("paramsReadString") void readString(String string) throws IOException { final byte[] strBytes = string.getBytes(StandardCharsets.UTF_8); final byte[] bytes = new byte[strBytes.length + 1]; bytes[0] = (byte) string.codePoints().count(); // здесь считается, что размер поместится в один байт System.arraycopy(strBytes, 0, bytes, 1, strBytes.length); baos.write(bytes); assertEquals(string, createNetInputStream(baos.toByteArray()).readString()); } @Test void readString_overSize() throws IOException { String string = "123"; final byte[] strBytes = string.getBytes(StandardCharsets.UTF_8); final byte[] bytes = new byte[strBytes.length + 1]; final int length = string.length(); bytes[0] = (byte) (length + 1); // здесь считается, что размер поместится в один байт System.arraycopy(strBytes, 0, bytes, 1, strBytes.length); baos.write(bytes); assertThrows(DecoderException.class, () -> createNetInputStream(baos.toByteArray()).readString(length)); } @Test void readString_lessZero() throws IOException { String string = "123"; final byte[] strBytes = string.getBytes(StandardCharsets.UTF_8); final byte[] bytes = new byte[strBytes.length + 5]; bytes[0] = (byte) 0xFF; bytes[1] = (byte) 0xFF; bytes[2] = (byte) 0xFF; bytes[3] = (byte) 0xFF; bytes[4] = (byte) 0x0F; System.arraycopy(strBytes, 0, bytes, 5, strBytes.length); baos.write(bytes); assertThrows(DecoderException.class, () -> createNetInputStream(baos.toByteArray()).readString(-1)); } @ParameterizedTest @MethodSource("paramsReadVarInt") void readVarInt(byte[] sourceBytes, int expectedValue) throws IOException { baos.write(sourceBytes); assertEquals(expectedValue, createNetInputStream(baos.toByteArray()).readVarInt()); } @Test void readVarInt_tooBig() throws IOException { baos.write(new byte[]{ (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0x0F }); assertEquals(-1, createNetInputStream(baos.toByteArray()).readVarInt()); } @ParameterizedTest @MethodSource({"paramsReadVarInt", "paramsReadVarLong"}) void readVarLong(byte[] sourceBytes, long expectedValue) throws IOException { baos.write(sourceBytes); assertEquals(expectedValue, createNetInputStream(baos.toByteArray()).readVarLong()); } @Test void readVarLong_tooBig() throws IOException { baos.write(new byte[]{ (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0x0F }); assertEquals(-1, createNetInputStream(baos.toByteArray()).readVarLong()); } @Test void readUUID() throws IOException { final UUID uuid = UUID.randomUUID(); final long mostSignificantBits = uuid.getMostSignificantBits(); final long leastSignificantBits = uuid.getLeastSignificantBits(); baos.write(new byte[]{ (byte) ((mostSignificantBits >>> 56) & 0xFF), (byte) ((mostSignificantBits >>> 48) & 0xFF), (byte) ((mostSignificantBits >>> 40) & 0xFF), (byte) ((mostSignificantBits >>> 32) & 0xFF), (byte) ((mostSignificantBits >>> 24) & 0xFF), (byte) ((mostSignificantBits >>> 16) & 0xFF), (byte) ((mostSignificantBits >>> 8) & 0xFF), (byte) (mostSignificantBits & 0xFF) }); baos.write(new byte[]{ (byte) ((leastSignificantBits >>> 56) & 0xFF), (byte) ((leastSignificantBits >>> 48) & 0xFF), (byte) ((leastSignificantBits >>> 40) & 0xFF), (byte) ((leastSignificantBits >>> 32) & 0xFF), (byte) ((leastSignificantBits >>> 24) & 0xFF), (byte) ((leastSignificantBits >>> 16) & 0xFF), (byte) ((leastSignificantBits >>> 8) & 0xFF), (byte) (leastSignificantBits & 0xFF) }); assertEquals(uuid, createNetInputStream(baos.toByteArray()).readUUID()); } @Test void readBytes() throws IOException { byte[] bytes = new byte[128]; random.nextBytes(bytes); baos.write(bytes); byte[] actualBytes = new byte[128]; createNetInputStream(baos.toByteArray()).readBytes(actualBytes); assertArrayEquals(bytes, actualBytes); actualBytes = new byte[128]; int read = createNetInputStream(baos.toByteArray()).read(actualBytes); assertArrayEquals(bytes, actualBytes); assertEquals(128, read); } @Test void read_offset() throws IOException { byte[] bytes = new byte[128]; random.nextBytes(bytes); baos.write(bytes); byte[] actualBytes = new byte[128]; int read = createNetInputStream(baos.toByteArray()).read(actualBytes, 3, 11); assertEquals(11, read); byte[] buff1 = new byte[11]; System.arraycopy(bytes, 0, buff1, 0, 11); byte[] buff2 = new byte[11]; System.arraycopy(actualBytes, 3, buff2, 0, 11); assertArrayEquals(buff1, buff2); } private NetInputStream createNetInputStream(byte[] buffer) { return new ByteArrayNetInputStream(buffer); } private DataOutputStream createDataOutputStream() { return new DataOutputStream(baos); } private static Stream paramsReadBoolean() { return Stream.of( Arguments.of((byte) 0x00, false), Arguments.of((byte) 0x01, true) ); } private static Stream paramsReadUnsignedByte() { return Stream.of( Arguments.of((byte) 30, 30), Arguments.of((byte) (0xFF & 130), 130) ); } private static Stream paramsReadString() { return Stream.of( Arguments.of(""), Arguments.of("Latin"), Arguments.of("Кириллица"), Arguments.of("العربية"), Arguments.of("ﬦﬣﬡ"), // Алфавитные формы представления Arguments.of("\uD800\uDD07") // Эгейские цифры, [один] ); } private static Stream paramsReadVarInt() { return Stream.of( Arguments.of(new byte[]{ 0x78 }, 120), Arguments.of(new byte[]{ (byte) 0xE0, 0x5D }, 12000), Arguments.of(new byte[]{ (byte) 0xC0, (byte) 0xA9, 0x07 }, 120000), Arguments.of(new byte[]{ (byte) 0x80, (byte) 0x9C, (byte) 0x9C, (byte) 0x39 }, 120_000_000), Arguments.of(new byte[]{ (byte) 0x80, (byte) 0x98, (byte) 0x9A, (byte) 0xBC, 0x04 }, 1_200_000_000) ); } private static Stream paramsReadVarLong() { return Stream.of( Arguments.of( new byte[]{ (byte) 0x80, (byte) 0xF0, (byte) 0x85, (byte) 0xDA, 0x2C }, 12_000_000_000L), Arguments.of( new byte[]{ (byte) 0x80, (byte) 0xE0, (byte) 0xBA, (byte) 0x84, (byte) 0xBF, 0x03 }, 120_000_000_000L), Arguments.of( new byte[]{ (byte) 0x80, (byte) 0x80, (byte) 0xF3, (byte) 0xBD, (byte) 0x9F, (byte) 0xDD, 0x02 }, 12_000_000_000_000L), Arguments.of( new byte[]{ (byte) 0x80, (byte) 0x80, (byte) 0xEC, (byte) 0xAD, (byte) 0xCC, (byte) 0xEC, (byte) 0x90, 0x02}, 1_200_000_000_000_000L), Arguments.of( new byte[]{ (byte) 0x80, (byte) 0x80, (byte) 0xB0, (byte) 0xE8, (byte) 0xD3, (byte) 0xEB, (byte) 0x94, (byte) 0xD5, 0x01 }, 120_000_000_000_000_000L), Arguments.of( new byte[]{ (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, 0x01 }, Long.MIN_VALUE) ); } }