From 338717143a09038927e039bed73c2350c5d05553 Mon Sep 17 00:00:00 2001 From: Purdze Date: Thu, 12 Feb 2026 18:11:30 +0000 Subject: [PATCH] add conformance test plugin and fix command registration panic - new patchbukkit-test-plugin gradle subproject with 69 tests across 10 categories (server, plugin_manager, command_system, event_system, permissions, registry, entity, console_sender, unsafe_values, stubs) - test framework with reflection-based discovery, ansi-colored output, and ci-parseable [PBTEST_SUMMARY] line - /pbtest [category|all] command for manual test execution - fix .unwrap() panic in command_manager.rs register_permission that crashed the jvm worker thread when plugins declared commands --- java/patchbukkit-test-plugin/build.gradle.kts | 27 +++ .../testplugin/ConformanceTest.java | 14 ++ .../testplugin/PatchBukkitTestPlugin.java | 39 ++++ .../testplugin/PbTestBukkitCommand.java | 26 +++ .../patchbukkit/testplugin/PbTestCommand.java | 56 ++++++ .../patchbukkit/testplugin/TestCategory.java | 14 ++ .../testplugin/TestExpectation.java | 6 + .../patchbukkit/testplugin/TestFramework.java | 172 ++++++++++++++++++ .../patchbukkit/testplugin/TestResult.java | 13 ++ .../testplugin/tests/CommandSystemTests.java | 106 +++++++++++ .../testplugin/tests/ConsoleSenderTests.java | 64 +++++++ .../testplugin/tests/EntityTests.java | 40 ++++ .../testplugin/tests/EventSystemTests.java | 64 +++++++ .../testplugin/tests/PermissionsTests.java | 87 +++++++++ .../testplugin/tests/PluginManagerTests.java | 70 +++++++ .../testplugin/tests/RegistryTests.java | 54 ++++++ .../testplugin/tests/ServerTests.java | 115 ++++++++++++ .../testplugin/tests/StubTests.java | 134 ++++++++++++++ .../testplugin/tests/UnsafeValuesTests.java | 61 +++++++ .../src/main/resources/plugin.yml | 9 + java/settings.gradle.kts | 1 + rust/src/java/plugin/command_manager.rs | 6 +- 22 files changed, 1176 insertions(+), 2 deletions(-) create mode 100644 java/patchbukkit-test-plugin/build.gradle.kts create mode 100644 java/patchbukkit-test-plugin/src/main/java/org/patchbukkit/testplugin/ConformanceTest.java create mode 100644 java/patchbukkit-test-plugin/src/main/java/org/patchbukkit/testplugin/PatchBukkitTestPlugin.java create mode 100644 java/patchbukkit-test-plugin/src/main/java/org/patchbukkit/testplugin/PbTestBukkitCommand.java create mode 100644 java/patchbukkit-test-plugin/src/main/java/org/patchbukkit/testplugin/PbTestCommand.java create mode 100644 java/patchbukkit-test-plugin/src/main/java/org/patchbukkit/testplugin/TestCategory.java create mode 100644 java/patchbukkit-test-plugin/src/main/java/org/patchbukkit/testplugin/TestExpectation.java create mode 100644 java/patchbukkit-test-plugin/src/main/java/org/patchbukkit/testplugin/TestFramework.java create mode 100644 java/patchbukkit-test-plugin/src/main/java/org/patchbukkit/testplugin/TestResult.java create mode 100644 java/patchbukkit-test-plugin/src/main/java/org/patchbukkit/testplugin/tests/CommandSystemTests.java create mode 100644 java/patchbukkit-test-plugin/src/main/java/org/patchbukkit/testplugin/tests/ConsoleSenderTests.java create mode 100644 java/patchbukkit-test-plugin/src/main/java/org/patchbukkit/testplugin/tests/EntityTests.java create mode 100644 java/patchbukkit-test-plugin/src/main/java/org/patchbukkit/testplugin/tests/EventSystemTests.java create mode 100644 java/patchbukkit-test-plugin/src/main/java/org/patchbukkit/testplugin/tests/PermissionsTests.java create mode 100644 java/patchbukkit-test-plugin/src/main/java/org/patchbukkit/testplugin/tests/PluginManagerTests.java create mode 100644 java/patchbukkit-test-plugin/src/main/java/org/patchbukkit/testplugin/tests/RegistryTests.java create mode 100644 java/patchbukkit-test-plugin/src/main/java/org/patchbukkit/testplugin/tests/ServerTests.java create mode 100644 java/patchbukkit-test-plugin/src/main/java/org/patchbukkit/testplugin/tests/StubTests.java create mode 100644 java/patchbukkit-test-plugin/src/main/java/org/patchbukkit/testplugin/tests/UnsafeValuesTests.java create mode 100644 java/patchbukkit-test-plugin/src/main/resources/plugin.yml diff --git a/java/patchbukkit-test-plugin/build.gradle.kts b/java/patchbukkit-test-plugin/build.gradle.kts new file mode 100644 index 0000000..9e9728f --- /dev/null +++ b/java/patchbukkit-test-plugin/build.gradle.kts @@ -0,0 +1,27 @@ +plugins { + java +} + +repositories { + maven { + name = "papermc" + url = uri("https://repo.papermc.io/repository/maven-public/") + } +} + +dependencies { + compileOnly("io.papermc.paper:paper-api:1.21.11-R0.1-SNAPSHOT") +} + +java { + toolchain { + languageVersion = JavaLanguageVersion.of(25) + } +} + +tasks.withType { + options.compilerArgs.addAll(listOf( + "-Xlint:-removal", + "-Xlint:-deprecation" + )) +} diff --git a/java/patchbukkit-test-plugin/src/main/java/org/patchbukkit/testplugin/ConformanceTest.java b/java/patchbukkit-test-plugin/src/main/java/org/patchbukkit/testplugin/ConformanceTest.java new file mode 100644 index 0000000..59b3f2a --- /dev/null +++ b/java/patchbukkit-test-plugin/src/main/java/org/patchbukkit/testplugin/ConformanceTest.java @@ -0,0 +1,14 @@ +package org.patchbukkit.testplugin; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.METHOD) +public @interface ConformanceTest { + String name(); + TestCategory category(); + TestExpectation expectation() default TestExpectation.SHOULD_WORK; +} diff --git a/java/patchbukkit-test-plugin/src/main/java/org/patchbukkit/testplugin/PatchBukkitTestPlugin.java b/java/patchbukkit-test-plugin/src/main/java/org/patchbukkit/testplugin/PatchBukkitTestPlugin.java new file mode 100644 index 0000000..9d00266 --- /dev/null +++ b/java/patchbukkit-test-plugin/src/main/java/org/patchbukkit/testplugin/PatchBukkitTestPlugin.java @@ -0,0 +1,39 @@ +package org.patchbukkit.testplugin; + +import org.bukkit.Bukkit; +import org.bukkit.command.Command; +import org.bukkit.command.PluginCommand; +import org.bukkit.plugin.java.JavaPlugin; +import org.patchbukkit.testplugin.tests.*; + +public final class PatchBukkitTestPlugin extends JavaPlugin { + + @Override + public void onEnable() { + TestFramework framework = new TestFramework(getLogger()); + + framework.registerSuite(new ServerTests(this)); + framework.registerSuite(new PluginManagerTests(this)); + framework.registerSuite(new CommandSystemTests(this)); + framework.registerSuite(new EventSystemTests(this)); + framework.registerSuite(new PermissionsTests(this)); + framework.registerSuite(new RegistryTests(this)); + framework.registerSuite(new EntityTests(this)); + framework.registerSuite(new ConsoleSenderTests(this)); + framework.registerSuite(new UnsafeValuesTests(this)); + framework.registerSuite(new StubTests(this)); + + // Set executor on the PluginCommand created by PatchBukkit's Rust side + PbTestCommand cmd = new PbTestCommand(framework); + Command existing = Bukkit.getServer().getCommandMap().getCommand("pbtest"); + if (existing instanceof PluginCommand pluginCmd) { + pluginCmd.setExecutor(cmd); + pluginCmd.setTabCompleter(cmd); + } else { + // Fallback: register directly on CommandMap + Bukkit.getServer().getCommandMap().register("patchbukkittest", new PbTestBukkitCommand(cmd)); + } + + getLogger().info("PatchBukkit Test Plugin loaded. Run /pbtest [category|all] to execute tests."); + } +} diff --git a/java/patchbukkit-test-plugin/src/main/java/org/patchbukkit/testplugin/PbTestBukkitCommand.java b/java/patchbukkit-test-plugin/src/main/java/org/patchbukkit/testplugin/PbTestBukkitCommand.java new file mode 100644 index 0000000..336b88b --- /dev/null +++ b/java/patchbukkit-test-plugin/src/main/java/org/patchbukkit/testplugin/PbTestBukkitCommand.java @@ -0,0 +1,26 @@ +package org.patchbukkit.testplugin; + +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; + +import java.util.List; + +public final class PbTestBukkitCommand extends Command { + + private final PbTestCommand handler; + + public PbTestBukkitCommand(PbTestCommand handler) { + super("pbtest", "Run PatchBukkit conformance tests", "/pbtest [category|all]", List.of()); + this.handler = handler; + } + + @Override + public boolean execute(CommandSender sender, String label, String[] args) { + return handler.onCommand(sender, this, label, args); + } + + @Override + public List tabComplete(CommandSender sender, String alias, String[] args) { + return handler.onTabComplete(sender, this, alias, args); + } +} diff --git a/java/patchbukkit-test-plugin/src/main/java/org/patchbukkit/testplugin/PbTestCommand.java b/java/patchbukkit-test-plugin/src/main/java/org/patchbukkit/testplugin/PbTestCommand.java new file mode 100644 index 0000000..3f928e6 --- /dev/null +++ b/java/patchbukkit-test-plugin/src/main/java/org/patchbukkit/testplugin/PbTestCommand.java @@ -0,0 +1,56 @@ +package org.patchbukkit.testplugin; + +import org.bukkit.command.Command; +import org.bukkit.command.CommandExecutor; +import org.bukkit.command.CommandSender; +import org.bukkit.command.TabCompleter; + +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; + +public final class PbTestCommand implements CommandExecutor, TabCompleter { + + private final TestFramework framework; + + public PbTestCommand(TestFramework framework) { + this.framework = framework; + } + + @Override + public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { + String target = (args.length > 0) ? args[0].toUpperCase(Locale.ROOT) : "ALL"; + + List results; + if ("ALL".equals(target)) { + results = framework.runAll(); + } else { + TestCategory category; + try { + category = TestCategory.valueOf(target); + } catch (IllegalArgumentException e) { + sender.sendMessage("Unknown category: " + target + ". Use /pbtest [category|all]"); + return true; + } + results = framework.runCategory(category); + } + + framework.reportResults(results, sender); + return true; + } + + @Override + public List onTabComplete(CommandSender sender, Command command, String alias, String[] args) { + if (args.length != 1) return List.of(); + + String prefix = args[0].toUpperCase(Locale.ROOT); + List completions = new ArrayList<>(); + completions.add("all"); + for (TestCategory cat : TestCategory.values()) { + completions.add(cat.name().toLowerCase(Locale.ROOT)); + } + + completions.removeIf(s -> !s.toUpperCase(Locale.ROOT).startsWith(prefix)); + return completions; + } +} diff --git a/java/patchbukkit-test-plugin/src/main/java/org/patchbukkit/testplugin/TestCategory.java b/java/patchbukkit-test-plugin/src/main/java/org/patchbukkit/testplugin/TestCategory.java new file mode 100644 index 0000000..5c42f91 --- /dev/null +++ b/java/patchbukkit-test-plugin/src/main/java/org/patchbukkit/testplugin/TestCategory.java @@ -0,0 +1,14 @@ +package org.patchbukkit.testplugin; + +public enum TestCategory { + SERVER, + PLUGIN_MANAGER, + COMMAND_SYSTEM, + EVENT_SYSTEM, + PERMISSIONS, + REGISTRY, + ENTITY, + CONSOLE_SENDER, + UNSAFE_VALUES, + STUBS +} diff --git a/java/patchbukkit-test-plugin/src/main/java/org/patchbukkit/testplugin/TestExpectation.java b/java/patchbukkit-test-plugin/src/main/java/org/patchbukkit/testplugin/TestExpectation.java new file mode 100644 index 0000000..1a07c14 --- /dev/null +++ b/java/patchbukkit-test-plugin/src/main/java/org/patchbukkit/testplugin/TestExpectation.java @@ -0,0 +1,6 @@ +package org.patchbukkit.testplugin; + +public enum TestExpectation { + SHOULD_WORK, + EXPECT_UNSUPPORTED +} diff --git a/java/patchbukkit-test-plugin/src/main/java/org/patchbukkit/testplugin/TestFramework.java b/java/patchbukkit-test-plugin/src/main/java/org/patchbukkit/testplugin/TestFramework.java new file mode 100644 index 0000000..6900889 --- /dev/null +++ b/java/patchbukkit-test-plugin/src/main/java/org/patchbukkit/testplugin/TestFramework.java @@ -0,0 +1,172 @@ +package org.patchbukkit.testplugin; + +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.EnumMap; +import java.util.List; +import java.util.Map; +import java.util.logging.Logger; + +import org.bukkit.command.CommandSender; + +public final class TestFramework { + + private final Logger logger; + private final List suites = new ArrayList<>(); + + public TestFramework(Logger logger) { + this.logger = logger; + } + + public void registerSuite(Object suite) { + suites.add(suite); + } + + public List runAll() { + List results = new ArrayList<>(); + for (Object suite : suites) { + results.addAll(runSuite(suite)); + } + return results; + } + + public List runCategory(TestCategory category) { + List results = new ArrayList<>(); + for (Object suite : suites) { + for (Method method : suite.getClass().getDeclaredMethods()) { + ConformanceTest ann = method.getAnnotation(ConformanceTest.class); + if (ann != null && ann.category() == category) { + results.add(runTest(suite, method, ann)); + } + } + } + return results; + } + + private List runSuite(Object suite) { + List results = new ArrayList<>(); + for (Method method : suite.getClass().getDeclaredMethods()) { + ConformanceTest ann = method.getAnnotation(ConformanceTest.class); + if (ann != null) { + results.add(runTest(suite, method, ann)); + } + } + return results; + } + + private TestResult runTest(Object suite, Method method, ConformanceTest ann) { + try { + method.setAccessible(true); + method.invoke(suite); + + if (ann.expectation() == TestExpectation.SHOULD_WORK) { + return new TestResult(ann.name(), ann.category(), ann.expectation(), true, null); + } else { + return new TestResult(ann.name(), ann.category(), ann.expectation(), false, + "Expected UnsupportedOperationException but method succeeded"); + } + } catch (Exception e) { + Throwable cause = e.getCause() != null ? e.getCause() : e; + + if (ann.expectation() == TestExpectation.EXPECT_UNSUPPORTED) { + if (cause instanceof UnsupportedOperationException) { + return new TestResult(ann.name(), ann.category(), ann.expectation(), true, null); + } else { + return new TestResult(ann.name(), ann.category(), ann.expectation(), false, + "Expected UnsupportedOperationException but got " + cause.getClass().getSimpleName() + ": " + cause.getMessage()); + } + } else { + return new TestResult(ann.name(), ann.category(), ann.expectation(), false, + cause.getClass().getSimpleName() + ": " + cause.getMessage()); + } + } + } + + // ANSI color codes + private static final String RESET = "\u001B[0m"; + private static final String BOLD = "\u001B[1m"; + private static final String RED = "\u001B[31m"; + private static final String GREEN = "\u001B[32m"; + private static final String YELLOW = "\u001B[33m"; + private static final String CYAN = "\u001B[36m"; + private static final String WHITE = "\u001B[37m"; + + public void reportResults(List results) { + Map> grouped = new EnumMap<>(TestCategory.class); + for (TestResult r : results) { + grouped.computeIfAbsent(r.category(), k -> new ArrayList<>()).add(r); + } + + int total = results.size(); + int passed = 0; + for (TestResult r : results) { + if (r.passed()) passed++; + } + int failed = total - passed; + + logger.info(CYAN + BOLD + "========================================" + RESET); + logger.info(CYAN + BOLD + " PatchBukkit Conformance Test Results" + RESET); + logger.info(CYAN + BOLD + "========================================" + RESET); + + for (TestCategory cat : TestCategory.values()) { + List catResults = grouped.get(cat); + if (catResults == null || catResults.isEmpty()) continue; + + logger.info(YELLOW + BOLD + "--- " + cat.name() + " ---" + RESET); + for (TestResult r : catResults) { + if (r.passed()) { + logger.info(GREEN + " PASS" + RESET + " [" + r.tag() + "] " + r.name()); + } else { + logger.info(RED + " FAIL" + RESET + " [" + r.tag() + "] " + r.name() + RED + " -- " + r.detail() + RESET); + } + } + } + + logger.info(CYAN + BOLD + "========================================" + RESET); + String summaryColor = failed == 0 ? GREEN : RED; + logger.info(BOLD + " Total: " + WHITE + total + RESET + + BOLD + " Passed: " + GREEN + passed + RESET + + BOLD + " Failed: " + summaryColor + failed + RESET); + logger.info(CYAN + BOLD + "========================================" + RESET); + logger.info("[PBTEST_SUMMARY] total=" + total + " passed=" + passed + " failed=" + failed); + } + + public void reportResults(List results, CommandSender sender) { + Map> grouped = new EnumMap<>(TestCategory.class); + for (TestResult r : results) { + grouped.computeIfAbsent(r.category(), k -> new ArrayList<>()).add(r); + } + + int total = results.size(); + int passed = 0; + for (TestResult r : results) { + if (r.passed()) passed++; + } + int failed = total - passed; + + sender.sendMessage(CYAN + BOLD + "========================================" + RESET); + sender.sendMessage(CYAN + BOLD + " PatchBukkit Conformance Test Results" + RESET); + sender.sendMessage(CYAN + BOLD + "========================================" + RESET); + + for (TestCategory cat : TestCategory.values()) { + List catResults = grouped.get(cat); + if (catResults == null || catResults.isEmpty()) continue; + + sender.sendMessage(YELLOW + BOLD + "--- " + cat.name() + " ---" + RESET); + for (TestResult r : catResults) { + if (r.passed()) { + sender.sendMessage(GREEN + " PASS" + RESET + " [" + r.tag() + "] " + r.name()); + } else { + sender.sendMessage(RED + " FAIL" + RESET + " [" + r.tag() + "] " + r.name() + RED + " -- " + r.detail() + RESET); + } + } + } + + sender.sendMessage(CYAN + BOLD + "========================================" + RESET); + String summaryColor = failed == 0 ? GREEN : RED; + sender.sendMessage(BOLD + " Total: " + WHITE + total + RESET + + BOLD + " Passed: " + GREEN + passed + RESET + + BOLD + " Failed: " + summaryColor + failed + RESET); + sender.sendMessage(CYAN + BOLD + "========================================" + RESET); + } +} diff --git a/java/patchbukkit-test-plugin/src/main/java/org/patchbukkit/testplugin/TestResult.java b/java/patchbukkit-test-plugin/src/main/java/org/patchbukkit/testplugin/TestResult.java new file mode 100644 index 0000000..79046ef --- /dev/null +++ b/java/patchbukkit-test-plugin/src/main/java/org/patchbukkit/testplugin/TestResult.java @@ -0,0 +1,13 @@ +package org.patchbukkit.testplugin; + +public record TestResult( + String name, + TestCategory category, + TestExpectation expectation, + boolean passed, + String detail +) { + public String tag() { + return expectation == TestExpectation.SHOULD_WORK ? "IMPL" : "STUB"; + } +} diff --git a/java/patchbukkit-test-plugin/src/main/java/org/patchbukkit/testplugin/tests/CommandSystemTests.java b/java/patchbukkit-test-plugin/src/main/java/org/patchbukkit/testplugin/tests/CommandSystemTests.java new file mode 100644 index 0000000..59382bd --- /dev/null +++ b/java/patchbukkit-test-plugin/src/main/java/org/patchbukkit/testplugin/tests/CommandSystemTests.java @@ -0,0 +1,106 @@ +package org.patchbukkit.testplugin.tests; + +import org.bukkit.Bukkit; +import org.bukkit.command.Command; +import org.bukkit.command.CommandMap; +import org.bukkit.command.CommandSender; +import org.bukkit.command.defaults.BukkitCommand; +import org.patchbukkit.testplugin.ConformanceTest; +import org.patchbukkit.testplugin.TestCategory; +import org.bukkit.plugin.java.JavaPlugin; + +import java.util.Arrays; +import java.util.List; + +public final class CommandSystemTests { + + private final JavaPlugin plugin; + + public CommandSystemTests(JavaPlugin plugin) { + this.plugin = plugin; + } + + @ConformanceTest(name = "CommandMap.register() accepts a command", category = TestCategory.COMMAND_SYSTEM) + public void testRegister() { + CommandMap map = Bukkit.getServer().getCommandMap(); + Command testCmd = new BukkitCommand("pbtestdummy", "test", "/pbtestdummy", List.of()) { + @Override + public boolean execute(CommandSender sender, String label, String[] args) { + return true; + } + }; + boolean registered = map.register("patchbukkittest", testCmd); + // registered is true if direct label was available, false if only fallback was used + // Either way it should not throw + } + + @ConformanceTest(name = "CommandMap.getCommand() retrieves registered command", category = TestCategory.COMMAND_SYSTEM) + public void testGetCommand() { + CommandMap map = Bukkit.getServer().getCommandMap(); + // "pbtest" was registered by the plugin + Command cmd = map.getCommand("pbtest"); + assertNotNull(cmd, "CommandMap.getCommand(\"pbtest\")"); + } + + @ConformanceTest(name = "CommandMap.getKnownCommands() returns map", category = TestCategory.COMMAND_SYSTEM) + public void testGetKnownCommands() { + CommandMap map = Bukkit.getServer().getCommandMap(); + var known = map.getKnownCommands(); + assertNotNull(known, "CommandMap.getKnownCommands()"); + assertTrue(!known.isEmpty(), "getKnownCommands() should not be empty"); + } + + @ConformanceTest(name = "CommandMap.dispatch() executes a command", category = TestCategory.COMMAND_SYSTEM) + public void testDispatch() { + CommandMap map = Bukkit.getServer().getCommandMap(); + + // Register a command that sets a flag + final boolean[] executed = {false}; + Command testCmd = new BukkitCommand("pbtestexec", "test", "/pbtestexec", List.of()) { + @Override + public boolean execute(CommandSender sender, String label, String[] args) { + executed[0] = true; + return true; + } + }; + map.register("patchbukkittest", testCmd); + + CommandSender sender = Bukkit.getConsoleSender(); + boolean result = map.dispatch(sender, "pbtestexec"); + assertTrue(result, "dispatch() should return true"); + assertTrue(executed[0], "command should have been executed"); + } + + @ConformanceTest(name = "CommandMap.tabComplete() returns completions", category = TestCategory.COMMAND_SYSTEM) + public void testTabComplete() { + CommandMap map = Bukkit.getServer().getCommandMap(); + CommandSender sender = Bukkit.getConsoleSender(); + List completions = map.tabComplete(sender, "pb"); + assertNotNull(completions, "CommandMap.tabComplete()"); + // Should include "pbtest" at minimum + } + + @ConformanceTest(name = "CommandMap.register() with fallback prefix creates alias", category = TestCategory.COMMAND_SYSTEM) + public void testFallbackAlias() { + CommandMap map = Bukkit.getServer().getCommandMap(); + Command testCmd = new BukkitCommand("pbtestalias", "test", "/pbtestalias", List.of("pbta")) { + @Override + public boolean execute(CommandSender sender, String label, String[] args) { + return true; + } + }; + map.register("myprefix", testCmd); + + // Should be accessible via fallback prefix + Command found = map.getCommand("myprefix:pbtestalias"); + assertNotNull(found, "CommandMap.getCommand(\"myprefix:pbtestalias\")"); + } + + private static void assertNotNull(Object value, String what) { + if (value == null) throw new AssertionError(what + " returned null"); + } + + private static void assertTrue(boolean condition, String what) { + if (!condition) throw new AssertionError(what); + } +} diff --git a/java/patchbukkit-test-plugin/src/main/java/org/patchbukkit/testplugin/tests/ConsoleSenderTests.java b/java/patchbukkit-test-plugin/src/main/java/org/patchbukkit/testplugin/tests/ConsoleSenderTests.java new file mode 100644 index 0000000..fec7638 --- /dev/null +++ b/java/patchbukkit-test-plugin/src/main/java/org/patchbukkit/testplugin/tests/ConsoleSenderTests.java @@ -0,0 +1,64 @@ +package org.patchbukkit.testplugin.tests; + +import org.bukkit.Bukkit; +import org.bukkit.Server; +import org.bukkit.command.ConsoleCommandSender; +import org.patchbukkit.testplugin.ConformanceTest; +import org.patchbukkit.testplugin.TestCategory; +import org.bukkit.plugin.java.JavaPlugin; + +public final class ConsoleSenderTests { + + private final JavaPlugin plugin; + + public ConsoleSenderTests(JavaPlugin plugin) { + this.plugin = plugin; + } + + @ConformanceTest(name = "ConsoleSender.getName() returns non-null", category = TestCategory.CONSOLE_SENDER) + public void testGetName() { + ConsoleCommandSender console = Bukkit.getConsoleSender(); + String name = console.getName(); + assertNotNull(name, "ConsoleSender.getName()"); + } + + @ConformanceTest(name = "ConsoleSender.sendMessage(String) does not throw", category = TestCategory.CONSOLE_SENDER) + public void testSendMessage() { + ConsoleCommandSender console = Bukkit.getConsoleSender(); + console.sendMessage("[PBTest] Console sendMessage test"); + } + + @ConformanceTest(name = "ConsoleSender.sendMessage(String[]) does not throw", category = TestCategory.CONSOLE_SENDER) + public void testSendMessageArray() { + ConsoleCommandSender console = Bukkit.getConsoleSender(); + console.sendMessage(new String[]{"[PBTest] Line 1", "[PBTest] Line 2"}); + } + + @ConformanceTest(name = "ConsoleSender.isOp() returns true", category = TestCategory.CONSOLE_SENDER) + public void testIsOp() { + ConsoleCommandSender console = Bukkit.getConsoleSender(); + assertTrue(console.isOp(), "ConsoleSender.isOp() should be true"); + } + + @ConformanceTest(name = "ConsoleSender.getServer() returns non-null", category = TestCategory.CONSOLE_SENDER) + public void testGetServer() { + ConsoleCommandSender console = Bukkit.getConsoleSender(); + Server server = console.getServer(); + assertNotNull(server, "ConsoleSender.getServer()"); + } + + @ConformanceTest(name = "ConsoleSender.spigot() returns non-null", category = TestCategory.CONSOLE_SENDER) + public void testSpigot() { + ConsoleCommandSender console = Bukkit.getConsoleSender(); + var spigot = console.spigot(); + assertNotNull(spigot, "ConsoleSender.spigot()"); + } + + private static void assertNotNull(Object value, String what) { + if (value == null) throw new AssertionError(what + " returned null"); + } + + private static void assertTrue(boolean condition, String what) { + if (!condition) throw new AssertionError(what); + } +} diff --git a/java/patchbukkit-test-plugin/src/main/java/org/patchbukkit/testplugin/tests/EntityTests.java b/java/patchbukkit-test-plugin/src/main/java/org/patchbukkit/testplugin/tests/EntityTests.java new file mode 100644 index 0000000..dd06c71 --- /dev/null +++ b/java/patchbukkit-test-plugin/src/main/java/org/patchbukkit/testplugin/tests/EntityTests.java @@ -0,0 +1,40 @@ +package org.patchbukkit.testplugin.tests; + +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; +import org.patchbukkit.testplugin.ConformanceTest; +import org.patchbukkit.testplugin.TestCategory; +import org.patchbukkit.testplugin.TestExpectation; +import org.bukkit.plugin.java.JavaPlugin; + +import java.util.Collection; +import java.util.UUID; + +public final class EntityTests { + + private final JavaPlugin plugin; + + public EntityTests(JavaPlugin plugin) { + this.plugin = plugin; + } + + @ConformanceTest(name = "Server.getOnlinePlayers() returns iterable collection", category = TestCategory.ENTITY) + public void testOnlinePlayersIteration() { + Collection players = Bukkit.getOnlinePlayers(); + assertNotNull(players, "Bukkit.getOnlinePlayers()"); + // Iterate without error + for (Player p : players) { + assertNotNull(p, "Player in online players collection"); + } + } + + @ConformanceTest(name = "Server.getEntity(UUID) stub", category = TestCategory.ENTITY, + expectation = TestExpectation.EXPECT_UNSUPPORTED) + public void testGetEntity() { + Bukkit.getServer().getEntity(UUID.randomUUID()); + } + + private static void assertNotNull(Object value, String what) { + if (value == null) throw new AssertionError(what + " returned null"); + } +} diff --git a/java/patchbukkit-test-plugin/src/main/java/org/patchbukkit/testplugin/tests/EventSystemTests.java b/java/patchbukkit-test-plugin/src/main/java/org/patchbukkit/testplugin/tests/EventSystemTests.java new file mode 100644 index 0000000..df63177 --- /dev/null +++ b/java/patchbukkit-test-plugin/src/main/java/org/patchbukkit/testplugin/tests/EventSystemTests.java @@ -0,0 +1,64 @@ +package org.patchbukkit.testplugin.tests; + +import org.bukkit.Bukkit; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.Listener; +import org.bukkit.event.server.PluginEnableEvent; +import org.bukkit.plugin.EventExecutor; +import org.patchbukkit.testplugin.ConformanceTest; +import org.patchbukkit.testplugin.TestCategory; +import org.bukkit.plugin.java.JavaPlugin; + +public final class EventSystemTests { + + private final JavaPlugin plugin; + + public EventSystemTests(JavaPlugin plugin) { + this.plugin = plugin; + } + + @ConformanceTest(name = "registerEvents() with annotated listener", category = TestCategory.EVENT_SYSTEM) + public void testRegisterEvents() { + Listener listener = new Listener() { + @EventHandler + public void onPluginEnable(PluginEnableEvent event) { + // no-op, just verifying registration doesn't throw + } + }; + Bukkit.getPluginManager().registerEvents(listener, plugin); + } + + @ConformanceTest(name = "registerEvent() with explicit priority", category = TestCategory.EVENT_SYSTEM) + public void testRegisterEventWithPriority() { + EventExecutor executor = (listener, event) -> { + // no-op + }; + Bukkit.getPluginManager().registerEvent( + PluginEnableEvent.class, + new Listener() {}, + EventPriority.HIGH, + executor, + plugin + ); + } + + @ConformanceTest(name = "registerEvent() with ignoreCancelled flag", category = TestCategory.EVENT_SYSTEM) + public void testRegisterEventIgnoreCancelled() { + EventExecutor executor = (listener, event) -> { + // no-op + }; + Bukkit.getPluginManager().registerEvent( + PluginEnableEvent.class, + new Listener() {}, + EventPriority.NORMAL, + executor, + plugin, + true + ); + } + + private static void assertNotNull(Object value, String what) { + if (value == null) throw new AssertionError(what + " returned null"); + } +} diff --git a/java/patchbukkit-test-plugin/src/main/java/org/patchbukkit/testplugin/tests/PermissionsTests.java b/java/patchbukkit-test-plugin/src/main/java/org/patchbukkit/testplugin/tests/PermissionsTests.java new file mode 100644 index 0000000..726fdcf --- /dev/null +++ b/java/patchbukkit-test-plugin/src/main/java/org/patchbukkit/testplugin/tests/PermissionsTests.java @@ -0,0 +1,87 @@ +package org.patchbukkit.testplugin.tests; + +import org.bukkit.Bukkit; +import org.bukkit.permissions.Permission; +import org.bukkit.permissions.PermissionDefault; +import org.bukkit.plugin.PluginManager; +import org.patchbukkit.testplugin.ConformanceTest; +import org.patchbukkit.testplugin.TestCategory; +import org.bukkit.plugin.java.JavaPlugin; + +import java.util.Set; + +public final class PermissionsTests { + + private final JavaPlugin plugin; + + public PermissionsTests(JavaPlugin plugin) { + this.plugin = plugin; + } + + @ConformanceTest(name = "addPermission() and getPermission()", category = TestCategory.PERMISSIONS) + public void testAddAndGetPermission() { + PluginManager pm = Bukkit.getPluginManager(); + // Clean up from previous runs + pm.removePermission("patchbukkit.test.add"); + Permission perm = new Permission("patchbukkit.test.add", PermissionDefault.OP); + pm.addPermission(perm); + Permission found = pm.getPermission("patchbukkit.test.add"); + assertNotNull(found, "getPermission() after addPermission()"); + assertTrue(found == perm, "getPermission() should return the same instance"); + pm.removePermission(perm); + } + + @ConformanceTest(name = "removePermission() removes added permission", category = TestCategory.PERMISSIONS) + public void testRemovePermission() { + PluginManager pm = Bukkit.getPluginManager(); + pm.removePermission("patchbukkit.test.remove"); + Permission perm = new Permission("patchbukkit.test.remove", PermissionDefault.OP); + pm.addPermission(perm); + pm.removePermission(perm); + Permission found = pm.getPermission("patchbukkit.test.remove"); + assertTrue(found == null, "getPermission() should return null after remove"); + } + + @ConformanceTest(name = "getPermission() returns null for unknown", category = TestCategory.PERMISSIONS) + public void testGetPermissionUnknown() { + Permission found = Bukkit.getPluginManager().getPermission("patchbukkit.test.nonexistent"); + assertTrue(found == null, "getPermission(unknown) should return null"); + } + + @ConformanceTest(name = "getDefaultPermissions(op=true) returns set", category = TestCategory.PERMISSIONS) + public void testGetDefaultPermissionsOp() { + Set defaults = Bukkit.getPluginManager().getDefaultPermissions(true); + assertNotNull(defaults, "getDefaultPermissions(true)"); + } + + @ConformanceTest(name = "getDefaultPermissions(op=false) returns set", category = TestCategory.PERMISSIONS) + public void testGetDefaultPermissionsNonOp() { + Set defaults = Bukkit.getPluginManager().getDefaultPermissions(false); + assertNotNull(defaults, "getDefaultPermissions(false)"); + } + + @ConformanceTest(name = "Console sender has op permissions", category = TestCategory.PERMISSIONS) + public void testConsoleSenderHasOpPermissions() { + var console = Bukkit.getConsoleSender(); + assertTrue(console.isOp(), "Console sender should be op"); + + // Add a permission with OP default + PluginManager pm = Bukkit.getPluginManager(); + pm.removePermission("patchbukkit.test.oponly"); + Permission perm = new Permission("patchbukkit.test.oponly", PermissionDefault.OP); + pm.addPermission(perm); + + // Console (op=true) should have this permission + boolean hasPerm = console.hasPermission("patchbukkit.test.oponly"); + pm.removePermission(perm); + assertTrue(hasPerm, "Console (op) should have OP-default permission"); + } + + private static void assertNotNull(Object value, String what) { + if (value == null) throw new AssertionError(what + " returned null"); + } + + private static void assertTrue(boolean condition, String what) { + if (!condition) throw new AssertionError(what); + } +} diff --git a/java/patchbukkit-test-plugin/src/main/java/org/patchbukkit/testplugin/tests/PluginManagerTests.java b/java/patchbukkit-test-plugin/src/main/java/org/patchbukkit/testplugin/tests/PluginManagerTests.java new file mode 100644 index 0000000..8a6a841 --- /dev/null +++ b/java/patchbukkit-test-plugin/src/main/java/org/patchbukkit/testplugin/tests/PluginManagerTests.java @@ -0,0 +1,70 @@ +package org.patchbukkit.testplugin.tests; + +import org.bukkit.Bukkit; +import org.bukkit.plugin.Plugin; +import org.bukkit.plugin.PluginManager; +import org.patchbukkit.testplugin.ConformanceTest; +import org.patchbukkit.testplugin.TestCategory; +import org.bukkit.plugin.java.JavaPlugin; + +public final class PluginManagerTests { + + private final JavaPlugin plugin; + + public PluginManagerTests(JavaPlugin plugin) { + this.plugin = plugin; + } + + @ConformanceTest(name = "PluginManager.getPlugins() contains this plugin", category = TestCategory.PLUGIN_MANAGER) + public void testGetPlugins() { + Plugin[] plugins = Bukkit.getPluginManager().getPlugins(); + assertNotNull(plugins, "PluginManager.getPlugins()"); + boolean found = false; + for (Plugin p : plugins) { + if (p == plugin) { + found = true; + break; + } + } + assertTrue(found, "getPlugins() should contain this plugin"); + } + + @ConformanceTest(name = "PluginManager.getPlugin() finds this plugin by name", category = TestCategory.PLUGIN_MANAGER) + public void testGetPlugin() { + Plugin found = Bukkit.getPluginManager().getPlugin("PatchBukkitTest"); + assertNotNull(found, "PluginManager.getPlugin(\"PatchBukkitTest\")"); + assertTrue(found == plugin, "getPlugin() should return this plugin instance"); + } + + @ConformanceTest(name = "PluginManager.getPlugin() returns null for unknown", category = TestCategory.PLUGIN_MANAGER) + public void testGetPluginUnknown() { + Plugin found = Bukkit.getPluginManager().getPlugin("NoSuchPlugin_XYZ_12345"); + assertTrue(found == null, "getPlugin(unknown) should return null"); + } + + @ConformanceTest(name = "PluginManager.isPluginEnabled(String) for this plugin", category = TestCategory.PLUGIN_MANAGER) + public void testIsPluginEnabledByName() { + boolean enabled = Bukkit.getPluginManager().isPluginEnabled("PatchBukkitTest"); + assertTrue(enabled, "isPluginEnabled(\"PatchBukkitTest\") should be true"); + } + + @ConformanceTest(name = "PluginManager.isPluginEnabled(Plugin) for this plugin", category = TestCategory.PLUGIN_MANAGER) + public void testIsPluginEnabledByInstance() { + boolean enabled = Bukkit.getPluginManager().isPluginEnabled(plugin); + assertTrue(enabled, "isPluginEnabled(plugin) should be true"); + } + + @ConformanceTest(name = "PluginManager.getPermissions() returns set", category = TestCategory.PLUGIN_MANAGER) + public void testGetPermissions() { + var perms = Bukkit.getPluginManager().getPermissions(); + assertNotNull(perms, "PluginManager.getPermissions()"); + } + + private static void assertNotNull(Object value, String what) { + if (value == null) throw new AssertionError(what + " returned null"); + } + + private static void assertTrue(boolean condition, String what) { + if (!condition) throw new AssertionError(what); + } +} diff --git a/java/patchbukkit-test-plugin/src/main/java/org/patchbukkit/testplugin/tests/RegistryTests.java b/java/patchbukkit-test-plugin/src/main/java/org/patchbukkit/testplugin/tests/RegistryTests.java new file mode 100644 index 0000000..5ab0636 --- /dev/null +++ b/java/patchbukkit-test-plugin/src/main/java/org/patchbukkit/testplugin/tests/RegistryTests.java @@ -0,0 +1,54 @@ +package org.patchbukkit.testplugin.tests; + +import org.bukkit.Bukkit; +import org.bukkit.Registry; +import org.bukkit.Sound; +import org.patchbukkit.testplugin.ConformanceTest; +import org.patchbukkit.testplugin.TestCategory; +import org.patchbukkit.testplugin.TestExpectation; +import org.bukkit.plugin.java.JavaPlugin; + +public final class RegistryTests { + + private final JavaPlugin plugin; + + public RegistryTests(JavaPlugin plugin) { + this.plugin = plugin; + } + + @ConformanceTest(name = "Registry.SOUNDS is accessible", category = TestCategory.REGISTRY) + public void testSoundsRegistryExists() { + Registry sounds = Registry.SOUNDS; + assertNotNull(sounds, "Registry.SOUNDS"); + } + + @ConformanceTest(name = "Registry.SOUNDS.iterator() works", category = TestCategory.REGISTRY) + public void testSoundsIteration() { + Registry sounds = Registry.SOUNDS; + assertNotNull(sounds, "Registry.SOUNDS"); + var iterator = sounds.iterator(); + assertNotNull(iterator, "Registry.SOUNDS.iterator()"); + } + + @ConformanceTest(name = "Registry.SOUNDS.stream() works", category = TestCategory.REGISTRY) + public void testSoundsStream() { + Registry sounds = Registry.SOUNDS; + assertNotNull(sounds, "Registry.SOUNDS"); + var stream = sounds.stream(); + assertNotNull(stream, "Registry.SOUNDS.stream()"); + } + + @ConformanceTest(name = "Server.getRegistry(Sound.class) returns registry", category = TestCategory.REGISTRY, + expectation = TestExpectation.EXPECT_UNSUPPORTED) + public void testServerGetRegistry() { + // Server.getRegistry() returns null in PatchBukkit — might throw or return null + Registry reg = Bukkit.getServer().getRegistry(Sound.class); + if (reg == null) { + throw new UnsupportedOperationException("Server.getRegistry() returned null (not implemented)"); + } + } + + private static void assertNotNull(Object value, String what) { + if (value == null) throw new AssertionError(what + " returned null"); + } +} diff --git a/java/patchbukkit-test-plugin/src/main/java/org/patchbukkit/testplugin/tests/ServerTests.java b/java/patchbukkit-test-plugin/src/main/java/org/patchbukkit/testplugin/tests/ServerTests.java new file mode 100644 index 0000000..c2dd5d0 --- /dev/null +++ b/java/patchbukkit-test-plugin/src/main/java/org/patchbukkit/testplugin/tests/ServerTests.java @@ -0,0 +1,115 @@ +package org.patchbukkit.testplugin.tests; + +import org.bukkit.Bukkit; +import org.bukkit.Server; +import org.bukkit.command.CommandMap; +import org.bukkit.command.ConsoleCommandSender; +import org.bukkit.plugin.PluginManager; +import org.bukkit.scheduler.BukkitScheduler; +import org.patchbukkit.testplugin.ConformanceTest; +import org.patchbukkit.testplugin.TestCategory; +import org.patchbukkit.testplugin.TestExpectation; +import org.bukkit.plugin.java.JavaPlugin; + +public final class ServerTests { + + private final JavaPlugin plugin; + + public ServerTests(JavaPlugin plugin) { + this.plugin = plugin; + } + + @ConformanceTest(name = "Server.getName() returns non-null", category = TestCategory.SERVER) + public void testGetName() { + String name = Bukkit.getServer().getName(); + assertNotNull(name, "Server.getName()"); + } + + @ConformanceTest(name = "Server.getVersion() returns non-null", category = TestCategory.SERVER) + public void testGetVersion() { + String version = Bukkit.getServer().getVersion(); + assertNotNull(version, "Server.getVersion()"); + } + + @ConformanceTest(name = "Server.getBukkitVersion() returns non-null", category = TestCategory.SERVER) + public void testGetBukkitVersion() { + String version = Bukkit.getServer().getBukkitVersion(); + assertNotNull(version, "Server.getBukkitVersion()"); + } + + @ConformanceTest(name = "Server.getPluginManager() returns non-null", category = TestCategory.SERVER) + public void testGetPluginManager() { + PluginManager pm = Bukkit.getServer().getPluginManager(); + assertNotNull(pm, "Server.getPluginManager()"); + } + + @ConformanceTest(name = "Server.getConsoleSender() returns non-null", category = TestCategory.SERVER) + public void testGetConsoleSender() { + ConsoleCommandSender cs = Bukkit.getServer().getConsoleSender(); + assertNotNull(cs, "Server.getConsoleSender()"); + } + + @ConformanceTest(name = "Server.getCommandMap() returns non-null", category = TestCategory.SERVER) + public void testGetCommandMap() { + CommandMap cm = Bukkit.getServer().getCommandMap(); + assertNotNull(cm, "Server.getCommandMap()"); + } + + @ConformanceTest(name = "Server.getScheduler() returns non-null", category = TestCategory.SERVER) + public void testGetScheduler() { + BukkitScheduler sched = Bukkit.getServer().getScheduler(); + assertNotNull(sched, "Server.getScheduler()"); + } + + @ConformanceTest(name = "Server.getOnlinePlayers() returns collection", category = TestCategory.SERVER) + public void testGetOnlinePlayers() { + var players = Bukkit.getServer().getOnlinePlayers(); + assertNotNull(players, "Server.getOnlinePlayers()"); + } + + @ConformanceTest(name = "Server.getUnsafe() returns non-null", category = TestCategory.SERVER) + public void testGetUnsafe() { + Object unsafe = Bukkit.getUnsafe(); + assertNotNull(unsafe, "Server.getUnsafe()"); + } + + @ConformanceTest(name = "Server.suggestPlayerNamesWhenNullTabCompletions() returns boolean", category = TestCategory.SERVER) + public void testSuggestPlayerNames() { + // Should return true according to implementation + boolean result = Bukkit.getServer().suggestPlayerNamesWhenNullTabCompletions(); + assertTrue(result, "Server.suggestPlayerNamesWhenNullTabCompletions()"); + } + + @ConformanceTest(name = "Bukkit.getServer() matches plugin.getServer()", category = TestCategory.SERVER) + public void testServerConsistency() { + Server bukkit = Bukkit.getServer(); + Server fromPlugin = plugin.getServer(); + assertTrue(bukkit == fromPlugin, "Bukkit.getServer() == plugin.getServer()"); + } + + @ConformanceTest(name = "Server.getPluginCommand() returns null for unregistered command", category = TestCategory.SERVER) + public void testGetPluginCommand() { + // getPluginCommand only finds PluginCommands registered via plugin.yml; + // commands registered directly on the CommandMap are not PluginCommands + var cmd = Bukkit.getServer().getPluginCommand("nonexistent_abc"); + assertTrue(cmd == null, "Server.getPluginCommand(unregistered) should be null"); + } + + @ConformanceTest(name = "Server.getPluginCommand() returns null for unknown", category = TestCategory.SERVER) + public void testGetPluginCommandUnknown() { + var cmd = Bukkit.getServer().getPluginCommand("nonexistent_command_xyz"); + assertTrue(cmd == null, "Server.getPluginCommand(unknown) should be null"); + } + + private static void assertNotNull(Object value, String what) { + if (value == null) { + throw new AssertionError(what + " returned null"); + } + } + + private static void assertTrue(boolean condition, String what) { + if (!condition) { + throw new AssertionError(what); + } + } +} diff --git a/java/patchbukkit-test-plugin/src/main/java/org/patchbukkit/testplugin/tests/StubTests.java b/java/patchbukkit-test-plugin/src/main/java/org/patchbukkit/testplugin/tests/StubTests.java new file mode 100644 index 0000000..09e153a --- /dev/null +++ b/java/patchbukkit-test-plugin/src/main/java/org/patchbukkit/testplugin/tests/StubTests.java @@ -0,0 +1,134 @@ +package org.patchbukkit.testplugin.tests; + +import org.bukkit.Bukkit; +import org.bukkit.Server; +import org.patchbukkit.testplugin.ConformanceTest; +import org.patchbukkit.testplugin.TestCategory; +import org.patchbukkit.testplugin.TestExpectation; +import org.bukkit.plugin.java.JavaPlugin; + +import java.util.UUID; + +public final class StubTests { + + private final JavaPlugin plugin; + + public StubTests(JavaPlugin plugin) { + this.plugin = plugin; + } + + // --- Server stubs that should throw UnsupportedOperationException --- + + @ConformanceTest(name = "Server.getWorlds() throws UnsupportedOperationException", + category = TestCategory.STUBS, expectation = TestExpectation.EXPECT_UNSUPPORTED) + public void testGetWorlds() { + Bukkit.getServer().getWorlds(); + } + + @ConformanceTest(name = "Server.getMaxPlayers() throws UnsupportedOperationException", + category = TestCategory.STUBS, expectation = TestExpectation.EXPECT_UNSUPPORTED) + public void testGetMaxPlayers() { + Bukkit.getServer().getMaxPlayers(); + } + + @ConformanceTest(name = "Server.getPort() throws UnsupportedOperationException", + category = TestCategory.STUBS, expectation = TestExpectation.EXPECT_UNSUPPORTED) + public void testGetPort() { + Bukkit.getServer().getPort(); + } + + @ConformanceTest(name = "Server.getIp() throws UnsupportedOperationException", + category = TestCategory.STUBS, expectation = TestExpectation.EXPECT_UNSUPPORTED) + public void testGetIp() { + Bukkit.getServer().getIp(); + } + + @ConformanceTest(name = "Server.getViewDistance() throws UnsupportedOperationException", + category = TestCategory.STUBS, expectation = TestExpectation.EXPECT_UNSUPPORTED) + public void testGetViewDistance() { + Bukkit.getServer().getViewDistance(); + } + + @ConformanceTest(name = "Server.getSimulationDistance() throws UnsupportedOperationException", + category = TestCategory.STUBS, expectation = TestExpectation.EXPECT_UNSUPPORTED) + public void testGetSimulationDistance() { + Bukkit.getServer().getSimulationDistance(); + } + + @ConformanceTest(name = "Server.getUpdateFolder() throws UnsupportedOperationException", + category = TestCategory.STUBS, expectation = TestExpectation.EXPECT_UNSUPPORTED) + public void testGetUpdateFolder() { + Bukkit.getServer().getUpdateFolder(); + } + + @ConformanceTest(name = "Server.getUpdateFolderFile() throws UnsupportedOperationException", + category = TestCategory.STUBS, expectation = TestExpectation.EXPECT_UNSUPPORTED) + public void testGetUpdateFolderFile() { + Bukkit.getServer().getUpdateFolderFile(); + } + + @ConformanceTest(name = "Server.getConnectionThrottle() throws UnsupportedOperationException", + category = TestCategory.STUBS, expectation = TestExpectation.EXPECT_UNSUPPORTED) + public void testGetConnectionThrottle() { + Bukkit.getServer().getConnectionThrottle(); + } + + @ConformanceTest(name = "Server.broadcastMessage() throws UnsupportedOperationException", + category = TestCategory.STUBS, expectation = TestExpectation.EXPECT_UNSUPPORTED) + @SuppressWarnings("deprecation") + public void testBroadcastMessage() { + Bukkit.getServer().broadcastMessage("test"); + } + + @ConformanceTest(name = "Server.getOfflinePlayer(UUID) throws UnsupportedOperationException", + category = TestCategory.STUBS, expectation = TestExpectation.EXPECT_UNSUPPORTED) + public void testGetOfflinePlayer() { + Bukkit.getServer().getOfflinePlayer(UUID.randomUUID()); + } + + @ConformanceTest(name = "Server.getBanList() throws UnsupportedOperationException", + category = TestCategory.STUBS, expectation = TestExpectation.EXPECT_UNSUPPORTED) + public void testGetBanList() { + Bukkit.getServer().getBanList(org.bukkit.BanList.Type.NAME); + } + + @ConformanceTest(name = "Server.getOperators() throws UnsupportedOperationException", + category = TestCategory.STUBS, expectation = TestExpectation.EXPECT_UNSUPPORTED) + public void testGetOperators() { + Bukkit.getServer().getOperators(); + } + + @ConformanceTest(name = "Server.getWhitelistedPlayers() throws UnsupportedOperationException", + category = TestCategory.STUBS, expectation = TestExpectation.EXPECT_UNSUPPORTED) + public void testGetWhitelistedPlayers() { + Bukkit.getServer().getWhitelistedPlayers(); + } + + @ConformanceTest(name = "Server.reloadWhitelist() throws UnsupportedOperationException", + category = TestCategory.STUBS, expectation = TestExpectation.EXPECT_UNSUPPORTED) + public void testReloadWhitelist() { + Bukkit.getServer().reloadWhitelist(); + } + + @ConformanceTest(name = "Server.shutdown() throws UnsupportedOperationException", + category = TestCategory.STUBS, expectation = TestExpectation.EXPECT_UNSUPPORTED) + public void testShutdown() { + Bukkit.getServer().shutdown(); + } + + @ConformanceTest(name = "Server.getMotd() throws UnsupportedOperationException", + category = TestCategory.STUBS, expectation = TestExpectation.EXPECT_UNSUPPORTED) + public void testGetMotd() { + Bukkit.getServer().getMotd(); + } + + @ConformanceTest(name = "Server.getAllowNether() throws UnsupportedOperationException", + category = TestCategory.STUBS, expectation = TestExpectation.EXPECT_UNSUPPORTED) + public void testGetAllowNether() { + Bukkit.getServer().getAllowNether(); + } + + private static void assertNotNull(Object value, String what) { + if (value == null) throw new AssertionError(what + " returned null"); + } +} diff --git a/java/patchbukkit-test-plugin/src/main/java/org/patchbukkit/testplugin/tests/UnsafeValuesTests.java b/java/patchbukkit-test-plugin/src/main/java/org/patchbukkit/testplugin/tests/UnsafeValuesTests.java new file mode 100644 index 0000000..79d4689 --- /dev/null +++ b/java/patchbukkit-test-plugin/src/main/java/org/patchbukkit/testplugin/tests/UnsafeValuesTests.java @@ -0,0 +1,61 @@ +package org.patchbukkit.testplugin.tests; + +import net.kyori.adventure.text.flattener.ComponentFlattener; +import org.bukkit.Bukkit; +import org.bukkit.UnsafeValues; +import org.patchbukkit.testplugin.ConformanceTest; +import org.patchbukkit.testplugin.TestCategory; +import org.bukkit.plugin.java.JavaPlugin; + +@SuppressWarnings("deprecation") +public final class UnsafeValuesTests { + + private final JavaPlugin plugin; + + public UnsafeValuesTests(JavaPlugin plugin) { + this.plugin = plugin; + } + + @ConformanceTest(name = "UnsafeValues.getDataVersion() returns positive int", category = TestCategory.UNSAFE_VALUES) + public void testGetDataVersion() { + UnsafeValues unsafe = Bukkit.getUnsafe(); + int version = unsafe.getDataVersion(); + assertTrue(version > 0, "getDataVersion() should be > 0, got " + version); + } + + @ConformanceTest(name = "UnsafeValues.isSupportedApiVersion(\"1.21\") returns true", category = TestCategory.UNSAFE_VALUES) + public void testIsSupportedApiVersion121() { + UnsafeValues unsafe = Bukkit.getUnsafe(); + boolean supported = unsafe.isSupportedApiVersion("1.21"); + assertTrue(supported, "isSupportedApiVersion(\"1.21\") should be true"); + } + + @ConformanceTest(name = "UnsafeValues.isSupportedApiVersion(\"1.20\") returns true", category = TestCategory.UNSAFE_VALUES) + public void testIsSupportedApiVersion120() { + UnsafeValues unsafe = Bukkit.getUnsafe(); + boolean supported = unsafe.isSupportedApiVersion("1.20"); + assertTrue(supported, "isSupportedApiVersion(\"1.20\") should be true"); + } + + @ConformanceTest(name = "UnsafeValues.isSupportedApiVersion(\"1.0\") returns false", category = TestCategory.UNSAFE_VALUES) + public void testIsSupportedApiVersionOld() { + UnsafeValues unsafe = Bukkit.getUnsafe(); + boolean supported = unsafe.isSupportedApiVersion("1.0"); + assertTrue(!supported, "isSupportedApiVersion(\"1.0\") should be false"); + } + + @ConformanceTest(name = "UnsafeValues.componentFlattener() returns non-null", category = TestCategory.UNSAFE_VALUES) + public void testComponentFlattener() { + UnsafeValues unsafe = Bukkit.getUnsafe(); + ComponentFlattener flattener = unsafe.componentFlattener(); + assertNotNull(flattener, "UnsafeValues.componentFlattener()"); + } + + private static void assertNotNull(Object value, String what) { + if (value == null) throw new AssertionError(what + " returned null"); + } + + private static void assertTrue(boolean condition, String what) { + if (!condition) throw new AssertionError(what); + } +} diff --git a/java/patchbukkit-test-plugin/src/main/resources/plugin.yml b/java/patchbukkit-test-plugin/src/main/resources/plugin.yml new file mode 100644 index 0000000..a49a8ed --- /dev/null +++ b/java/patchbukkit-test-plugin/src/main/resources/plugin.yml @@ -0,0 +1,9 @@ +name: PatchBukkitTest +version: "1.0.0" +main: org.patchbukkit.testplugin.PatchBukkitTestPlugin +api-version: "1.21" +description: Conformance test plugin for PatchBukkit API surface +commands: + pbtest: + description: Run PatchBukkit conformance tests + usage: /pbtest [category|all] diff --git a/java/settings.gradle.kts b/java/settings.gradle.kts index 4693885..a50f741 100644 --- a/java/settings.gradle.kts +++ b/java/settings.gradle.kts @@ -13,3 +13,4 @@ plugins { rootProject.name = "patchbukkit" include("patchbukkit") include("protoc-gen-ffi") +include("patchbukkit-test-plugin") diff --git a/rust/src/java/plugin/command_manager.rs b/rust/src/java/plugin/command_manager.rs index dc14cd0..f9bab49 100644 --- a/rust/src/java/plugin/command_manager.rs +++ b/rust/src/java/plugin/command_manager.rs @@ -198,14 +198,16 @@ impl CommandManager { // }; let permission = format!("patchbukkit:{cmd_name}"); - context + if let Err(e) = context .register_permission(Permission::new( &permission, &permission, PermissionDefault::Allow, )) .await - .unwrap(); + { + log::warn!("Failed to register permission for command {}: {:?}", cmd_name, e); + } context.register_command(node, permission).await;