package mc.protocol.io; import org.apache.commons.lang3.RandomStringUtils; import org.junit.jupiter.api.BeforeAll; 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.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.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; class NetOutputStreamTest { private static Random random; @BeforeAll static void setUp() { random = new Random(System.currentTimeMillis()); } @ParameterizedTest @MethodSource("paramsWriteBoolean") void writeBoolean(boolean sourceValue, byte expectedByte) { NetOutputStream netOutputStream = createNetOutputStream(); netOutputStream.writeBoolean(sourceValue); assertEquals(expectedByte, toByteArray(netOutputStream)[0]); } @ParameterizedTest @MethodSource("paramsWriteByte") void writeByte(byte sourceValue, byte expectedByte) throws IOException { NetOutputStream netOutputStream = createNetOutputStream(); netOutputStream.writeByte(sourceValue); assertEquals(expectedByte, toByteArray(netOutputStream)[0]); netOutputStream = createNetOutputStream(); netOutputStream.write(sourceValue); assertEquals(expectedByte, toByteArray(netOutputStream)[0]); } @ParameterizedTest @MethodSource("paramsWriteString") void writeString(String string) { NetOutputStream netOutputStream = createNetOutputStream(); netOutputStream.writeString(string); byte[] bytes = toByteArray(netOutputStream); int length = bytes[0]; assertEquals(string.codePoints().count(), length); byte[] dataBytes = new byte[bytes.length - 1]; System.arraycopy(bytes, 1, dataBytes, 0, dataBytes.length); assertEquals(string, new String(dataBytes, StandardCharsets.UTF_8)); } @Test void writeString_overSize() { String overSizeString = RandomStringUtils.randomAscii(Short.MAX_VALUE + Short.MAX_VALUE); NetOutputStream netOutputStream = createNetOutputStream(); netOutputStream.writeString(overSizeString); NetInputStream netInputStream = new ByteArrayNetInputStream(toByteArray(netOutputStream)); String actualString = netInputStream.readString(); String expectedString = overSizeString.substring(0, Short.MAX_VALUE); assertEquals(expectedString, actualString); } @ParameterizedTest @MethodSource("paramsWriteVarInt") void writeVarInt(int sourceValue, byte[] expectedBytes) { NetOutputStream netOutputStream = createNetOutputStream(); netOutputStream.writeVarInt(sourceValue); assertArrayEquals(expectedBytes, toByteArray(netOutputStream)); } @ParameterizedTest @MethodSource({"paramsWriteVarInt", "paramsWriteVarLong"}) void writeVarLong(long sourceValue, byte[] expectedBytes) { NetOutputStream netOutputStream = createNetOutputStream(); netOutputStream.writeVarLong(sourceValue); assertArrayEquals(expectedBytes, toByteArray(netOutputStream)); } @Test void writeUUID() { final UUID uuid = UUID.randomUUID(); NetOutputStream netOutputStream = createNetOutputStream(); netOutputStream.writeUUID(uuid); final long mostSignificantBits = uuid.getMostSignificantBits(); final long leastSignificantBits = uuid.getLeastSignificantBits(); assertArrayEquals(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), (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) }, toByteArray(netOutputStream)); } @Test void writeBytes() throws IOException { byte[] bytes = new byte[128]; random.nextBytes(bytes); NetOutputStream netOutputStream = createNetOutputStream(); netOutputStream.writeBytes(bytes); assertArrayEquals(bytes, toByteArray(netOutputStream)); netOutputStream = createNetOutputStream(); netOutputStream.write(bytes); assertArrayEquals(bytes, toByteArray(netOutputStream)); } @Test void write_offset() throws IOException { byte[] bytes = new byte[128]; random.nextBytes(bytes); NetOutputStream netOutputStream = createNetOutputStream(); netOutputStream.write(bytes, 3, 11); byte[] actualBytes = new byte[11]; System.arraycopy(toByteArray(netOutputStream), 0, actualBytes, 0, 11); byte[] expectedBytes = new byte[11]; System.arraycopy(bytes, 3, expectedBytes, 0, 11); assertArrayEquals(expectedBytes, actualBytes); } private NetOutputStream createNetOutputStream() { return new ByteArrayNetOutputStream(); } private byte[] toByteArray(NetOutputStream netOutputStream) { return ((ByteArrayNetOutputStream) netOutputStream).toByteArray(); } private static Stream paramsWriteBoolean() { return Stream.of( Arguments.of(false, (byte) 0x00), Arguments.of(true, (byte) 0x01) ); } private static Stream paramsWriteByte() { byte b = Integer.valueOf(random.nextInt()).byteValue(); return Stream.of( Arguments.of(b, b), Arguments.of((byte) 128, (byte) -128) ); } private static Stream paramsWriteString() { return Stream.of( Arguments.of(""), Arguments.of("Latin"), Arguments.of("Кириллица"), Arguments.of("العربية"), Arguments.of("ﬦﬣﬡ"), // Алфавитные формы представления Arguments.of("\uD800\uDD07") // Эгейские цифры, [один] ); } private static Stream paramsWriteVarInt() { return Stream.of( Arguments.of(120, new byte[]{ 0x78 }), Arguments.of(12000, new byte[]{ (byte) 0xE0, 0x5D }), Arguments.of(120000, new byte[]{ (byte) 0xC0, (byte) 0xA9, 0x07 }), Arguments.of(120000000, new byte[]{ (byte) 0x80, (byte) 0x9C, (byte) 0x9C, (byte) 0x39 }), Arguments.of(1200000000, new byte[]{ (byte) 0x80, (byte) 0x98, (byte) 0x9A, (byte) 0xBC, 0x04 }) ); } private static Stream paramsWriteVarLong() { return Stream.of( Arguments.of( 12_000_000_000L, new byte[]{ (byte) 0x80, (byte) 0xF0, (byte) 0x85, (byte) 0xDA, 0x2C }), Arguments.of( 120_000_000_000L, new byte[]{ (byte) 0x80, (byte) 0xE0, (byte) 0xBA, (byte) 0x84, (byte) 0xBF, 0x03 }), Arguments.of( 12_000_000_000_000L, new byte[]{ (byte) 0x80, (byte) 0x80, (byte) 0xF3, (byte) 0xBD, (byte) 0x9F, (byte) 0xDD, 0x02 }), Arguments.of( 1_200_000_000_000_000L, new byte[]{ (byte) 0x80, (byte) 0x80, (byte) 0xEC, (byte) 0xAD, (byte) 0xCC, (byte) 0xEC, (byte) 0x90, 0x02}), Arguments.of( 120_000_000_000_000_000L, new byte[]{ (byte) 0x80, (byte) 0x80, (byte) 0xB0, (byte) 0xE8, (byte) 0xD3, (byte) 0xEB, (byte) 0x94, (byte) 0xD5, 0x01 }), Arguments.of( Long.MIN_VALUE, new byte[]{ (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, 0x01 }) ); } }