mirror of
https://github.com/PaperMC/Paper.git
synced 2024-11-05 10:20:53 +01:00
71c84c8132
Upstream has released updates that appear to apply and compile correctly. This update has not been tested by PaperMC and as with ANY update, please do your own testing Bukkit Changes: 9a80d38c SPIGOT-336, SPIGOT-3366, SPIGOT-5768, SPIGOT-6409, SPIGOT-6861, PR-722: Add EntityRemoveEvent 258086d9 SPIGOT-7417, PR-967: Add Sign#getTargetSide and Sign#getAllowedEditor ffaba051 SPIGOT-7584: Add missing Tag.ITEMS_NON_FLAMMABLE_WOOD CraftBukkit Changes: 98b6c1ac7 SPIGOT-7589 Fix NullPointerException when bans expire a2736ddb0 SPIGOT-336, SPIGOT-3366, SPIGOT-5768, SPIGOT-6409, SPIGOT-6861, PR-1008: Add EntityRemoveEvent 5bf12cb89 SPIGOT-7565: Throw a more descriptive error message when a developer tries to spawn an entity from a CraftBukkit class 76d95fe7e SPIGOT-7417, PR-1343: Add Sign#getTargetSide and Sign#getAllowedEditor Spigot Changes: e9ec5485 Rebuild patches f1b62e0c Rebuild patches
360 lines
16 KiB
Diff
360 lines
16 KiB
Diff
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
|
From: Jake Potrebic <jake.m.potrebic@gmail.com>
|
|
Date: Mon, 13 Feb 2023 14:14:56 -0800
|
|
Subject: [PATCH] Test changes
|
|
|
|
|
|
diff --git a/build.gradle.kts b/build.gradle.kts
|
|
index 517aaae21f8fbd6916ab23d85dac5f74aea9e984..9098a40f944a3c6c0e907013c212eb3f1895e010 100644
|
|
--- a/build.gradle.kts
|
|
+++ b/build.gradle.kts
|
|
@@ -23,6 +23,7 @@ dependencies {
|
|
testImplementation("org.hamcrest:hamcrest:2.2")
|
|
testImplementation("org.mockito:mockito-core:5.5.0")
|
|
testImplementation("org.ow2.asm:asm-tree:9.5")
|
|
+ testImplementation("org.junit-pioneer:junit-pioneer:2.2.0") // Paper - CartesianTest
|
|
}
|
|
|
|
val craftbukkitPackageVersion = "1_20_R3" // Paper
|
|
@@ -58,6 +59,12 @@ tasks.compileJava {
|
|
options.setIncremental(false)
|
|
}
|
|
|
|
+// Paper start - compile tests with -parameters for better junit parameterized test names
|
|
+tasks.compileTestJava {
|
|
+ options.compilerArgs.add("-parameters")
|
|
+}
|
|
+// Paper end
|
|
+
|
|
publishing {
|
|
publications.create<MavenPublication>("maven") {
|
|
artifact(tasks.shadowJar)
|
|
diff --git a/src/test/java/io/papermc/paper/registry/RegistryKeyTest.java b/src/test/java/io/papermc/paper/registry/RegistryKeyTest.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..e070aa1bb69859224493d958621389ee757f8752
|
|
--- /dev/null
|
|
+++ b/src/test/java/io/papermc/paper/registry/RegistryKeyTest.java
|
|
@@ -0,0 +1,33 @@
|
|
+package io.papermc.paper.registry;
|
|
+
|
|
+import java.util.Optional;
|
|
+import java.util.stream.Stream;
|
|
+import net.minecraft.core.Registry;
|
|
+import net.minecraft.resources.ResourceKey;
|
|
+import net.minecraft.resources.ResourceLocation;
|
|
+import org.bukkit.support.AbstractTestingBase;
|
|
+import org.junit.jupiter.api.BeforeAll;
|
|
+import org.junit.jupiter.params.ParameterizedTest;
|
|
+import org.junit.jupiter.params.provider.MethodSource;
|
|
+
|
|
+import static org.junit.jupiter.api.Assertions.assertTrue;
|
|
+
|
|
+class RegistryKeyTest extends AbstractTestingBase {
|
|
+
|
|
+ @BeforeAll
|
|
+ static void before() throws ClassNotFoundException {
|
|
+ Class.forName(RegistryKey.class.getName()); // load all keys so they are found for the test
|
|
+ }
|
|
+
|
|
+ static Stream<RegistryKey<?>> data() {
|
|
+ return RegistryKeyImpl.REGISTRY_KEYS.stream();
|
|
+ }
|
|
+
|
|
+ @ParameterizedTest
|
|
+ @MethodSource("data")
|
|
+ void testApiRegistryKeysExist(final RegistryKey<?> key) {
|
|
+ final Optional<Registry<Object>> registry = AbstractTestingBase.REGISTRY_CUSTOM.registry(ResourceKey.createRegistryKey(new ResourceLocation(key.key().asString())));
|
|
+ assertTrue(registry.isPresent(), "Missing vanilla registry for " + key.key().asString());
|
|
+
|
|
+ }
|
|
+}
|
|
diff --git a/src/test/java/io/papermc/paper/util/EmptyTag.java b/src/test/java/io/papermc/paper/util/EmptyTag.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..6eb95a5e2534974c0e52e2b78b04e7c2b2f28525
|
|
--- /dev/null
|
|
+++ b/src/test/java/io/papermc/paper/util/EmptyTag.java
|
|
@@ -0,0 +1,31 @@
|
|
+package io.papermc.paper.util;
|
|
+
|
|
+import java.util.Collections;
|
|
+import java.util.Set;
|
|
+import org.bukkit.Keyed;
|
|
+import org.bukkit.NamespacedKey;
|
|
+import org.bukkit.Tag;
|
|
+import org.jetbrains.annotations.NotNull;
|
|
+
|
|
+public record EmptyTag(NamespacedKey key) implements Tag<Keyed> {
|
|
+
|
|
+ @SuppressWarnings("deprecation")
|
|
+ public EmptyTag() {
|
|
+ this(NamespacedKey.randomKey());
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public @NotNull NamespacedKey getKey() {
|
|
+ return this.key;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isTagged(@NotNull final Keyed item) {
|
|
+ return false;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public @NotNull Set<Keyed> getValues() {
|
|
+ return Collections.emptySet();
|
|
+ }
|
|
+}
|
|
diff --git a/src/test/java/io/papermc/paper/util/MethodParameterProvider.java b/src/test/java/io/papermc/paper/util/MethodParameterProvider.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..3f58ef36df34cd15fcb72189eeff057654adf0c6
|
|
--- /dev/null
|
|
+++ b/src/test/java/io/papermc/paper/util/MethodParameterProvider.java
|
|
@@ -0,0 +1,206 @@
|
|
+/*
|
|
+ * Copyright 2015-2023 the original author or authors of https://github.com/junit-team/junit5/blob/6593317c15fb556febbde11914fa7afe00abf8cd/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/MethodArgumentsProvider.java
|
|
+ *
|
|
+ * All rights reserved. This program and the accompanying materials are
|
|
+ * made available under the terms of the Eclipse Public License v2.0 which
|
|
+ * accompanies this distribution and is available at
|
|
+ *
|
|
+ * https://www.eclipse.org/legal/epl-v20.html
|
|
+ */
|
|
+
|
|
+package io.papermc.paper.util;
|
|
+
|
|
+import java.lang.reflect.Method;
|
|
+import java.lang.reflect.Parameter;
|
|
+import java.util.List;
|
|
+import java.util.function.Predicate;
|
|
+import java.util.stream.Stream;
|
|
+import org.junit.jupiter.api.Test;
|
|
+import org.junit.jupiter.api.TestFactory;
|
|
+import org.junit.jupiter.api.TestTemplate;
|
|
+import org.junit.jupiter.api.extension.ExtensionContext;
|
|
+import org.junit.jupiter.params.support.AnnotationConsumer;
|
|
+import org.junit.platform.commons.JUnitException;
|
|
+import org.junit.platform.commons.PreconditionViolationException;
|
|
+import org.junit.platform.commons.util.ClassLoaderUtils;
|
|
+import org.junit.platform.commons.util.CollectionUtils;
|
|
+import org.junit.platform.commons.util.Preconditions;
|
|
+import org.junit.platform.commons.util.ReflectionUtils;
|
|
+import org.junit.platform.commons.util.StringUtils;
|
|
+import org.junitpioneer.jupiter.cartesian.CartesianParameterArgumentsProvider;
|
|
+
|
|
+import static java.lang.String.format;
|
|
+import static java.util.Arrays.stream;
|
|
+import static java.util.stream.Collectors.toList;
|
|
+import static org.junit.platform.commons.util.AnnotationUtils.isAnnotated;
|
|
+import static org.junit.platform.commons.util.CollectionUtils.isConvertibleToStream;
|
|
+
|
|
+public class MethodParameterProvider implements CartesianParameterArgumentsProvider<Object>, AnnotationConsumer<MethodParameterSource> {
|
|
+ private MethodParameterSource source;
|
|
+
|
|
+ MethodParameterProvider() {
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void accept(final MethodParameterSource source) {
|
|
+ this.source = source;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public Stream<Object> provideArguments(ExtensionContext context, Parameter parameter) {
|
|
+ return this.provideArguments(context, this.source);
|
|
+ }
|
|
+
|
|
+ // Below is mostly copied from MethodArgumentsProvider
|
|
+
|
|
+ private static final Predicate<Method> isFactoryMethod = //
|
|
+ method -> isConvertibleToStream(method.getReturnType()) && !isTestMethod(method);
|
|
+
|
|
+ protected Stream<Object> provideArguments(ExtensionContext context, MethodParameterSource methodSource) {
|
|
+ Class<?> testClass = context.getRequiredTestClass();
|
|
+ Method testMethod = context.getRequiredTestMethod();
|
|
+ Object testInstance = context.getTestInstance().orElse(null);
|
|
+ String[] methodNames = methodSource.value();
|
|
+ // @formatter:off
|
|
+ return stream(methodNames)
|
|
+ .map(factoryMethodName -> findFactoryMethod(testClass, testMethod, factoryMethodName))
|
|
+ .map(factoryMethod -> validateFactoryMethod(factoryMethod, testInstance))
|
|
+ .map(factoryMethod -> context.getExecutableInvoker().invoke(factoryMethod, testInstance))
|
|
+ .flatMap(CollectionUtils::toStream);
|
|
+ // @formatter:on
|
|
+ }
|
|
+
|
|
+ private static Method findFactoryMethod(Class<?> testClass, Method testMethod, String factoryMethodName) {
|
|
+ String originalFactoryMethodName = factoryMethodName;
|
|
+
|
|
+ // If the user did not provide a factory method name, find a "default" local
|
|
+ // factory method with the same name as the parameterized test method.
|
|
+ if (StringUtils.isBlank(factoryMethodName)) {
|
|
+ factoryMethodName = testMethod.getName();
|
|
+ return findFactoryMethodBySimpleName(testClass, testMethod, factoryMethodName);
|
|
+ }
|
|
+
|
|
+ // Convert local factory method name to fully-qualified method name.
|
|
+ if (!looksLikeAFullyQualifiedMethodName(factoryMethodName)) {
|
|
+ factoryMethodName = testClass.getName() + "#" + factoryMethodName;
|
|
+ }
|
|
+
|
|
+ // Find factory method using fully-qualified name.
|
|
+ Method factoryMethod = findFactoryMethodByFullyQualifiedName(testClass, testMethod, factoryMethodName);
|
|
+
|
|
+ // Ensure factory method has a valid return type and is not a test method.
|
|
+ Preconditions.condition(isFactoryMethod.test(factoryMethod), () -> format(
|
|
+ "Could not find valid factory method [%s] for test class [%s] but found the following invalid candidate: %s",
|
|
+ originalFactoryMethodName, testClass.getName(), factoryMethod));
|
|
+
|
|
+ return factoryMethod;
|
|
+ }
|
|
+
|
|
+ private static boolean looksLikeAFullyQualifiedMethodName(String factoryMethodName) {
|
|
+ if (factoryMethodName.contains("#")) {
|
|
+ return true;
|
|
+ }
|
|
+ int indexOfFirstDot = factoryMethodName.indexOf('.');
|
|
+ if (indexOfFirstDot == -1) {
|
|
+ return false;
|
|
+ }
|
|
+ int indexOfLastOpeningParenthesis = factoryMethodName.lastIndexOf('(');
|
|
+ if (indexOfLastOpeningParenthesis > 0) {
|
|
+ // Exclude simple/local method names with parameters
|
|
+ return indexOfFirstDot < indexOfLastOpeningParenthesis;
|
|
+ }
|
|
+ // If we get this far, we conclude the supplied factory method name "looks"
|
|
+ // like it was intended to be a fully-qualified method name, even if the
|
|
+ // syntax is invalid. We do this in order to provide better diagnostics for
|
|
+ // the user when a fully-qualified method name is in fact invalid.
|
|
+ return true;
|
|
+ }
|
|
+
|
|
+ // package-private for testing
|
|
+ static Method findFactoryMethodByFullyQualifiedName(
|
|
+ Class<?> testClass, Method testMethod,
|
|
+ String fullyQualifiedMethodName
|
|
+ ) {
|
|
+ String[] methodParts = ReflectionUtils.parseFullyQualifiedMethodName(fullyQualifiedMethodName);
|
|
+ String className = methodParts[0];
|
|
+ String methodName = methodParts[1];
|
|
+ String methodParameters = methodParts[2];
|
|
+ ClassLoader classLoader = ClassLoaderUtils.getClassLoader(testClass);
|
|
+ Class<?> clazz = loadRequiredClass(className, classLoader);
|
|
+
|
|
+ // Attempt to find an exact match first.
|
|
+ Method factoryMethod = ReflectionUtils.findMethod(clazz, methodName, methodParameters).orElse(null);
|
|
+ if (factoryMethod != null) {
|
|
+ return factoryMethod;
|
|
+ }
|
|
+
|
|
+ boolean explicitParameterListSpecified = //
|
|
+ StringUtils.isNotBlank(methodParameters) || fullyQualifiedMethodName.endsWith("()");
|
|
+
|
|
+ // If we didn't find an exact match but an explicit parameter list was specified,
|
|
+ // that's a user configuration error.
|
|
+ Preconditions.condition(!explicitParameterListSpecified,
|
|
+ () -> format("Could not find factory method [%s(%s)] in class [%s]", methodName, methodParameters,
|
|
+ className));
|
|
+
|
|
+ // Otherwise, fall back to the same lenient search semantics that are used
|
|
+ // to locate a "default" local factory method.
|
|
+ return findFactoryMethodBySimpleName(clazz, testMethod, methodName);
|
|
+ }
|
|
+
|
|
+ /**
|
|
+ * Find the factory method by searching for all methods in the given {@code clazz}
|
|
+ * with the desired {@code factoryMethodName} which have return types that can be
|
|
+ * converted to a {@link Stream}, ignoring the {@code testMethod} itself as well
|
|
+ * as any {@code @Test}, {@code @TestTemplate}, or {@code @TestFactory} methods
|
|
+ * with the same name.
|
|
+ *
|
|
+ * @return the single factory method matching the search criteria
|
|
+ * @throws PreconditionViolationException if the factory method was not found or
|
|
+ * multiple competing factory methods with the same name were found
|
|
+ */
|
|
+ private static Method findFactoryMethodBySimpleName(Class<?> clazz, Method testMethod, String factoryMethodName) {
|
|
+ Predicate<Method> isCandidate = candidate -> factoryMethodName.equals(candidate.getName())
|
|
+ && !testMethod.equals(candidate);
|
|
+ List<Method> candidates = ReflectionUtils.findMethods(clazz, isCandidate);
|
|
+
|
|
+ List<Method> factoryMethods = candidates.stream().filter(isFactoryMethod).collect(toList());
|
|
+
|
|
+ Preconditions.condition(factoryMethods.size() > 0, () -> {
|
|
+ // If we didn't find the factory method using the isFactoryMethod Predicate, perhaps
|
|
+ // the specified factory method has an invalid return type or is a test method.
|
|
+ // In that case, we report the invalid candidates that were found.
|
|
+ if (candidates.size() > 0) {
|
|
+ return format(
|
|
+ "Could not find valid factory method [%s] in class [%s] but found the following invalid candidates: %s",
|
|
+ factoryMethodName, clazz.getName(), candidates);
|
|
+ }
|
|
+ // Otherwise, report that we didn't find anything.
|
|
+ return format("Could not find factory method [%s] in class [%s]", factoryMethodName, clazz.getName());
|
|
+ });
|
|
+ Preconditions.condition(factoryMethods.size() == 1,
|
|
+ () -> format("%d factory methods named [%s] were found in class [%s]: %s", factoryMethods.size(),
|
|
+ factoryMethodName, clazz.getName(), factoryMethods));
|
|
+ return factoryMethods.get(0);
|
|
+ }
|
|
+
|
|
+ private static boolean isTestMethod(Method candidate) {
|
|
+ return isAnnotated(candidate, Test.class) || isAnnotated(candidate, TestTemplate.class)
|
|
+ || isAnnotated(candidate, TestFactory.class);
|
|
+ }
|
|
+
|
|
+ private static Class<?> loadRequiredClass(String className, ClassLoader classLoader) {
|
|
+ return ReflectionUtils.tryToLoadClass(className, classLoader).getOrThrow(
|
|
+ cause -> new JUnitException(format("Could not load class [%s]", className), cause));
|
|
+ }
|
|
+
|
|
+ private static Method validateFactoryMethod(Method factoryMethod, Object testInstance) {
|
|
+ Preconditions.condition(
|
|
+ factoryMethod.getDeclaringClass().isInstance(testInstance) || ReflectionUtils.isStatic(factoryMethod),
|
|
+ () -> format("Method '%s' must be static: local factory methods must be static "
|
|
+ + "unless the PER_CLASS @TestInstance lifecycle mode is used; "
|
|
+ + "external factory methods must always be static.",
|
|
+ factoryMethod.toGenericString()));
|
|
+ return factoryMethod;
|
|
+ }
|
|
+}
|
|
diff --git a/src/test/java/io/papermc/paper/util/MethodParameterSource.java b/src/test/java/io/papermc/paper/util/MethodParameterSource.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..6cbf11c898439834cffb99ef84e5df1494356809
|
|
--- /dev/null
|
|
+++ b/src/test/java/io/papermc/paper/util/MethodParameterSource.java
|
|
@@ -0,0 +1,14 @@
|
|
+package io.papermc.paper.util;
|
|
+
|
|
+import java.lang.annotation.ElementType;
|
|
+import java.lang.annotation.Retention;
|
|
+import java.lang.annotation.RetentionPolicy;
|
|
+import java.lang.annotation.Target;
|
|
+import org.junitpioneer.jupiter.cartesian.CartesianArgumentsSource;
|
|
+
|
|
+@Retention(RetentionPolicy.RUNTIME)
|
|
+@Target({ElementType.PARAMETER, ElementType.ANNOTATION_TYPE})
|
|
+@CartesianArgumentsSource(MethodParameterProvider.class)
|
|
+public @interface MethodParameterSource {
|
|
+ String[] value() default {};
|
|
+}
|
|
diff --git a/src/test/java/org/bukkit/support/DummyServer.java b/src/test/java/org/bukkit/support/DummyServer.java
|
|
index b19d4f7d1fcb604b448a5084f6bfe56d47ab12b3..f3017525b0c2397fdc7ce0778add2e7b38e9e2ba 100644
|
|
--- a/src/test/java/org/bukkit/support/DummyServer.java
|
|
+++ b/src/test/java/org/bukkit/support/DummyServer.java
|
|
@@ -48,6 +48,15 @@ public final class DummyServer {
|
|
return registers.computeIfAbsent(aClass, key -> CraftRegistry.createRegistry(aClass, AbstractTestingBase.REGISTRY_CUSTOM));
|
|
});
|
|
|
|
+ // Paper start - testing additions
|
|
+ final Thread currentThread = Thread.currentThread();
|
|
+ when(instance.isPrimaryThread()).thenAnswer(ignored -> Thread.currentThread().equals(currentThread));
|
|
+
|
|
+ final org.bukkit.plugin.PluginManager pluginManager = new org.bukkit.plugin.SimplePluginManager(instance, new org.bukkit.command.SimpleCommandMap(instance));
|
|
+ when(instance.getPluginManager()).thenReturn(pluginManager);
|
|
+ when(instance.getTag(anyString(), any(org.bukkit.NamespacedKey.class), any())).thenAnswer(ignored -> new io.papermc.paper.util.EmptyTag());
|
|
+ // paper end - testing additions
|
|
+
|
|
Bukkit.setServer(instance);
|
|
} catch (Throwable t) {
|
|
throw new Error(t);
|