geforkt von Mirrors/Velocity
The first bug in #548 (and the only issue in #547) was a typo. The second bug was fixed by tracking "friends" of the event types, so we can invalidate everything as needed.
Dieser Commit ist enthalten in:
Ursprung
f79736b8b7
Commit
e017949abf
@ -0,0 +1,41 @@
|
|||||||
|
package com.velocitypowered.proxy.event;
|
||||||
|
|
||||||
|
import com.google.common.collect.ArrayListMultimap;
|
||||||
|
import com.google.common.collect.HashMultimap;
|
||||||
|
import com.google.common.collect.ImmutableList;
|
||||||
|
import com.google.common.collect.ListMultimap;
|
||||||
|
import com.google.common.collect.Multimap;
|
||||||
|
import com.google.common.reflect.TypeToken;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
class EventTypeTracker {
|
||||||
|
|
||||||
|
private final ListMultimap<Class<?>, Class<?>> friends;
|
||||||
|
|
||||||
|
public EventTypeTracker() {
|
||||||
|
this.friends = ArrayListMultimap.create();
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Class<?>> getFriendsOf(final Class<?> eventType) {
|
||||||
|
if (friends.containsKey(eventType)) {
|
||||||
|
return ImmutableList.copyOf(friends.get(eventType));
|
||||||
|
}
|
||||||
|
|
||||||
|
final List<Class<?>> types = getEventTypes(eventType);
|
||||||
|
for (Class<?> type : types) {
|
||||||
|
if (type == eventType) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
friends.put(type, eventType);
|
||||||
|
}
|
||||||
|
return types;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List<Class<?>> getEventTypes(final Class<?> eventType) {
|
||||||
|
return TypeToken.of(eventType).getTypes().rawTypes().stream()
|
||||||
|
.filter(type -> type != Object.class)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
}
|
@ -99,6 +99,7 @@ public class VelocityEventManager implements EventManager {
|
|||||||
private final ReadWriteLock lock = new ReentrantReadWriteLock();
|
private final ReadWriteLock lock = new ReentrantReadWriteLock();
|
||||||
|
|
||||||
private final List<CustomHandlerAdapter<?>> handlerAdapters = new ArrayList<>();
|
private final List<CustomHandlerAdapter<?>> handlerAdapters = new ArrayList<>();
|
||||||
|
private final EventTypeTracker eventTypeTracker = new EventTypeTracker();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initializes the Velocity event manager.
|
* Initializes the Velocity event manager.
|
||||||
@ -172,15 +173,9 @@ public class VelocityEventManager implements EventManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static List<Class<?>> getEventTypes(final Class<?> eventType) {
|
|
||||||
return TypeToken.of(eventType).getTypes().rawTypes().stream()
|
|
||||||
.filter(type -> type != Object.class)
|
|
||||||
.collect(Collectors.toList());
|
|
||||||
}
|
|
||||||
|
|
||||||
private @Nullable HandlersCache bakeHandlers(final Class<?> eventType) {
|
private @Nullable HandlersCache bakeHandlers(final Class<?> eventType) {
|
||||||
final List<HandlerRegistration> baked = new ArrayList<>();
|
final List<HandlerRegistration> baked = new ArrayList<>();
|
||||||
final List<Class<?>> types = getEventTypes(eventType);
|
final List<Class<?>> types = eventTypeTracker.getFriendsOf(eventType);
|
||||||
|
|
||||||
lock.readLock().lock();
|
lock.readLock().lock();
|
||||||
try {
|
try {
|
||||||
@ -336,7 +331,7 @@ public class VelocityEventManager implements EventManager {
|
|||||||
}
|
}
|
||||||
// Invalidate all the affected event subtypes
|
// Invalidate all the affected event subtypes
|
||||||
handlersCache.invalidateAll(registrations.stream()
|
handlersCache.invalidateAll(registrations.stream()
|
||||||
.flatMap(registration -> getEventTypes(registration.eventType).stream())
|
.flatMap(registration -> eventTypeTracker.getFriendsOf(registration.eventType).stream())
|
||||||
.distinct()
|
.distinct()
|
||||||
.collect(Collectors.toList()));
|
.collect(Collectors.toList()));
|
||||||
}
|
}
|
||||||
@ -407,7 +402,7 @@ public class VelocityEventManager implements EventManager {
|
|||||||
final PluginContainer pluginContainer = pluginManager.ensurePluginContainer(plugin);
|
final PluginContainer pluginContainer = pluginManager.ensurePluginContainer(plugin);
|
||||||
requireNonNull(handler, "handler");
|
requireNonNull(handler, "handler");
|
||||||
unregisterIf(registration ->
|
unregisterIf(registration ->
|
||||||
registration.plugin == pluginContainer && registration.handler == handler);
|
registration.plugin == pluginContainer && registration.instance == handler);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -433,7 +428,7 @@ public class VelocityEventManager implements EventManager {
|
|||||||
|
|
||||||
// Invalidate all the affected event subtypes
|
// Invalidate all the affected event subtypes
|
||||||
handlersCache.invalidateAll(removed.stream()
|
handlersCache.invalidateAll(removed.stream()
|
||||||
.flatMap(registration -> getEventTypes(registration.eventType).stream())
|
.flatMap(registration -> eventTypeTracker.getFriendsOf(registration.eventType).stream())
|
||||||
.distinct()
|
.distinct()
|
||||||
.collect(Collectors.toList()));
|
.collect(Collectors.toList()));
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,199 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2018 Velocity Contributors
|
||||||
|
*
|
||||||
|
* 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 <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.velocitypowered.proxy.event;
|
||||||
|
|
||||||
|
import static com.velocitypowered.proxy.testutil.FakePluginManager.PLUGIN_A;
|
||||||
|
import static com.velocitypowered.proxy.testutil.FakePluginManager.PLUGIN_B;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertSame;
|
||||||
|
|
||||||
|
import com.velocitypowered.api.event.EventHandler;
|
||||||
|
import com.velocitypowered.api.event.EventManager;
|
||||||
|
import com.velocitypowered.api.event.Subscribe;
|
||||||
|
import com.velocitypowered.api.plugin.PluginManager;
|
||||||
|
import com.velocitypowered.proxy.testutil.FakePluginManager;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
import org.junit.jupiter.api.DynamicNode;
|
||||||
|
import org.junit.jupiter.api.DynamicTest;
|
||||||
|
import org.junit.jupiter.api.TestFactory;
|
||||||
|
|
||||||
|
public class RegistrationTest {
|
||||||
|
|
||||||
|
private EventManager eventManager;
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
public final void setup() {
|
||||||
|
resetEventManager();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void resetEventManager() {
|
||||||
|
eventManager = createEventManager(new FakePluginManager());
|
||||||
|
}
|
||||||
|
|
||||||
|
protected EventManager createEventManager(PluginManager pluginManager) {
|
||||||
|
return new VelocityEventManager(pluginManager);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Must be public in order to generate a method calling it
|
||||||
|
public static class SimpleEvent {
|
||||||
|
int value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class SimpleSubclassedEvent extends SimpleEvent { }
|
||||||
|
|
||||||
|
public static class HandlerListener implements EventHandler<SimpleEvent> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void execute(SimpleEvent event) {
|
||||||
|
event.value++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class AnnotatedListener {
|
||||||
|
|
||||||
|
@Subscribe
|
||||||
|
public void increment(SimpleEvent event) {
|
||||||
|
event.value++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private interface EventGenerator {
|
||||||
|
|
||||||
|
void assertFiredEventValue(int value);
|
||||||
|
}
|
||||||
|
|
||||||
|
private interface TestFunction {
|
||||||
|
|
||||||
|
void runTest(boolean annotated, EventGenerator generator);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Stream<DynamicNode> composeTests(String name, TestFunction testFunction) {
|
||||||
|
Set<DynamicNode> tests = new HashSet<>();
|
||||||
|
boolean[] trueAndFalse = new boolean[] {true, false};
|
||||||
|
for (boolean annotated : trueAndFalse) {
|
||||||
|
for (boolean subclassed : trueAndFalse) {
|
||||||
|
|
||||||
|
EventGenerator generator = (value) -> {
|
||||||
|
SimpleEvent simpleEvent = (subclassed) ? new SimpleSubclassedEvent() : new SimpleEvent();
|
||||||
|
SimpleEvent shouldBeSameEvent = eventManager.fire(simpleEvent).join();
|
||||||
|
assertSame(simpleEvent, shouldBeSameEvent);
|
||||||
|
assertEquals(value, simpleEvent.value);
|
||||||
|
};
|
||||||
|
tests.add(DynamicTest.dynamicTest(name + ". Annotated : " + annotated + ", Subclassed: " + subclassed, () -> {
|
||||||
|
try {
|
||||||
|
testFunction.runTest(annotated, generator);
|
||||||
|
} finally {
|
||||||
|
resetEventManager();
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return tests.stream();
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestFactory
|
||||||
|
Stream<DynamicNode> simpleRegisterAndUnregister() {
|
||||||
|
return composeTests("simpleRegisterAndUnregister", (annotated, generator) -> {
|
||||||
|
if (annotated) {
|
||||||
|
eventManager.register(PLUGIN_A, new AnnotatedListener());
|
||||||
|
} else {
|
||||||
|
eventManager.register(PLUGIN_A, SimpleEvent.class, new HandlerListener());
|
||||||
|
}
|
||||||
|
generator.assertFiredEventValue(1);
|
||||||
|
eventManager.unregisterListeners(PLUGIN_A);
|
||||||
|
generator.assertFiredEventValue(0);
|
||||||
|
assertDoesNotThrow(() -> eventManager.unregisterListeners(PLUGIN_A), "Extra unregister is a no-op");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestFactory
|
||||||
|
Stream<DynamicNode> doubleRegisterListener() {
|
||||||
|
return composeTests("doubleRegisterListener", (annotated, generator) -> {
|
||||||
|
if (annotated) {
|
||||||
|
Object annotatedListener = new AnnotatedListener();
|
||||||
|
eventManager.register(PLUGIN_A, annotatedListener);
|
||||||
|
eventManager.register(PLUGIN_A, annotatedListener);
|
||||||
|
} else {
|
||||||
|
EventHandler<SimpleEvent> handler = new HandlerListener();
|
||||||
|
eventManager.register(PLUGIN_A, SimpleEvent.class, handler);
|
||||||
|
eventManager.register(PLUGIN_A, SimpleEvent.class, handler);
|
||||||
|
}
|
||||||
|
generator.assertFiredEventValue(2);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestFactory
|
||||||
|
Stream<DynamicNode> doubleRegisterListenerDifferentPlugins() {
|
||||||
|
return composeTests("doubleRegisterListenerDifferentPlugins", (annotated, generator) -> {
|
||||||
|
if (annotated) {
|
||||||
|
Object annotatedListener = new AnnotatedListener();
|
||||||
|
eventManager.register(PLUGIN_A, annotatedListener);
|
||||||
|
eventManager.register(PLUGIN_B, annotatedListener);
|
||||||
|
} else {
|
||||||
|
EventHandler<SimpleEvent> handler = new HandlerListener();
|
||||||
|
eventManager.register(PLUGIN_A, SimpleEvent.class, handler);
|
||||||
|
eventManager.register(PLUGIN_B, SimpleEvent.class, handler);
|
||||||
|
}
|
||||||
|
generator.assertFiredEventValue(2);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestFactory
|
||||||
|
Stream<DynamicNode> doubleRegisterListenerThenUnregister() {
|
||||||
|
return composeTests("doubleRegisterListenerThenUnregister", (annotated, generator) -> {
|
||||||
|
if (annotated) {
|
||||||
|
Object annotatedListener = new AnnotatedListener();
|
||||||
|
eventManager.register(PLUGIN_A, annotatedListener);
|
||||||
|
eventManager.register(PLUGIN_A, annotatedListener);
|
||||||
|
eventManager.unregisterListener(PLUGIN_A, annotatedListener);
|
||||||
|
} else {
|
||||||
|
EventHandler<SimpleEvent> handler = new HandlerListener();
|
||||||
|
eventManager.register(PLUGIN_A, SimpleEvent.class, handler);
|
||||||
|
eventManager.register(PLUGIN_A, SimpleEvent.class, handler);
|
||||||
|
eventManager.unregister(PLUGIN_A, handler);
|
||||||
|
}
|
||||||
|
generator.assertFiredEventValue(0);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestFactory
|
||||||
|
Stream<DynamicNode> doubleUnregisterListener() {
|
||||||
|
return composeTests("doubleUnregisterListener", (annotated, generator) -> {
|
||||||
|
if (annotated) {
|
||||||
|
Object annotatedListener = new AnnotatedListener();
|
||||||
|
eventManager.register(PLUGIN_A, annotatedListener);
|
||||||
|
eventManager.unregisterListener(PLUGIN_A, annotatedListener);
|
||||||
|
assertDoesNotThrow(() -> eventManager.unregisterListener(PLUGIN_A, annotatedListener),
|
||||||
|
"Extra unregister is a no-op");
|
||||||
|
} else {
|
||||||
|
EventHandler<SimpleEvent> handler = new HandlerListener();
|
||||||
|
eventManager.register(PLUGIN_A, SimpleEvent.class, handler);
|
||||||
|
eventManager.unregister(PLUGIN_A, handler);
|
||||||
|
assertDoesNotThrow(() -> eventManager.unregister(PLUGIN_A, handler),
|
||||||
|
"Extra unregister is a no-op");
|
||||||
|
}
|
||||||
|
generator.assertFiredEventValue(0);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Laden…
In neuem Issue referenzieren
Einen Benutzer sperren