Plugin API (#34)

The Velocity API has had a lot of community input (special thanks to @hugmanrique who started the work, @lucko who contributed permissions support, and @Minecrell for providing initial feedback and an initial version of ServerListPlus).

While the API is far from complete, there is enough available for people to start doing useful stuff with Velocity.
This commit is contained in:
Andrew Steinborn
2018-08-20 19:30:32 -04:00
committed by GitHub
parent 8e836a5066
commit a028467e66
89 changed files with 3453 additions and 358 deletions

View File

@@ -0,0 +1,51 @@
package com.velocitypowered.proxy.scheduler;
import com.velocitypowered.api.scheduler.ScheduledTask;
import com.velocitypowered.api.scheduler.TaskStatus;
import com.velocitypowered.proxy.testutil.FakePluginManager;
import org.junit.jupiter.api.Test;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import static org.junit.jupiter.api.Assertions.*;
class VelocitySchedulerTest {
// TODO: The timings here will be inaccurate on slow systems. Need to find a testing-friendly replacement for Thread.sleep()
@Test
void buildTask() throws Exception {
VelocityScheduler scheduler = new VelocityScheduler(new FakePluginManager(), Sleeper.SYSTEM);
CountDownLatch latch = new CountDownLatch(1);
ScheduledTask task = scheduler.buildTask(FakePluginManager.PLUGIN_A, latch::countDown).schedule();
latch.await();
assertEquals(TaskStatus.FINISHED, task.status());
}
@Test
void cancelWorks() throws Exception {
VelocityScheduler scheduler = new VelocityScheduler(new FakePluginManager(), Sleeper.SYSTEM);
AtomicInteger i = new AtomicInteger(3);
ScheduledTask task = scheduler.buildTask(FakePluginManager.PLUGIN_A, i::decrementAndGet)
.delay(100, TimeUnit.SECONDS)
.schedule();
task.cancel();
Thread.sleep(200);
assertEquals(3, i.get());
assertEquals(TaskStatus.CANCELLED, task.status());
}
@Test
void repeatTaskWorks() throws Exception {
VelocityScheduler scheduler = new VelocityScheduler(new FakePluginManager(), Sleeper.SYSTEM);
CountDownLatch latch = new CountDownLatch(3);
ScheduledTask task = scheduler.buildTask(FakePluginManager.PLUGIN_A, latch::countDown)
.delay(100, TimeUnit.MILLISECONDS)
.repeat(100, TimeUnit.MILLISECONDS)
.schedule();
latch.await();
task.cancel();
}
}

View File

@@ -0,0 +1,92 @@
package com.velocitypowered.proxy.testutil;
import com.google.common.collect.ImmutableList;
import com.velocitypowered.api.plugin.PluginContainer;
import com.velocitypowered.api.plugin.PluginDescription;
import com.velocitypowered.api.plugin.PluginManager;
import org.checkerframework.checker.nullness.qual.NonNull;
import java.nio.file.Path;
import java.util.Collection;
import java.util.Optional;
public class FakePluginManager implements PluginManager {
public static final Object PLUGIN_A = new Object();
public static final Object PLUGIN_B = new Object();
public static final PluginContainer PC_A = new FakePluginContainer("a", PLUGIN_A);
public static final PluginContainer PC_B = new FakePluginContainer("b", PLUGIN_B);
@Override
public @NonNull Optional<PluginContainer> fromInstance(@NonNull Object instance) {
if (instance == PLUGIN_A) {
return Optional.of(PC_A);
} else if (instance == PLUGIN_B) {
return Optional.of(PC_B);
} else {
return Optional.empty();
}
}
@Override
public @NonNull Optional<PluginContainer> getPlugin(@NonNull String id) {
switch (id) {
case "a":
return Optional.of(PC_A);
case "b":
return Optional.of(PC_B);
default:
return Optional.empty();
}
}
@Override
public @NonNull Collection<PluginContainer> getPlugins() {
return ImmutableList.of(PC_A, PC_B);
}
@Override
public boolean isLoaded(@NonNull String id) {
return id.equals("a") || id.equals("b");
}
@Override
public void addToClasspath(@NonNull Object plugin, @NonNull Path path) {
throw new UnsupportedOperationException();
}
private static class FakePluginContainer implements PluginContainer {
private final String id;
private final Object instance;
private FakePluginContainer(String id, Object instance) {
this.id = id;
this.instance = instance;
}
@Override
public @NonNull PluginDescription getDescription() {
return new PluginDescription() {
@Override
public String getId() {
return id;
}
@Override
public String getVersion() {
return "";
}
@Override
public String getAuthor() {
return "";
}
};
}
@Override
public Optional<?> getInstance() {
return Optional.of(instance);
}
}
}

View File

@@ -1,5 +1,6 @@
package com.velocitypowered.proxy.util;
import com.velocitypowered.api.util.UuidUtils;
import org.junit.jupiter.api.Test;
import java.util.UUID;

View File

@@ -0,0 +1,36 @@
package com.velocitypowered.proxy.util.concurrency;
import org.junit.jupiter.api.Test;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executors;
import static org.junit.jupiter.api.Assertions.*;
class ThreadRecorderThreadFactoryTest {
@Test
void newThread() throws Exception {
ThreadRecorderThreadFactory factory = new ThreadRecorderThreadFactory(Executors.defaultThreadFactory());
CountDownLatch started = new CountDownLatch(1);
CountDownLatch endThread = new CountDownLatch(1);
factory.newThread(() -> {
started.countDown();
assertTrue(factory.currentlyInFactory());
assertEquals(1, factory.size());
try {
endThread.await();
} catch (InterruptedException e) {
fail(e);
}
}).start();
started.await();
assertFalse(factory.currentlyInFactory());
assertEquals(1, factory.size());
endThread.countDown();
// Wait a little bit to ensure the thread got shut down
Thread.sleep(10);
assertEquals(0, factory.size());
}
}