From 5f457585a4257539e6ecdf25da094b54aefdd85d Mon Sep 17 00:00:00 2001 From: Constructor Date: Wed, 26 Feb 2025 03:56:58 +0100 Subject: [PATCH 01/25] Added Kotlin Test lib --- common/build.gradle.kts | 7 +++++ common/src/test/kotlin/FastVectorTest.kt | 34 ++++++++++++++++++++++++ 2 files changed, 41 insertions(+) create mode 100644 common/src/test/kotlin/FastVectorTest.kt diff --git a/common/build.gradle.kts b/common/build.gradle.kts index d5a6ba0b5..d858e08e0 100644 --- a/common/build.gradle.kts +++ b/common/build.gradle.kts @@ -55,6 +55,7 @@ dependencies { // Baritone modImplementation("baritone-api:baritone-unoptimized-fabric:1.10.2") { isTransitive = false } + testImplementation(kotlin("test")) } tasks { @@ -66,3 +67,9 @@ tasks { useJUnitPlatform() } } + +tasks.withType { + kotlinOptions { + jvmTarget = "17" + } +} \ No newline at end of file diff --git a/common/src/test/kotlin/FastVectorTest.kt b/common/src/test/kotlin/FastVectorTest.kt new file mode 100644 index 000000000..a42efc63a --- /dev/null +++ b/common/src/test/kotlin/FastVectorTest.kt @@ -0,0 +1,34 @@ +import com.lambda.util.world.FastVector +import com.lambda.util.world.fastVectorOf +import com.lambda.util.world.x +import com.lambda.util.world.y +import com.lambda.util.world.z +import kotlin.test.Test + +/* + * Copyright 2025 Lambda + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + + +class FastVectorTest { + @Test + fun testZero() { + val vec = fastVectorOf(0, 0, 0) + assert(vec.x == 0) + assert(vec.y == 0) + assert(vec.z == 0) + } +} \ No newline at end of file From c5a4001729005ef5f512a664a6462cd53e6a9590 Mon Sep 17 00:00:00 2001 From: Constructor Date: Wed, 26 Feb 2025 03:56:58 +0100 Subject: [PATCH 02/25] Added Kotlin Test lib --- common/build.gradle.kts | 7 +++++ common/src/test/kotlin/FastVectorTest.kt | 34 ++++++++++++++++++++++++ 2 files changed, 41 insertions(+) create mode 100644 common/src/test/kotlin/FastVectorTest.kt diff --git a/common/build.gradle.kts b/common/build.gradle.kts index 08af8e3d7..355df3a2d 100644 --- a/common/build.gradle.kts +++ b/common/build.gradle.kts @@ -57,6 +57,7 @@ dependencies { // Baritone modImplementation("baritone-api:baritone-unoptimized-fabric:1.10.2") { isTransitive = false } + testImplementation(kotlin("test")) } tasks { @@ -68,3 +69,9 @@ tasks { useJUnitPlatform() } } + +tasks.withType { + kotlinOptions { + jvmTarget = "17" + } +} \ No newline at end of file diff --git a/common/src/test/kotlin/FastVectorTest.kt b/common/src/test/kotlin/FastVectorTest.kt new file mode 100644 index 000000000..a42efc63a --- /dev/null +++ b/common/src/test/kotlin/FastVectorTest.kt @@ -0,0 +1,34 @@ +import com.lambda.util.world.FastVector +import com.lambda.util.world.fastVectorOf +import com.lambda.util.world.x +import com.lambda.util.world.y +import com.lambda.util.world.z +import kotlin.test.Test + +/* + * Copyright 2025 Lambda + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + + +class FastVectorTest { + @Test + fun testZero() { + val vec = fastVectorOf(0, 0, 0) + assert(vec.x == 0) + assert(vec.y == 0) + assert(vec.z == 0) + } +} \ No newline at end of file From e08759ac282fd756b84e5fb243acc65297e65d09 Mon Sep 17 00:00:00 2001 From: Edouard127 <46357922+Edouard127@users.noreply.github.com> Date: Fri, 14 Mar 2025 18:37:03 -0400 Subject: [PATCH 03/25] Fixed fastvec operator shadowing --- common/src/main/kotlin/com/lambda/util/world/Position.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/util/world/Position.kt b/common/src/main/kotlin/com/lambda/util/world/Position.kt index 6e19a639b..201d76436 100644 --- a/common/src/main/kotlin/com/lambda/util/world/Position.kt +++ b/common/src/main/kotlin/com/lambda/util/world/Position.kt @@ -179,12 +179,12 @@ infix fun FastVector.div(scalar: Double): FastVector = /** * Modulo the position by the given scalar. */ -infix fun FastVector.mod(scalar: Int): FastVector = fastVectorOf(x % scalar, y % scalar, z % scalar) +infix fun FastVector.remainder(scalar: Int): FastVector = fastVectorOf(x % scalar, y % scalar, z % scalar) /** * Modulo the position by the given scalar. */ -infix fun FastVector.mod(scalar: Double): FastVector = +infix fun FastVector.remainder(scalar: Double): FastVector = fastVectorOf((x % scalar).toLong(), (y % scalar).toLong(), (z % scalar).toLong()) /** From f0ad176d9475a39e1680c192563a52fb67112204 Mon Sep 17 00:00:00 2001 From: Edouard127 <46357922+Edouard127@users.noreply.github.com> Date: Fri, 14 Mar 2025 18:37:07 -0400 Subject: [PATCH 04/25] Update FastVectorTest.kt --- common/src/test/kotlin/FastVectorTest.kt | 216 ++++++++++++++++++++++- 1 file changed, 210 insertions(+), 6 deletions(-) diff --git a/common/src/test/kotlin/FastVectorTest.kt b/common/src/test/kotlin/FastVectorTest.kt index a42efc63a..8f041ea3f 100644 --- a/common/src/test/kotlin/FastVectorTest.kt +++ b/common/src/test/kotlin/FastVectorTest.kt @@ -1,9 +1,24 @@ -import com.lambda.util.world.FastVector +import com.lambda.util.world.X_BITS +import com.lambda.util.world.Z_BITS +import com.lambda.util.world.addX +import com.lambda.util.world.addY +import com.lambda.util.world.addZ +import com.lambda.util.world.distSq import com.lambda.util.world.fastVectorOf +import com.lambda.util.world.offset +import com.lambda.util.world.remainder +import com.lambda.util.world.setX +import com.lambda.util.world.setY +import com.lambda.util.world.setZ +import com.lambda.util.world.toBlockPos +import com.lambda.util.world.toVec3d import com.lambda.util.world.x import com.lambda.util.world.y import com.lambda.util.world.z +import net.minecraft.util.math.Direction import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertFails /* * Copyright 2025 Lambda @@ -25,10 +40,199 @@ import kotlin.test.Test class FastVectorTest { @Test - fun testZero() { + fun `test fast vector with valid coordinates`() { + val x = 123456 + val y = 789 + val z = -12345 + + val fastVec = fastVectorOf(x, y, z) + + assertEquals(x, fastVec.x) + assertEquals(y, fastVec.y) + assertEquals(z, fastVec.z) + } + + @Test + fun `test fast vector with invalid X coordinate`() { + val x = (1L shl X_BITS - 1) + val y = 10L + val z = 20L + + assertFails { fastVectorOf(x, y, z) } + } + + @Test + fun `test fast vector with invalid Z coordinate`() { + val x = 10L + val y = 20L + val z = (1L shl Z_BITS - 1) + + assertFails { fastVectorOf(x, y, z) } + } + + @Test + fun `test fast vector with Y overflow`() { + val x = 10L + val y = 2049L + val z = 20L + + val fastVec = fastVectorOf(x, y, z) + + assertEquals(-2047, fastVec.y) + } + + @Test + fun `test fast vector with Y underflow`() { + val x = 10L + val y = -2049L + val z = 20L + + val fastVec = fastVectorOf(x, y, z) + + assertEquals(2047, fastVec.y) + } + + @Test + fun `test setX correctly sets the X coordinate`() { + var fastVec = fastVectorOf(10, 20, 30) + fastVec = fastVec setX 40 + + assertEquals(40, fastVec.x) + } + + @Test + fun `test setY correctly sets the Y coordinate`() { + var fastVec = fastVectorOf(10, 20, 30) + fastVec = fastVec setY 50 + + assertEquals(50, fastVec.y) + } + + @Test + fun `test setZ correctly sets the Z coordinate`() { + var fastVec = fastVectorOf(10, 20, 30) + fastVec = fastVec setZ 60 + + assertEquals(60, fastVec.z) + } + + @Test + fun `test addX correctly adds to the X coordinate`() { + val fastVec = fastVectorOf(10, 20, 30) + val newVec = fastVec addX 5 + + assertEquals(15, newVec.x) + } + + @Test + fun `test addY correctly adds to the Y coordinate`() { + val fastVec = fastVectorOf(10, 20, 30) + val newVec = fastVec addY 5 + + assertEquals(25, newVec.y) + } + + @Test + fun `test addZ correctly adds to the Z coordinate`() { + val fastVec = fastVectorOf(10, 20, 30) + val newVec = fastVec addZ 5 + + assertEquals(35, newVec.z) + } + + @Test + fun `test plus operation with another FastVector`() { + val vec1 = fastVectorOf(1, 2, 3) + val vec2 = fastVectorOf(4, 5, 6) + + val result = vec1 + vec2 + + assertEquals(5, result.x) + assertEquals(7, result.y) + assertEquals(9, result.z) + } + + @Test + fun `test minus operation with another FastVector`() { + val vec1 = fastVectorOf(5, 6, 7) + val vec2 = fastVectorOf(2, 2, 2) + + val result = vec1 - vec2 + + assertEquals(3, result.x) + assertEquals(4, result.y) + assertEquals(5, result.z) + } + + @Test + fun `test multiplication by scalar`() { + val vec = fastVectorOf(1, 2, 3) + val result = vec * 2 + + assertEquals(2, result.x) + assertEquals(4, result.y) + assertEquals(6, result.z) + } + + @Test + fun `test division by scalar`() { + val vec = fastVectorOf(10, 20, 30) + val result = vec / 2 + + assertEquals(5, result.x) + assertEquals(10, result.y) + assertEquals(15, result.z) + } + + @Test + fun `test modulo operation with scalar`() { + val vec = fastVectorOf(10, 20, 30) + val result = vec remainder 7 + + assertEquals(3, result.x) + assertEquals(6, result.y) + assertEquals(2, result.z) + } + + @Test + fun `test distSq with another FastVector`() { + val vec1 = fastVectorOf(1, 2, 3) + val vec2 = fastVectorOf(4, 5, 6) + + val distSq = vec1 distSq vec2 + + assertEquals(27.0, distSq) + } + + @Test + fun `test offset with Direction`() { val vec = fastVectorOf(0, 0, 0) - assert(vec.x == 0) - assert(vec.y == 0) - assert(vec.z == 0) + val direction = Direction.NORTH // offset: (0, 0, -1) + + val newVec = vec.offset(direction) + + assertEquals(0, newVec.x) + assertEquals(0, newVec.y) + assertEquals(-1, newVec.z) + } + + @Test + fun `test toBlockPos conversion`() { + val vec = fastVectorOf(10, 20, 30) + val blockPos = vec.toBlockPos() + + assertEquals(10, blockPos.x) + assertEquals(20, blockPos.y) + assertEquals(30, blockPos.z) + } + + @Test + fun `test toVec3d conversion`() { + val vec = fastVectorOf(10, 20, 30) + val vec3d = vec.toVec3d() + + assertEquals(10.0, vec3d.x) + assertEquals(20.0, vec3d.y) + assertEquals(30.0, vec3d.z) } -} \ No newline at end of file +} From b2ad25d0d72b2e9250136fa6f3fa0eb797201f29 Mon Sep 17 00:00:00 2001 From: Edouard127 <46357922+Edouard127@users.noreply.github.com> Date: Fri, 14 Mar 2025 18:50:05 -0400 Subject: [PATCH 05/25] Added collection tests --- .../src/test/kotlin/LimitedDecayQueueTest.kt | 180 ++++++++++++++++++ .../src/test/kotlin/LimitedOrderedSetTest.kt | 120 ++++++++++++ 2 files changed, 300 insertions(+) create mode 100644 common/src/test/kotlin/LimitedDecayQueueTest.kt create mode 100644 common/src/test/kotlin/LimitedOrderedSetTest.kt diff --git a/common/src/test/kotlin/LimitedDecayQueueTest.kt b/common/src/test/kotlin/LimitedDecayQueueTest.kt new file mode 100644 index 000000000..2a9a27bc7 --- /dev/null +++ b/common/src/test/kotlin/LimitedDecayQueueTest.kt @@ -0,0 +1,180 @@ +import com.lambda.util.collections.LimitedDecayQueue +import java.util.concurrent.TimeUnit +import kotlin.test.BeforeTest +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertFalse +import kotlin.test.assertTrue + +/* + * Copyright 2025 Lambda + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +class LimitedDecayQueueTest { + + private lateinit var queue: LimitedDecayQueue + private lateinit var onDecayCalled: MutableList + + @BeforeTest + fun setUp() { + // Initialize the onDecay callback + onDecayCalled = mutableListOf() + queue = LimitedDecayQueue(3, 1000) { onDecayCalled.add(it) } // 1 second decay time + } + + @Test + fun `test add element to queue`() { + // Add an element + val result = queue.add("Element1") + + assertTrue(result) + assertEquals(1, queue.size) + } + + @Test + fun `test add element beyond size limit`() { + queue.add("Element1") + queue.add("Element2") + queue.add("Element3") + + // Try adding a 4th element when size limit is 3 + val result = queue.add("Element4") + + assertFalse(result) // Should fail to add + assertEquals(3, queue.size) // Size should remain 3 + } + + @Test + fun `test elements expire after max age`() { + queue.add("Element1") + queue.add("Element2") + + // Simulate passage of time (greater than maxAge) + TimeUnit.MILLISECONDS.sleep(1500) + + // Add new element after expiration + queue.add("Element3") + + // Ensure expired elements are removed and "onDecay" callback is triggered + assertEquals(1, queue.size) + assertTrue(onDecayCalled.contains("Element1")) + assertTrue(onDecayCalled.contains("Element2")) + } + + @Test + fun `test add all elements`() { + queue.add("Element1") + queue.add("Element2") + + val result = queue.addAll(listOf("Element3", "Element4")) + + assertTrue(result) + assertEquals(3, queue.size) // Size limit is 3 + } + + @Test + fun `test remove element`() { + queue.add("Element1") + queue.add("Element2") + + val result = queue.remove("Element1") + + assertTrue(result) + assertEquals(1, queue.size) + } + + @Test + fun `test remove all elements`() { + queue.add("Element1") + queue.add("Element2") + queue.add("Element3") + + val result = queue.removeAll(listOf("Element1", "Element2")) + + assertTrue(result) + assertEquals(1, queue.size) // Only "Element3" should remain + } + + @Test + fun `test retain all elements`() { + queue.add("Element1") + queue.add("Element2") + queue.add("Element3") + + val result = queue.retainAll(listOf("Element2", "Element3")) + + assertTrue(result) + assertEquals(2, queue.size) // Only "Element2" and "Element3" should remain + } + + @Test + fun `test clear the queue`() { + queue.add("Element1") + queue.add("Element2") + queue.clear() + + assertEquals(0, queue.size) // Queue should be empty + } + + @Test + fun `test set max size`() { + queue.add("Element1") + queue.add("Element2") + queue.add("Element3") + + queue.setMaxSize(2) // Reduce size limit to 2 + + queue.add("Element4") + + // Verify that the queue is limited to 2 elements, and oldest element is removed + assertEquals(2, queue.size) + assertTrue(onDecayCalled.contains("Element1")) // "Element1" should be decayed + } + + @Test + fun `test set decay time`() { + queue.add("Element1") + queue.add("Element2") + + queue.setDecayTime(500) // Set a shorter decay time of 500 ms + + // Simulate passage of time (greater than decay time) + TimeUnit.MILLISECONDS.sleep(600) + + queue.add("Element3") + + // Ensure expired elements are removed and "onDecay" callback is triggered + assertEquals(1, queue.size) + assertTrue(onDecayCalled.contains("Element1")) + assertTrue(onDecayCalled.contains("Element2")) + } + + @Test + fun `test clean up function when iterating`() { + queue.add("Element1") + queue.add("Element2") + + // Simulate some delay to allow elements to decay + TimeUnit.MILLISECONDS.sleep(1500) + + queue.add("Element3") + + // Iterator should only return "Element3" because the others are expired + val elements = queue.toList() + assertEquals(1, elements.size) + assertEquals("Element3", elements[0]) + } +} diff --git a/common/src/test/kotlin/LimitedOrderedSetTest.kt b/common/src/test/kotlin/LimitedOrderedSetTest.kt new file mode 100644 index 000000000..e5816e4bb --- /dev/null +++ b/common/src/test/kotlin/LimitedOrderedSetTest.kt @@ -0,0 +1,120 @@ +import com.lambda.util.collections.LimitedOrderedSet +import kotlin.test.BeforeTest +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertFalse +import kotlin.test.assertTrue + +/* + * Copyright 2025 Lambda + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +class LimitedOrderedSetTest { + + private lateinit var set: LimitedOrderedSet + + @BeforeTest + fun setUp() { + // Initialize the set with a max size of 3 + set = LimitedOrderedSet(3) + } + + @Test + fun `test adding elements to the set`() { + val added = set.add("Element1") + assertTrue(added) + assertEquals(1, set.size) + } + + @Test + fun `test adding more elements than maxSize`() { + set.add("Element1") + set.add("Element2") + set.add("Element3") + + // Adding a fourth element when the max size is 3 + val added = set.add("Element4") + assertTrue(added) + assertEquals(3, set.size) // The size should not exceed maxSize + + // The first element ("Element1") should be removed, as the set is maintaining order + assertFalse(set.contains("Element1")) + assertTrue(set.contains("Element2")) + assertTrue(set.contains("Element3")) + assertTrue(set.contains("Element4")) + } + + @Test + fun `test maintaining the order of elements`() { + set.add("Element1") + set.add("Element2") + set.add("Element3") + + // Add a fourth element, and the first one should be removed + set.add("Element4") + + // The order should now be Element2, Element3, Element4 + val expectedOrder = listOf("Element2", "Element3", "Element4") + assertEquals(expectedOrder, set.toList()) + } + + @Test + fun `test addAll method`() { + // Initially, the set is empty + set.add("Element1") + set.add("Element2") + + // Adding multiple elements + val added = set.addAll(listOf("Element3", "Element4")) + + assertTrue(added) + assertEquals(3, set.size) // Set should contain up to maxSize elements + assertFalse(set.contains("Element1")) // Element1 should be removed because we are over the max size + assertTrue(set.contains("Element2")) + assertTrue(set.contains("Element3")) + assertTrue(set.contains("Element4")) + } + + @Test + fun `test adding more elements than maxSize with addAll`() { + set.addAll(listOf("Element1", "Element2", "Element3")) + + // Add more elements, exceeding the max size + val added = set.addAll(listOf("Element4", "Element5")) + + assertTrue(added) + assertEquals(3, set.size) // The set should maintain the max size + assertFalse(set.contains("Element1")) + assertFalse(set.contains("Element2")) + assertTrue(set.contains("Element3")) + assertTrue(set.contains("Element4")) + assertTrue(set.contains("Element5")) + } + + @Test + fun `test the set size does not exceed maxSize`() { + set.add("Element1") + set.add("Element2") + set.add("Element3") + + set.add("Element4") // This should push out "Element1" + assertEquals(3, set.size) + assertFalse(set.contains("Element1")) + assertTrue(set.contains("Element2")) + assertTrue(set.contains("Element3")) + assertTrue(set.contains("Element4")) + } +} From 80b01e0cc68b6d0e595ad892a9d9109a6621e475 Mon Sep 17 00:00:00 2001 From: Edouard127 <46357922+Edouard127@users.noreply.github.com> Date: Fri, 14 Mar 2025 19:53:45 -0400 Subject: [PATCH 06/25] Changed varint to use only 7 bits --- .../kotlin/com/lambda/util/VarIntIterator.kt | 28 +++++++++---------- .../com/lambda/util/extension/Structures.kt | 5 ++-- 2 files changed, 16 insertions(+), 17 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/util/VarIntIterator.kt b/common/src/main/kotlin/com/lambda/util/VarIntIterator.kt index cb9a742ee..2245d6b45 100644 --- a/common/src/main/kotlin/com/lambda/util/VarIntIterator.kt +++ b/common/src/main/kotlin/com/lambda/util/VarIntIterator.kt @@ -19,8 +19,6 @@ package com.lambda.util class VarIntIterator( private val bytes: ByteArray, - private val bitsPerEntry: Int = 7, - private val maxGroups: Int = 5, ) : Iterator { private var index: Int = 0 @@ -31,23 +29,23 @@ class VarIntIterator( throw NoSuchElementException("No more elements to read") var value = 0 - var bitsRead = 0 + var size = 0 - val groupMask = (1 shl bitsPerEntry) - 1 - val continuationBit = 1 shl bitsPerEntry - - var b: Byte do { - if (index >= bytes.size) - throw NoSuchElementException("Unexpected end of byte array while reading VarInt") - - b = bytes[index++] - value = value or ((b.toInt() and groupMask) shl bitsRead) - bitsRead += bitsPerEntry + val b = bytes[index++].toInt() + value = value or ((b and SEGMENT_BIT) shl (size++ * 7)) - require(bitsRead <= bitsPerEntry * maxGroups) { "VarInt size cannot exceed $maxGroups bytes" } - } while ((b.toInt() and continuationBit) != 0) + if (size > 5) throw IllegalArgumentException("VarInt size cannot exceed 5 bytes") + } while ((b and CONTINUE_BIT) != 0) return value } + + companion object { + const val SEGMENT_BIT = 127 + const val CONTINUE_BIT = 128 + } } + +inline fun ByteArray.varIterator(block: (Int) -> Unit) = + VarIntIterator(this).forEach(block) diff --git a/common/src/main/kotlin/com/lambda/util/extension/Structures.kt b/common/src/main/kotlin/com/lambda/util/extension/Structures.kt index 9d4421339..fccec79d6 100644 --- a/common/src/main/kotlin/com/lambda/util/extension/Structures.kt +++ b/common/src/main/kotlin/com/lambda/util/extension/Structures.kt @@ -20,6 +20,7 @@ package com.lambda.util.extension import com.lambda.Lambda.mc import com.lambda.util.VarIntIterator import com.lambda.util.math.MathUtils.logCap +import com.lambda.util.varIterator import com.lambda.util.world.FastVector import com.lambda.util.world.fastVectorOf import com.lambda.util.world.x @@ -117,8 +118,8 @@ private fun StructureTemplate.readSpongeV1OrException( val newBlocks = NbtList() var blockIndex = 0 - VarIntIterator(nbt.getByteArray("BlockData")) - .forEach { blockId -> + nbt.getByteArray("BlockData") + .varIterator { blockId -> val blockpos = positionFromIndex(width, length, blockIndex++) newBlocks.add(NbtCompound().apply { From cc7d9fdefa5c3c9faa0ca492c052ab62f44ad4ea Mon Sep 17 00:00:00 2001 From: Edouard127 <46357922+Edouard127@users.noreply.github.com> Date: Fri, 14 Mar 2025 19:53:48 -0400 Subject: [PATCH 07/25] Create VarIntIteratorTest.kt --- common/src/test/kotlin/VarIntIteratorTest.kt | 87 ++++++++++++++++++++ 1 file changed, 87 insertions(+) create mode 100644 common/src/test/kotlin/VarIntIteratorTest.kt diff --git a/common/src/test/kotlin/VarIntIteratorTest.kt b/common/src/test/kotlin/VarIntIteratorTest.kt new file mode 100644 index 000000000..4e203665d --- /dev/null +++ b/common/src/test/kotlin/VarIntIteratorTest.kt @@ -0,0 +1,87 @@ +import com.lambda.util.VarIntIterator +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertFails +import kotlin.test.assertFalse +import kotlin.test.assertTrue + +/* + * Copyright 2025 Lambda + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +class VarIntIteratorTest { + + private lateinit var iterator: VarIntIterator + + @Test + fun `test single byte varint`() { + val bytes = byteArrayOf(0b01111111.toByte()) // Maximum single-byte VarInt (127) + iterator = VarIntIterator(bytes) + + assertTrue(iterator.hasNext()) + assertEquals(127, iterator.next()) + assertFalse(iterator.hasNext()) // No more elements after this + } + + @Test + fun `test multi-byte varint`() { + val bytes = byteArrayOf(0b10000001.toByte(), 0b00000001.toByte()) // Represents 129 + iterator = VarIntIterator(bytes) + + assertTrue(iterator.hasNext()) + assertEquals(129, iterator.next()) + assertFalse(iterator.hasNext()) // No more elements after this + } + + @Test + fun `test varint iterator with multiple values`() { + val bytes = byteArrayOf( + 0b10000001.toByte(), 0b00000001.toByte(), // 129 + 0b01111111.toByte(), // 127 + 0b10000000.toByte(), 0b00000001.toByte() // 128 + ) + iterator = VarIntIterator(bytes) + + assertTrue(iterator.hasNext()) + assertEquals(129, iterator.next()) + assertTrue(iterator.hasNext()) + assertEquals(127, iterator.next()) + assertTrue(iterator.hasNext()) + assertEquals(128, iterator.next()) + assertFalse(iterator.hasNext()) // No more elements after this + } + + @Test + fun `test varint iterator with no elements`() { + val bytes = byteArrayOf() // Empty byte array + iterator = VarIntIterator(bytes) + + assertFalse(iterator.hasNext()) // There are no elements + assertFails { + iterator.next() // Should throw exception since there are no elements + } + } + + @Test + fun `test reading varint with unexpected end of byte array`() { + val bytes = byteArrayOf(0b10000001.toByte()) // Only part of a VarInt + iterator = VarIntIterator(bytes) + + assertFails { + iterator.next() // Should throw exception since the VarInt is incomplete + } + } +} From c7924df35b97867f4f86a8410635da98586b2e57 Mon Sep 17 00:00:00 2001 From: Edouard127 <46357922+Edouard127@users.noreply.github.com> Date: Fri, 14 Mar 2025 20:05:35 -0400 Subject: [PATCH 08/25] Fixed integer division --- common/src/main/kotlin/com/lambda/util/math/Vectors.kt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/util/math/Vectors.kt b/common/src/main/kotlin/com/lambda/util/math/Vectors.kt index 8784cf45b..900e9255d 100644 --- a/common/src/main/kotlin/com/lambda/util/math/Vectors.kt +++ b/common/src/main/kotlin/com/lambda/util/math/Vectors.kt @@ -84,9 +84,9 @@ infix operator fun Vec3d.times(other: Int): Vec3d = multiply(other.toDouble()) infix operator fun Vec3d.div(other: Vec3d): Vec3d = multiply(1.0 / other.x, 1.0 / other.y, 1.0 / other.z) infix operator fun Vec3d.div(other: Vec3i): Vec3d = Vec3d(x / other.x, y / other.y, z / other.z) -infix operator fun Vec3d.div(other: Double): Vec3d = times(1 / other) -infix operator fun Vec3d.div(other: Float): Vec3d = times(1 / other) -infix operator fun Vec3d.div(other: Int): Vec3d = times(1 / other) +infix operator fun Vec3d.div(other: Double): Vec3d = times(1.0 / other) +infix operator fun Vec3d.div(other: Float): Vec3d = times(1.0 / other) +infix operator fun Vec3d.div(other: Int): Vec3d = times(1.0 / other) /* Vec3i */ val Vec3i.vec3d get() = From 5166decab6452fa8f0ad6bc97f11638ed6737b9e Mon Sep 17 00:00:00 2001 From: Edouard127 <46357922+Edouard127@users.noreply.github.com> Date: Fri, 14 Mar 2025 20:05:42 -0400 Subject: [PATCH 09/25] Added more square extensions --- common/src/main/kotlin/com/lambda/util/math/MathUtils.kt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/common/src/main/kotlin/com/lambda/util/math/MathUtils.kt b/common/src/main/kotlin/com/lambda/util/math/MathUtils.kt index ece4115a0..c6ce1271e 100644 --- a/common/src/main/kotlin/com/lambda/util/math/MathUtils.kt +++ b/common/src/main/kotlin/com/lambda/util/math/MathUtils.kt @@ -26,6 +26,8 @@ object MathUtils { private const val PI_FLOAT = 3.141593f inline val Int.sq: Int get() = this * this + inline val Float.sq: Float get() = this * this + inline val Double.sq: Double get() = this * this fun Float.toRadian() = this / 180.0f * PI_FLOAT fun Double.toRadian() = this / 180.0 * PI From 5c7cd6a94c11fa4880d20a0ffd5905afe68f7565 Mon Sep 17 00:00:00 2001 From: Edouard127 <46357922+Edouard127@users.noreply.github.com> Date: Fri, 14 Mar 2025 20:06:12 -0400 Subject: [PATCH 10/25] Created math test units --- common/src/test/kotlin/RangeTest.kt | 164 +++++++++++++++++++++++++ common/src/test/kotlin/Vec2dTest.kt | 180 ++++++++++++++++++++++++++++ common/src/test/kotlin/Vec3Test.kt | 169 ++++++++++++++++++++++++++ 3 files changed, 513 insertions(+) create mode 100644 common/src/test/kotlin/RangeTest.kt create mode 100644 common/src/test/kotlin/Vec2dTest.kt create mode 100644 common/src/test/kotlin/Vec3Test.kt diff --git a/common/src/test/kotlin/RangeTest.kt b/common/src/test/kotlin/RangeTest.kt new file mode 100644 index 000000000..f7bb86c95 --- /dev/null +++ b/common/src/test/kotlin/RangeTest.kt @@ -0,0 +1,164 @@ +import com.lambda.util.math.Vec2d +import com.lambda.util.math.coerceIn +import com.lambda.util.math.inv +import com.lambda.util.math.lerp +import com.lambda.util.math.normalize +import com.lambda.util.math.random +import com.lambda.util.math.step +import com.lambda.util.math.transform +import net.minecraft.util.math.Vec3d +import java.awt.Color +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertTrue + +/* + * Copyright 2025 Lambda + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +class RangeTest { + + @Test + fun `test step over double range`() { + val range = 0.0..10.0 + val iterator = range.step(2.0) + + val result = iterator.asSequence().toList() + assertEquals(listOf(0.0, 2.0, 4.0, 6.0, 8.0, 10.0), result) + } + + @Test + fun `test step over float range`() { + val range = 0.0f..10.0f + val iterator = range.step(2.0f) + + val result = iterator.asSequence().toList() + assertEquals(listOf(0.0f, 2.0f, 4.0f, 6.0f, 8.0f, 10.0f), result) + } + + @Test + fun `test random within range`() { + val range = 0.0..10.0 + val randomValue = range.random() + + assertTrue(randomValue in range) // The value must be in the range [0.0, 10.0] + } + + @Test + fun `test normalize double value`() { + val range = 0.0..100.0 + val normalized = range.normalize(50.0) + + assertEquals(0.5, normalized) // 50.0 should be normalized to 0.5 in the range [0.0, 100.0] + } + + @Test + fun `test normalize float value`() { + val range = 0f..100f + val normalized = range.normalize(50f) + + assertEquals(0.5f, normalized) // 50f should be normalized to 0.5f in the range [0f, 100f] + } + + @Test + fun `test invert float range`() { + val range = 0f..100f + val inverted = range.inv() + + assertEquals(100f to 0f, inverted) // Inverting the range [0f, 100f] gives (100f, 0f) + } + + @Test + fun `test transform double range`() { + val range = 0.0..10.0 + val transformed = range.transform(5.0, 0.0, 100.0) + + assertEquals(50.0, transformed) // 5.0 in range [0.0, 10.0] maps to 50.0 in range [0.0, 100.0] + } + + @Test + fun `test transform float range`() { + val range = 0f..10f + val transformed = range.transform(5f, 0f, 100f) + + assertEquals(50f, transformed) // 5f in range [0f, 10f] maps to 50f in range [0f, 100f] + } + + @Test + fun `test lerp double values`() { + val lerpedValue = lerp(0.5, 0.0, 10.0) + + assertEquals(5.0, lerpedValue) // Linear interpolation between 0.0 and 10.0 at 0.5 results in 5.0 + } + + @Test + fun `test lerp float values`() { + val lerpedValue = lerp(0.5f, 0f, 10f) + + assertEquals(5.0f, lerpedValue) // Linear interpolation between 0f and 10f at 0.5 results in 5.0f + } + + @Test + fun `test lerp Vec2d`() { + val start = Vec2d(0.0, 0.0) + val end = Vec2d(10.0, 10.0) + val lerpedValue = lerp(0.5, start, end) + + assertEquals(Vec2d(5.0, 5.0), lerpedValue) // Interpolated 50% between (0, 0) and (10, 10) + } + + @Test + fun `test lerp Vec3d`() { + val start = Vec3d(0.0, 0.0, 0.0) + val end = Vec3d(10.0, 10.0, 10.0) + val lerpedValue = lerp(0.5, start, end) + + assertEquals(Vec3d(5.0, 5.0, 5.0), lerpedValue) // Interpolated 50% between (0, 0, 0) and (10, 10, 10) + } + + @Test + fun `test lerp Color`() { + val start = Color(255, 0, 0) // Red + val end = Color(0, 0, 255) // Blue + val lerpedValue = lerp(0.5, start, end) + + assertEquals(Color(128, 0, 128), lerpedValue) // Interpolated color should be purple + } + + @Test + fun `test coercing value in double range`() { + val range = 0.0..10.0 + val coercedValue = range.coerceIn(15.0) + + assertEquals(10.0, coercedValue) // Coerced value should be within the range [0.0, 10.0] + } + + @Test + fun `test coercing value in float range`() { + val range = 0f..10f + val coercedValue = range.coerceIn(15f) + + assertEquals(10f, coercedValue) // Coerced value should be within the range [0f, 10f] + } + + @Test + fun `test coercing value in 2d vector`() { + val vec = Vec2d(5.0, 5.0) + val coercedVec = vec.coerceIn(0.0, 10.0, 0.0, 10.0) + + assertEquals(Vec2d(5.0, 5.0), coercedVec) // Vec should stay the same + } +} diff --git a/common/src/test/kotlin/Vec2dTest.kt b/common/src/test/kotlin/Vec2dTest.kt new file mode 100644 index 000000000..876b2d956 --- /dev/null +++ b/common/src/test/kotlin/Vec2dTest.kt @@ -0,0 +1,180 @@ +import com.lambda.util.math.Vec2d +import kotlin.test.Test +import kotlin.test.assertEquals + +/* + * Copyright 2025 Lambda + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +class Vec2dTest { + @Test + fun `test unary minus`() { + val vector = Vec2d(1.0, -2.0) + val result = -vector + + assertEquals(Vec2d(-1.0, 2.0), result) + } + + @Test + fun `test addition with another Vec2d`() { + val vector1 = Vec2d(1.0, 2.0) + val vector2 = Vec2d(3.0, 4.0) + val result = vector1 + vector2 + + assertEquals(Vec2d(4.0, 6.0), result) + } + + @Test + fun `test addition with scalar (Double)`() { + val vector = Vec2d(1.0, 2.0) + val result = vector + 3.0 + + assertEquals(Vec2d(4.0, 5.0), result) + } + + @Test + fun `test addition with scalar (Float)`() { + val vector = Vec2d(1.0, 2.0) + val result = vector + 3.0f + + assertEquals(Vec2d(4.0, 5.0), result) + } + + @Test + fun `test addition with scalar (Int)`() { + val vector = Vec2d(1.0, 2.0) + val result = vector + 3 + + assertEquals(Vec2d(4.0, 5.0), result) + } + + @Test + fun `test subtraction with another Vec2d`() { + val vector1 = Vec2d(5.0, 6.0) + val vector2 = Vec2d(3.0, 4.0) + val result = vector1 - vector2 + + assertEquals(Vec2d(2.0, 2.0), result) + } + + @Test + fun `test subtraction with scalar (Double)`() { + val vector = Vec2d(5.0, 6.0) + val result = vector - 2.0 + + assertEquals(Vec2d(3.0, 4.0), result) + } + + @Test + fun `test subtraction with scalar (Float)`() { + val vector = Vec2d(5.0, 6.0) + val result = vector - 2.0f + + assertEquals(Vec2d(3.0, 4.0), result) + } + + @Test + fun `test subtraction with scalar (Int)`() { + val vector = Vec2d(5.0, 6.0) + val result = vector - 2 + + assertEquals(Vec2d(3.0, 4.0), result) + } + + @Test + fun `test multiplication with scalar (Double)`() { + val vector = Vec2d(2.0, 3.0) + val result = vector * 2.0 + + assertEquals(Vec2d(4.0, 6.0), result) + } + + @Test + fun `test multiplication with scalar (Float)`() { + val vector = Vec2d(2.0, 3.0) + val result = vector * 2.0f + + assertEquals(Vec2d(4.0, 6.0), result) + } + + @Test + fun `test multiplication with scalar (Int)`() { + val vector = Vec2d(2.0, 3.0) + val result = vector * 2 + + assertEquals(Vec2d(4.0, 6.0), result) + } + + @Test + fun `test multiplication with another Vec2d`() { + val vector1 = Vec2d(2.0, 3.0) + val vector2 = Vec2d(4.0, 5.0) + val result = vector1 * vector2 + + assertEquals(Vec2d(8.0, 15.0), result) + } + + @Test + fun `test division with scalar (Double)`() { + val vector = Vec2d(6.0, 8.0) + val result = vector / 2.0 + + assertEquals(Vec2d(3.0, 4.0), result) + } + + @Test + fun `test division with scalar (Float)`() { + val vector = Vec2d(6.0, 8.0) + val result = vector / 2.0f + + assertEquals(Vec2d(3.0, 4.0), result) + } + + @Test + fun `test division with scalar (Int)`() { + val vector = Vec2d(6.0, 8.0) + val result = vector / 2 + + assertEquals(Vec2d(3.0, 4.0), result) + } + + @Test + fun `test division with another Vec2d`() { + val vector1 = Vec2d(6.0, 8.0) + val vector2 = Vec2d(2.0, 4.0) + val result = vector1 / vector2 + + assertEquals(Vec2d(3.0, 2.0), result) + } + + @Test + fun `test round to Int`() { + val vector = Vec2d(3.5, 4.5) + val result = vector.roundToInt() + + assertEquals(Vec2d(4.0, 5.0), result) + } + + @Test + fun `test Vec2d constants`() { + assertEquals(Vec2d.ZERO, Vec2d(0.0, 0.0)) + assertEquals(Vec2d.ONE, Vec2d(1.0, 1.0)) + assertEquals(Vec2d.LEFT, Vec2d(-1.0, 0.0)) + assertEquals(Vec2d.RIGHT, Vec2d(1.0, 0.0)) + assertEquals(Vec2d.TOP, Vec2d(0.0, -1.0)) + assertEquals(Vec2d.BOTTOM, Vec2d(0.0, 1.0)) + } +} diff --git a/common/src/test/kotlin/Vec3Test.kt b/common/src/test/kotlin/Vec3Test.kt new file mode 100644 index 000000000..e7b7f4a1d --- /dev/null +++ b/common/src/test/kotlin/Vec3Test.kt @@ -0,0 +1,169 @@ +import com.lambda.util.math.CENTER +import com.lambda.util.math.DOWN +import com.lambda.util.math.UP +import com.lambda.util.math.dist +import com.lambda.util.math.distSq +import com.lambda.util.math.MathUtils.sq +import com.lambda.util.math.div +import com.lambda.util.math.minus +import com.lambda.util.math.plus +import com.lambda.util.math.times +import com.lambda.util.math.vec3d +import net.minecraft.util.math.Vec3d +import net.minecraft.util.math.Vec3i +import kotlin.math.sqrt +import kotlin.test.Test +import kotlin.test.assertEquals + +/* + * Copyright 2025 Lambda + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +class Vec3Test { + + @Test + fun `test dist with another Vec3d`() { + val vector1 = Vec3d(1.0, 2.0, 3.0) + val vector2 = Vec3d(4.0, 5.0, 6.0) + val result = vector1 dist vector2 + + val expected = sqrt((1.0 - 4.0).sq + (2.0 - 5.0).sq + (3.0 - 6.0).sq) + assertEquals(expected, result) + } + + @Test + fun `test dist with Vec3i`() { + val vector1 = Vec3d(1.0, 2.0, 3.0) + val vector2 = Vec3i(4, 5, 6) + val result = vector1 dist vector2 + + val expected = sqrt((1.0 - 4).sq + (2.0 - 5).sq + (3.0 - 6).sq) + assertEquals(expected, result) + } + + @Test + fun `test distSq with another Vec3d`() { + val vector1 = Vec3d(1.0, 2.0, 3.0) + val vector2 = Vec3d(4.0, 5.0, 6.0) + val result = vector1 distSq vector2 + + val expected = (1.0 - 4.0).sq + (2.0 - 5.0).sq + (3.0 - 6.0).sq + assertEquals(expected, result) + } + + @Test + fun `test distSq with Vec3i`() { + val vector1 = Vec3d(1.0, 2.0, 3.0) + val vector2 = Vec3i(4, 5, 6) + val result = vector1 distSq vector2 + + val expected = (1.0 - 4).sq + (2.0 - 5).sq + (3.0 - 6).sq + assertEquals(expected, result) + } + + @Test + fun `test plus with another Vec3d`() { + val vector1 = Vec3d(1.0, 2.0, 3.0) + val vector2 = Vec3d(4.0, 5.0, 6.0) + val result = vector1 + vector2 + + assertEquals(Vec3d(5.0, 7.0, 9.0), result) + } + + @Test + fun `test plus with Vec3i`() { + val vector1 = Vec3d(1.0, 2.0, 3.0) + val vector2 = Vec3i(4, 5, 6) + val result = vector1 + vector2 + + assertEquals(Vec3d(5.0, 7.0, 9.0), result) + } + + @Test + fun `test plus with scalar (Double)`() { + val vector = Vec3d(1.0, 2.0, 3.0) + val result = vector + 2.0 + + assertEquals(Vec3d(3.0, 4.0, 5.0), result) + } + + @Test + fun `test minus with another Vec3d`() { + val vector1 = Vec3d(5.0, 7.0, 9.0) + val vector2 = Vec3d(4.0, 5.0, 6.0) + val result = vector1 - vector2 + + assertEquals(Vec3d(1.0, 2.0, 3.0), result) + } + + @Test + fun `test minus with Vec3i`() { + val vector1 = Vec3d(5.0, 7.0, 9.0) + val vector2 = Vec3i(4, 5, 6) + val result = vector1 - vector2 + + assertEquals(Vec3d(1.0, 2.0, 3.0), result) + } + + @Test + fun `test multiplication with scalar (Double)`() { + val vector = Vec3d(1.0, 2.0, 3.0) + val result = vector * 2.0 + + assertEquals(Vec3d(2.0, 4.0, 6.0), result) + } + + @Test + fun `test multiplication with scalar (Int)`() { + val vector = Vec3d(1.0, 2.0, 3.0) + val result = vector * 2 + + assertEquals(Vec3d(2.0, 4.0, 6.0), result) + } + + @Test + fun `test division with scalar (Double)`() { + val vector = Vec3d(4.0, 8.0, 12.0) + val result = vector / 2.0 + + assertEquals(Vec3d(2.0, 4.0, 6.0), result) + } + + @Test + fun `test division with scalar (Int)`() { + println(1 / 2.0) + + val vector = Vec3d(4.0, 8.0, 12.0) + val result = vector / 2 + + assertEquals(Vec3d(2.0, 4.0, 6.0), result) + } + + @Test + fun `test Vec3i conversion to Vec3d`() { + val vector = Vec3i(1, 2, 3) + val result = vector.vec3d + + assertEquals(Vec3d(1.0, 2.0, 3.0), result) + } + + @Test + fun `test constants`() { + assertEquals(Vec3d(0.0, 1.0, 0.0), UP) + assertEquals(Vec3d(0.0, -1.0, 0.0), DOWN) + assertEquals(Vec3d(0.5, 0.5, 0.5), CENTER) + } +} From a786dfefa252df9972da3e4556eb9c982713e1b1 Mon Sep 17 00:00:00 2001 From: Edouard127 <46357922+Edouard127@users.noreply.github.com> Date: Fri, 14 Mar 2025 20:15:40 -0400 Subject: [PATCH 11/25] Fixed decay queue set max size --- .../com/lambda/util/collections/LimitedDecayQueue.kt | 8 +++++++- common/src/test/kotlin/LimitedDecayQueueTest.kt | 4 ---- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/util/collections/LimitedDecayQueue.kt b/common/src/main/kotlin/com/lambda/util/collections/LimitedDecayQueue.kt index 9dceafcb6..10368d4d5 100644 --- a/common/src/main/kotlin/com/lambda/util/collections/LimitedDecayQueue.kt +++ b/common/src/main/kotlin/com/lambda/util/collections/LimitedDecayQueue.kt @@ -106,11 +106,17 @@ class LimitedDecayQueue( * Updates the maximum allowed size for the queue and triggers a cleanup operation * to remove elements exceeding the new size or falling outside the allowed time interval. * + * Elements starting from the head will be removed. + * * @param newSize The new maximum size for the queue. Must be a non-negative integer. */ fun setMaxSize(newSize: Int) { sizeLimit = newSize cleanUp() + + while (queue.size > newSize) { + queue.poll() + } } /** @@ -131,4 +137,4 @@ class LimitedDecayQueue( onDecay(queue.poll().first) } } -} \ No newline at end of file +} diff --git a/common/src/test/kotlin/LimitedDecayQueueTest.kt b/common/src/test/kotlin/LimitedDecayQueueTest.kt index 2a9a27bc7..98a2120dd 100644 --- a/common/src/test/kotlin/LimitedDecayQueueTest.kt +++ b/common/src/test/kotlin/LimitedDecayQueueTest.kt @@ -137,11 +137,7 @@ class LimitedDecayQueueTest { queue.setMaxSize(2) // Reduce size limit to 2 - queue.add("Element4") - - // Verify that the queue is limited to 2 elements, and oldest element is removed assertEquals(2, queue.size) - assertTrue(onDecayCalled.contains("Element1")) // "Element1" should be decayed } @Test From aac70b47eae6644eba8220cd6cf40d0c33d87849 Mon Sep 17 00:00:00 2001 From: Constructor Date: Sun, 16 Mar 2025 05:26:17 +0100 Subject: [PATCH 12/25] Task tests --- common/build.gradle.kts | 4 + .../module/modules/network/PacketLimiter.kt | 2 +- .../src/main/kotlin/com/lambda/task/Task.kt | 4 +- .../com/lambda/task/tasks/OpenContainer.kt | 16 ++-- .../util/collections/LimitedDecayQueue.kt | 2 +- .../src/test/kotlin/LimitedDecayQueueTest.kt | 2 +- common/src/test/kotlin/TaskTest.kt | 80 +++++++++++++++++++ .../test/kotlin/{Vec3Test.kt => Vec3dTest.kt} | 2 +- gradle.properties | 2 + 9 files changed, 100 insertions(+), 14 deletions(-) create mode 100644 common/src/test/kotlin/TaskTest.kt rename common/src/test/kotlin/{Vec3Test.kt => Vec3dTest.kt} (99%) diff --git a/common/build.gradle.kts b/common/build.gradle.kts index 355df3a2d..c1501107c 100644 --- a/common/build.gradle.kts +++ b/common/build.gradle.kts @@ -23,6 +23,8 @@ val kotlinxCoroutinesVersion: String by project val discordIPCVersion: String by project val fuelVersion: String by project val resultVersion: String by project +val mockitoKotlin: String by project +val mockitoInline: String by project base.archivesName = "${base.archivesName.get()}-api" @@ -58,6 +60,8 @@ dependencies { // Baritone modImplementation("baritone-api:baritone-unoptimized-fabric:1.10.2") { isTransitive = false } testImplementation(kotlin("test")) + testImplementation("org.mockito.kotlin:mockito-kotlin:$mockitoKotlin") + testImplementation("org.mockito:mockito-inline:$mockitoInline") } tasks { diff --git a/common/src/main/kotlin/com/lambda/module/modules/network/PacketLimiter.kt b/common/src/main/kotlin/com/lambda/module/modules/network/PacketLimiter.kt index 04aa8e78a..5508b4264 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/network/PacketLimiter.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/network/PacketLimiter.kt @@ -35,7 +35,7 @@ object PacketLimiter : Module( ) { private var packetQueue = LimitedDecayQueue(99, 1000) private val limit by setting("Limit", 99, 1..100, 1, "The maximum amount of packets to send per given time interval", unit = " packets") - .onValueChange { _, to -> packetQueue.setMaxSize(to) } + .onValueChange { _, to -> packetQueue.setSizeLimit(to) } private val interval by setting("Duration", 1000L, 1L..1000L, 50L, "The interval / duration in milliseconds to limit packets for", unit = " ms") .onValueChange { _, to -> packetQueue.setDecayTime(to) } diff --git a/common/src/main/kotlin/com/lambda/task/Task.kt b/common/src/main/kotlin/com/lambda/task/Task.kt index 5da60e19c..f8efeab7a 100644 --- a/common/src/main/kotlin/com/lambda/task/Task.kt +++ b/common/src/main/kotlin/com/lambda/task/Task.kt @@ -38,8 +38,8 @@ typealias TaskGeneratorUnit = SafeContext.(R) -> Unit abstract class Task : Nameable, Muteable { private var parent: Task<*>? = null - private val subTasks = mutableListOf>() - private var state = State.INIT + val subTasks = mutableListOf>() + var state = State.INIT override val isMuted: Boolean get() = state == State.PAUSED || state == State.INIT var age = 0 private val depth: Int get() = parent?.depth?.plus(1) ?: 0 diff --git a/common/src/main/kotlin/com/lambda/task/tasks/OpenContainer.kt b/common/src/main/kotlin/com/lambda/task/tasks/OpenContainer.kt index 9c222b169..ca363f441 100644 --- a/common/src/main/kotlin/com/lambda/task/tasks/OpenContainer.kt +++ b/common/src/main/kotlin/com/lambda/task/tasks/OpenContainer.kt @@ -39,10 +39,10 @@ class OpenContainer @Ta5kBuilder constructor( private val interact: InteractionConfig = TaskFlowModule.interact, private val sides: Set = Direction.entries.toSet(), ) : Task() { - override val name get() = "${state.description(inScope)} at ${blockPos.toShortString()}" + override val name get() = "${containerState.description(inScope)} at ${blockPos.toShortString()}" private var screenHandler: ScreenHandler? = null - private var state = State.SCOPING + private var containerState = State.SCOPING private var inScope = 0 enum class State { @@ -57,10 +57,10 @@ class OpenContainer @Ta5kBuilder constructor( init { listen { - if (state != State.OPENING) return@listen + if (containerState != State.OPENING) return@listen screenHandler = it.screenHandler - state = State.SLOT_LOADING + containerState = State.SLOT_LOADING if (!waitForSlotLoad) success(it.screenHandler) } @@ -68,12 +68,12 @@ class OpenContainer @Ta5kBuilder constructor( listen { if (screenHandler != it.screenHandler) return@listen - state = State.SCOPING + containerState = State.SCOPING screenHandler = null } listen { - if (state != State.SLOT_LOADING) return@listen + if (containerState != State.SLOT_LOADING) return@listen screenHandler?.let { success(it) @@ -81,7 +81,7 @@ class OpenContainer @Ta5kBuilder constructor( } listen { - if (state != State.SCOPING) return@listen + if (containerState != State.SCOPING) return@listen val target = lookAtBlock(blockPos, sides, config = interact) if (rotate && !target.requestBy(rotation).done) return@listen @@ -89,7 +89,7 @@ class OpenContainer @Ta5kBuilder constructor( val hitResult = target.hit?.hitIfValid()?.blockResult ?: return@listen interaction.interactBlock(player, Hand.MAIN_HAND, hitResult) - state = State.OPENING + containerState = State.OPENING } } } diff --git a/common/src/main/kotlin/com/lambda/util/collections/LimitedDecayQueue.kt b/common/src/main/kotlin/com/lambda/util/collections/LimitedDecayQueue.kt index 10368d4d5..7bf1d0534 100644 --- a/common/src/main/kotlin/com/lambda/util/collections/LimitedDecayQueue.kt +++ b/common/src/main/kotlin/com/lambda/util/collections/LimitedDecayQueue.kt @@ -110,7 +110,7 @@ class LimitedDecayQueue( * * @param newSize The new maximum size for the queue. Must be a non-negative integer. */ - fun setMaxSize(newSize: Int) { + fun setSizeLimit(newSize: Int) { sizeLimit = newSize cleanUp() diff --git a/common/src/test/kotlin/LimitedDecayQueueTest.kt b/common/src/test/kotlin/LimitedDecayQueueTest.kt index 98a2120dd..0f48bcdc7 100644 --- a/common/src/test/kotlin/LimitedDecayQueueTest.kt +++ b/common/src/test/kotlin/LimitedDecayQueueTest.kt @@ -135,7 +135,7 @@ class LimitedDecayQueueTest { queue.add("Element2") queue.add("Element3") - queue.setMaxSize(2) // Reduce size limit to 2 + queue.setSizeLimit(2) // Reduce size limit to 2 assertEquals(2, queue.size) } diff --git a/common/src/test/kotlin/TaskTest.kt b/common/src/test/kotlin/TaskTest.kt new file mode 100644 index 000000000..56564e912 --- /dev/null +++ b/common/src/test/kotlin/TaskTest.kt @@ -0,0 +1,80 @@ +import com.lambda.context.ClientContext +import com.lambda.context.SafeContext +import com.lambda.task.RootTask +import com.lambda.task.RootTask.run +import com.lambda.task.Task +import org.junit.jupiter.api.AfterEach +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import org.mockito.Mock +import org.mockito.MockedConstruction +import org.mockito.Mockito +import org.mockito.Mockito.`when` +import org.mockito.MockitoAnnotations +import kotlin.test.assertEquals +import kotlin.test.assertTrue + +/* + * Copyright 2025 Lambda + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + + +class TaskTest { + + @Mock + private lateinit var mockSafeContext: SafeContext + + private lateinit var clientContextMock: MockedConstruction + + class TestTask(private val i: Int = 0) : Task() { + override val name get() = "TestTask of $i" + + override fun SafeContext.onStart() { + success(i + 1) + } + + override fun SafeContext.onCancel() { + success(i) + } + } + + @BeforeEach + fun setUp() { + MockitoAnnotations.openMocks(this) + clientContextMock = Mockito.mockConstruction(ClientContext::class.java) { mock, _ -> + `when`(mock.toSafe()).thenReturn(mockSafeContext) + } + } + + @AfterEach + fun tearDown() { + clientContextMock.close() + RootTask.clear() + } + + @Test + fun `test task`() { + val task = TestTask(5) + + assertEquals(task.name, "TestTask of 5") + + task.finally { result -> + assertEquals(result, 6) + assertEquals(task.state, Task.State.COMPLETED) + assertTrue(task.isCompleted) + }.run() + } +} \ No newline at end of file diff --git a/common/src/test/kotlin/Vec3Test.kt b/common/src/test/kotlin/Vec3dTest.kt similarity index 99% rename from common/src/test/kotlin/Vec3Test.kt rename to common/src/test/kotlin/Vec3dTest.kt index e7b7f4a1d..e1c5a431c 100644 --- a/common/src/test/kotlin/Vec3Test.kt +++ b/common/src/test/kotlin/Vec3dTest.kt @@ -32,7 +32,7 @@ import kotlin.test.assertEquals * along with this program. If not, see . */ -class Vec3Test { +class Vec3dTest { @Test fun `test dist with another Vec3d`() { diff --git a/gradle.properties b/gradle.properties index 86aec0a00..77b536f81 100644 --- a/gradle.properties +++ b/gradle.properties @@ -34,6 +34,8 @@ baritoneVersion=1.10.2 discordIPCVersion=8edf2dbeda fuelVersion=2.3.1 resultVersion=5.6.0 +mockitoKotlin=5.4.0 +mockitoInline=5.2.0 # Fabric https://fabricmc.net/develop/ fabricLoaderVersion=0.16.9 From 136f943037e0bea93d831e4184842541ab83c132 Mon Sep 17 00:00:00 2001 From: Edouard127 <46357922+Edouard127@users.noreply.github.com> Date: Sun, 16 Mar 2025 14:49:07 -0400 Subject: [PATCH 13/25] Removed print statement --- common/src/test/kotlin/Vec3dTest.kt | 2 -- 1 file changed, 2 deletions(-) diff --git a/common/src/test/kotlin/Vec3dTest.kt b/common/src/test/kotlin/Vec3dTest.kt index e1c5a431c..2ea45df70 100644 --- a/common/src/test/kotlin/Vec3dTest.kt +++ b/common/src/test/kotlin/Vec3dTest.kt @@ -144,8 +144,6 @@ class Vec3dTest { @Test fun `test division with scalar (Int)`() { - println(1 / 2.0) - val vector = Vec3d(4.0, 8.0, 12.0) val result = vector / 2 From 9b5b23b2b5c251860a6a3733402d225cf9f7cbd2 Mon Sep 17 00:00:00 2001 From: Edouard127 <46357922+Edouard127@users.noreply.github.com> Date: Sun, 16 Mar 2025 14:49:50 -0400 Subject: [PATCH 14/25] Create test.yml --- .github/workflows/test.yml | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 .github/workflows/test.yml diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 000000000..c2dee10c7 --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,27 @@ +name: Run Tests + +on: + push: + branches: + - 'master' + pull_request: + +jobs: + test: + name: Build Lambda + runs-on: ubuntu-latest + + steps: + - name: Checkout Repository + uses: actions/checkout@v4 + + - name: Set-Up JDK + uses: actions/setup-java@v4 + with: + distribution: 'temurin' + java-version: '23' + architecture: x64 + cache: 'gradle' + + - name: Build Lambda + run: ./gradlew common:test From 3463385380ab3bc46fb37ea6e4cd5cb0d65c52f7 Mon Sep 17 00:00:00 2001 From: Edouard127 <46357922+Edouard127@users.noreply.github.com> Date: Sun, 16 Mar 2025 15:07:45 -0400 Subject: [PATCH 15/25] Fixed mockito warning --- common/build.gradle.kts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/common/build.gradle.kts b/common/build.gradle.kts index c1501107c..6a4cbcc15 100644 --- a/common/build.gradle.kts +++ b/common/build.gradle.kts @@ -71,6 +71,7 @@ tasks { test { useJUnitPlatform() + jvmArgs("-XX:+EnableDynamicAgentLoading", "-Xshare:off") } } @@ -78,4 +79,4 @@ tasks.withType { kotlinOptions { jvmTarget = "17" } -} \ No newline at end of file +} From 3c85dd0e77e030db45b0f4846ce528d73b818993 Mon Sep 17 00:00:00 2001 From: Edouard127 <46357922+Edouard127@users.noreply.github.com> Date: Sun, 16 Mar 2025 15:07:57 -0400 Subject: [PATCH 16/25] Moved test into build workflow --- .github/workflows/build.yml | 61 +++++++++---------------------------- .github/workflows/test.yml | 27 ---------------- 2 files changed, 15 insertions(+), 73 deletions(-) delete mode 100644 .github/workflows/test.yml diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 9fe6f78a2..6cb52a7ca 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -3,61 +3,24 @@ name: Build Lambda on: push: branches: - - '**' + - 'master' + pull_request: jobs: - check-runner: - name: Check Runner Availability - runs-on: ubuntu-latest - - outputs: - runner-label: ${{ steps.set-runner.outputs.runner-label }} - - steps: - - name: Set runner - id: set-runner - run: | - runners=$(curl -v -s -H "Accept: application/vnd.github+json" -H "Authorization: token ${{ secrets.REPO_ACCESS_TOKEN }}" "https://api.github.com/repos/${{ github.repository }}/actions/runners" --http1.1) - if [ $? -ne 0 ]; then - echo "Error: Failed to fetch runners from GitHub API" >&2 - exit 1 - fi - - runners_count=$(echo "$runners" | jq '.runners | length') - if [ "$runners_count" -eq 0 ]; then - echo "No runners available or failed to retrieve runners." >&2 - echo "runner-label=ubuntu-latest" >> $GITHUB_OUTPUT - exit 0 - fi - - available=$(echo "$runners" | jq '.runners[] | select(.status == "online" and .busy == false and .labels[] .name == "self-hosted")') - if [ $? -ne 0 ]; then - echo "Error: Failed to parse JSON response" >&2 - exit 1 - fi - - if [ -n "$available" ]; then - echo "runner-label=lambda-linux-runner" >> $GITHUB_OUTPUT - else - echo "runner-label=ubuntu-latest" >> $GITHUB_OUTPUT - fi build: concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true - - needs: check-runner - runs-on: ${{ needs.check-runner.outputs.runner-label }} - name: Build Lambda + name: Build and test Lambda + runs-on: ubuntu-latest + permissions: contents: write + env: SEGMENT_DOWNLOAD_TIMEOUT_MINS: '5' steps: - - name: Checkout Repository - uses: actions/checkout@v4.1.1 - - name: Set current date as env variable run: echo "DATE=$(date +'%Y-%m-%dT%H:%M:%S')" >> $GITHUB_ENV @@ -65,11 +28,14 @@ jobs: id: vars run: echo "COMMIT_HASH=$(git rev-parse --short HEAD)" >> $GITHUB_ENV - - name: Set-Up JDK 17 + - name: Checkout Repository + uses: actions/checkout@v4 + + - name: Set-Up JDK uses: actions/setup-java@v4 with: distribution: 'temurin' - java-version: '17' + java-version: '23' architecture: x64 cache: 'gradle' @@ -81,7 +47,10 @@ jobs: all: true - name: Build Lambda - run: ./gradlew build + run: ./gradlew build --no-daemon + + - name: Test Lambda + run: ./gradlew common:test --no-daemon - name: Rename Files with Commit Hash run: | diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml deleted file mode 100644 index c2dee10c7..000000000 --- a/.github/workflows/test.yml +++ /dev/null @@ -1,27 +0,0 @@ -name: Run Tests - -on: - push: - branches: - - 'master' - pull_request: - -jobs: - test: - name: Build Lambda - runs-on: ubuntu-latest - - steps: - - name: Checkout Repository - uses: actions/checkout@v4 - - - name: Set-Up JDK - uses: actions/setup-java@v4 - with: - distribution: 'temurin' - java-version: '23' - architecture: x64 - cache: 'gradle' - - - name: Build Lambda - run: ./gradlew common:test From 27e9974cc54c463b17b34caa6e424dfa13c51813 Mon Sep 17 00:00:00 2001 From: Edouard127 <46357922+Edouard127@users.noreply.github.com> Date: Sun, 16 Mar 2025 15:09:26 -0400 Subject: [PATCH 17/25] Restored daemon --- .github/workflows/build.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 6cb52a7ca..21334a739 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -47,10 +47,10 @@ jobs: all: true - name: Build Lambda - run: ./gradlew build --no-daemon + run: ./gradlew build - name: Test Lambda - run: ./gradlew common:test --no-daemon + run: ./gradlew :common:test - name: Rename Files with Commit Hash run: | From 9be6466ed6d0593c3f30a7914d2b48e5bd5f9c8c Mon Sep 17 00:00:00 2001 From: Edouard127 <46357922+Edouard127@users.noreply.github.com> Date: Sun, 16 Mar 2025 15:21:07 -0400 Subject: [PATCH 18/25] Trying to fix mockito --- .github/workflows/build.yml | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 21334a739..7d15e81af 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -12,7 +12,7 @@ jobs: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true - name: Build and test Lambda + name: Build Lambda runs-on: ubuntu-latest permissions: @@ -35,7 +35,7 @@ jobs: uses: actions/setup-java@v4 with: distribution: 'temurin' - java-version: '23' + java-version: '17' architecture: x64 cache: 'gradle' @@ -49,9 +49,6 @@ jobs: - name: Build Lambda run: ./gradlew build - - name: Test Lambda - run: ./gradlew :common:test - - name: Rename Files with Commit Hash run: | mv ./fabric/build/libs/lambda-fabric-${{ steps.all.outputs.modVersion }}+${{ steps.all.outputs.minecraftVersion }}.jar ./fabric/build/libs/lambda-fabric-${{ steps.all.outputs.modVersion }}+${{ steps.all.outputs.minecraftVersion }}-${{ env.COMMIT_HASH }}.jar From 3c8b7f0d7efad3835e2ba608761f85f6d8aba2b3 Mon Sep 17 00:00:00 2001 From: Edouard127 <46357922+Edouard127@users.noreply.github.com> Date: Sun, 16 Mar 2025 15:22:04 -0400 Subject: [PATCH 19/25] Removed daemon --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 7d15e81af..13eb156e4 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -47,7 +47,7 @@ jobs: all: true - name: Build Lambda - run: ./gradlew build + run: ./gradlew build --no-daemon - name: Rename Files with Commit Hash run: | From 0692abc5a4a4702eb9be120131583bdc5c00783d Mon Sep 17 00:00:00 2001 From: Edouard127 <46357922+Edouard127@users.noreply.github.com> Date: Sun, 16 Mar 2025 15:33:59 -0400 Subject: [PATCH 20/25] Test test fail --- common/src/test/kotlin/FastVectorTest.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/common/src/test/kotlin/FastVectorTest.kt b/common/src/test/kotlin/FastVectorTest.kt index 8f041ea3f..b5c3da0a0 100644 --- a/common/src/test/kotlin/FastVectorTest.kt +++ b/common/src/test/kotlin/FastVectorTest.kt @@ -47,6 +47,7 @@ class FastVectorTest { val fastVec = fastVectorOf(x, y, z) + assertEquals(true, false) assertEquals(x, fastVec.x) assertEquals(y, fastVec.y) assertEquals(z, fastVec.z) From 38680212e2b346fcb1927661894f27a5fe7e7c6c Mon Sep 17 00:00:00 2001 From: Edouard127 <46357922+Edouard127@users.noreply.github.com> Date: Sun, 16 Mar 2025 15:36:50 -0400 Subject: [PATCH 21/25] Update FastVectorTest.kt --- common/src/test/kotlin/FastVectorTest.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/common/src/test/kotlin/FastVectorTest.kt b/common/src/test/kotlin/FastVectorTest.kt index b5c3da0a0..8f041ea3f 100644 --- a/common/src/test/kotlin/FastVectorTest.kt +++ b/common/src/test/kotlin/FastVectorTest.kt @@ -47,7 +47,6 @@ class FastVectorTest { val fastVec = fastVectorOf(x, y, z) - assertEquals(true, false) assertEquals(x, fastVec.x) assertEquals(y, fastVec.y) assertEquals(z, fastVec.z) From f0ed228386849d3b44babe38ef732c87b82ec431 Mon Sep 17 00:00:00 2001 From: Edouard127 <46357922+Edouard127@users.noreply.github.com> Date: Sun, 16 Mar 2025 15:36:57 -0400 Subject: [PATCH 22/25] Update build.yml --- .github/workflows/build.yml | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 13eb156e4..b6c1ff1ae 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -21,6 +21,9 @@ jobs: env: SEGMENT_DOWNLOAD_TIMEOUT_MINS: '5' steps: + - name: Checkout Repository + uses: actions/checkout@v4 + - name: Set current date as env variable run: echo "DATE=$(date +'%Y-%m-%dT%H:%M:%S')" >> $GITHUB_ENV @@ -28,14 +31,11 @@ jobs: id: vars run: echo "COMMIT_HASH=$(git rev-parse --short HEAD)" >> $GITHUB_ENV - - name: Checkout Repository - uses: actions/checkout@v4 - - name: Set-Up JDK uses: actions/setup-java@v4 with: distribution: 'temurin' - java-version: '17' + java-version: '21' architecture: x64 cache: 'gradle' @@ -77,8 +77,6 @@ jobs: ### [Lambda Forge ${{ steps.all.outputs.modVersion }} ${{ steps.all.outputs.minecraftVersion }} (${{ env.COMMIT_HASH }})](https://r2-bucket.edouard127.christmas/${{ env.DATE }}-${{ env.COMMIT_HASH }}/lambda-forge-${{ steps.all.outputs.modVersion }}+${{ steps.all.outputs.minecraftVersion }}-${{ env.COMMIT_HASH }}.jar) #### [API (Developer Dependency)](https://r2-bucket.edouard127.christmas/${{ env.DATE }}-${{ env.COMMIT_HASH }}/lambda-api-${{ steps.all.outputs.modVersion }}+${{ steps.all.outputs.minecraftVersion }}-${{ env.COMMIT_HASH }}.jar) - - **Runner:** \`${{ needs.check-runner.outputs.runner-label }}\` EOF - name: Failover Upload From c0c36b0d13cb47c250248c73b15ed94429d580c6 Mon Sep 17 00:00:00 2001 From: Constructor Date: Mon, 7 Apr 2025 05:25:20 +0200 Subject: [PATCH 23/25] Some more task tests has issues with mocking logging ingame thats why some tests are disabled for now --- common/build.gradle.kts | 2 + .../src/main/kotlin/com/lambda/task/Task.kt | 2 +- common/src/test/kotlin/TaskTest.kt | 210 +++++++++++++++++- gradle.properties | 1 + 4 files changed, 210 insertions(+), 5 deletions(-) diff --git a/common/build.gradle.kts b/common/build.gradle.kts index 6a4cbcc15..d72e6c4ec 100644 --- a/common/build.gradle.kts +++ b/common/build.gradle.kts @@ -25,6 +25,7 @@ val fuelVersion: String by project val resultVersion: String by project val mockitoKotlin: String by project val mockitoInline: String by project +val mockkVersion: String by project base.archivesName = "${base.archivesName.get()}-api" @@ -62,6 +63,7 @@ dependencies { testImplementation(kotlin("test")) testImplementation("org.mockito.kotlin:mockito-kotlin:$mockitoKotlin") testImplementation("org.mockito:mockito-inline:$mockitoInline") + testImplementation("io.mockk:mockk:${mockkVersion}") } tasks { diff --git a/common/src/main/kotlin/com/lambda/task/Task.kt b/common/src/main/kotlin/com/lambda/task/Task.kt index f8efeab7a..ba5431388 100644 --- a/common/src/main/kotlin/com/lambda/task/Task.kt +++ b/common/src/main/kotlin/com/lambda/task/Task.kt @@ -37,7 +37,7 @@ typealias TaskGeneratorOrNull = SafeContext.(R) -> Task<*>? typealias TaskGeneratorUnit = SafeContext.(R) -> Unit abstract class Task : Nameable, Muteable { - private var parent: Task<*>? = null + var parent: Task<*>? = null val subTasks = mutableListOf>() var state = State.INIT override val isMuted: Boolean get() = state == State.PAUSED || state == State.INIT diff --git a/common/src/test/kotlin/TaskTest.kt b/common/src/test/kotlin/TaskTest.kt index 56564e912..9d20b9343 100644 --- a/common/src/test/kotlin/TaskTest.kt +++ b/common/src/test/kotlin/TaskTest.kt @@ -3,15 +3,21 @@ import com.lambda.context.SafeContext import com.lambda.task.RootTask import com.lambda.task.RootTask.run import com.lambda.task.Task +import com.lambda.util.Communication +import com.lambda.util.Communication.log +import io.mockk.every +import io.mockk.mockkObject import org.junit.jupiter.api.AfterEach import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test +import org.junit.jupiter.api.assertThrows import org.mockito.Mock import org.mockito.MockedConstruction -import org.mockito.Mockito -import org.mockito.Mockito.`when` +import org.mockito.Mockito.mockConstruction import org.mockito.MockitoAnnotations +import org.mockito.kotlin.whenever import kotlin.test.assertEquals +import kotlin.test.assertFalse import kotlin.test.assertTrue /* @@ -54,9 +60,11 @@ class TaskTest { @BeforeEach fun setUp() { MockitoAnnotations.openMocks(this) - clientContextMock = Mockito.mockConstruction(ClientContext::class.java) { mock, _ -> - `when`(mock.toSafe()).thenReturn(mockSafeContext) + clientContextMock = mockConstruction(ClientContext::class.java) { mock, _ -> + whenever(mock.toSafe()).thenReturn(mockSafeContext) } + mockkObject(Communication) + every { Communication.log(any(), any(), any(), any()) } returns Unit } @AfterEach @@ -65,6 +73,200 @@ class TaskTest { RootTask.clear() } + @Test + fun `task initial state is INIT`() { + val task = TestTask(0) + assertEquals(Task.State.INIT, task.state) + assertTrue(task.subTasks.isEmpty()) + assertEquals(0, task.age) + } + + @Test + fun `execute transitions to RUNNING and adds to parent subTasks`() { + val parent = TestTask(0) + val child = TestTask(1) + + child.execute(parent) + + assertEquals(Task.State.COMPLETED, child.state) + assertTrue(parent.subTasks.contains(child)) + assertEquals(parent, child.parent) + } + + @Test + fun `success transitions to COMPLETED and executes finally block`() { + val task = TestTask(5) + var finallyCalled = false + + task.finally { result -> + assertEquals(6, result) + finallyCalled = true + }.run() + + assertEquals(Task.State.COMPLETED, task.state) + assertTrue(finallyCalled) + } + +// @Test +// fun `cancel transitions to CANCELLED and cancels subTasks`() { +// val parent = TestTask(0) +// val child = TestTask(1).apply { execute(parent) } +// +// child.cancel() +// +// assertEquals(Task.State.CANCELLED, child.state) +// assertTrue(child.subTasks.all { it.state == Task.State.CANCELLED }) +// } + + @Test + fun `pause and activate change state between PAUSED and RUNNING`() { + val task = TestTask(0).apply { state = Task.State.RUNNING } + + task.pause() + assertEquals(Task.State.PAUSED, task.state) + + task.activate() + assertEquals(Task.State.RUNNING, task.state) + } + +// @Test +// fun `subtask pauses parent when executed with pauseParent true`() { +// val parent = TestTask(0).apply { state = Task.State.RUNNING } +// val child = TestTask(1) +// +// child.execute(parent, pauseParent = true) +// +// assertEquals(Task.State.PAUSED, parent.state) +// } + + @Test + fun `then chains tasks in sequence`() { + val task1 = TestTask(1) + val task2 = TestTask(2) + + task1.then(task2).run() + + task1.success(6) // Simulate success to trigger next task + assertTrue(task1.parent?.subTasks?.contains(task2) == true) + } + +// @Test +// fun `finally block is called on failure`() { +// var finallyCalled = false +// val task = object : Task() { +// override val name = "FailingTask" +// override fun SafeContext.onStart() { +// throw RuntimeException("Simulated failure") +// } +// }.finally { finallyCalled = true } +// +// task.run() +// +// assertEquals(Task.State.FAILED, task.state) +// assertTrue(finallyCalled) +// } + + @Test + fun `execute with self as owner throws exception`() { + val task = TestTask(0) + assertThrows { + task.execute(task) + } + } + + @Test + fun `then with self throws exception`() { + val task = TestTask(0) + assertThrows { + task.then(task) + } + } + +// @Test +// fun `duration is formatted correctly`() { +// val task = TestTask(0).apply { age = 120 } // 120 * 50ms = 6000ms +// assertEquals("000:00:00:06.00", task.duration) +// } + + @Test + fun `toString includes task hierarchy and state`() { + val parent = TestTask(1) + val child = TestTask(2).apply { execute(parent) } + + val expected = """ + TestTask of 1 [Initialized] + TestTask of 2 [Running] 0ms + """.trimIndent().replace("\n", System.lineSeparator()) + + assertTrue(parent.toString().contains("TestTask of 1")) + assertTrue(parent.toString().contains("TestTask of 2")) + } + + @Test + fun `clear removes all subTasks`() { + val parent = TestTask(0) + TestTask(1).execute(parent) + TestTask(2).execute(parent) + + parent.clear() + + assertTrue(parent.subTasks.isEmpty()) + } + + @Test + fun `isMuted returns true when PAUSED or INIT`() { + val task = TestTask(0) + assertTrue(task.isMuted) // INIT state + + task.state = Task.State.PAUSED + assertTrue(task.isMuted) + + task.state = Task.State.RUNNING + assertFalse(task.isMuted) + } + +// @Test +// fun `failure propagates to parent with stacktrace`() { +// val grandParent = TestTask(0) +// val parent = TestTask(1).apply { execute(grandParent) } +// val child = TestTask(2).apply { execute(parent) } +// +// val exception = RuntimeException("Child failed") +// child.failure(exception) +// +// assertEquals(Task.State.FAILED, child.state) +// assertEquals(Task.State.FAILED, parent.state) +// assertEquals(Task.State.FAILED, grandParent.state) +// } + + @Test + fun `task with thenOrNull executes next task conditionally`() { + val task = TestTask(0) + var nextTaskExecuted = false + + task.thenOrNull { result -> + if (result == 1) TestTask(1).also { nextTaskExecuted = true } else null + } + + task.success(1) + assertTrue(nextTaskExecuted) + + nextTaskExecuted = false + task.success(0) + assertFalse(nextTaskExecuted) + } + + @Test + fun `subtask resumes parent when completed`() { + val parent = TestTask(0).apply { state = Task.State.RUNNING } + val child = TestTask(1).apply { execute(parent, pauseParent = true) } + + child.success(2) + + assertEquals(Task.State.COMPLETED, child.state) + assertEquals(Task.State.RUNNING, parent.state) + } + @Test fun `test task`() { val task = TestTask(5) diff --git a/gradle.properties b/gradle.properties index 77b536f81..aa18956ef 100644 --- a/gradle.properties +++ b/gradle.properties @@ -36,6 +36,7 @@ fuelVersion=2.3.1 resultVersion=5.6.0 mockitoKotlin=5.4.0 mockitoInline=5.2.0 +mockkVersion=1.13.17 # Fabric https://fabricmc.net/develop/ fabricLoaderVersion=0.16.9 From 3ef2f480d9b5539ae540f58dc33702de79d3a643 Mon Sep 17 00:00:00 2001 From: Constructor Date: Mon, 7 Apr 2025 05:44:26 +0200 Subject: [PATCH 24/25] CI: Parallel test task --- .github/workflows/build.yml | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index b6c1ff1ae..537056321 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -7,6 +7,23 @@ on: pull_request: jobs: + test: + name: Run Unit Tests + runs-on: ubuntu-latest + steps: + - name: Checkout Repository + uses: actions/checkout@v4 + + - name: Set-Up JDK + uses: actions/setup-java@v4 + with: + distribution: 'temurin' + java-version: '21' + cache: 'gradle' + + - name: Run Tests + run: ./gradlew test --no-daemon + build: concurrency: group: ${{ github.workflow }}-${{ github.ref }} From fc78398f46348181ff0d5687736450eac08132fa Mon Sep 17 00:00:00 2001 From: Constructor Date: Mon, 7 Apr 2025 20:05:21 +0200 Subject: [PATCH 25/25] Move tests to build task --- .github/workflows/build.yml | 17 ----------------- common/build.gradle.kts | 6 ++++++ 2 files changed, 6 insertions(+), 17 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 537056321..b6c1ff1ae 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -7,23 +7,6 @@ on: pull_request: jobs: - test: - name: Run Unit Tests - runs-on: ubuntu-latest - steps: - - name: Checkout Repository - uses: actions/checkout@v4 - - - name: Set-Up JDK - uses: actions/setup-java@v4 - with: - distribution: 'temurin' - java-version: '21' - cache: 'gradle' - - - name: Run Tests - run: ./gradlew test --no-daemon - build: concurrency: group: ${{ github.workflow }}-${{ github.ref }} diff --git a/common/build.gradle.kts b/common/build.gradle.kts index d72e6c4ec..49696e87b 100644 --- a/common/build.gradle.kts +++ b/common/build.gradle.kts @@ -82,3 +82,9 @@ tasks.withType { jvmTarget = "17" } } + +subprojects { + tasks.named("build") { + dependsOn("test") + } +}