13
0
geforkt von Mirrors/Velocity

Add more suggestion tests

Dieser Commit ist enthalten in:
Hugo Manrique 2021-06-09 22:22:15 +02:00
Ursprung bcb68c8d0f
Commit ba34e4729b
Es konnte kein GPG-Schlüssel zu dieser Signatur gefunden werden
GPG-Schlüssel-ID: A60730A4A4ACE782
7 geänderte Dateien mit 407 neuen und 69 gelöschten Zeilen

Datei anzeigen

@ -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();
} }

Datei anzeigen

@ -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));
}
} }
/** /**

Datei anzeigen

@ -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());
}
} }

Datei anzeigen

@ -129,7 +129,6 @@ public class CommandManagerTests {
// Un-registration // Un-registration
@Test @Test
void testUnregisterUnregisteredAliasIsIgnored() { void testUnregisterUnregisteredAliasIsIgnored() {
manager.unregister("hello"); manager.unregister("hello");

Datei anzeigen

@ -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());
}
} }

Datei anzeigen

@ -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());
}
} }

Datei anzeigen

@ -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 {