Mirror von
https://github.com/PaperMC/Velocity.git
synchronisiert 2024-12-25 15:50:19 +01:00
Add more suggestion tests
Dieser Commit ist enthalten in:
Ursprung
bcb68c8d0f
Commit
ba34e4729b
@ -121,16 +121,15 @@ final class SuggestionsProvider<S> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns whether a literal node with the given name should be considered for
|
* Returns whether a literal node with the given lowercase name should be considered for
|
||||||
* suggestions given the specified input.
|
* suggestions given the specified input.
|
||||||
*
|
*
|
||||||
* @param name the literal name
|
* @param name the lowercase literal name
|
||||||
* @param input the partial input
|
* @param input the partial input
|
||||||
* @return true if the literal should be considered; false otherwise
|
* @return true if the literal should be considered; false otherwise
|
||||||
*/
|
*/
|
||||||
private static boolean shouldConsider(final String name, final String input) {
|
private static boolean shouldConsider(final String name, final String input) {
|
||||||
// TODO (perf) If we expect input to be lowercase, no need to ignore case
|
return name.regionMatches(false, 0, input, 0, input.length());
|
||||||
return name.regionMatches(true, 0, input, 0, input.length());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -143,7 +142,9 @@ final class SuggestionsProvider<S> {
|
|||||||
private CompletableFuture<Suggestions> provideAliasSuggestions(
|
private CompletableFuture<Suggestions> provideAliasSuggestions(
|
||||||
final StringReader reader, final CommandContextBuilder<S> contextSoFar) {
|
final StringReader reader, final CommandContextBuilder<S> contextSoFar) {
|
||||||
final S source = contextSoFar.getSource();
|
final S source = contextSoFar.getSource();
|
||||||
final String input = reader.getRead();
|
// Lowercase the alias here so all comparisons can be case-sensitive (cheaper)
|
||||||
|
// TODO Is this actually faster?
|
||||||
|
final String input = reader.getRead().toLowerCase(Locale.ENGLISH);
|
||||||
|
|
||||||
final Collection<CommandNode<S>> aliases = contextSoFar.getRootNode().getChildren();
|
final Collection<CommandNode<S>> aliases = contextSoFar.getRootNode().getChildren();
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
@ -188,7 +189,13 @@ final class SuggestionsProvider<S> {
|
|||||||
// This is a BrigadierCommand, fallback to regular suggestions
|
// This is a BrigadierCommand, fallback to regular suggestions
|
||||||
reader.setCursor(0);
|
reader.setCursor(0);
|
||||||
final ParseResults<S> parse = this.dispatcher.parse(reader, source);
|
final ParseResults<S> parse = this.dispatcher.parse(reader, source);
|
||||||
return this.dispatcher.getCompletionSuggestions(parse);
|
try {
|
||||||
|
return this.dispatcher.getCompletionSuggestions(parse);
|
||||||
|
} catch (final Throwable e) {
|
||||||
|
// Ugly, ugly swallowing of everything Throwable, because plugins are naughty.
|
||||||
|
LOGGER.error("Command node cannot provide suggestions for " + fullInput, e);
|
||||||
|
return Suggestions.empty();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!argsNode.canUse(source)) {
|
if (!argsNode.canUse(source)) {
|
||||||
@ -243,7 +250,7 @@ final class SuggestionsProvider<S> {
|
|||||||
try {
|
try {
|
||||||
return node.listSuggestions(built, new SuggestionsBuilder(fullInput, start));
|
return node.listSuggestions(built, new SuggestionsBuilder(fullInput, start));
|
||||||
} catch (final Throwable e) {
|
} catch (final Throwable e) {
|
||||||
// Ugly, ugly swallowing of everything Throwable, because plugins are naughty.
|
// Again, plugins are naughty
|
||||||
LOGGER.error("Arguments node cannot provide suggestions", e);
|
LOGGER.error("Arguments node cannot provide suggestions", e);
|
||||||
return Suggestions.empty();
|
return Suggestions.empty();
|
||||||
}
|
}
|
||||||
@ -266,7 +273,7 @@ final class SuggestionsProvider<S> {
|
|||||||
try {
|
try {
|
||||||
return this.dispatcher.getCompletionSuggestions(parse);
|
return this.dispatcher.getCompletionSuggestions(parse);
|
||||||
} catch (final Throwable e) {
|
} catch (final Throwable e) {
|
||||||
// Again, plugins are naughty.
|
// Yet again, plugins are naughty.
|
||||||
LOGGER.error("Hint node cannot provide suggestions", e);
|
LOGGER.error("Hint node cannot provide suggestions", e);
|
||||||
return Suggestions.empty();
|
return Suggestions.empty();
|
||||||
}
|
}
|
||||||
|
@ -25,6 +25,7 @@ import com.mojang.brigadier.ParseResults;
|
|||||||
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
||||||
import com.mojang.brigadier.suggestion.Suggestion;
|
import com.mojang.brigadier.suggestion.Suggestion;
|
||||||
import com.mojang.brigadier.tree.RootCommandNode;
|
import com.mojang.brigadier.tree.RootCommandNode;
|
||||||
|
import com.spotify.futures.CompletableFutures;
|
||||||
import com.velocitypowered.api.command.BrigadierCommand;
|
import com.velocitypowered.api.command.BrigadierCommand;
|
||||||
import com.velocitypowered.api.command.Command;
|
import com.velocitypowered.api.command.Command;
|
||||||
import com.velocitypowered.api.command.CommandManager;
|
import com.velocitypowered.api.command.CommandManager;
|
||||||
@ -150,8 +151,9 @@ public class VelocityCommandManager implements CommandManager {
|
|||||||
Preconditions.checkNotNull(cmdLine, "cmdLine");
|
Preconditions.checkNotNull(cmdLine, "cmdLine");
|
||||||
|
|
||||||
final String normalizedInput = VelocityCommands.normalizeInput(cmdLine, true);
|
final String normalizedInput = VelocityCommands.normalizeInput(cmdLine, true);
|
||||||
final ParseResults<CommandSource> parse = this.parse(normalizedInput, source);
|
|
||||||
try {
|
try {
|
||||||
|
// The parse can fail if the requirement predicates throw
|
||||||
|
final ParseResults<CommandSource> parse = this.parse(normalizedInput, source);
|
||||||
return dispatcher.execute(parse) != BrigadierCommand.FORWARD;
|
return dispatcher.execute(parse) != BrigadierCommand.FORWARD;
|
||||||
} catch (final CommandSyntaxException e) {
|
} catch (final CommandSyntaxException e) {
|
||||||
boolean isSyntaxError = !e.getType().equals(
|
boolean isSyntaxError = !e.getType().equals(
|
||||||
@ -207,8 +209,14 @@ public class VelocityCommandManager implements CommandManager {
|
|||||||
Preconditions.checkNotNull(cmdLine, "cmdLine");
|
Preconditions.checkNotNull(cmdLine, "cmdLine");
|
||||||
|
|
||||||
final String normalizedInput = VelocityCommands.normalizeInput(cmdLine, false);
|
final String normalizedInput = VelocityCommands.normalizeInput(cmdLine, false);
|
||||||
return suggestionsProvider.provideSuggestions(normalizedInput, source)
|
try {
|
||||||
.thenApply(suggestions -> Lists.transform(suggestions.getList(), Suggestion::getText));
|
return suggestionsProvider.provideSuggestions(normalizedInput, source)
|
||||||
|
.thenApply(suggestions -> Lists.transform(suggestions.getList(), Suggestion::getText));
|
||||||
|
} catch (final Throwable e) {
|
||||||
|
// Again, plugins are naughty
|
||||||
|
return CompletableFutures.exceptionallyCompletedFuture(
|
||||||
|
new RuntimeException("Unable to provide suggestions for " + cmdLine + " for " + source, e));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -4,12 +4,16 @@ import static com.mojang.brigadier.arguments.IntegerArgumentType.getInteger;
|
|||||||
import static com.mojang.brigadier.arguments.IntegerArgumentType.integer;
|
import static com.mojang.brigadier.arguments.IntegerArgumentType.integer;
|
||||||
import static com.mojang.brigadier.arguments.StringArgumentType.word;
|
import static com.mojang.brigadier.arguments.StringArgumentType.word;
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertSame;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||||
import static org.junit.jupiter.api.Assertions.fail;
|
import static org.junit.jupiter.api.Assertions.fail;
|
||||||
|
|
||||||
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
|
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
|
||||||
import com.mojang.brigadier.builder.RequiredArgumentBuilder;
|
import com.mojang.brigadier.builder.RequiredArgumentBuilder;
|
||||||
|
import com.spotify.futures.CompletableFutures;
|
||||||
import com.velocitypowered.api.command.BrigadierCommand;
|
import com.velocitypowered.api.command.BrigadierCommand;
|
||||||
import com.velocitypowered.api.command.CommandSource;
|
import com.velocitypowered.api.command.CommandSource;
|
||||||
|
import java.util.concurrent.CompletionException;
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
@ -121,6 +125,41 @@ public class BrigadierCommandTests extends CommandTestSuite {
|
|||||||
assertEquals(1, callCount.get());
|
assertEquals(1, callCount.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testExecuteAsyncCompletesExceptionallyOnCallbackException() {
|
||||||
|
final var expected = new RuntimeException();
|
||||||
|
final var node = LiteralArgumentBuilder
|
||||||
|
.<CommandSource>literal("hello")
|
||||||
|
.executes(context -> {
|
||||||
|
throw expected;
|
||||||
|
})
|
||||||
|
.build();
|
||||||
|
manager.register(new BrigadierCommand(node));
|
||||||
|
|
||||||
|
final Exception wrapper = assertThrows(CompletionException.class, () ->
|
||||||
|
manager.executeAsync(source, "hello").join());
|
||||||
|
|
||||||
|
assertSame(expected, wrapper.getCause().getCause());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testExecuteAsyncCompletesExceptionallyOnRequirementException() {
|
||||||
|
final var expected = new RuntimeException();
|
||||||
|
final var node = LiteralArgumentBuilder
|
||||||
|
.<CommandSource>literal("hello")
|
||||||
|
.requires(source1 -> {
|
||||||
|
throw expected;
|
||||||
|
})
|
||||||
|
.executes(context -> fail()) // needed for dispatcher to consider the node
|
||||||
|
.build();
|
||||||
|
manager.register(new BrigadierCommand(node));
|
||||||
|
|
||||||
|
final Exception wrapper = assertThrows(CompletionException.class, () ->
|
||||||
|
manager.executeAsync(source, "hello").join());
|
||||||
|
|
||||||
|
assertSame(expected, wrapper.getCause().getCause());
|
||||||
|
}
|
||||||
|
|
||||||
// Suggestions
|
// Suggestions
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -182,4 +221,50 @@ public class BrigadierCommandTests extends CommandTestSuite {
|
|||||||
assertSuggestions("parent child");
|
assertSuggestions("parent child");
|
||||||
assertEquals(1, callCount.get());
|
assertEquals(1, callCount.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testDoesNotSuggestIfCustomSuggestionProviderFutureCompletesExceptionally() {
|
||||||
|
final var node = LiteralArgumentBuilder
|
||||||
|
.<CommandSource>literal("parent")
|
||||||
|
.then(RequiredArgumentBuilder
|
||||||
|
.<CommandSource, String>argument("child", word())
|
||||||
|
.suggests((context, builder) ->
|
||||||
|
CompletableFutures.exceptionallyCompletedFuture(new RuntimeException())))
|
||||||
|
.build();
|
||||||
|
manager.register(new BrigadierCommand(node));
|
||||||
|
|
||||||
|
assertSuggestions("parent ");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testDoesNotSuggestIfCustomSuggestionProviderThrows() {
|
||||||
|
final var node = LiteralArgumentBuilder
|
||||||
|
.<CommandSource>literal("parent")
|
||||||
|
.then(RequiredArgumentBuilder
|
||||||
|
.<CommandSource, String>argument("child", word())
|
||||||
|
.suggests((context, builder) -> {
|
||||||
|
throw new RuntimeException();
|
||||||
|
}))
|
||||||
|
.build();
|
||||||
|
manager.register(new BrigadierCommand(node));
|
||||||
|
|
||||||
|
assertSuggestions("parent ");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testSuggestCompletesExceptionallyIfRequirementPredicateThrows() {
|
||||||
|
final var node = LiteralArgumentBuilder
|
||||||
|
.<CommandSource>literal("parent")
|
||||||
|
.requires(source1 -> {
|
||||||
|
throw new RuntimeException();
|
||||||
|
})
|
||||||
|
.then(RequiredArgumentBuilder
|
||||||
|
.<CommandSource, String>argument("child", word())
|
||||||
|
.suggests((context, builder) -> fail()))
|
||||||
|
.build();
|
||||||
|
manager.register(new BrigadierCommand(node));
|
||||||
|
|
||||||
|
assertThrows(CompletionException.class, () ->
|
||||||
|
manager.offerSuggestions(source, "parent ").join());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -129,7 +129,6 @@ public class CommandManagerTests {
|
|||||||
|
|
||||||
// Un-registration
|
// Un-registration
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testUnregisterUnregisteredAliasIsIgnored() {
|
void testUnregisterUnregisteredAliasIsIgnored() {
|
||||||
manager.unregister("hello");
|
manager.unregister("hello");
|
||||||
|
@ -1,12 +1,18 @@
|
|||||||
package com.velocitypowered.proxy.command;
|
package com.velocitypowered.proxy.command;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||||
import static org.junit.jupiter.api.Assertions.fail;
|
import static org.junit.jupiter.api.Assertions.fail;
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
|
import com.spotify.futures.CompletableFutures;
|
||||||
import com.velocitypowered.api.command.RawCommand;
|
import com.velocitypowered.api.command.RawCommand;
|
||||||
|
import com.velocitypowered.api.command.SimpleCommand;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
import java.util.concurrent.CompletionException;
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
@ -96,6 +102,29 @@ public class RawCommandTests extends CommandTestSuite {
|
|||||||
|
|
||||||
// Suggestions
|
// Suggestions
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testDoesNotSuggestAliasIfImpermissible() {
|
||||||
|
final var meta = manager.metaBuilder("hello").build();
|
||||||
|
manager.register(meta, new RawCommand() {
|
||||||
|
@Override
|
||||||
|
public void execute(final Invocation invocation) {
|
||||||
|
fail();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasPermission(final Invocation invocation) {
|
||||||
|
assertEquals("hello", invocation.alias());
|
||||||
|
assertEquals("", invocation.arguments());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<String> suggest(final Invocation invocation) {
|
||||||
|
return fail();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testSuggestsArgumentsAfterAlias() {
|
void testSuggestsArgumentsAfterAlias() {
|
||||||
final var meta = manager.metaBuilder("hello").build();
|
final var meta = manager.metaBuilder("hello").build();
|
||||||
@ -116,6 +145,25 @@ public class RawCommandTests extends CommandTestSuite {
|
|||||||
assertSuggestions("hello ", "people", "world"); // in alphabetical order
|
assertSuggestions("hello ", "people", "world"); // in alphabetical order
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testSuggestsArgumentsAfterAliasIgnoresAliasCase() {
|
||||||
|
final var meta = manager.metaBuilder("hello").build();
|
||||||
|
manager.register(meta, new RawCommand() {
|
||||||
|
@Override
|
||||||
|
public void execute(final Invocation invocation) {
|
||||||
|
fail();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<String> suggest(final Invocation invocation) {
|
||||||
|
assertEquals("hello", invocation.alias());
|
||||||
|
return ImmutableList.of("world");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
assertSuggestions("Hello ", "world");
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testSuggestsArgumentsAfterPartialArguments() {
|
void testSuggestsArgumentsAfterPartialArguments() {
|
||||||
final var meta = manager.metaBuilder("numbers").build();
|
final var meta = manager.metaBuilder("numbers").build();
|
||||||
@ -192,4 +240,65 @@ public class RawCommandTests extends CommandTestSuite {
|
|||||||
assertSuggestions("foo bar baz ");
|
assertSuggestions("foo bar baz ");
|
||||||
assertEquals(1, callCount.get());
|
assertEquals(1, callCount.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testDoesNotSuggestIfFutureCompletesExceptionally() {
|
||||||
|
final var meta = manager.metaBuilder("hello").build();
|
||||||
|
manager.register(meta, new RawCommand() {
|
||||||
|
@Override
|
||||||
|
public void execute(final Invocation invocation) {
|
||||||
|
fail();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CompletableFuture<List<String>> suggestAsync(final Invocation invocation) {
|
||||||
|
return CompletableFutures.exceptionallyCompletedFuture(new RuntimeException());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
assertSuggestions("hello ");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testDoesNotSuggestIfSuggestAsyncThrows() {
|
||||||
|
final var meta = manager.metaBuilder("hello").build();
|
||||||
|
manager.register(meta, new RawCommand() {
|
||||||
|
@Override
|
||||||
|
public void execute(final Invocation invocation) {
|
||||||
|
fail();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CompletableFuture<List<String>> suggestAsync(final Invocation invocation) {
|
||||||
|
throw new RuntimeException();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Also logs an error to the console, but testing this is quite involved
|
||||||
|
assertSuggestions("hello ");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testSuggestCompletesExceptionallyIfHasPermissionThrows() {
|
||||||
|
final var meta = manager.metaBuilder("hello").build();
|
||||||
|
manager.register(meta, new RawCommand() {
|
||||||
|
@Override
|
||||||
|
public void execute(final Invocation invocation) {
|
||||||
|
fail();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasPermission(final Invocation invocation) {
|
||||||
|
throw new RuntimeException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CompletableFuture<List<String>> suggestAsync(final Invocation invocation) {
|
||||||
|
return fail();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
assertThrows(CompletionException.class, () ->
|
||||||
|
manager.offerSuggestions(source, "hello ").join());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,12 +2,16 @@ package com.velocitypowered.proxy.command;
|
|||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
|
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||||
import static org.junit.jupiter.api.Assertions.fail;
|
import static org.junit.jupiter.api.Assertions.fail;
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
|
import com.spotify.futures.CompletableFutures;
|
||||||
import com.velocitypowered.api.command.SimpleCommand;
|
import com.velocitypowered.api.command.SimpleCommand;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
import java.util.concurrent.CompletionException;
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
@ -97,6 +101,29 @@ public class SimpleCommandTests extends CommandTestSuite {
|
|||||||
|
|
||||||
// Suggestions
|
// Suggestions
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testDoesNotSuggestAliasIfImpermissible() {
|
||||||
|
final var meta = manager.metaBuilder("hello").build();
|
||||||
|
manager.register(meta, new SimpleCommand() {
|
||||||
|
@Override
|
||||||
|
public void execute(final Invocation invocation) {
|
||||||
|
fail();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasPermission(final Invocation invocation) {
|
||||||
|
assertEquals("hello", invocation.alias());
|
||||||
|
assertArrayEquals(new String[0], invocation.arguments());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<String> suggest(final Invocation invocation) {
|
||||||
|
return fail();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testSuggestsArgumentsAfterAlias() {
|
void testSuggestsArgumentsAfterAlias() {
|
||||||
final var meta = manager.metaBuilder("hello").build();
|
final var meta = manager.metaBuilder("hello").build();
|
||||||
@ -117,6 +144,25 @@ public class SimpleCommandTests extends CommandTestSuite {
|
|||||||
assertSuggestions("hello ", "people", "world"); // in alphabetical order
|
assertSuggestions("hello ", "people", "world"); // in alphabetical order
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testSuggestsArgumentsAfterAliasIgnoresAliasCase() {
|
||||||
|
final var meta = manager.metaBuilder("hello").build();
|
||||||
|
manager.register(meta, new SimpleCommand() {
|
||||||
|
@Override
|
||||||
|
public void execute(final Invocation invocation) {
|
||||||
|
fail();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<String> suggest(final Invocation invocation) {
|
||||||
|
assertEquals("hello", invocation.alias());
|
||||||
|
return ImmutableList.of("world");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
assertSuggestions("Hello ", "world");
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testSuggestsArgumentsAfterPartialArguments() {
|
void testSuggestsArgumentsAfterPartialArguments() {
|
||||||
final var meta = manager.metaBuilder("numbers").build();
|
final var meta = manager.metaBuilder("numbers").build();
|
||||||
@ -193,4 +239,65 @@ public class SimpleCommandTests extends CommandTestSuite {
|
|||||||
assertSuggestions("foo bar baz ");
|
assertSuggestions("foo bar baz ");
|
||||||
assertEquals(1, callCount.get());
|
assertEquals(1, callCount.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testDoesNotSuggestIfFutureCompletesExceptionally() {
|
||||||
|
final var meta = manager.metaBuilder("hello").build();
|
||||||
|
manager.register(meta, new SimpleCommand() {
|
||||||
|
@Override
|
||||||
|
public void execute(final Invocation invocation) {
|
||||||
|
fail();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CompletableFuture<List<String>> suggestAsync(final Invocation invocation) {
|
||||||
|
return CompletableFutures.exceptionallyCompletedFuture(new RuntimeException());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
assertSuggestions("hello ");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testDoesNotSuggestIfSuggestAsyncThrows() {
|
||||||
|
final var meta = manager.metaBuilder("hello").build();
|
||||||
|
manager.register(meta, new SimpleCommand() {
|
||||||
|
@Override
|
||||||
|
public void execute(final Invocation invocation) {
|
||||||
|
fail();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CompletableFuture<List<String>> suggestAsync(final Invocation invocation) {
|
||||||
|
throw new RuntimeException();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Also logs an error to the console, but testing this is quite involved
|
||||||
|
assertSuggestions("hello ");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testSuggestCompletesExceptionallyIfHasPermissionThrows() {
|
||||||
|
final var meta = manager.metaBuilder("hello").build();
|
||||||
|
manager.register(meta, new SimpleCommand() {
|
||||||
|
@Override
|
||||||
|
public void execute(final Invocation invocation) {
|
||||||
|
fail();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasPermission(final Invocation invocation) {
|
||||||
|
throw new RuntimeException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CompletableFuture<List<String>> suggestAsync(final Invocation invocation) {
|
||||||
|
return fail();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
assertThrows(CompletionException.class, () ->
|
||||||
|
manager.offerSuggestions(source, "hello ").join());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -18,18 +18,22 @@
|
|||||||
package com.velocitypowered.proxy.command;
|
package com.velocitypowered.proxy.command;
|
||||||
|
|
||||||
import static com.mojang.brigadier.arguments.StringArgumentType.word;
|
import static com.mojang.brigadier.arguments.StringArgumentType.word;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||||
import static org.junit.jupiter.api.Assertions.fail;
|
import static org.junit.jupiter.api.Assertions.fail;
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
|
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
|
||||||
import com.mojang.brigadier.builder.RequiredArgumentBuilder;
|
import com.mojang.brigadier.builder.RequiredArgumentBuilder;
|
||||||
import com.spotify.futures.CompletableFutures;
|
import com.spotify.futures.CompletableFutures;
|
||||||
|
import com.velocitypowered.api.command.BrigadierCommand;
|
||||||
import com.velocitypowered.api.command.Command;
|
import com.velocitypowered.api.command.Command;
|
||||||
import com.velocitypowered.api.command.CommandSource;
|
import com.velocitypowered.api.command.CommandSource;
|
||||||
import com.velocitypowered.api.command.RawCommand;
|
import com.velocitypowered.api.command.RawCommand;
|
||||||
import com.velocitypowered.api.command.SimpleCommand;
|
import com.velocitypowered.api.command.SimpleCommand;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
import java.util.concurrent.CompletionException;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -39,13 +43,12 @@ import org.junit.jupiter.api.Test;
|
|||||||
public class SuggestionsProviderTests extends CommandTestSuite {
|
public class SuggestionsProviderTests extends CommandTestSuite {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testSuggestsAliasForEmptyInput() {
|
void testSuggestsAliasesForEmptyInput() {
|
||||||
final var meta = manager.metaBuilder("foo")
|
manager.register(manager.metaBuilder("foo").build(), NoSuggestionsCommand.INSTANCE);
|
||||||
.aliases("bar", "baz")
|
manager.register(manager.metaBuilder("bar").build(), NoSuggestionsCommand.INSTANCE);
|
||||||
.build();
|
manager.register(manager.metaBuilder("baz").build(), NoSuggestionsCommand.INSTANCE);
|
||||||
manager.register(meta, NoSuggestionsCommand.INSTANCE);
|
|
||||||
|
|
||||||
assertSuggestions("", "bar", "baz", "foo");
|
assertSuggestions("", "bar", "baz", "foo"); // in alphabetical order
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -58,14 +61,20 @@ public class SuggestionsProviderTests extends CommandTestSuite {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testSuggestsAliasesForPartialAlias() {
|
void testSuggestsAliasesForPartialAlias() {
|
||||||
final var meta = manager.metaBuilder("foo")
|
manager.register(manager.metaBuilder("hello").build(), NoSuggestionsCommand.INSTANCE);
|
||||||
.aliases("bar", "baz")
|
manager.register(manager.metaBuilder("hey").build(), NoSuggestionsCommand.INSTANCE);
|
||||||
.build();
|
|
||||||
|
assertSuggestions("hell", "hello");
|
||||||
|
assertSuggestions("He", "hello", "hey");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testDoesNotSuggestForFullAlias() {
|
||||||
|
final var meta = manager.metaBuilder("hello").build();
|
||||||
manager.register(meta, NoSuggestionsCommand.INSTANCE);
|
manager.register(meta, NoSuggestionsCommand.INSTANCE);
|
||||||
|
|
||||||
assertSuggestions("ba", "bar", "baz");
|
assertSuggestions("hello");
|
||||||
assertSuggestions("fo", "foo");
|
assertSuggestions("Hello");
|
||||||
assertSuggestions("bar");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -78,9 +87,9 @@ public class SuggestionsProviderTests extends CommandTestSuite {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testDoesNotSuggestArgumentsForPartialAlias() {
|
void testDoesNotSuggestArgumentsForIncorrectAlias() {
|
||||||
final var meta = manager.metaBuilder("hello").build();
|
final var meta = manager.metaBuilder("hello").build();
|
||||||
manager.register(meta, new SimpleCommand() {
|
manager.register(meta, new RawCommand() {
|
||||||
@Override
|
@Override
|
||||||
public void execute(final Invocation invocation) {
|
public void execute(final Invocation invocation) {
|
||||||
fail();
|
fail();
|
||||||
@ -95,10 +104,26 @@ public class SuggestionsProviderTests extends CommandTestSuite {
|
|||||||
assertSuggestions("hell ");
|
assertSuggestions("hell ");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Secondary aliases
|
||||||
|
// The following tests check for inconsistencies between the primary alias node and
|
||||||
|
// a secondary alias literal.
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testSuggestsArgumentsIgnoresAliasCase() {
|
void testSuggestsAllAliases() {
|
||||||
final var meta = manager.metaBuilder("hello").build();
|
final var meta = manager.metaBuilder("foo")
|
||||||
manager.register(meta, new SimpleCommand() {
|
.aliases("bar", "baz")
|
||||||
|
.build();
|
||||||
|
manager.register(meta, NoSuggestionsCommand.INSTANCE);
|
||||||
|
|
||||||
|
assertSuggestions("", "bar", "baz", "foo");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testSuggestsArgumentsViaAlias() {
|
||||||
|
final var meta = manager.metaBuilder("hello")
|
||||||
|
.aliases("hi")
|
||||||
|
.build();
|
||||||
|
manager.register(meta, new RawCommand() {
|
||||||
@Override
|
@Override
|
||||||
public void execute(final Invocation invocation) {
|
public void execute(final Invocation invocation) {
|
||||||
fail();
|
fail();
|
||||||
@ -110,9 +135,11 @@ public class SuggestionsProviderTests extends CommandTestSuite {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
assertSuggestions("Hello ", "world");
|
assertSuggestions("hi ", "world");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Hinting
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testSuggestsHintLiteral() {
|
void testSuggestsHintLiteral() {
|
||||||
final var hint = LiteralArgumentBuilder
|
final var hint = LiteralArgumentBuilder
|
||||||
@ -169,49 +196,26 @@ public class SuggestionsProviderTests extends CommandTestSuite {
|
|||||||
|
|
||||||
assertSuggestions("foo ", "bar", "baz", "qux");
|
assertSuggestions("foo ", "bar", "baz", "qux");
|
||||||
assertSuggestions("foo bar", "baz", "qux");
|
assertSuggestions("foo bar", "baz", "qux");
|
||||||
}
|
assertSuggestions("foo baz", "bar", "qux");
|
||||||
|
|
||||||
// Exception handling
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void testSkipsSuggestionWhenNodeSuggestionProviderFutureCompletesExceptionally() {
|
|
||||||
final var meta = manager.metaBuilder("hello").build();
|
|
||||||
manager.register(meta, new RawCommand() {
|
|
||||||
@Override
|
|
||||||
public void execute(final Invocation invocation) {
|
|
||||||
fail();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public CompletableFuture<List<String>> suggestAsync(final Invocation invocation) {
|
|
||||||
return CompletableFutures.exceptionallyCompletedFuture(new RuntimeException());
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
assertSuggestions("hello ");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testSkipsSuggestionWhenNodeSuggestionProviderThrows() {
|
// This doesn't make much sense, but emulates Brigadier behavior
|
||||||
final var meta = manager.metaBuilder("hello").build();
|
void testSuggestsImpermissibleHint() {
|
||||||
manager.register(meta, new RawCommand() {
|
final var hint = LiteralArgumentBuilder
|
||||||
@Override
|
.<CommandSource>literal("hint")
|
||||||
public void execute(final Invocation invocation) {
|
.requires(source1 -> false)
|
||||||
fail();
|
.build();
|
||||||
}
|
final var meta = manager.metaBuilder("hello")
|
||||||
|
.hint(hint)
|
||||||
|
.build();
|
||||||
|
manager.register(meta, NoSuggestionsCommand.INSTANCE);
|
||||||
|
|
||||||
@Override
|
assertSuggestions("hello ", "hint");
|
||||||
public CompletableFuture<List<String>> suggestAsync(final Invocation invocation) {
|
|
||||||
throw new RuntimeException();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Also logs an error to the console, but testing this is quite involved
|
|
||||||
assertSuggestions("hello ");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testSkipsSuggestionWhenHintSuggestionProviderFutureCompletesExceptionally() {
|
void testDoesNotSuggestHintIfHintSuggestionProviderFutureCompletesExceptionally() {
|
||||||
final var hint = RequiredArgumentBuilder
|
final var hint = RequiredArgumentBuilder
|
||||||
.<CommandSource, String>argument("hint", word())
|
.<CommandSource, String>argument("hint", word())
|
||||||
.suggests((context, builder) ->
|
.suggests((context, builder) ->
|
||||||
@ -226,7 +230,7 @@ public class SuggestionsProviderTests extends CommandTestSuite {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testSkipsSuggestionWhenHintSuggestionProviderThrows() {
|
void testDoesNotSuggestHintIfCustomSuggestionProviderThrows() {
|
||||||
final var hint = RequiredArgumentBuilder
|
final var hint = RequiredArgumentBuilder
|
||||||
.<CommandSource, String>argument("hint", word())
|
.<CommandSource, String>argument("hint", word())
|
||||||
.suggests((context, builder) -> {
|
.suggests((context, builder) -> {
|
||||||
@ -238,10 +242,29 @@ public class SuggestionsProviderTests extends CommandTestSuite {
|
|||||||
.build();
|
.build();
|
||||||
manager.register(meta, NoSuggestionsCommand.INSTANCE);
|
manager.register(meta, NoSuggestionsCommand.INSTANCE);
|
||||||
|
|
||||||
// Also logs an error to the console, but testing this is quite involved
|
|
||||||
assertSuggestions("hello ");
|
assertSuggestions("hello ");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testSuggestCompletesExceptionallyIfHintRequirementPredicateThrows() {
|
||||||
|
final var hint = RequiredArgumentBuilder
|
||||||
|
.<CommandSource, String>argument("hint", word())
|
||||||
|
.requires(source1 -> {
|
||||||
|
throw new RuntimeException();
|
||||||
|
})
|
||||||
|
.suggests((context, builder) -> fail())
|
||||||
|
.build();
|
||||||
|
final var meta = manager.metaBuilder("hello")
|
||||||
|
.hint(hint)
|
||||||
|
.build();
|
||||||
|
manager.register(meta, NoSuggestionsCommand.INSTANCE);
|
||||||
|
|
||||||
|
assertThrows(CompletionException.class, () ->
|
||||||
|
manager.offerSuggestions(source, "hello ").join());
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testSuggestionMergingIgnoresExceptionallyCompletedSuggestionFutures() {
|
void testSuggestionMergingIgnoresExceptionallyCompletedSuggestionFutures() {
|
||||||
final var hint = RequiredArgumentBuilder
|
final var hint = RequiredArgumentBuilder
|
||||||
@ -265,7 +288,7 @@ public class SuggestionsProviderTests extends CommandTestSuite {
|
|||||||
});
|
});
|
||||||
|
|
||||||
assertSuggestions("hello ", "world");
|
assertSuggestions("hello ", "world");
|
||||||
}
|
}*/
|
||||||
|
|
||||||
static final class NoSuggestionsCommand implements RawCommand {
|
static final class NoSuggestionsCommand implements RawCommand {
|
||||||
|
|
||||||
|
Laden…
In neuem Issue referenzieren
Einen Benutzer sperren