From 866cdb71f71d44aecc869df4275ffb4b897e351c Mon Sep 17 00:00:00 2001 From: theEvilReaper Date: Mon, 1 Sep 2025 10:31:56 +0200 Subject: [PATCH 1/2] Update package-info files --- .../java/net/minestom/testing/extension/package-info.java | 5 ++++- src/main/java/net/minestom/testing/package-info.java | 4 ++++ 2 files changed, 8 insertions(+), 1 deletion(-) create mode 100644 src/main/java/net/minestom/testing/package-info.java diff --git a/src/main/java/net/minestom/testing/extension/package-info.java b/src/main/java/net/minestom/testing/extension/package-info.java index 8a40149..76353d5 100644 --- a/src/main/java/net/minestom/testing/extension/package-info.java +++ b/src/main/java/net/minestom/testing/extension/package-info.java @@ -18,4 +18,7 @@ * * @since 1.5.0 */ -package net.minestom.testing.extension; \ No newline at end of file +@NotNullByDefault +package net.minestom.testing.extension; + +import org.jetbrains.annotations.NotNullByDefault; \ No newline at end of file diff --git a/src/main/java/net/minestom/testing/package-info.java b/src/main/java/net/minestom/testing/package-info.java new file mode 100644 index 0000000..8cb7bd1 --- /dev/null +++ b/src/main/java/net/minestom/testing/package-info.java @@ -0,0 +1,4 @@ +@NotNullByDefault +package net.minestom.testing; + +import org.jetbrains.annotations.NotNullByDefault; \ No newline at end of file From 8d6cda4c3408e12b546601a93f78d31a3aacc6e8 Mon Sep 17 00:00:00 2001 From: theEvilReaper Date: Mon, 1 Sep 2025 10:32:12 +0200 Subject: [PATCH 2/2] Remove annotation usage and align code to the upstream --- .../java/net/minestom/testing/Collector.java | 37 ++++++++++++-- src/main/java/net/minestom/testing/Env.java | 22 +++++++-- .../java/net/minestom/testing/EnvImpl.java | 19 ++++---- .../minestom/testing/FlexibleListener.java | 3 +- .../net/minestom/testing/TestConnection.java | 9 ++-- .../minestom/testing/TestConnectionImpl.java | 20 ++++---- .../net/minestom/testing/TestPlayerImpl.java | 5 +- .../testing/extension/MicrotusExtension.java | 48 ++++++++----------- 8 files changed, 96 insertions(+), 67 deletions(-) diff --git a/src/main/java/net/minestom/testing/Collector.java b/src/main/java/net/minestom/testing/Collector.java index d47d015..694c204 100644 --- a/src/main/java/net/minestom/testing/Collector.java +++ b/src/main/java/net/minestom/testing/Collector.java @@ -4,9 +4,12 @@ import java.util.List; import java.util.function.Consumer; +import java.util.function.Predicate; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertInstanceOf; +import static org.junit.jupiter.api.Assertions.assertTrue; /** * A utility interface for collecting and asserting on collections of elements in tests. @@ -36,11 +39,12 @@ public interface Collector { * @param consumer the consumer to apply to the single element * @param

the type of the single element */ - default

void assertSingle(@NotNull Class

type, @NotNull Consumer

consumer) { + default

void assertSingle(Class

type, Consumer

consumer) { List elements = collect(); assertEquals(1, elements.size(), "Expected 1 element, got " + elements); - T element = elements.getFirst(); + var element = elements.getFirst(); assertInstanceOf(type, element, "Expected type " + type.getSimpleName() + ", got " + element.getClass().getSimpleName()); + //noinspection unchecked consumer.accept((P) element); } @@ -49,7 +53,7 @@ default

void assertSingle(@NotNull Class

type, @NotNull Consume * * @param consumer the consumer to apply to the single element */ - default void assertSingle(@NotNull Consumer consumer) { + default void assertSingle(Consumer consumer) { List elements = collect(); assertEquals(1, elements.size(), "Expected 1 element, got " + elements); consumer.accept(elements.getFirst()); @@ -78,4 +82,31 @@ default void assertSingle() { default void assertEmpty() { assertCount(0); } + + /** + * Asserts that at least one element matches the given predicate. + */ + default void assertAnyMatch(Predicate predicate) { + List elements = collect(); + assertTrue(elements.stream().anyMatch(predicate), + "No elements matched the predicate. Elements: " + elements); + } + + /** + * Asserts that no elements match the given predicate. + */ + default void assertNoneMatch(Predicate predicate) { + List elements = collect(); + assertFalse(elements.stream().anyMatch(predicate), + "Found elements that matched the predicate: " + elements.stream().filter(predicate).toList()); + } + + /** + * Asserts that all elements match the given predicate. + */ + default void assertAllMatch(Predicate predicate) { + List elements = collect(); + assertTrue(elements.stream().allMatch(predicate), + "Not all elements matched the predicate. Elements: " + elements); + } } diff --git a/src/main/java/net/minestom/testing/Env.java b/src/main/java/net/minestom/testing/Env.java index e557cff..9a487cb 100644 --- a/src/main/java/net/minestom/testing/Env.java +++ b/src/main/java/net/minestom/testing/Env.java @@ -8,11 +8,13 @@ import net.minestom.server.instance.IChunkLoader; import net.minestom.server.instance.Instance; import net.minestom.server.instance.block.Block; +import net.minestom.server.network.player.GameProfile; import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.time.Duration; +import java.util.UUID; import java.util.function.BooleanSupplier; /** @@ -46,7 +48,7 @@ public interface Env { * @return a new instance of {@link Env} */ @Contract(value = "_ -> new", pure = true) - static @NotNull Env createInstance(@NotNull ServerProcess process) { + static Env createInstance(ServerProcess process) { return new EnvImpl(process); } @@ -55,14 +57,24 @@ public interface Env { * * @return the server process */ - @NotNull ServerProcess process(); + ServerProcess process(); /** * Creates a new {@link TestConnection} which can be used in the test environment. * * @return the created connection */ - @NotNull TestConnection createConnection(); + default TestConnection createConnection() { + return createConnection(new GameProfile(UUID.randomUUID(), "RandName")); + } + + /** + * Creates a new {@link TestConnection} which can be used in the test environment. + * + * @param gameProfile the game profile to use for the connection + * @return the created connection + */ + TestConnection createConnection(GameProfile gameProfile); /** * Tracks a specific event type in the test environment. @@ -74,7 +86,7 @@ public interface Env { * @param the handler type * @return the {@link Collector} instance to use */ - @NotNull Collector trackEvent(@NotNull Class eventType, @NotNull EventFilter filter, @NotNull H actor); + Collector trackEvent(Class eventType, EventFilter filter, @NotNull H actor); /** * Listen for a specific event type in the test environment. @@ -83,7 +95,7 @@ public interface Env { * @param the event type * @return the {@link FlexibleListener} instance to use */ - @NotNull FlexibleListener listen(@NotNull Class eventType); + FlexibleListener listen(Class eventType); /** * Ticks the {@link ServerProcess} which is involved into the env instance. diff --git a/src/main/java/net/minestom/testing/EnvImpl.java b/src/main/java/net/minestom/testing/EnvImpl.java index b3e58d2..2553f42 100644 --- a/src/main/java/net/minestom/testing/EnvImpl.java +++ b/src/main/java/net/minestom/testing/EnvImpl.java @@ -4,6 +4,7 @@ import net.minestom.server.event.Event; import net.minestom.server.event.EventFilter; import net.minestom.server.event.EventListener; +import net.minestom.server.network.player.GameProfile; import org.jetbrains.annotations.NotNull; import java.util.List; @@ -13,7 +14,7 @@ import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; -final class EnvImpl implements Env { +public final class EnvImpl implements Env { private final ServerProcess process; private final List> listeners = new CopyOnWriteArrayList<>(); @@ -29,24 +30,24 @@ public EnvImpl(ServerProcess process) { } @Override - public @NotNull ServerProcess process() { + public ServerProcess process() { return process; } @Override - public @NotNull TestConnection createConnection() { - return new TestConnectionImpl(this); + public TestConnection createConnection(GameProfile profile) { + return new TestConnectionImpl(this, profile); } @Override - public @NotNull Collector trackEvent(@NotNull Class eventType, @NotNull EventFilter filter, @NotNull H actor) { + public Collector trackEvent(Class eventType, EventFilter filter, @NotNull H actor) { var tracker = new EventCollector(actor); this.process.eventHandler().map(actor, filter).addListener(eventType, tracker.events::add); return tracker; } @Override - public @NotNull FlexibleListener listen(@NotNull Class eventType) { + public FlexibleListener listen(Class eventType) { var handler = process.eventHandler(); var flexible = new FlexibleListenerImpl<>(eventType); var listener = EventListener.of(eventType, e -> flexible.handler.accept(e)); @@ -70,7 +71,7 @@ public EventCollector(Object handler) { } @Override - public @NotNull List collect() { + public List collect() { process.eventHandler().unmap(handler); return List.copyOf(events); } @@ -88,7 +89,7 @@ static final class FlexibleListenerImpl implements FlexibleList } @Override - public void followup(@NotNull Consumer handler) { + public void followup(Consumer handler) { updateHandler(handler); } @@ -97,7 +98,7 @@ public void failFollowup() { updateHandler(e -> fail("Event " + e.getClass().getSimpleName() + " was not expected")); } - void updateHandler(@NotNull Consumer handler) { + void updateHandler(Consumer handler) { check(); this.initialized = true; this.called = false; diff --git a/src/main/java/net/minestom/testing/FlexibleListener.java b/src/main/java/net/minestom/testing/FlexibleListener.java index 68ae762..a45110a 100644 --- a/src/main/java/net/minestom/testing/FlexibleListener.java +++ b/src/main/java/net/minestom/testing/FlexibleListener.java @@ -1,7 +1,6 @@ package net.minestom.testing; import net.minestom.server.event.Event; -import org.jetbrains.annotations.NotNull; import java.util.function.Consumer; @@ -21,7 +20,7 @@ public interface FlexibleListener { * * @param handler the consumer to handle the next event */ - void followup(@NotNull Consumer handler); + void followup( Consumer handler); /** * Empty followup handler. diff --git a/src/main/java/net/minestom/testing/TestConnection.java b/src/main/java/net/minestom/testing/TestConnection.java index df0d811..ea40afa 100644 --- a/src/main/java/net/minestom/testing/TestConnection.java +++ b/src/main/java/net/minestom/testing/TestConnection.java @@ -4,7 +4,6 @@ import net.minestom.server.entity.Player; import net.minestom.server.instance.Instance; import net.minestom.server.network.packet.server.ServerPacket; -import org.jetbrains.annotations.NotNull; /** * The {@link TestConnection} represents a connection from a player to a test server instance. @@ -22,7 +21,7 @@ public interface TestConnection { * @param pos the position to connect at * @return the connected player */ - @NotNull Player connect(@NotNull Instance instance, @NotNull Pos pos); + Player connect(Instance instance, Pos pos); /** * Connects a player to the given instance at the default position (0, 0, 0). @@ -30,7 +29,7 @@ public interface TestConnection { * @param instance the instance to connect to * @return the connected player */ - default @NotNull Player connect(@NotNull Instance instance) { + default Player connect(Instance instance) { return connect(instance, Pos.ZERO); } @@ -41,14 +40,14 @@ public interface TestConnection { * @param the type of the packet * @return a collector for the specified packet type */ - @NotNull Collector trackIncoming(@NotNull Class type); + Collector trackIncoming(Class type); /** * Tracks incoming packets of the default type {@link ServerPacket}. * * @return a collector for the default packet type */ - default @NotNull Collector trackIncoming() { + default Collector trackIncoming() { return trackIncoming(ServerPacket.class); } } diff --git a/src/main/java/net/minestom/testing/TestConnectionImpl.java b/src/main/java/net/minestom/testing/TestConnectionImpl.java index a363221..0f333a5 100644 --- a/src/main/java/net/minestom/testing/TestConnectionImpl.java +++ b/src/main/java/net/minestom/testing/TestConnectionImpl.java @@ -12,38 +12,36 @@ import net.minestom.server.network.packet.server.ServerPacket; import net.minestom.server.network.player.GameProfile; import net.minestom.server.network.player.PlayerConnection; -import org.jetbrains.annotations.NotNull; import java.net.InetSocketAddress; import java.net.SocketAddress; import java.util.List; import java.util.Objects; -import java.util.UUID; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.atomic.AtomicBoolean; final class TestConnectionImpl implements TestConnection { - private final Env env; + private final ServerProcess process; + private final GameProfile gameProfile; private final PlayerConnectionImpl playerConnection = new PlayerConnectionImpl(); private final AtomicBoolean connected = new AtomicBoolean(false); private final List> incomingTrackers = new CopyOnWriteArrayList<>(); - TestConnectionImpl(Env env) { - this.env = env; + TestConnectionImpl(Env env, GameProfile gameProfile) { this.process = env.process(); + this.gameProfile = gameProfile; } @Override - public @NotNull Player connect(@NotNull Instance instance, @NotNull Pos pos) { + public Player connect(Instance instance, Pos pos) { if (!connected.compareAndSet(false, true)) { throw new IllegalStateException("Already connected"); } - final GameProfile gameProfile = new GameProfile(UUID.randomUUID(), "RandName"); var player = process.connection().createPlayer(playerConnection, gameProfile); player.eventNode().addListener(AsyncPlayerConfigurationEvent.class, event -> { event.setSpawningInstance(instance); @@ -67,7 +65,7 @@ final class TestConnectionImpl implements TestConnection { } @Override - public @NotNull Collector trackIncoming(@NotNull Class type) { + public Collector trackIncoming(Class type) { var tracker = new IncomingCollector<>(type); this.incomingTrackers.add(IncomingCollector.class.cast(tracker)); return tracker; @@ -77,7 +75,7 @@ final class PlayerConnectionImpl extends PlayerConnection { private boolean online = true; @Override - public void sendPacket(@NotNull SendablePacket packet) { + public void sendPacket(SendablePacket packet) { final var serverPacket = this.extractPacket(packet); for (var tracker : incomingTrackers) { if (tracker.type.isAssignableFrom(serverPacket.getClass())) tracker.packets.add(serverPacket); @@ -100,7 +98,7 @@ private ServerPacket extractPacket(final SendablePacket packet) { } @Override - public @NotNull SocketAddress getRemoteAddress() { + public SocketAddress getRemoteAddress() { return new InetSocketAddress("localhost", 25565); } @@ -124,7 +122,7 @@ public IncomingCollector(Class type) { } @Override - public @NotNull List collect() { + public List collect() { incomingTrackers.remove(this); return List.copyOf(packets); } diff --git a/src/main/java/net/minestom/testing/TestPlayerImpl.java b/src/main/java/net/minestom/testing/TestPlayerImpl.java index 7d26144..38cba51 100644 --- a/src/main/java/net/minestom/testing/TestPlayerImpl.java +++ b/src/main/java/net/minestom/testing/TestPlayerImpl.java @@ -4,7 +4,6 @@ import net.minestom.server.instance.Chunk; import net.minestom.server.network.player.GameProfile; import net.minestom.server.network.player.PlayerConnection; -import org.jetbrains.annotations.NotNull; /** * The test environment can't really use a real player for the test. @@ -21,7 +20,7 @@ public class TestPlayerImpl extends Player { * @param playerConnection the player's connection * @param gameProfile the player's game profile */ - public TestPlayerImpl(@NotNull PlayerConnection playerConnection, @NotNull GameProfile gameProfile) { + public TestPlayerImpl(PlayerConnection playerConnection, GameProfile gameProfile) { super(playerConnection, gameProfile); } @@ -31,7 +30,7 @@ public TestPlayerImpl(@NotNull PlayerConnection playerConnection, @NotNull GameP * @param chunk the chunk to send */ @Override - public void sendChunk(@NotNull Chunk chunk) { + public void sendChunk(Chunk chunk) { // Send immediately sendPacket(chunk.getFullDataPacket()); } diff --git a/src/main/java/net/minestom/testing/extension/MicrotusExtension.java b/src/main/java/net/minestom/testing/extension/MicrotusExtension.java index 1f1fb1f..85c3714 100644 --- a/src/main/java/net/minestom/testing/extension/MicrotusExtension.java +++ b/src/main/java/net/minestom/testing/extension/MicrotusExtension.java @@ -1,14 +1,11 @@ package net.minestom.testing.extension; import net.minestom.server.MinecraftServer; -import org.jetbrains.annotations.NotNull; +import net.minestom.testing.EnvImpl; import org.junit.jupiter.api.extension.*; import org.junit.jupiter.api.extension.support.TypeBasedParameterResolver; import net.minestom.testing.Env; -import java.lang.reflect.Method; -import java.util.List; - /** * The {@link MicrotusExtension} class extends {@link TypeBasedParameterResolver} and implements {@link InvocationInterceptor}. * This extension is used to resolve parameters of type {@link Env} and to intercept test method invocations. @@ -16,48 +13,41 @@ * @since 1.5.0 * @version 1.1.0 */ -public class MicrotusExtension extends TypeBasedParameterResolver implements InvocationInterceptor, BeforeEachCallback { +public class MicrotusExtension extends TypeBasedParameterResolver implements BeforeEachCallback, AfterEachCallback { + + private static final String ENV_KEY = "minestom.env"; /** * Resolves the parameter of type {@link Env}. * - * @param parameterContext the context for the parameter for which an argument should be resolved; never {@code null} - * @param extensionContext the extension context for the {@code Executable} about to be invoked; never {@code null} + * @param parameterContext the context for the parameter for which an argument should be resolved + * @param extensionContext the extension context for the {@code Executable} about to be invoked * @return an instance of {@link Env} * @throws ParameterResolutionException if an error occurs during parameter resolution */ @Override - public Env resolveParameter(@NotNull ParameterContext parameterContext, @NotNull ExtensionContext extensionContext) - throws ParameterResolutionException { - return Env.createInstance(MinecraftServer.updateProcess()); - } - - /** - * Intercepts the test method invocation to perform additional actions before or after the test method execution. - * - * @param invocation the invocation to be intercepted; never {@code null} - * @param invocationContext the context for the reflective invocation of the test method; never {@code null} - * @param extensionContext the context for the extension; never {@code null} - * @throws Throwable if an error occurs during the interception - */ - @Override - public void interceptTestMethod(@NotNull Invocation invocation, @NotNull ReflectiveInvocationContext invocationContext, @NotNull ExtensionContext extensionContext) throws Throwable { - invocation.proceed(); - List arguments = invocationContext.getArguments(); - arguments.stream().filter(Env.class::isInstance).findFirst().ifPresent(o -> { - Env env = (Env) o; - env.cleanup(); - }); + public Env resolveParameter(ParameterContext parameterContext, ExtensionContext extensionContext) { + return extensionContext.getStore(ExtensionContext.Namespace.create(getClass())) + .getOrComputeIfAbsent(ENV_KEY, + key -> new EnvImpl(MinecraftServer.updateProcess()), + EnvImpl.class); } /** * This method is called before each test method execution to set up the environment. * It sets the system property "minestom.viewable-packet" to "false". * - * @param context the extension context for the test method about to be executed; never {@code null} + * @param context the extension context for the test method about to be executed */ @Override public void beforeEach(ExtensionContext context) { System.setProperty("minestom.viewable-packet", "false"); } + + @Override + public void afterEach(ExtensionContext context) { + ExtensionContext.Store store = context.getStore(ExtensionContext.Namespace.create(getClass())); + EnvImpl env = store.remove(ENV_KEY, EnvImpl.class); + if (env != null) env.cleanup(); + } }