702 lines
28 KiB
Java
702 lines
28 KiB
Java
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
// Licensed under the MIT license.
|
|
|
|
package com.mojang.brigadier;
|
|
|
|
import com.google.common.collect.Lists;
|
|
import com.mojang.brigadier.arguments.IntegerArgumentType;
|
|
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
|
|
import com.mojang.brigadier.builder.RequiredArgumentBuilder;
|
|
import com.mojang.brigadier.context.CommandContext;
|
|
import com.mojang.brigadier.context.CommandContextBuilder;
|
|
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
|
import com.mojang.brigadier.suggestion.Suggestion;
|
|
import com.mojang.brigadier.suggestion.Suggestions;
|
|
import com.mojang.brigadier.tree.ArgumentCommandNode;
|
|
import com.mojang.brigadier.tree.CommandNode;
|
|
import com.mojang.brigadier.tree.LiteralCommandNode;
|
|
import com.mojang.brigadier.tree.RootCommandNode;
|
|
import org.hamcrest.CustomMatcher;
|
|
import org.hamcrest.Matcher;
|
|
import org.junit.Before;
|
|
import org.junit.Test;
|
|
import org.junit.runner.RunWith;
|
|
import org.mockito.Mock;
|
|
import org.mockito.runners.MockitoJUnitRunner;
|
|
|
|
import java.util.Arrays;
|
|
import java.util.Collection;
|
|
import java.util.Collections;
|
|
import java.util.concurrent.CompletableFuture;
|
|
import java.util.stream.Collectors;
|
|
|
|
import static com.mojang.brigadier.arguments.IntegerArgumentType.getInteger;
|
|
import static com.mojang.brigadier.arguments.IntegerArgumentType.integer;
|
|
import static com.mojang.brigadier.builder.LiteralArgumentBuilder.literal;
|
|
import static com.mojang.brigadier.builder.RequiredArgumentBuilder.argument;
|
|
import static org.hamcrest.Matchers.*;
|
|
import static org.junit.Assert.assertThat;
|
|
import static org.junit.Assert.fail;
|
|
import static org.mockito.Matchers.argThat;
|
|
import static org.mockito.Matchers.eq;
|
|
import static org.mockito.Mockito.any;
|
|
import static org.mockito.Mockito.*;
|
|
|
|
@RunWith(MockitoJUnitRunner.class)
|
|
public class CommandDispatcherTest {
|
|
private CommandDispatcher<Object> subject;
|
|
@Mock
|
|
private Command<Object> command;
|
|
@Mock
|
|
private Object source;
|
|
@Mock
|
|
private ResultConsumer<Object> consumer;
|
|
|
|
@Before
|
|
public void setUp() throws Exception {
|
|
subject = new CommandDispatcher<>();
|
|
when(command.run(any())).thenReturn(42);
|
|
}
|
|
|
|
private static StringReader inputWithOffset(final String input, final int offset) {
|
|
final StringReader result = new StringReader(input);
|
|
result.setCursor(offset);
|
|
return result;
|
|
}
|
|
|
|
@SuppressWarnings("unchecked")
|
|
@Test
|
|
public void testCreateAndExecuteCommand() throws Exception {
|
|
subject.register(literal("foo").executes(command));
|
|
|
|
assertThat(subject.execute("foo", source), is(42));
|
|
verify(command).run(any(CommandContext.class));
|
|
}
|
|
|
|
@SuppressWarnings("unchecked")
|
|
@Test
|
|
public void testCreateAndExecuteOffsetCommand() throws Exception {
|
|
subject.register(literal("foo").executes(command));
|
|
|
|
assertThat(subject.execute(inputWithOffset("/foo", 1), source), is(42));
|
|
verify(command).run(any(CommandContext.class));
|
|
}
|
|
|
|
@SuppressWarnings("unchecked")
|
|
@Test
|
|
public void testCreateAndMergeCommands() throws Exception {
|
|
subject.register(literal("base").then(literal("foo").executes(command)));
|
|
subject.register(literal("base").then(literal("bar").executes(command)));
|
|
|
|
assertThat(subject.execute("base foo", source), is(42));
|
|
assertThat(subject.execute("base bar", source), is(42));
|
|
verify(command, times(2)).run(any(CommandContext.class));
|
|
}
|
|
|
|
@Test
|
|
public void testExecuteUnknownCommand() {
|
|
subject.register(literal("bar"));
|
|
subject.register(literal("baz"));
|
|
|
|
try {
|
|
subject.execute("foo", source);
|
|
fail();
|
|
} catch (final CommandSyntaxException ex) {
|
|
assertThat(ex.getType(), is(CommandSyntaxException.BUILT_IN_EXCEPTIONS.dispatcherUnknownCommand()));
|
|
assertThat(ex.getCursor(), is(0));
|
|
}
|
|
}
|
|
|
|
@Test
|
|
public void testExecuteImpermissibleCommand() {
|
|
subject.register(literal("foo").requires(s -> false));
|
|
|
|
try {
|
|
subject.execute("foo", source);
|
|
fail();
|
|
} catch (final CommandSyntaxException ex) {
|
|
assertThat(ex.getType(), is(CommandSyntaxException.BUILT_IN_EXCEPTIONS.dispatcherUnknownCommand()));
|
|
assertThat(ex.getCursor(), is(0));
|
|
}
|
|
}
|
|
|
|
@Test
|
|
public void testExecuteEmptyCommand() {
|
|
subject.register(literal(""));
|
|
|
|
try {
|
|
subject.execute("", source);
|
|
fail();
|
|
} catch (final CommandSyntaxException ex) {
|
|
assertThat(ex.getType(), is(CommandSyntaxException.BUILT_IN_EXCEPTIONS.dispatcherUnknownCommand()));
|
|
assertThat(ex.getCursor(), is(0));
|
|
}
|
|
}
|
|
|
|
@Test
|
|
public void testExecuteUnknownSubcommand() {
|
|
subject.register(literal("foo").executes(command));
|
|
|
|
try {
|
|
subject.execute("foo bar", source);
|
|
fail();
|
|
} catch (final CommandSyntaxException ex) {
|
|
assertThat(ex.getType(), is(CommandSyntaxException.BUILT_IN_EXCEPTIONS.dispatcherUnknownArgument()));
|
|
assertThat(ex.getCursor(), is(4));
|
|
}
|
|
}
|
|
|
|
@Test
|
|
public void testExecuteIncorrectLiteral() {
|
|
subject.register(literal("foo").executes(command).then(literal("bar")));
|
|
|
|
try {
|
|
subject.execute("foo baz", source);
|
|
fail();
|
|
} catch (final CommandSyntaxException ex) {
|
|
assertThat(ex.getType(), is(CommandSyntaxException.BUILT_IN_EXCEPTIONS.dispatcherUnknownArgument()));
|
|
assertThat(ex.getCursor(), is(4));
|
|
}
|
|
}
|
|
|
|
@Test
|
|
public void testExecuteAmbiguousIncorrectArgument() {
|
|
subject.register(
|
|
literal("foo").executes(command)
|
|
.then(literal("bar"))
|
|
.then(literal("baz"))
|
|
);
|
|
|
|
try {
|
|
subject.execute("foo unknown", source);
|
|
fail();
|
|
} catch (final CommandSyntaxException ex) {
|
|
assertThat(ex.getType(), is(CommandSyntaxException.BUILT_IN_EXCEPTIONS.dispatcherUnknownArgument()));
|
|
assertThat(ex.getCursor(), is(4));
|
|
}
|
|
}
|
|
|
|
@SuppressWarnings("unchecked")
|
|
@Test
|
|
public void testExecuteSubcommand() throws Exception {
|
|
final Command<Object> subCommand = mock(Command.class);
|
|
when(subCommand.run(any())).thenReturn(100);
|
|
|
|
subject.register(literal("foo").then(
|
|
literal("a")
|
|
).then(
|
|
literal("=").executes(subCommand)
|
|
).then(
|
|
literal("c")
|
|
).executes(command));
|
|
|
|
assertThat(subject.execute("foo =", source), is(100));
|
|
verify(subCommand).run(any(CommandContext.class));
|
|
}
|
|
|
|
@Test
|
|
public void testParseIncompleteLiteral() {
|
|
subject.register(literal("foo").then(literal("bar").executes(command)));
|
|
|
|
final ParseResults<Object> parse = subject.parse("foo ", source);
|
|
assertThat(parse.getReader().getRemaining(), equalTo(" "));
|
|
assertThat(parse.getContext().getNodes().size(), is(1));
|
|
}
|
|
|
|
@Test
|
|
public void testParseIncompleteArgument() {
|
|
subject.register(literal("foo").then(argument("bar", integer()).executes(command)));
|
|
|
|
final ParseResults<Object> parse = subject.parse("foo ", source);
|
|
assertThat(parse.getReader().getRemaining(), equalTo(" "));
|
|
assertThat(parse.getContext().getNodes().size(), is(1));
|
|
}
|
|
|
|
@Test
|
|
public void testParseChildlessRedirect() throws Exception {
|
|
final CommandNode<Object> target = subject.register(literal("foo").executes(command));
|
|
final CommandNode<Object> redirect = subject.register(literal("redirect").redirect(target));
|
|
|
|
final ParseResults<Object> parse = subject.parse("redirect", source);
|
|
assertThat(parse.getContext().getCommand(), equalTo(target.getCommand()));
|
|
assertThat(parse.getContext().getNodes().get(0).getNode(), equalTo(redirect));
|
|
}
|
|
|
|
@SuppressWarnings("unchecked")
|
|
@Test
|
|
public void testExecuteAmbiguiousParentSubcommand() throws Exception {
|
|
final Command<Object> subCommand = mock(Command.class);
|
|
when(subCommand.run(any())).thenReturn(100);
|
|
|
|
subject.register(
|
|
literal("test")
|
|
.then(
|
|
argument("incorrect", integer())
|
|
.executes(command)
|
|
)
|
|
.then(
|
|
argument("right", integer())
|
|
.then(
|
|
argument("sub", integer())
|
|
.executes(subCommand)
|
|
)
|
|
)
|
|
);
|
|
|
|
assertThat(subject.execute("test 1 2", source), is(100));
|
|
verify(subCommand).run(any(CommandContext.class));
|
|
verify(command, never()).run(any());
|
|
}
|
|
|
|
@SuppressWarnings("unchecked")
|
|
@Test
|
|
public void testExecuteAmbiguiousParentSubcommandViaRedirect() throws Exception {
|
|
final Command<Object> subCommand = mock(Command.class);
|
|
when(subCommand.run(any())).thenReturn(100);
|
|
|
|
final LiteralCommandNode<Object> real = subject.register(
|
|
literal("test")
|
|
.then(
|
|
argument("incorrect", integer())
|
|
.executes(command)
|
|
)
|
|
.then(
|
|
argument("right", integer())
|
|
.then(
|
|
argument("sub", integer())
|
|
.executes(subCommand)
|
|
)
|
|
)
|
|
);
|
|
|
|
subject.register(literal("redirect").redirect(real));
|
|
|
|
assertThat(subject.execute("redirect 1 2", source), is(100));
|
|
verify(subCommand).run(any(CommandContext.class));
|
|
verify(command, never()).run(any());
|
|
}
|
|
|
|
@SuppressWarnings("unchecked")
|
|
@Test
|
|
public void testExecuteRedirectedMultipleTimes() throws Exception {
|
|
final LiteralCommandNode<Object> concreteNode = subject.register(literal("actual").executes(command));
|
|
final LiteralCommandNode<Object> redirectNode = subject.register(literal("redirected").redirect(subject.getRoot()));
|
|
|
|
final String input = "redirected redirected actual";
|
|
|
|
final ParseResults<Object> parse = subject.parse(input, source);
|
|
assertThat(parse.getContext().getRange().get(input), equalTo("redirected"));
|
|
assertThat(parse.getContext().getNodes().size(), is(1));
|
|
assertThat(parse.getContext().getRootNode(), is(subject.getRoot()));
|
|
assertThat(parse.getContext().getNodes().get(0).getRange(), equalTo(parse.getContext().getRange()));
|
|
assertThat(parse.getContext().getNodes().get(0).getNode(), is(redirectNode));
|
|
|
|
final CommandContextBuilder<Object> child1 = parse.getContext().getChild();
|
|
assertThat(child1, is(notNullValue()));
|
|
assertThat(child1.getRange().get(input), equalTo("redirected"));
|
|
assertThat(child1.getNodes().size(), is(1));
|
|
assertThat(child1.getRootNode(), is(subject.getRoot()));
|
|
assertThat(child1.getNodes().get(0).getRange(), equalTo(child1.getRange()));
|
|
assertThat(child1.getNodes().get(0).getNode(), is(redirectNode));
|
|
|
|
final CommandContextBuilder<Object> child2 = child1.getChild();
|
|
assertThat(child2, is(notNullValue()));
|
|
assertThat(child2.getRange().get(input), equalTo("actual"));
|
|
assertThat(child2.getNodes().size(), is(1));
|
|
assertThat(child2.getRootNode(), is(subject.getRoot()));
|
|
assertThat(child2.getNodes().get(0).getRange(), equalTo(child2.getRange()));
|
|
assertThat(child2.getNodes().get(0).getNode(), is(concreteNode));
|
|
|
|
assertThat(subject.execute(parse), is(42));
|
|
verify(command).run(any(CommandContext.class));
|
|
}
|
|
|
|
@Test
|
|
public void testCorrectExecuteContextAfterRedirect() throws Exception {
|
|
final CommandDispatcher<Integer> subject = new CommandDispatcher<>();
|
|
|
|
final RootCommandNode<Integer> root = subject.getRoot();
|
|
final LiteralArgumentBuilder<Integer> add = literal("add");
|
|
final LiteralArgumentBuilder<Integer> blank = literal("blank");
|
|
final RequiredArgumentBuilder<Integer, Integer> addArg = argument("value", integer());
|
|
final LiteralArgumentBuilder<Integer> run = literal("run");
|
|
|
|
subject.register(add.then(addArg.redirect(root, c -> c.getSource() + getInteger(c, "value"))));
|
|
subject.register(blank.redirect(root));
|
|
subject.register(run.executes(CommandContext::getSource));
|
|
|
|
assertThat(subject.execute("run", 0), is(0));
|
|
assertThat(subject.execute("run", 1), is(1));
|
|
|
|
assertThat(subject.execute("add 5 run", 1), is(1 + 5));
|
|
assertThat(subject.execute("add 5 add 6 run", 2), is(2 + 5 + 6));
|
|
assertThat(subject.execute("add 5 blank run", 1), is(1 + 5));
|
|
assertThat(subject.execute("blank add 5 run", 1), is(1 + 5));
|
|
assertThat(subject.execute("add 5 blank add 6 run", 2), is(2 + 5 + 6));
|
|
assertThat(subject.execute("add 5 blank blank add 6 run", 2), is(2 + 5 + 6));
|
|
}
|
|
|
|
@Test
|
|
public void testSharedRedirectAndExecuteNodes() throws CommandSyntaxException {
|
|
final CommandDispatcher<Integer> subject = new CommandDispatcher<>();
|
|
|
|
final RootCommandNode<Integer> root = subject.getRoot();
|
|
final LiteralArgumentBuilder<Integer> add = literal("add");
|
|
final RequiredArgumentBuilder<Integer, Integer> addArg = argument("value", integer());
|
|
|
|
subject.register(add.then(
|
|
addArg
|
|
.redirect(root, c -> c.getSource() + getInteger(c, "value"))
|
|
.executes(CommandContext::getSource)
|
|
));
|
|
|
|
assertThat(subject.execute("add 5", 1), is(1));
|
|
assertThat(subject.execute("add 5 add 6", 1), is(1 + 5));
|
|
}
|
|
|
|
@SuppressWarnings("unchecked")
|
|
@Test
|
|
public void testExecuteRedirected() throws Exception {
|
|
final RedirectModifier<Object> modifier = mock(RedirectModifier.class);
|
|
final Object source1 = new Object();
|
|
final Object source2 = new Object();
|
|
|
|
when(modifier.apply(argThat(hasProperty("source", is(source))))).thenReturn(Lists.newArrayList(source1, source2));
|
|
|
|
final LiteralCommandNode<Object> concreteNode = subject.register(literal("actual").executes(command));
|
|
final LiteralCommandNode<Object> redirectNode = subject.register(literal("redirected").fork(subject.getRoot(), modifier));
|
|
|
|
final String input = "redirected actual";
|
|
final ParseResults<Object> parse = subject.parse(input, source);
|
|
assertThat(parse.getContext().getRange().get(input), equalTo("redirected"));
|
|
assertThat(parse.getContext().getNodes().size(), is(1));
|
|
assertThat(parse.getContext().getRootNode(), equalTo(subject.getRoot()));
|
|
assertThat(parse.getContext().getNodes().get(0).getRange(), equalTo(parse.getContext().getRange()));
|
|
assertThat(parse.getContext().getNodes().get(0).getNode(), is(redirectNode));
|
|
assertThat(parse.getContext().getSource(), is(source));
|
|
|
|
final CommandContextBuilder<Object> parent = parse.getContext().getChild();
|
|
assertThat(parent, is(notNullValue()));
|
|
assertThat(parent.getRange().get(input), equalTo("actual"));
|
|
assertThat(parent.getNodes().size(), is(1));
|
|
assertThat(parse.getContext().getRootNode(), equalTo(subject.getRoot()));
|
|
assertThat(parent.getNodes().get(0).getRange(), equalTo(parent.getRange()));
|
|
assertThat(parent.getNodes().get(0).getNode(), is(concreteNode));
|
|
assertThat(parent.getSource(), is(source));
|
|
|
|
assertThat(subject.execute(parse), is(2));
|
|
verify(command).run(argThat(hasProperty("source", is(source1))));
|
|
verify(command).run(argThat(hasProperty("source", is(source2))));
|
|
}
|
|
|
|
@Test
|
|
public void testIncompleteRedirectShouldThrow() {
|
|
final LiteralCommandNode<Object> foo = subject.register(literal("foo")
|
|
.then(literal("bar")
|
|
.then(argument("value", integer()).executes(context -> IntegerArgumentType.getInteger(context, "value"))))
|
|
.then(literal("awa").executes(context -> 2)));
|
|
subject.register(literal("baz").redirect(foo));
|
|
try {
|
|
subject.execute("baz bar", source);
|
|
fail("Should have thrown an exception");
|
|
} catch (CommandSyntaxException e) {
|
|
assertThat(e.getType(), is(CommandSyntaxException.BUILT_IN_EXCEPTIONS.dispatcherUnknownCommand()));
|
|
}
|
|
}
|
|
|
|
@Test
|
|
public void testRedirectModifierEmptyResult() throws CommandSyntaxException {
|
|
final LiteralCommandNode<Object> foo = subject.register(literal("foo")
|
|
.then(literal("bar")
|
|
.then(argument("value", integer()).executes(context -> IntegerArgumentType.getInteger(context, "value"))))
|
|
.then(literal("awa").executes(context -> 2)));
|
|
final RedirectModifier<Object> emptyModifier = context -> Collections.emptyList();
|
|
subject.register(literal("baz").fork(foo, emptyModifier));
|
|
int result = subject.execute("baz bar 100", source);
|
|
assertThat(result, is(0)); // No commands executed, so result is 0
|
|
}
|
|
|
|
@Test
|
|
public void testRedirectPreservesPreviousArguments() throws CommandSyntaxException {
|
|
final LiteralCommandNode<Object> ending = literal("ending")
|
|
.executes(context -> context.getArgument("number", int.class)).build();
|
|
final ArgumentCommandNode<Object, Integer> lowNumber = argument("number", integer(1, 10))
|
|
.then(ending).build();
|
|
final ArgumentCommandNode<Object, Integer> highNumber = argument("number", integer(11, 20))
|
|
.redirect(lowNumber).build();
|
|
subject.register(literal("base")
|
|
.then(literal("low").then(lowNumber))
|
|
.then(literal("high").then(highNumber)));
|
|
|
|
assertThat(subject.execute("base low 5 ending", source), is(5));
|
|
assertThat(subject.execute("base high 15 ending", source), is(15));
|
|
}
|
|
|
|
@Test
|
|
public void testExecuteOrphanedSubcommand() {
|
|
subject.register(literal("foo").then(
|
|
argument("bar", integer())
|
|
).executes(command));
|
|
|
|
try {
|
|
subject.execute("foo 5", source);
|
|
fail();
|
|
} catch (final CommandSyntaxException ex) {
|
|
assertThat(ex.getType(), is(CommandSyntaxException.BUILT_IN_EXCEPTIONS.dispatcherUnknownCommand()));
|
|
assertThat(ex.getCursor(), is(5));
|
|
}
|
|
}
|
|
|
|
@SuppressWarnings("unchecked")
|
|
@Test
|
|
public void testExecute_invalidOther() throws Exception {
|
|
final Command<Object> wrongCommand = mock(Command.class);
|
|
subject.register(literal("w").executes(wrongCommand));
|
|
subject.register(literal("world").executes(command));
|
|
|
|
assertThat(subject.execute("world", source), is(42));
|
|
verify(wrongCommand, never()).run(any());
|
|
verify(command).run(any());
|
|
}
|
|
|
|
@Test
|
|
public void parse_noSpaceSeparator() {
|
|
subject.register(literal("foo").then(argument("bar", integer()).executes(command)));
|
|
|
|
try {
|
|
subject.execute("foo$", source);
|
|
fail();
|
|
} catch (final CommandSyntaxException ex) {
|
|
assertThat(ex.getType(), is(CommandSyntaxException.BUILT_IN_EXCEPTIONS.dispatcherUnknownCommand()));
|
|
assertThat(ex.getCursor(), is(0));
|
|
}
|
|
}
|
|
|
|
@SuppressWarnings("unchecked")
|
|
@Test
|
|
public void parse_multipleSpaceSeparator() throws Exception {
|
|
subject.register(literal("foo").then(literal("bar").executes(command)));
|
|
|
|
assertThat(subject.execute("foo bar", source), is(42));
|
|
verify(command).run(any(CommandContext.class));
|
|
}
|
|
|
|
@Test
|
|
public void testExecuteInvalidSubcommand() {
|
|
subject.register(literal("foo").then(
|
|
argument("bar", integer())
|
|
).executes(command));
|
|
|
|
try {
|
|
subject.execute("foo bar", source);
|
|
fail();
|
|
} catch (final CommandSyntaxException ex) {
|
|
assertThat(ex.getType(), is(CommandSyntaxException.BUILT_IN_EXCEPTIONS.readerExpectedInt()));
|
|
assertThat(ex.getCursor(), is(4));
|
|
}
|
|
}
|
|
|
|
@Test
|
|
public void testGetPath() {
|
|
final LiteralCommandNode<Object> bar = literal("bar").build();
|
|
subject.register(literal("foo").then(bar));
|
|
|
|
assertThat(subject.getPath(bar), equalTo(Lists.newArrayList("foo", "bar")));
|
|
}
|
|
|
|
@Test
|
|
public void testFindNodeExists() {
|
|
final LiteralCommandNode<Object> bar = literal("bar").build();
|
|
subject.register(literal("foo").then(bar));
|
|
|
|
assertThat(subject.findNode(Lists.newArrayList("foo", "bar")), is(bar));
|
|
}
|
|
|
|
@Test
|
|
public void testFindNodeDoesntExist() {
|
|
assertThat(subject.findNode(Lists.newArrayList("foo", "bar")), is(nullValue()));
|
|
}
|
|
|
|
@SuppressWarnings("unchecked")
|
|
@Test
|
|
public void testCompletionWithErroredFutureReturnsCompletedFuture() {
|
|
final LiteralCommandNode<Object> bar = literal("bar").build();
|
|
final LiteralCommandNode<Object> baz = mock(LiteralCommandNode.class);
|
|
when(baz.getLiteral()).thenReturn("baz");
|
|
when(baz.listSuggestions(any(), any())).thenAnswer(x -> {
|
|
final CompletableFuture<Suggestions> future = new CompletableFuture<>();
|
|
future.completeExceptionally(new IllegalArgumentException());
|
|
return future;
|
|
});
|
|
subject.register(literal("foo").then(bar).then(baz));
|
|
|
|
final ParseResults<Object> parseResults = subject.parse("foo b", source);
|
|
final Suggestions suggestions = subject.getCompletionSuggestions(parseResults).join();
|
|
final Collection<String> suggestionCollection = suggestions.getList().stream().map(Suggestion::getText).collect(Collectors.toList());
|
|
assertThat(Lists.newArrayList("bar"), is(suggestionCollection));
|
|
}
|
|
|
|
@Test
|
|
public void testResultConsumerInNonErrorRun() throws CommandSyntaxException {
|
|
subject.setConsumer(consumer);
|
|
|
|
subject.register(literal("foo").executes(command));
|
|
when(command.run(any())).thenReturn(5);
|
|
|
|
assertThat(subject.execute("foo", source), is(5));
|
|
verify(consumer).onCommandComplete(any(), eq(true), eq(5));
|
|
verifyNoMoreInteractions(consumer);
|
|
}
|
|
|
|
@Test
|
|
public void testResultConsumerInForkedNonErrorRun() throws CommandSyntaxException {
|
|
subject.setConsumer(consumer);
|
|
|
|
subject.register(literal("foo").executes(c -> (Integer)(c.getSource())));
|
|
final Object[] contexts = new Object[] {9, 10, 11};
|
|
|
|
subject.register(literal("repeat").fork(subject.getRoot(), context -> Arrays.asList(contexts)));
|
|
|
|
assertThat(subject.execute("repeat foo", source), is(contexts.length));
|
|
verify(consumer).onCommandComplete(argThat(contextSourceMatches(contexts[0])), eq(true), eq(9));
|
|
verify(consumer).onCommandComplete(argThat(contextSourceMatches(contexts[1])), eq(true), eq(10));
|
|
verify(consumer).onCommandComplete(argThat(contextSourceMatches(contexts[2])), eq(true), eq(11));
|
|
verifyNoMoreInteractions(consumer);
|
|
}
|
|
|
|
@Test
|
|
public void testExceptionInNonForkedCommand() throws CommandSyntaxException {
|
|
subject.setConsumer(consumer);
|
|
subject.register(literal("crash").executes(command));
|
|
final CommandSyntaxException exception = CommandSyntaxException.BUILT_IN_EXCEPTIONS.readerExpectedBool().create();
|
|
when(command.run(any())).thenThrow(exception);
|
|
|
|
try {
|
|
subject.execute("crash", source);
|
|
fail();
|
|
} catch (final CommandSyntaxException ex) {
|
|
assertThat(ex, is(exception));
|
|
}
|
|
|
|
verify(consumer).onCommandComplete(any(), eq(false), eq(0));
|
|
verifyNoMoreInteractions(consumer);
|
|
}
|
|
|
|
@Test
|
|
public void testExceptionInNonForkedRedirectedCommand() throws CommandSyntaxException {
|
|
subject.setConsumer(consumer);
|
|
subject.register(literal("crash").executes(command));
|
|
subject.register(literal("redirect").redirect(subject.getRoot()));
|
|
|
|
final CommandSyntaxException exception = CommandSyntaxException.BUILT_IN_EXCEPTIONS.readerExpectedBool().create();
|
|
when(command.run(any())).thenThrow(exception);
|
|
|
|
try {
|
|
subject.execute("redirect crash", source);
|
|
fail();
|
|
} catch (final CommandSyntaxException ex) {
|
|
assertThat(ex, is(exception));
|
|
}
|
|
|
|
verify(consumer).onCommandComplete(any(), eq(false), eq(0));
|
|
verifyNoMoreInteractions(consumer);
|
|
}
|
|
|
|
@Test
|
|
public void testExceptionInForkedRedirectedCommand() throws CommandSyntaxException {
|
|
subject.setConsumer(consumer);
|
|
subject.register(literal("crash").executes(command));
|
|
subject.register(literal("redirect").fork(subject.getRoot(), Collections::singleton));
|
|
|
|
final CommandSyntaxException exception = CommandSyntaxException.BUILT_IN_EXCEPTIONS.readerExpectedBool().create();
|
|
when(command.run(any())).thenThrow(exception);
|
|
|
|
assertThat(subject.execute("redirect crash", source), is(0));
|
|
verify(consumer).onCommandComplete(any(), eq(false), eq(0));
|
|
verifyNoMoreInteractions(consumer);
|
|
}
|
|
|
|
@Test
|
|
public void testExceptionInNonForkedRedirect() throws CommandSyntaxException {
|
|
final CommandSyntaxException exception = CommandSyntaxException.BUILT_IN_EXCEPTIONS.readerExpectedBool().create();
|
|
|
|
subject.setConsumer(consumer);
|
|
subject.register(literal("noop").executes(command));
|
|
subject.register(literal("redirect").redirect(subject.getRoot(), context -> {
|
|
throw exception;
|
|
}));
|
|
|
|
when(command.run(any())).thenReturn(3);
|
|
|
|
try {
|
|
subject.execute("redirect noop", source);
|
|
fail();
|
|
} catch (final CommandSyntaxException ex) {
|
|
assertThat(ex, is(exception));
|
|
}
|
|
|
|
verifyZeroInteractions(command);
|
|
verify(consumer).onCommandComplete(any(), eq(false), eq(0));
|
|
verifyNoMoreInteractions(consumer);
|
|
}
|
|
|
|
@Test
|
|
public void testExceptionInForkedRedirect() throws CommandSyntaxException {
|
|
final CommandSyntaxException exception = CommandSyntaxException.BUILT_IN_EXCEPTIONS.readerExpectedBool().create();
|
|
|
|
subject.setConsumer(consumer);
|
|
subject.register(literal("noop").executes(command));
|
|
subject.register(literal("redirect").fork(subject.getRoot(), context -> {
|
|
throw exception;
|
|
}));
|
|
|
|
when(command.run(any())).thenReturn(3);
|
|
|
|
|
|
assertThat(subject.execute("redirect noop", source), is(0));
|
|
|
|
verifyZeroInteractions(command);
|
|
verify(consumer).onCommandComplete(any(), eq(false), eq(0));
|
|
verifyNoMoreInteractions(consumer);
|
|
}
|
|
|
|
@Test
|
|
public void testPartialExceptionInForkedRedirect() throws CommandSyntaxException {
|
|
final CommandSyntaxException exception = CommandSyntaxException.BUILT_IN_EXCEPTIONS.readerExpectedBool().create();
|
|
final Object otherSource = new Object();
|
|
final Object rejectedSource = new Object();
|
|
|
|
subject.setConsumer(consumer);
|
|
subject.register(literal("run").executes(command));
|
|
subject.register(literal("split").fork(subject.getRoot(), context -> Arrays.asList(source, rejectedSource, otherSource)));
|
|
subject.register(literal("filter").fork(subject.getRoot(), context -> {
|
|
final Object currentSource = context.getSource();
|
|
if (currentSource == rejectedSource) {
|
|
throw exception;
|
|
}
|
|
return Collections.singleton(currentSource);
|
|
}));
|
|
|
|
when(command.run(any())).thenReturn(3);
|
|
|
|
assertThat(subject.execute("split filter run", source), is(2));
|
|
|
|
verify(command).run(argThat(contextSourceMatches(source)));
|
|
verify(command).run(argThat(contextSourceMatches(otherSource)));
|
|
verifyNoMoreInteractions(command);
|
|
|
|
verify(consumer).onCommandComplete(argThat(contextSourceMatches(rejectedSource)), eq(false), eq(0));
|
|
verify(consumer).onCommandComplete(argThat(contextSourceMatches(source)), eq(true), eq(3));
|
|
verify(consumer).onCommandComplete(argThat(contextSourceMatches(otherSource)), eq(true), eq(3));
|
|
verifyNoMoreInteractions(consumer);
|
|
}
|
|
|
|
public static Matcher<CommandContext<Object>> contextSourceMatches(final Object source) {
|
|
return new CustomMatcher<CommandContext<Object>>("source " + source) {
|
|
@Override
|
|
public boolean matches(Object object) {
|
|
return (object instanceof CommandContext) && ((CommandContext<?>) object).getSource() == source;
|
|
}
|
|
};
|
|
}
|
|
} |