diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index d761ec4..51da419 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -33,7 +33,7 @@ jobs: run: chmod +x gradlew - name: Build and Test - run: ./gradlew build test spotlessCheck + run: ./gradlew build test spotlessCheck koverXmlReport publish: name: Publish diff --git a/.github/workflows/pullrequest-build.yml b/.github/workflows/pullrequest-build.yml index e6215fd..f48735d 100644 --- a/.github/workflows/pullrequest-build.yml +++ b/.github/workflows/pullrequest-build.yml @@ -27,4 +27,4 @@ jobs: run: chmod +x gradlew - name: Build, Test, and Check - run: ./gradlew build test spotlessCheck + run: ./gradlew build test spotlessCheck koverXmlReport diff --git a/.gitignore b/.gitignore index 718b1a7..9926af7 100644 --- a/.gitignore +++ b/.gitignore @@ -4,4 +4,5 @@ build/ docs-publishing/ runs/ +logs/ *.iml diff --git a/build.gradle.kts b/build.gradle.kts index 95492a9..bee4d6f 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -14,6 +14,7 @@ plugins { alias(libs.plugins.changelog) alias(libs.plugins.dokka) alias(libs.plugins.minotaur) + alias(libs.plugins.kotlinx.kover) `maven-publish` } @@ -38,7 +39,10 @@ dependencies { modImplementation(libs.fabric.loader) modImplementation(fabricApi.module("fabric-networking-api-v1", libs.versions.fabricApi.get())) implementation(libs.kotlinx.serializationCore) + testImplementation(libs.kotlin.test) + testImplementation(libs.kotlin.test.junit5) + testImplementation(libs.junit.params) } tasks { diff --git a/libs.versions.toml b/libs.versions.toml index 2401df8..c99b345 100644 --- a/libs.versions.toml +++ b/libs.versions.toml @@ -5,7 +5,9 @@ fabricApi = "0.100.7+1.21" # https://github.com/FabricMC/fabric/releases fabricKotlin = "1.11.0+kotlin.2.0.0" # https://github.com/FabricMC/fabric-language-kotlin/releases fabricLoader = "0.16.0" # https://github.com/FabricMC/fabric-loader/releases fabricLoom = "1.7-SNAPSHOT" # https://github.com/FabricMC/fabric-loom/releases +junit-params = "5.10.1" # check which version of junit-jupiter-api/engine kotlin-test adds kotlin = "2.0.0" +kotlinx-kover = "0.8.3" #https://github.com/Kotlin/kotlinx-kover/releases kotlinxSerialization = "1.7.1" # https://github.com/Kotlin/kotlinx.serialization/releases minecraft = "1.21" minotaur = "2.8.7" # https://github.com/modrinth/minotaur/releases @@ -16,7 +18,9 @@ dokka-pluginBase = { group = "org.jetbrains.dokka", name = "dokka-base", version dokka-pluginVersioning = { group = "org.jetbrains.dokka", name = "versioning-plugin", version.ref = "dokka" } fabric-kotlin = { group = "net.fabricmc", name = "fabric-language-kotlin", version.ref = "fabricKotlin" } fabric-loader = { group = "net.fabricmc", name = "fabric-loader", version.ref = "fabricLoader" } +junit-params = { group = "org.junit.jupiter", name = "junit-jupiter-params", version.ref = "junit-params" } kotlin-test = { group = "org.jetbrains.kotlin", name = "kotlin-test" } +kotlin-test-junit5 = { group = "org.jetbrains.kotlin", name = "kotlin-test-junit5" } kotlinx-serializationCore = { group = "org.jetbrains.kotlinx", name = "kotlinx-serialization-core", version.ref = "kotlinxSerialization" } minecraft = { group = "com.mojang", name = "minecraft", version.ref = "minecraft" } @@ -25,6 +29,7 @@ changelog = { id = "org.jetbrains.changelog", version.ref = "changelog" } dokka = { id = "org.jetbrains.dokka", version.ref = "dokka" } kotlin = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" } kotlin-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" } +kotlinx-kover = { id = "org.jetbrains.kotlinx.kover", version.ref = "kotlinx-kover" } loom = { id = "fabric-loom", version.ref = "fabricLoom" } minotaur = { id = "com.modrinth.minotaur", version.ref = "minotaur" } spotless = { id = "com.diffplug.spotless", version.ref = "spotless" } diff --git a/src/test/kotlin/EncodeDecodeTests.kt b/src/test/kotlin/EncodeDecodeTests.kt new file mode 100644 index 0000000..593df9b --- /dev/null +++ b/src/test/kotlin/EncodeDecodeTests.kt @@ -0,0 +1,217 @@ +/* + * Copyright (C) 2024 Wanderia - All Rights Reserved + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ +@file:OptIn(ExperimentalSerializationApi::class) + +import dev.wanderia.netlib.format.decodeFrom +import dev.wanderia.netlib.format.encodeTo +import dev.wanderia.netlib.payload.api.PayloadChannel +import java.util.UUID +import java.util.stream.Stream +import kotlin.random.Random +import kotlinx.serialization.ExperimentalSerializationApi +import kotlinx.serialization.Serializable +import net.fabricmc.fabric.api.networking.v1.PacketByteBufs +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.Arguments +import org.junit.jupiter.params.provider.MethodSource + +class EncodeDecodeTests { + + @Serializable + data class TestDataA( + val boolean: Boolean?, + val byte: Byte?, + val char: Char?, + val double: Double?, + val float: Float?, + val int: Int?, + val long: Long?, + val short: Short?, + val string: String?, + val enum: PayloadChannel? + ) + + @Serializable + data class TestDataB( + val booleanCollection: List, + val byteCollection: List, + val charCollection: List, + val doubleCollection: List, + val floatCollection: List, + val intCollection: List, + val longCollection: List, + val shortCollection: List, + val stringCollection: List, + val enumCollection: List + ) + + @Serializable + data class TestDataC( + val structureA: TestDataA, + val structureB: TestDataB, + val collectionA: List, + val collectionB: List, + ) + + @ParameterizedTest + @MethodSource("testDataAProvider") + fun `should encode and decode values`(data: TestDataA) { + // given + val buf = PacketByteBufs.create() + + // when + encodeTo(buf, data) + + // then + assertEquals(data, decodeFrom(buf)) + } + + @ParameterizedTest + @MethodSource("testDataBProvider") + fun `should encode and decode collections`(data: TestDataB) { + // given + val buf = PacketByteBufs.create() + + // when + encodeTo(buf, data) + + // then + assertEquals(data, decodeFrom(buf)) + } + + @ParameterizedTest + @MethodSource("testDataCProvider") + fun `should encode and decode structures`(data: TestDataC) { + // given + val buf = PacketByteBufs.create() + + // when + encodeTo(buf, data) + + // then + assertEquals(data, decodeFrom(buf)) + } + + companion object { + @JvmStatic + fun testDataAProvider(): Stream = + Stream.of( + Arguments.of(randomDataA(0)), + Arguments.of(randomDataA(1)), + Arguments.of(randomDataA(2)), + Arguments.of(randomDataA(3)), + Arguments.of(randomDataA(4)), + ) + + @JvmStatic + fun testDataBProvider(): Stream = + Stream.of( + Arguments.of(randomDataB()), + Arguments.of(randomDataB()), + Arguments.of(randomDataB()), + Arguments.of(randomDataB()), + Arguments.of(randomDataB()), + ) + + @JvmStatic + fun testDataCProvider(): Stream = + Stream.of( + Arguments.of(randomDataC()), + Arguments.of(randomDataC()), + Arguments.of(randomDataC()), + Arguments.of(randomDataC()), + Arguments.of(randomDataC()), + ) + + @JvmStatic + fun randomDataA(index: Int): TestDataA = + TestDataA( + boolean = listOf(null, true, false)[index.coerceIn(0, 2)], + byte = + listOf(null, 0b0, 0b1, (Byte.MIN_VALUE..Byte.MAX_VALUE).random().toByte())[ + index.coerceIn(0, 3)], + char = + listOf(null, ('a'..'z').random(), (Char.MIN_VALUE..Char.MAX_VALUE).random())[ + index.coerceIn(0, 2)], + double = listOf(null, 0.0, -1.0, 1.0, Random.nextDouble())[index.coerceIn(0, 4)], + float = listOf(null, 0f, -1f, 1f, Random.nextFloat())[index.coerceIn(0, 4)], + int = listOf(null, 0, -1, 1, Random.nextInt())[index.coerceIn(0, 4)], + long = listOf(null, 0L, -1L, 1L, Random.nextLong())[index.coerceIn(0, 4)], + short = + listOf( + null, + 0.toShort(), + (-1).toShort(), + 1.toShort(), + (Short.MIN_VALUE..Short.MAX_VALUE).random().toShort() + )[index.coerceIn(0, 4)], + string = listOf(null, "", UUID.randomUUID().toString())[index.coerceIn(0, 2)], + enum = + listOf( + null, + PayloadChannel.ClientboundPlay, + PayloadChannel.ServerboundPlay, + PayloadChannel.ClientboundConfiguration, + PayloadChannel.ServerboundConfiguration + )[index.coerceIn(0, 4)], + ) + + @JvmStatic + fun randomDataB(): TestDataB = + TestDataB( + booleanCollection = listOf(true, false), + byteCollection = + listOf(0b0, 0b1, (Byte.MIN_VALUE..Byte.MAX_VALUE).random().toByte()), + charCollection = + listOf(('a'..'z').random(), (Char.MIN_VALUE..Char.MAX_VALUE).random()), + doubleCollection = listOf(0.0, -1.0, 1.0, Random.nextDouble()), + floatCollection = listOf(0f, -1f, 1f, Random.nextFloat()), + intCollection = listOf(0, -1, 1, Random.nextInt()), + longCollection = listOf(0L, -1L, 1L, Random.nextLong()), + shortCollection = + listOf( + 0.toShort(), + (-1).toShort(), + 1.toShort(), + (Short.MIN_VALUE..Short.MAX_VALUE).random().toShort() + ), + stringCollection = listOf("", UUID.randomUUID().toString()), + enumCollection = + listOf( + PayloadChannel.ClientboundPlay, + PayloadChannel.ServerboundPlay, + PayloadChannel.ClientboundConfiguration, + PayloadChannel.ServerboundConfiguration + ), + ) + + @JvmStatic + fun randomDataC(): TestDataC = + TestDataC( + structureA = randomDataA(Random.nextInt(5)), + structureB = randomDataB(), + collectionA = + listOf( + randomDataA(Random.nextInt(5)), + randomDataA(Random.nextInt(5)), + randomDataA(Random.nextInt(5)), + randomDataA(Random.nextInt(5)), + randomDataA(Random.nextInt(5)) + ), + collectionB = + listOf( + randomDataB(), + randomDataB(), + randomDataB(), + randomDataB(), + randomDataB() + ), + ) + } +} diff --git a/src/test/kotlin/SerializerTests.kt b/src/test/kotlin/SerializerTests.kt new file mode 100644 index 0000000..ddce499 --- /dev/null +++ b/src/test/kotlin/SerializerTests.kt @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2024 Wanderia - All Rights Reserved + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ +@file:OptIn(ExperimentalSerializationApi::class) + +import dev.wanderia.netlib.format.PacketByteBufDecoder +import dev.wanderia.netlib.format.PacketByteBufEncoder +import dev.wanderia.netlib.serializers.IdentifierSerializer +import dev.wanderia.netlib.serializers.UUIDSerializer +import java.util.* +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlinx.serialization.ExperimentalSerializationApi +import net.fabricmc.fabric.api.networking.v1.PacketByteBufs +import net.minecraft.resources.ResourceLocation + +class SerializerTests { + + @Test + fun `identifiers should serialize and deserialize`() { + // given + val buf = PacketByteBufs.create() + val id = ResourceLocation.parse("test") + + // when + IdentifierSerializer.serialize(PacketByteBufEncoder(buf), id) + + // then + assertEquals(id, IdentifierSerializer.deserialize(PacketByteBufDecoder(buf))) + } + + @Test + fun `uuids should serialize and deserialize`() { + // given + val buf = PacketByteBufs.create() + val uuid = UUID.randomUUID() + + // when + UUIDSerializer.serialize(PacketByteBufEncoder(buf), uuid) + + // then + assertEquals(uuid, UUIDSerializer.deserialize(PacketByteBufDecoder(buf))) + } +} diff --git a/src/test/kotlin/SerializersModuleTests.kt b/src/test/kotlin/SerializersModuleTests.kt new file mode 100644 index 0000000..c67c7bc --- /dev/null +++ b/src/test/kotlin/SerializersModuleTests.kt @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2024 Wanderia - All Rights Reserved + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ +@file:OptIn(ExperimentalSerializationApi::class) + +import dev.wanderia.netlib.format.decodeFrom +import dev.wanderia.netlib.format.encodeTo +import java.util.UUID +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlinx.serialization.Contextual +import kotlinx.serialization.ExperimentalSerializationApi +import kotlinx.serialization.Serializable +import net.fabricmc.fabric.api.networking.v1.PacketByteBufs +import net.minecraft.resources.ResourceLocation + +class SerializersModuleTests { + + @Serializable + data class TestData(@Contextual val identifier: ResourceLocation, @Contextual val uuid: UUID) + + @Test + fun `should use contextual serializers`() { + // given + val buf = PacketByteBufs.create() + val testData = TestData(ResourceLocation.parse("test"), UUID.randomUUID()) + + // when + encodeTo(buf, testData) + + // then + assertEquals(testData, decodeFrom(buf)) + } +}