chore(core): improve (some) tests

This commit is contained in:
Citymonstret 2022-05-27 14:06:05 +02:00 committed by Jason
parent db17fb5d10
commit ebb25e9d41
3 changed files with 643 additions and 371 deletions

View file

@ -0,0 +1,178 @@
//
// MIT License
//
// Copyright (c) 2021 Alexander Söderberg & Contributors
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
//
package cloud.commandframework;
import cloud.commandframework.arguments.CommandArgument;
import cloud.commandframework.arguments.standard.IntegerArgument;
import cloud.commandframework.context.CommandContext;
import cloud.commandframework.execution.CommandExecutionCoordinator;
import cloud.commandframework.execution.CommandExecutionHandler;
import cloud.commandframework.internal.CommandRegistrationHandler;
import cloud.commandframework.meta.CommandMeta;
import cloud.commandframework.meta.SimpleCommandMeta;
import io.leangen.geantyref.TypeToken;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.ArgumentCaptor;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
/**
* {@link CommandManager} integration tests.
*/
@SuppressWarnings("unchecked")
class CommandManagerTest {
private CommandManager<TestCommandSender> commandManager;
@BeforeEach
void setup() {
this.commandManager = new CommandManager<TestCommandSender>(
CommandExecutionCoordinator.simpleCoordinator(),
CommandRegistrationHandler.nullCommandRegistrationHandler()
) {
@Override
public boolean hasPermission(
final @NonNull TestCommandSender sender,
final @NonNull String permission
) {
return true;
}
@Override
public @NonNull CommandMeta createDefaultCommandMeta() {
return SimpleCommandMeta.empty();
}
};
}
@Test
void testMultiLiteralRouting() {
// Arrange
final CommandExecutionHandler<TestCommandSender> handlerA = mock(CommandExecutionHandler.class);
when(handlerA.executeFuture(any())).thenReturn(CompletableFuture.completedFuture(null));
final CommandExecutionHandler<TestCommandSender> handlerB = mock(CommandExecutionHandler.class);
when(handlerB.executeFuture(any())).thenReturn(CompletableFuture.completedFuture(null));
final CommandExecutionHandler<TestCommandSender> handlerC = mock(CommandExecutionHandler.class);
when(handlerC.executeFuture(any())).thenReturn(CompletableFuture.completedFuture(null));
final Command<TestCommandSender> commandA = this.commandManager.commandBuilder("test")
.literal("a")
.handler(handlerA)
.build();
final Command<TestCommandSender> commandB = this.commandManager.commandBuilder("test")
.literal("b")
.handler(handlerB)
.build();
final Command<TestCommandSender> commandC = this.commandManager.commandBuilder("test")
.literal("c")
.argument(IntegerArgument.optional("opt"))
.handler(handlerC)
.build();
this.commandManager.command(commandA);
this.commandManager.command(commandB);
this.commandManager.command(commandC);
// Act
this.commandManager.executeCommand(new TestCommandSender(), "test a").join();
this.commandManager.executeCommand(new TestCommandSender(), "test b").join();
this.commandManager.executeCommand(new TestCommandSender(), "test c").join();
this.commandManager.executeCommand(new TestCommandSender(), "test c 123").join();
// Assert
ArgumentCaptor<CommandContext<TestCommandSender>> contextArgumentCaptor = ArgumentCaptor.forClass(
CommandContext.class
);
verify(handlerA).executeFuture(contextArgumentCaptor.capture());
final CommandContext<TestCommandSender> contextA = contextArgumentCaptor.getValue();
assertThat(contextA.getRawInputJoined()).isEqualTo("test a");
verify(handlerB).executeFuture(contextArgumentCaptor.capture());
final CommandContext<TestCommandSender> contextB = contextArgumentCaptor.getValue();
assertThat(contextB.getRawInputJoined()).isEqualTo("test b");
// Reset captor to reset the list of values.
contextArgumentCaptor = ArgumentCaptor.forClass(
CommandContext.class
);
verify(handlerC, times(2)).executeFuture(contextArgumentCaptor.capture());
final CommandContext<TestCommandSender> contextC1 = contextArgumentCaptor.getAllValues().get(0);
assertThat(contextC1.getRawInputJoined()).isEqualTo("test c");
final CommandContext<TestCommandSender> contextC2 = contextArgumentCaptor.getAllValues().get(1);
assertThat(contextC2.getRawInputJoined()).isEqualTo("test c 123");
}
@Test
void testCommandBuilder() {
// Create and register a command
Command<TestCommandSender> command = this.commandManager.commandBuilder("component")
.literal("literal", "literalalias")
.literal("detail", ArgumentDescription.of("detaildescription"))
.argument(
CommandArgument.ofType(int.class, "argument"),
ArgumentDescription.of("argumentdescription")
)
.build();
this.commandManager.command(command);
// Verify all the details we have configured are present
List<CommandArgument<TestCommandSender, ?>> arguments = command.getArguments();
List<CommandComponent<TestCommandSender>> components = command.getComponents();
assertThat(arguments.size()).isEqualTo(components.size());
assertThat(components.size()).isEqualTo(4);;
// Arguments should exactly match the component getArgument() result, in same order
for (int i = 0; i < components.size(); i++) {
assertThat(components.get(i).getArgument()).isEqualTo(arguments.get(i));
}
// Argument configuration, we know component has the same argument so no need to test those
// TODO: Aliases
assertThat(arguments.get(0).getName()).isEqualTo("component");;
assertThat(arguments.get(1).getName()).isEqualTo("literal");;
assertThat(arguments.get(2).getName()).isEqualTo("detail");;
assertThat(arguments.get(3).getName()).isEqualTo("argument");;
// Check argument is indeed a command argument
assertThat(TypeToken.get(int.class)).isEqualTo(arguments.get(3).getValueType());
// Check description is set for all components, is empty when not specified
assertThat(components.get(0).getArgumentDescription().getDescription()).isEmpty();
assertThat(components.get(1).getArgumentDescription().getDescription()).isEmpty();
assertThat(components.get(2).getArgumentDescription().getDescription()).isEqualTo("detaildescription");;
assertThat(components.get(3).getArgumentDescription().getDescription()).isEqualTo("argumentdescription");;
}
}

View file

@ -23,44 +23,62 @@
//
package cloud.commandframework;
import cloud.commandframework.execution.CommandExecutionHandler;
import cloud.commandframework.execution.postprocessor.CommandPostprocessingContext;
import cloud.commandframework.execution.postprocessor.CommandPostprocessor;
import cloud.commandframework.meta.SimpleCommandMeta;
import cloud.commandframework.services.types.ConsumerService;
import java.util.concurrent.CompletableFuture;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import static cloud.commandframework.util.TestUtils.createManager;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.notNull;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
public class CommandPostProcessorTest {
@SuppressWarnings("unchecked")
class CommandPostProcessorTest {
private static final boolean[] state = new boolean[]{false};
private static CommandManager<TestCommandSender> manager;
private CommandManager<TestCommandSender> commandManager;
@BeforeAll
static void newTree() {
manager = createManager();
manager.command(manager.commandBuilder("test", SimpleCommandMeta.empty())
.handler(c -> state[0] = true)
.build());
manager.registerCommandPostProcessor(new SamplePostprocessor());
@BeforeEach
void setup() {
this.commandManager = createManager();
}
@Test
void testPreprocessing() {
manager.executeCommand(new TestCommandSender(), "test").join();
Assertions.assertFalse(state[0]);
void testPostProcessing() {
// Arrange
final CommandPostprocessor<TestCommandSender> postprocessor = spy(new SamplePostprocessor());
this.commandManager.registerCommandPostProcessor(postprocessor);
final CommandExecutionHandler<TestCommandSender> executionHandler = mock(CommandExecutionHandler.class);
when(executionHandler.executeFuture(any())).thenReturn(CompletableFuture.completedFuture(null));
this.commandManager.command(
this.commandManager.commandBuilder("test")
.handler(executionHandler)
.build()
);
// Act
this.commandManager.executeCommand(new TestCommandSender(), "test").join();
// Assert
verify(executionHandler, never()).executeFuture(notNull());
verify(postprocessor).accept(notNull());
}
static final class SamplePostprocessor implements CommandPostprocessor<TestCommandSender> {
static class SamplePostprocessor implements CommandPostprocessor<TestCommandSender> {
@Override
public void accept(final @NonNull CommandPostprocessingContext<TestCommandSender> context) {
ConsumerService.interrupt();
}
}
}

View file

@ -24,9 +24,7 @@
package cloud.commandframework;
import cloud.commandframework.arguments.CommandArgument;
import cloud.commandframework.arguments.compound.ArgumentPair;
import cloud.commandframework.arguments.flags.CommandFlag;
import cloud.commandframework.arguments.preprocessor.RegexPreprocessor;
import cloud.commandframework.arguments.standard.EnumArgument;
import cloud.commandframework.arguments.standard.FloatArgument;
import cloud.commandframework.arguments.standard.IntegerArgument;
@ -34,6 +32,8 @@ import cloud.commandframework.arguments.standard.StringArgument;
import cloud.commandframework.context.CommandContext;
import cloud.commandframework.exceptions.AmbiguousNodeException;
import cloud.commandframework.exceptions.NoPermissionException;
import cloud.commandframework.execution.CommandExecutionHandler;
import cloud.commandframework.keys.SimpleCloudKey;
import cloud.commandframework.meta.SimpleCommandMeta;
import cloud.commandframework.types.tuples.Pair;
import io.leangen.geantyref.TypeToken;
@ -41,344 +41,412 @@ import java.util.Arrays;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionException;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeAll;
import java.util.concurrent.ThreadLocalRandom;
import java.util.stream.Stream;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.ArgumentCaptor;
import static cloud.commandframework.util.TestUtils.createManager;
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth8.assertThat;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.notNull;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
/**
* {@link CommandTree} integration tests.
*/
@SuppressWarnings("unchecked")
class CommandTreeTest {
private static final int EXPECTED_INPUT_NUMBER = 15;
private static CommandManager<TestCommandSender> manager;
private CommandManager<TestCommandSender> commandManager;
@BeforeAll
static void newTree() {
manager = createManager();
@BeforeEach
void setup() {
this.commandManager = createManager();
}
/* Build general test commands */
manager.command(manager.commandBuilder("test", SimpleCommandMeta.empty())
.literal("one").build())
.command(manager.commandBuilder("test", SimpleCommandMeta.empty())
.literal("two").permission("no").build())
.command(manager.commandBuilder("test", Collections.singleton("other"),
SimpleCommandMeta.empty()
)
.literal("opt", "öpt")
.argument(IntegerArgument
.optional("num", EXPECTED_INPUT_NUMBER))
.build())
.command(manager.commandBuilder("req").senderType(SpecificCommandSender.class).build());
/* Build command to test command proxying */
final Command<TestCommandSender> toProxy = manager.commandBuilder("test")
.literal("unproxied")
.argument(StringArgument.of("string"))
.argument(IntegerArgument.of("int"))
.literal("anotherliteral")
.handler(c -> {
})
.build();
manager.command(toProxy);
manager.command(manager.commandBuilder("proxy").proxies(toProxy).build());
/* Build command for testing intermediary and final executors */
manager.command(manager.commandBuilder("command")
.permission("command.inner")
.literal("inner")
.handler(c -> System.out.println("Using inner command")));
manager.command(manager.commandBuilder("command")
.permission("command.outer")
.handler(c -> System.out.println("Using outer command")));
/* Build command for testing compound types */
manager.command(manager.commandBuilder("pos")
.argument(ArgumentPair.of(manager, "pos", Pair.of("x", "y"),
Pair.of(Integer.class, Integer.class)
)
.simple())
.handler(c -> {
final Pair<Integer, Integer> pair = c.get("pos");
System.out.printf("X: %d | Y: %d\n", pair.getFirst(), pair.getSecond());
}));
manager.command(manager.commandBuilder("vec")
.argument(ArgumentPair.of(manager, "vec", Pair.of("x", "y"),
Pair.of(Double.class, Double.class)
)
.withMapper(
Vector2.class,
(sender, pair) -> new Vector2(pair.getFirst(), pair.getSecond())
)
)
.handler(c -> {
final Vector2 vector2 = c.get("vec");
System.out.printf("X: %f | Y: %f\n", vector2.getX(), vector2.getY());
}));
/* Build command for testing flags */
final CommandFlag<Void> test = manager.flagBuilder("test")
.withAliases("t")
.build();
final CommandFlag<Integer> num = manager.flagBuilder("num")
.withArgument(IntegerArgument.of("num"))
.build();
manager.command(manager.commandBuilder("flags")
.flag(manager.flagBuilder("test")
.withAliases("t")
.build())
.flag(manager.flagBuilder("test2")
.withAliases("f")
.build())
.flag(num)
.flag(manager.flagBuilder("enum")
.withArgument(EnumArgument.of(FlagEnum.class, "enum")))
.handler(c -> {
System.out.println("Flag present? " + c.flags().isPresent(test));
System.out.println("Second flag present? " + c.flags().isPresent("test2"));
System.out.println("Numerical flag: " + c.flags().getValue(num, -10));
System.out.println("Enum: " + c.flags().getValue("enum", FlagEnum.PROXI));
})
.build());
/* Build command for testing float */
manager.command(manager.commandBuilder("float")
.argument(FloatArgument.of("num"))
.handler(c -> System.out.printf("%f\n", c.<Float>get("num"))));
/* Build command for testing preprocessing */
manager.command(manager.commandBuilder("preprocess")
.argument(
StringArgument.<TestCommandSender>of("argument")
.addPreprocessor(RegexPreprocessor.of("[A-Za-z]{3,5}"))
)
@Test
void testMultiLiteralParsing() {
// Arrange
final int defaultInputNumber = ThreadLocalRandom.current().nextInt();
this.commandManager.command(
this.commandManager.commandBuilder("test", SimpleCommandMeta.empty())
.literal("one")
.build()
).command(
this.commandManager.commandBuilder("test", SimpleCommandMeta.empty())
.literal("two")
.permission("no")
.build()
).command(
this.commandManager.commandBuilder("test", SimpleCommandMeta.empty())
.literal("opt")
.argument(IntegerArgument.optional("num", defaultInputNumber))
.build()
);
/* Build command for testing multiple optionals */
manager.command(
manager.commandBuilder("optionals")
.argument(StringArgument.optional("opt1"))
.argument(StringArgument.optional("opt2"))
// Act
final Pair<Command<TestCommandSender>, Exception> command1 = this.commandManager.getCommandTree().parse(
new CommandContext<>(new TestCommandSender(), this.commandManager),
new LinkedList<>(Arrays.asList("test", "one"))
);
final Pair<Command<TestCommandSender>, Exception> command2 = this.commandManager.getCommandTree().parse(
new CommandContext<>(new TestCommandSender(), this.commandManager),
new LinkedList<>(Arrays.asList("test", "two"))
);
final Pair<Command<TestCommandSender>, Exception> command3 = this.commandManager.getCommandTree().parse(
new CommandContext<>(new TestCommandSender(), this.commandManager),
new LinkedList<>(Arrays.asList("test", "opt"))
);
final Pair<Command<TestCommandSender>, Exception> command4 = this.commandManager.getCommandTree().parse(
new CommandContext<>(new TestCommandSender(), this.commandManager),
new LinkedList<>(Arrays.asList("test", "opt", "12"))
);
// Assert
assertThat(command1.getFirst()).isNotNull();
assertThat(command1.getSecond()).isNull();
assertThat(command2.getFirst()).isNull();
assertThat(command2.getSecond()).isInstanceOf(NoPermissionException.class);
assertThat(command3.getFirst()).isNotNull();
assertThat(command3.getFirst().toString()).isEqualTo("test opt num");
assertThat(command3.getSecond()).isNull();
assertThat(command4.getFirst()).isNotNull();
assertThat(command4.getFirst().toString()).isEqualTo("test opt num");
assertThat(command4.getSecond()).isNull();
}
@Test
void parse() {
final Pair<Command<TestCommandSender>, Exception> command = manager.getCommandTree()
.parse(
new CommandContext<>(
new TestCommandSender(),
manager
),
new LinkedList<>(
Arrays.asList(
"test",
"one"
))
);
Assertions.assertNotNull(command.getFirst());
Assertions.assertEquals(NoPermissionException.class, manager.getCommandTree()
.parse(
new CommandContext<>(
new TestCommandSender(),
manager
),
new LinkedList<>(
Arrays.asList("test", "two"))
)
.getSecond().getClass());
manager.getCommandTree()
.parse(
new CommandContext<>(new TestCommandSender(), manager),
new LinkedList<>(Arrays.asList("test", "opt"))
)
.getFirst().getCommandExecutionHandler().execute(new CommandContext<>(
new TestCommandSender(),
manager
));
manager.getCommandTree()
.parse(
new CommandContext<>(new TestCommandSender(), manager),
new LinkedList<>(Arrays.asList("test", "opt", "12"))
)
.getFirst().getCommandExecutionHandler().execute(new CommandContext<>(
new TestCommandSender(),
manager
));
}
@Test
void testAlias() {
manager.getCommandTree()
.parse(
new CommandContext<>(
new TestCommandSender(),
manager
),
new LinkedList<>(Arrays.asList(
"other",
"öpt",
"12"
))
)
.getFirst().getCommandExecutionHandler().execute(new CommandContext<>(
new TestCommandSender(),
manager
));
}
@Test
void getArgumentsAndComponents() {
// Create and register a command
Command<TestCommandSender> command = manager.commandBuilder("component")
.literal("literal", "literalalias")
.literal("detail", ArgumentDescription.of("detaildescription"))
.argument(CommandArgument.ofType(int.class, "argument"),
ArgumentDescription.of("argumentdescription"))
void testAliasedRouting() {
// Arrange
final int defaultInputNumber = ThreadLocalRandom.current().nextInt();
final Command<TestCommandSender> command = this.commandManager.commandBuilder(
"test", Collections.singleton("other"), SimpleCommandMeta.empty()
).literal("opt", "öpt")
.argument(IntegerArgument.optional("num", defaultInputNumber))
.build();
manager.command(command);
this.commandManager.command(command);
// Verify all the details we have configured are present
List<CommandArgument<TestCommandSender, ?>> arguments = command.getArguments();
List<CommandComponent<TestCommandSender>> components = command.getComponents();
Assertions.assertEquals(arguments.size(), components.size());
Assertions.assertEquals(4, components.size());
// Act
final Command<TestCommandSender> result = this.commandManager.getCommandTree().parse(
new CommandContext<>(new TestCommandSender(), this.commandManager),
new LinkedList<>(Arrays.asList("other", "öpt", "12"))
).getFirst();
// Arguments should exactly match the component getArgument() result, in same order
for (int i = 0; i < components.size(); i++) {
Assertions.assertEquals(components.get(i).getArgument(), arguments.get(i));
}
// Argument configuration, we know component has the same argument so no need to test those
// TODO: Aliases
Assertions.assertEquals("component", arguments.get(0).getName());
Assertions.assertEquals("literal", arguments.get(1).getName());
Assertions.assertEquals("detail", arguments.get(2).getName());
Assertions.assertEquals("argument", arguments.get(3).getName());
// Check argument is indeed a command argument
Assertions.assertEquals(TypeToken.get(int.class), arguments.get(3).getValueType());
// Check description is set for all components, is empty when not specified
Assertions.assertEquals("", components.get(0).getArgumentDescription().getDescription());
Assertions.assertEquals("", components.get(1).getArgumentDescription().getDescription());
Assertions.assertEquals("detaildescription", components.get(2).getArgumentDescription().getDescription());
Assertions.assertEquals("argumentdescription", components.get(3).getArgumentDescription().getDescription());
// Assert
assertThat(result).isEqualTo(command);
}
@Test
void getSuggestions() {
Assertions.assertFalse(
manager.getCommandTree().getSuggestions(
new CommandContext<>(new TestCommandSender(), manager),
new LinkedList<>(Arrays.asList("test", ""))
).isEmpty());
}
// Arrange
this.commandManager.command(
this.commandManager.commandBuilder("test")
.literal("a")
);
this.commandManager.command(
this.commandManager.commandBuilder("test")
.literal("b")
);
@Test
void testRequiredSender() {
Assertions.assertThrows(CompletionException.class, () ->
manager.executeCommand(new TestCommandSender(), "req").join());
// Act
final List<String> results = this.commandManager.getCommandTree().getSuggestions(
new CommandContext<>(new TestCommandSender(), this.commandManager),
new LinkedList<>(Arrays.asList("test", ""))
);
// Assert
assertThat(results).containsExactly("a", "b");
}
@Test
void testDefaultParser() {
manager.command(
manager.commandBuilder("default")
.argument(manager.argumentBuilder(Integer.class, "int"))
.handler(context -> {
final int number = context.get("int");
System.out.printf("Supplied number is: %d\n", number);
})
// Arrange
final CommandExecutionHandler<TestCommandSender> executionHandler = mock(CommandExecutionHandler.class);
when(executionHandler.executeFuture(any())).thenReturn(CompletableFuture.completedFuture(null));
this.commandManager.command(
this.commandManager.commandBuilder("default")
.argument(this.commandManager.argumentBuilder(Integer.class, "int"))
.handler(executionHandler)
.build()
);
manager.executeCommand(new TestCommandSender(), "default 5").join();
// Act
this.commandManager.executeCommand(new TestCommandSender(), "default 5").join();
// Assert
final ArgumentCaptor<CommandContext<TestCommandSender>> contextArgumentCaptor = ArgumentCaptor.forClass(
CommandContext.class
);
verify(executionHandler).executeFuture(contextArgumentCaptor.capture());
final CommandContext<TestCommandSender> context = contextArgumentCaptor.getValue();
assertThat(context.get(SimpleCloudKey.of("int", TypeToken.get(Integer.class)))).isEqualTo(5);
}
@Test
void invalidCommand() {
Assertions.assertThrows(CompletionException.class, () -> manager
assertThrows(CompletionException.class, () -> this.commandManager
.executeCommand(new TestCommandSender(), "invalid test").join());
}
@Test
void testProxy() {
manager.executeCommand(new TestCommandSender(), "test unproxied foo 10 anotherliteral").join();
manager.executeCommand(new TestCommandSender(), "proxy foo 10").join();
// Arrange
final CommandExecutionHandler<TestCommandSender> executionHandler = mock(CommandExecutionHandler.class);
when(executionHandler.executeFuture(any())).thenReturn(CompletableFuture.completedFuture(null));
final Command<TestCommandSender> toProxy = this.commandManager.commandBuilder("test")
.literal("unproxied")
.argument(StringArgument.of("string"))
.argument(IntegerArgument.of("int"))
.literal("anotherliteral")
.handler(executionHandler)
.build();
this.commandManager.command(toProxy);
this.commandManager.command(this.commandManager.commandBuilder("proxy").proxies(toProxy).build());
// Act
this.commandManager.executeCommand(new TestCommandSender(), "test unproxied foo 10 anotherliteral").join();
this.commandManager.executeCommand(new TestCommandSender(), "proxy foo 10").join();
// Assert
verify(executionHandler, times(2)).executeFuture(notNull());
}
private CommandExecutionHandler<TestCommandSender> setupFlags() {
final CommandExecutionHandler<TestCommandSender> executionHandler = mock(CommandExecutionHandler.class);
when(executionHandler.executeFuture(any())).thenReturn(CompletableFuture.completedFuture(null));
final CommandFlag<Integer> num = this.commandManager.flagBuilder("num")
.withArgument(IntegerArgument.of("num"))
.build();
this.commandManager.command(this.commandManager.commandBuilder("flags")
.flag(this.commandManager.flagBuilder("test")
.withAliases("t")
.build())
.flag(this.commandManager.flagBuilder("test2")
.withAliases("f")
.build())
.flag(num)
.flag(this.commandManager.flagBuilder("enum")
.withArgument(EnumArgument.of(FlagEnum.class, "enum")))
.handler(executionHandler)
.build());
return executionHandler;
}
@Test
void testIntermediary() {
manager.executeCommand(new TestCommandSender(), "command inner").join();
manager.executeCommand(new TestCommandSender(), "command").join();
void testFlags_NoFlags() {
// Arrange
final CommandExecutionHandler<TestCommandSender> executionHandler = this.setupFlags();
// Act
this.commandManager.executeCommand(new TestCommandSender(), "flags").join();
// Assert
final ArgumentCaptor<CommandContext<TestCommandSender>> contextArgumentCaptor = ArgumentCaptor.forClass(
CommandContext.class
);
verify(executionHandler).executeFuture(contextArgumentCaptor.capture());
final CommandContext<TestCommandSender> context = contextArgumentCaptor.getValue();
assertThat(context.flags().contains("test")).isFalse();
}
@Test
void testCompound() {
manager.executeCommand(new TestCommandSender(), "pos -3 2").join();
manager.executeCommand(new TestCommandSender(), "vec 1 1").join();
void testFlags_PresenceFlag() {
// Arrange
final CommandExecutionHandler<TestCommandSender> executionHandler = this.setupFlags();
// Act
this.commandManager.executeCommand(new TestCommandSender(), "flags --test").join();
// Assert
final ArgumentCaptor<CommandContext<TestCommandSender>> contextArgumentCaptor = ArgumentCaptor.forClass(
CommandContext.class
);
verify(executionHandler).executeFuture(contextArgumentCaptor.capture());
final CommandContext<TestCommandSender> context = contextArgumentCaptor.getValue();
assertThat(context.flags().contains("test")).isTrue();
}
@Test
void testFlags() {
manager.executeCommand(new TestCommandSender(), "flags").join();
manager.executeCommand(new TestCommandSender(), "flags --test").join();
manager.executeCommand(new TestCommandSender(), "flags -t").join();
Assertions.assertThrows(CompletionException.class, () ->
manager.executeCommand(new TestCommandSender(), "flags --test --nonexistant").join());
Assertions.assertThrows(CompletionException.class, () ->
manager.executeCommand(new TestCommandSender(), "flags --test --duplicate").join());
manager.executeCommand(new TestCommandSender(), "flags --test --test2").join();
Assertions.assertThrows(CompletionException.class, () ->
manager.executeCommand(new TestCommandSender(), "flags --test test2").join());
manager.executeCommand(new TestCommandSender(), "flags --num 500").join();
manager.executeCommand(new TestCommandSender(), "flags --num 63 --enum potato --test").join();
manager.executeCommand(new TestCommandSender(), "flags -tf --num 63 --enum potato").join();
void testFlags_PresenceFlagShortForm() {
// Arrange
final CommandExecutionHandler<TestCommandSender> executionHandler = this.setupFlags();
// Act
this.commandManager.executeCommand(new TestCommandSender(), "flags -t").join();
// Assert
final ArgumentCaptor<CommandContext<TestCommandSender>> contextArgumentCaptor = ArgumentCaptor.forClass(
CommandContext.class
);
verify(executionHandler).executeFuture(contextArgumentCaptor.capture());
final CommandContext<TestCommandSender> context = contextArgumentCaptor.getValue();
assertThat(context.flags().contains("test")).isTrue();
}
@Test
void testFlags_NonexistentFlag() {
// Arrange
this.setupFlags();
// Act & Assert
assertThrows(
CompletionException.class, () ->
this.commandManager.executeCommand(new TestCommandSender(), "flags --test --nonexistent").join()
);
}
@Test
void testFlags_MultiplePresenceFlags() {
// Arrange
final CommandExecutionHandler<TestCommandSender> executionHandler = this.setupFlags();
// Act
this.commandManager.executeCommand(new TestCommandSender(), "flags --test --test2").join();
// Assert
final ArgumentCaptor<CommandContext<TestCommandSender>> contextArgumentCaptor = ArgumentCaptor.forClass(
CommandContext.class
);
verify(executionHandler).executeFuture(contextArgumentCaptor.capture());
final CommandContext<TestCommandSender> context = contextArgumentCaptor.getValue();
assertThat(context.flags().contains("test")).isTrue();
assertThat(context.flags().contains("test2")).isTrue();
}
@Test
void testFlags_NonPrefixedPresenceFlag() {
// Arrange
this.setupFlags();
// Act
assertThrows(
CompletionException.class, () ->
this.commandManager.executeCommand(new TestCommandSender(), "flags --test test2").join()
);
}
@Test
void testFlags_ValueFlag() {
// Arrange
final CommandExecutionHandler<TestCommandSender> executionHandler = this.setupFlags();
// Act
this.commandManager.executeCommand(new TestCommandSender(), "flags --num 500").join();
// Assert
final ArgumentCaptor<CommandContext<TestCommandSender>> contextArgumentCaptor = ArgumentCaptor.forClass(
CommandContext.class
);
verify(executionHandler).executeFuture(contextArgumentCaptor.capture());
final CommandContext<TestCommandSender> context = contextArgumentCaptor.getValue();
assertThat(context.flags().<Integer>getValue("num")).hasValue(500);
}
@Test
void testFlags_MultipleValueFlagsFollowedByPresence() {
// Arrange
final CommandExecutionHandler<TestCommandSender> executionHandler = this.setupFlags();
// Act
this.commandManager.executeCommand(new TestCommandSender(), "flags --num 63 --enum potato --test").join();
// Assert
final ArgumentCaptor<CommandContext<TestCommandSender>> contextArgumentCaptor = ArgumentCaptor.forClass(
CommandContext.class
);
verify(executionHandler).executeFuture(contextArgumentCaptor.capture());
final CommandContext<TestCommandSender> context = contextArgumentCaptor.getValue();
assertThat(context.flags().<Integer>getValue("num")).hasValue(63);
assertThat(context.flags().<FlagEnum>getValue("enum")).hasValue(FlagEnum.POTATO);
}
@Test
void testFlags_ShortFormPresenceFlagsFollowedByMultipleValueFlags() {
// Arrange
final CommandExecutionHandler<TestCommandSender> executionHandler = this.setupFlags();
// Act
this.commandManager.executeCommand(new TestCommandSender(), "flags -tf --num 63 --enum potato").join();
// Assert
final ArgumentCaptor<CommandContext<TestCommandSender>> contextArgumentCaptor = ArgumentCaptor.forClass(
CommandContext.class
);
verify(executionHandler).executeFuture(contextArgumentCaptor.capture());
final CommandContext<TestCommandSender> context = contextArgumentCaptor.getValue();
assertThat(context.flags().contains("test")).isTrue();
assertThat(context.flags().contains("test2")).isTrue();
assertThat(context.flags().<Integer>getValue("num")).hasValue(63);
assertThat(context.flags().<FlagEnum>getValue("enum")).hasValue(FlagEnum.POTATO);
}
@Test
void testAmbiguousNodes() {
// Call newTree(); after each time we leave the Tree in an invalid state
manager.command(manager.commandBuilder("ambiguous")
// Call setup(); after each time we leave the Tree in an invalid state
this.commandManager.command(this.commandManager.commandBuilder("ambiguous")
.argument(StringArgument.of("string"))
);
Assertions.assertThrows(AmbiguousNodeException.class, () ->
manager.command(manager.commandBuilder("ambiguous")
assertThrows(AmbiguousNodeException.class, () ->
this.commandManager.command(this.commandManager.commandBuilder("ambiguous")
.argument(IntegerArgument.of("integer"))));
newTree();
this.setup();
// Literal and argument can co-exist, not ambiguous
manager.command(manager.commandBuilder("ambiguous")
this.commandManager.command(this.commandManager.commandBuilder("ambiguous")
.argument(StringArgument.of("string"))
);
manager.command(manager.commandBuilder("ambiguous")
this.commandManager.command(this.commandManager.commandBuilder("ambiguous")
.literal("literal"));
newTree();
this.setup();
// Two literals (different names) and argument can co-exist, not ambiguous
manager.command(manager.commandBuilder("ambiguous")
this.commandManager.command(this.commandManager.commandBuilder("ambiguous")
.literal("literal"));
manager.command(manager.commandBuilder("ambiguous")
this.commandManager.command(this.commandManager.commandBuilder("ambiguous")
.literal("literal2"));
manager.command(manager.commandBuilder("ambiguous")
this.commandManager.command(this.commandManager.commandBuilder("ambiguous")
.argument(IntegerArgument.of("integer")));
newTree();
this.setup();
// Two literals with the same name can not co-exist, causes 'duplicate command chains' error
manager.command(manager.commandBuilder("ambiguous")
this.commandManager.command(this.commandManager.commandBuilder("ambiguous")
.literal("literal"));
Assertions.assertThrows(IllegalStateException.class, () ->
manager.command(manager.commandBuilder("ambiguous")
assertThrows(IllegalStateException.class, () ->
this.commandManager.command(this.commandManager.commandBuilder("ambiguous")
.literal("literal")));
newTree();
this.setup();
}
@Test
void testLiteralRepeatingArgument() {
// Build a command with a literal repeating
Command<TestCommandSender> command = manager.commandBuilder("repeatingargscommand")
Command<TestCommandSender> command = this.commandManager.commandBuilder("repeatingargscommand")
.literal("repeat")
.literal("middle")
.literal("repeat")
@ -386,132 +454,140 @@ class CommandTreeTest {
// Verify built command has the repeat argument twice
List<CommandArgument<TestCommandSender, ?>> args = command.getArguments();
Assertions.assertEquals(4, args.size());
Assertions.assertEquals("repeatingargscommand", args.get(0).getName());
Assertions.assertEquals("repeat", args.get(1).getName());
Assertions.assertEquals("middle", args.get(2).getName());
Assertions.assertEquals("repeat", args.get(3).getName());
assertThat(args.size()).isEqualTo(4);;
assertThat(args.get(0).getName()).isEqualTo("repeatingargscommand");;
assertThat(args.get(1).getName()).isEqualTo("repeat");;
assertThat(args.get(2).getName()).isEqualTo("middle");;
assertThat(args.get(3).getName()).isEqualTo("repeat");;
// Register
manager.command(command);
this.commandManager.command(command);
// If internally it drops repeating arguments, then it would register:
// > /repeatingargscommand repeat middle
// So check that we can register that exact command without an ambiguity exception
manager.command(
manager.commandBuilder("repeatingargscommand")
.literal("repeat")
.literal("middle")
this.commandManager.command(
this.commandManager.commandBuilder("repeatingargscommand")
.literal("repeat")
.literal("middle")
);
}
@Test
void testAmbiguousLiteralOverridingArgument() {
/* Build two commands for testing literals overriding variable arguments */
manager.command(
manager.commandBuilder("literalwithvariable")
.argument(StringArgument.of("variable"))
this.commandManager.command(
this.commandManager.commandBuilder("literalwithvariable")
.argument(StringArgument.of("variable"))
);
manager.command(
manager.commandBuilder("literalwithvariable")
.literal("literal", "literalalias")
this.commandManager.command(
this.commandManager.commandBuilder("literalwithvariable")
.literal("literal", "literalalias")
);
/* Try parsing as a variable, which should match the variable command */
final Pair<Command<TestCommandSender>, Exception> variableResult = manager.getCommandTree().parse(
new CommandContext<>(new TestCommandSender(), manager),
new LinkedList<>(Arrays.asList("literalwithvariable", "argthatdoesnotmatch"))
);
Assertions.assertNull(variableResult.getSecond());
Assertions.assertEquals("literalwithvariable",
variableResult.getFirst().getArguments().get(0).getName());
Assertions.assertEquals("variable",
variableResult.getFirst().getArguments().get(1).getName());
final Pair<Command<TestCommandSender>, Exception> variableResult = this.commandManager.getCommandTree().parse(
new CommandContext<>(new TestCommandSender(), this.commandManager),
new LinkedList<>(Arrays.asList("literalwithvariable", "argthatdoesnotmatch"))
);
assertThat(variableResult.getSecond()).isNull();
assertThat(variableResult.getFirst().getArguments().get(0).getName()).isEqualTo("literalwithvariable");;
assertThat(variableResult.getFirst().getArguments().get(1).getName()).isEqualTo("variable");;
/* Try parsing with the main name literal, which should match the literal command */
final Pair<Command<TestCommandSender>, Exception> literalResult = manager.getCommandTree().parse(
new CommandContext<>(new TestCommandSender(), manager),
final Pair<Command<TestCommandSender>, Exception> literalResult = this.commandManager.getCommandTree().parse(
new CommandContext<>(new TestCommandSender(), this.commandManager),
new LinkedList<>(Arrays.asList("literalwithvariable", "literal"))
);
Assertions.assertNull(literalResult.getSecond());
Assertions.assertEquals("literalwithvariable",
literalResult.getFirst().getArguments().get(0).getName());
Assertions.assertEquals("literal",
literalResult.getFirst().getArguments().get(1).getName());
assertThat(literalResult.getSecond()).isNull();
assertThat(literalResult.getFirst().getArguments().get(0).getName()).isEqualTo("literalwithvariable");;
assertThat(literalResult.getFirst().getArguments().get(1).getName()).isEqualTo("literal");;
/* Try parsing with the alias of the literal, which should match the literal command */
final Pair<Command<TestCommandSender>, Exception> literalAliasResult = manager.getCommandTree().parse(
new CommandContext<>(new TestCommandSender(), manager),
final Pair<Command<TestCommandSender>, Exception> literalAliasResult = this.commandManager.getCommandTree().parse(
new CommandContext<>(new TestCommandSender(), this.commandManager),
new LinkedList<>(Arrays.asList("literalwithvariable", "literalalias"))
);
Assertions.assertNull(literalAliasResult.getSecond());
Assertions.assertEquals("literalwithvariable",
literalAliasResult.getFirst().getArguments().get(0).getName());
Assertions.assertEquals("literal",
literalAliasResult.getFirst().getArguments().get(1).getName());
assertThat(literalAliasResult.getSecond()).isNull();
assertThat(literalAliasResult.getFirst().getArguments().get(0).getName()).isEqualTo("literalwithvariable");;
assertThat(literalAliasResult.getFirst().getArguments().get(1).getName()).isEqualTo("literal");;
}
@Test
void testDuplicateArgument() {
// Arrange
final CommandArgument<TestCommandSender, String> argument = StringArgument.of("test");
manager.command(manager.commandBuilder("one").argument(argument));
Assertions.assertThrows(IllegalArgumentException.class, () ->
manager.command(manager.commandBuilder("two").argument(argument)));
}
this.commandManager.command(this.commandManager.commandBuilder("one").argument(argument));
@Test
void testFloats() {
manager.executeCommand(new TestCommandSender(), "float 0.0").join();
manager.executeCommand(new TestCommandSender(), "float 100").join();
}
@Test
void testPreprocessors() {
manager.executeCommand(new TestCommandSender(), "preprocess abc").join();
Assertions.assertThrows(
CompletionException.class,
() -> manager.executeCommand(new TestCommandSender(), "preprocess ab").join()
// Act & Assert
assertThrows(
IllegalArgumentException.class,
() -> this.commandManager.command(this.commandManager.commandBuilder("two").argument(argument))
);
}
@Test
void testFloats() {
// Arrange
final CommandExecutionHandler<TestCommandSender> executionHandler = mock(CommandExecutionHandler.class);
when(executionHandler.executeFuture(any())).thenReturn(CompletableFuture.completedFuture(null));
this.commandManager.command(
this.commandManager.commandBuilder("float")
.argument(FloatArgument.of("num"))
.handler(executionHandler)
.build()
);
// Act
this.commandManager.executeCommand(new TestCommandSender(), "float 0.0").join();
this.commandManager.executeCommand(new TestCommandSender(), "float 100").join();
// Assert
final ArgumentCaptor<CommandContext<TestCommandSender>> contextArgumentCaptor = ArgumentCaptor.forClass(
CommandContext.class
);
verify(executionHandler, times(2)).executeFuture(contextArgumentCaptor.capture());
final Stream<Float> values = contextArgumentCaptor.getAllValues()
.stream()
.map(context -> context.<Float>get("num"));
assertThat(values).containsExactly(0.0f, 100f);
}
@Test
void testOptionals() {
manager.executeCommand(new TestCommandSender(), "optionals").join();
// Arrange
final CommandExecutionHandler<TestCommandSender> executionHandler = mock(CommandExecutionHandler.class);
when(executionHandler.executeFuture(any())).thenReturn(CompletableFuture.completedFuture(null));
this.commandManager.command(
this.commandManager.commandBuilder("optionals")
.argument(StringArgument.optional("opt1"))
.argument(StringArgument.optional("opt2"))
.handler(executionHandler)
.build()
);
// Act
this.commandManager.executeCommand(new TestCommandSender(), "optionals").join();
// Assert
final ArgumentCaptor<CommandContext<TestCommandSender>> contextArgumentCaptor = ArgumentCaptor.forClass(
CommandContext.class
);
verify(executionHandler).executeFuture(contextArgumentCaptor.capture());
final CommandContext<TestCommandSender> context = contextArgumentCaptor.getValue();
assertThat(context.getOrDefault(SimpleCloudKey.of("opt1", TypeToken.get(String.class)), null)).isNull();
assertThat(context.getOrDefault(SimpleCloudKey.of("opt2", TypeToken.get(String.class)), null)).isNull();
}
public static final class SpecificCommandSender extends TestCommandSender {
}
public static final class Vector2 {
private final double x;
private final double y;
private Vector2(final double x, final double y) {
this.x = x;
this.y = y;
}
private double getX() {
return this.x;
}
private double getY() {
return this.y;
}
}
public enum FlagEnum {
enum FlagEnum {
POTATO,
CARROT,
ONION,
PROXI
}
}