Skip to content

Commit

Permalink
Add missing documentation to the test module (#100)
Browse files Browse the repository at this point in the history
* Remove deprecated code from the test module

* Add documentation

* Replace get(0) call with getFirst

* Add documentation

* Add NotNull annotation

* Improve class structure

* Add documentation

* Improve documentation

* Add some annotations
  • Loading branch information
theEvilReaper authored Nov 11, 2024
1 parent 029c818 commit b63f386
Show file tree
Hide file tree
Showing 7 changed files with 260 additions and 21 deletions.
45 changes: 40 additions & 5 deletions testing/src/main/java/net/minestom/testing/Collector.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,35 +5,70 @@
import java.util.List;
import java.util.function.Consumer;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertInstanceOf;
import static org.junit.jupiter.api.Assertions.*;

/**
* The {@link Collector} is a utility class to collect elements and apply assertions to them.
*
* @param <T> the type for the elements
* @version 1.0.0
* @since 1.0.0
*/
public interface Collector<T> {

/**
* Collects a list of elements
*
* @return the list of elements
*/
@NotNull List<@NotNull T> collect();

default <P extends T> void assertSingle(@NotNull Class<P> type, @NotNull Consumer<P> consumer) {
/**
* Asserts that the collector contains exactly one element of the given type and applies the consumer to it.
*
* @param type the type of the element
* @param consumer the consumer to apply to the element
*/
default <P extends T> void assertSingle(@NotNull Class<P> type, @NotNull Consumer<P> consumer) {
List<T> elements = collect();
assertEquals(1, elements.size(), "Expected 1 element, got " + elements);
var element = elements.get(0);
var element = elements.getFirst();
assertInstanceOf(type, element, "Expected type " + type.getSimpleName() + ", got " + element.getClass().getSimpleName());
consumer.accept((P) element);
}

/**
* Asserts that the collector contains exactly one element and applies the consumer to it.
* The consumer can be sued to inject own assertions to the element.
*
* @param consumer the consumer to apply to the element
*/
default void assertSingle(@NotNull Consumer<T> consumer) {
List<T> elements = collect();
assertEquals(1, elements.size(), "Expected 1 element, got " + elements);
consumer.accept(elements.get(0));
consumer.accept(elements.getFirst());
}

/**
* Asserts that the collector contains the given amount of elements.
*
* @param count the expected amount of elements
*/
default void assertCount(int count) {
List<T> elements = collect();
assertEquals(count, elements.size(), "Expected " + count + " element(s), got " + elements.size() + ": " + elements);
}

/**
* Asserts that the collector contains exactly one element.
*/
default void assertSingle() {
assertCount(1);
}

/**
* Asserts that the collector is empty.
*/
default void assertEmpty() {
assertCount(0);
}
Expand Down
92 changes: 89 additions & 3 deletions testing/src/main/java/net/minestom/testing/EnvImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,31 +13,71 @@
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.fail;

/**
* The {@link EnvImpl} is the implementation of the {@link Env} interface.
* It is used to create connections and more for the testing environment.
*
* @version 1.0.0
* @since 1.0.0
*/
final class EnvImpl implements Env {

private final ServerProcess process;
private final List<FlexibleListenerImpl<?>> listeners = new CopyOnWriteArrayList<>();

public EnvImpl(ServerProcess process) {
/**
* Creates a new instance of {@link EnvImpl} with the given {@link ServerProcess}.
*
* @param process the process to use
*/
public EnvImpl(@NotNull ServerProcess process) {
this.process = process;
}

/**
* Gets the {@link ServerProcess} used by this environment.
*
* @return the server process
*/
@Override
public @NotNull ServerProcess process() {
return process;
}

/**
* Creates a new connection.
*
* @return the created connection
*/
@Override
public @NotNull TestConnection createConnection() {
return new TestConnectionImpl(this);
}

/**
* Tracks a specific event type in the test environment.
*
* @param eventType the event type to track
* @param filter the filter to apply
* @param actor the actor to use
* @param <E> the event type
* @param <H> the actor type
* @return the {@link Collector} instance to use
*/
@Override
public @NotNull <E extends Event, H> Collector<E> trackEvent(@NotNull Class<E> eventType, @NotNull 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;
}

/**
* Listens for a specific event type in the test environment.
*
* @param eventType the event type to listen for
* @param <E> the event type
* @return the {@link FlexibleListener} instance from the event type
*/
@Override
public @NotNull <E extends Event> FlexibleListener<E> listen(@NotNull Class<E> eventType) {
var handler = process.eventHandler();
Expand All @@ -48,48 +88,91 @@ public EnvImpl(ServerProcess process) {
return flexible;
}

/**
* Removes all listener which are currently registered in the test environment.
*/
@Override
public void cleanup() {
this.listeners.forEach(FlexibleListenerImpl::check);
this.process.stop();
}

/**
* The class is the implementation of the {@link Collector} interface.
*
* @param <E> the event type
*/
final class EventCollector<E extends Event> implements Collector<E> {

private final Object handler;
private final List<E> events = new CopyOnWriteArrayList<>();

public EventCollector(Object handler) {
/**
* Creates a new instance of an event collector with the given handler.
*
* @param handler the handler to use
*/
public EventCollector(@NotNull Object handler) {
this.handler = handler;
}

/**
* Returns a list of all collected events.
*
* @return the list of events
*/
@Override
public @NotNull List<E> collect() {
process.eventHandler().unmap(handler);
return List.copyOf(events);
}
}

/**
* The class is the implementation of the {@link FlexibleListener} interface.
*
* @param <E> the event type
*/
static final class FlexibleListenerImpl<E extends Event> implements FlexibleListener<E> {

private final Class<E> eventType;
private Consumer<E> handler = e -> {
};
private boolean initialized;
private boolean called;

FlexibleListenerImpl(Class<E> eventType) {
/**
* Creates a new instance of a flexible listener with the given event type.
*
* @param eventType the event type
*/
FlexibleListenerImpl(@NotNull Class<E> eventType) {
this.eventType = eventType;
}

/**
* Updates the current handler reference.
*
* @param handler the handler to update
*/
@Override
public void followup(@NotNull Consumer<E> handler) {
updateHandler(handler);
}

/**
* Calls a fail assertions if the event can't be followed up.
*/
@Override
public void failFollowup() {
updateHandler(e -> fail("Event " + e.getClass().getSimpleName() + " was not expected"));
}

/**
* Updates the {@link Consumer} which acts as the handler.
*
* @param handler the handler to update
*/
void updateHandler(@NotNull Consumer<E> handler) {
check();
this.initialized = true;
Expand All @@ -100,6 +183,9 @@ void updateHandler(@NotNull Consumer<E> handler) {
};
}

/**
* Checks if the last listener has been called.
*/
void check() {
assertTrue(!initialized || called, "Last listener has not been called: " + eventType.getSimpleName());
}
Expand Down
11 changes: 11 additions & 0 deletions testing/src/main/java/net/minestom/testing/FlexibleListener.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,23 @@

import java.util.function.Consumer;

/**
* A FlexibleListener is a listener construct which works different from the normal listener code from Minestom.
* The use case is to follow listener during a test and can be used that the event was called or not
*
* @param <E> the event type
* @version 1.0.0
* @since 1.0.0
*/
public interface FlexibleListener<E extends Event> {
/**
* Updates the handler. Fails if the previous followup has not been called.
*/
void followup(@NotNull Consumer<E> handler);

/**
* Default followup which does nothing.
*/
default void followup() {
followup(event -> {
// Empty
Expand Down
Loading

0 comments on commit b63f386

Please sign in to comment.