Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 34 additions & 3 deletions src/main/java/net/minestom/testing/Collector.java
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -36,11 +39,12 @@ public interface Collector<T> {
* @param consumer the consumer to apply to the single element
* @param <P> the type of the single element
*/
default <P extends T> void assertSingle(@NotNull Class<P> type, @NotNull Consumer<P> consumer) {
default <P extends T> void assertSingle(Class<P> type, Consumer<P> consumer) {
List<T> 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);
}

Expand All @@ -49,7 +53,7 @@ default <P extends T> void assertSingle(@NotNull Class<P> type, @NotNull Consume
*
* @param consumer the consumer to apply to the single element
*/
default void assertSingle(@NotNull Consumer<T> consumer) {
default void assertSingle(Consumer<T> consumer) {
List<T> elements = collect();
assertEquals(1, elements.size(), "Expected 1 element, got " + elements);
consumer.accept(elements.getFirst());
Expand Down Expand Up @@ -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<T> predicate) {
List<T> 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<T> predicate) {
List<T> 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<T> predicate) {
List<T> elements = collect();
assertTrue(elements.stream().allMatch(predicate),
"Not all elements matched the predicate. Elements: " + elements);
}
}
22 changes: 17 additions & 5 deletions src/main/java/net/minestom/testing/Env.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;

/**
Expand Down Expand Up @@ -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);
}

Expand All @@ -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.
Expand All @@ -74,7 +86,7 @@ public interface Env {
* @param <H> the handler type
* @return the {@link Collector} instance to use
*/
<E extends Event, H> @NotNull Collector<E> trackEvent(@NotNull Class<E> eventType, @NotNull EventFilter<? super E, H> filter, @NotNull H actor);
<E extends Event, H> Collector<E> trackEvent(Class<E> eventType, EventFilter<? super E, H> filter, @NotNull H actor);

/**
* Listen for a specific event type in the test environment.
Expand All @@ -83,7 +95,7 @@ public interface Env {
* @param <E> the event type
* @return the {@link FlexibleListener} instance to use
*/
<E extends Event> @NotNull FlexibleListener<E> listen(@NotNull Class<E> eventType);
<E extends Event> FlexibleListener<E> listen(Class<E> eventType);

/**
* Ticks the {@link ServerProcess} which is involved into the env instance.
Expand Down
19 changes: 10 additions & 9 deletions src/main/java/net/minestom/testing/EnvImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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<FlexibleListenerImpl<?>> listeners = new CopyOnWriteArrayList<>();

Expand All @@ -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 <E extends Event, H> Collector<E> trackEvent(@NotNull Class<E> eventType, @NotNull EventFilter<? super E, H> filter, @NotNull H actor) {
public <E extends Event, H> Collector<E> trackEvent(Class<E> eventType, EventFilter<? super E, H> filter, @NotNull H actor) {
var tracker = new EventCollector<E>(actor);
this.process.eventHandler().map(actor, filter).addListener(eventType, tracker.events::add);
return tracker;
}

@Override
public @NotNull <E extends Event> FlexibleListener<E> listen(@NotNull Class<E> eventType) {
public <E extends Event> FlexibleListener<E> listen(Class<E> eventType) {
var handler = process.eventHandler();
var flexible = new FlexibleListenerImpl<>(eventType);
var listener = EventListener.of(eventType, e -> flexible.handler.accept(e));
Expand All @@ -70,7 +71,7 @@ public EventCollector(Object handler) {
}

@Override
public @NotNull List<E> collect() {
public List<E> collect() {
process.eventHandler().unmap(handler);
return List.copyOf(events);
}
Expand All @@ -88,7 +89,7 @@ static final class FlexibleListenerImpl<E extends Event> implements FlexibleList
}

@Override
public void followup(@NotNull Consumer<E> handler) {
public void followup(Consumer<E> handler) {
updateHandler(handler);
}

Expand All @@ -97,7 +98,7 @@ public void failFollowup() {
updateHandler(e -> fail("Event " + e.getClass().getSimpleName() + " was not expected"));
}

void updateHandler(@NotNull Consumer<E> handler) {
void updateHandler(Consumer<E> handler) {
check();
this.initialized = true;
this.called = false;
Expand Down
3 changes: 1 addition & 2 deletions src/main/java/net/minestom/testing/FlexibleListener.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package net.minestom.testing;

import net.minestom.server.event.Event;
import org.jetbrains.annotations.NotNull;

import java.util.function.Consumer;

Expand All @@ -21,7 +20,7 @@ public interface FlexibleListener<E extends Event> {
*
* @param handler the consumer to handle the next event
*/
void followup(@NotNull Consumer<E> handler);
void followup( Consumer<E> handler);

/**
* Empty followup handler.
Expand Down
9 changes: 4 additions & 5 deletions src/main/java/net/minestom/testing/TestConnection.java
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -22,15 +21,15 @@ 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).
*
* @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);
}

Expand All @@ -41,14 +40,14 @@ public interface TestConnection {
* @param <T> the type of the packet
* @return a collector for the specified packet type
*/
<T extends ServerPacket> @NotNull Collector<T> trackIncoming(@NotNull Class<T> type);
<T extends ServerPacket> Collector<T> trackIncoming(Class<T> type);

/**
* Tracks incoming packets of the default type {@link ServerPacket}.
*
* @return a collector for the default packet type
*/
default @NotNull Collector<ServerPacket> trackIncoming() {
default Collector<ServerPacket> trackIncoming() {
return trackIncoming(ServerPacket.class);
}
}
20 changes: 9 additions & 11 deletions src/main/java/net/minestom/testing/TestConnectionImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -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<IncomingCollector<ServerPacket>> 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);
Expand All @@ -67,7 +65,7 @@ final class TestConnectionImpl implements TestConnection {
}

@Override
public @NotNull <T extends ServerPacket> Collector<T> trackIncoming(@NotNull Class<T> type) {
public <T extends ServerPacket> Collector<T> trackIncoming(Class<T> type) {
var tracker = new IncomingCollector<>(type);
this.incomingTrackers.add(IncomingCollector.class.cast(tracker));
return tracker;
Expand All @@ -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);
Expand All @@ -100,7 +98,7 @@ private ServerPacket extractPacket(final SendablePacket packet) {
}

@Override
public @NotNull SocketAddress getRemoteAddress() {
public SocketAddress getRemoteAddress() {
return new InetSocketAddress("localhost", 25565);
}

Expand All @@ -124,7 +122,7 @@ public IncomingCollector(Class<T> type) {
}

@Override
public @NotNull List<T> collect() {
public List<T> collect() {
incomingTrackers.remove(this);
return List.copyOf(packets);
}
Expand Down
5 changes: 2 additions & 3 deletions src/main/java/net/minestom/testing/TestPlayerImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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);
}

Expand All @@ -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());
}
Expand Down
Loading
Loading