/* * Copyright 2011 ZerothAngel <zerothangel@tyrannyofheaven.org> * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.tyrannyofheaven.bukkit.util.command; import java.util.Arrays; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import junit.framework.Assert; import org.bukkit.Server; import org.bukkit.command.CommandSender; import org.bukkit.permissions.Permission; import org.bukkit.permissions.PermissionAttachment; import org.bukkit.permissions.PermissionAttachmentInfo; import org.bukkit.plugin.Plugin; import org.bukkit.util.StringUtil; import org.junit.Before; import org.junit.Test; import org.tyrannyofheaven.bukkit.util.permissions.PermissionException; public class CommandTest { private final StringBuilder out = new StringBuilder(); private final Set<String> permissions = new HashSet<>(); private final CommandSender dummySender; private final HandlerExecutor<MyPlugin> he; public CommandTest() { // Some mock objects MyPlugin plugin = new MyPlugin(); dummySender = new CommandSender() { @Override public Server getServer() { return null; } @Override public void sendMessage(String message) { out.append(message); out.append('\n'); } @Override public PermissionAttachment addAttachment(Plugin plugin, String name, boolean value) { return null; } @Override public PermissionAttachment addAttachment(Plugin plugin) { return null; } @Override public PermissionAttachment addAttachment(Plugin plugin, String name, boolean value, int ticks) { return null; } @Override public PermissionAttachment addAttachment(Plugin plugin, int ticks) { return null; } @Override public Set<PermissionAttachmentInfo> getEffectivePermissions() { return null; } @Override public boolean hasPermission(String name) { return permissions.contains(name); } @Override public boolean hasPermission(Permission perm) { return false; } @Override public boolean isPermissionSet(String name) { return false; } @Override public boolean isPermissionSet(Permission perm) { return false; } @Override public void recalculatePermissions() { } @Override public void removeAttachment(PermissionAttachment attachment) { } @Override public boolean isOp() { return false; } @Override public void setOp(boolean value) { } @Override public String getName() { return null; } @Override public void sendMessage(String[] arg0) { } }; he = new HandlerExecutor<>(plugin, new MyHandler()); } @Before public void setUp() { out.delete(0, out.length()); permissions.clear(); } @Test public void testHandlerExecutor() throws Throwable { // No positional params, boolean flag he.execute(dummySender, "hello", "hello", new String[0]); Assert.assertEquals("Hello World!\n", out.toString()); out.delete(0, out.length()); he.execute(dummySender, "hello", "hello", new String[] { "-f" }); Assert.assertEquals("Hello World!\nWith flag!\n", out.toString()); out.delete(0, out.length()); he.execute(dummySender, "hello", "greetings", new String[0]); Assert.assertEquals("Hello World!\n", out.toString()); out.delete(0, out.length()); he.execute(dummySender, "hello", "greetings", new String[] { "-f" }); Assert.assertEquals("Hello World!\nWith flag!\n", out.toString()); out.delete(0, out.length()); // Extra positional boolean good = false; try { he.execute(dummySender, "hello", "hello", new String[] { "extra" }); } catch (ParseException e) { good = true; } Assert.assertTrue(good); // Required positional param, flag with value good = false; try { he.execute(dummySender, "greet", "greet", new String[0]); } catch (ParseException e) { good = true; } Assert.assertTrue(good); good = false; try { he.execute(dummySender, "greet", "greet", new String[] { "-o" }); } catch (ParseException e) { good = true; } Assert.assertTrue(good); good = false; try { he.execute(dummySender, "greet", "greet", new String[] { "-o", "foo" }); } catch (ParseException e) { good = true; } Assert.assertTrue(good); he.execute(dummySender, "greet", "greet", new String[] { "-o", "foo", "bar" }); Assert.assertEquals("Hello, bar\nWith option = foo!\n", out.toString()); out.delete(0, out.length()); he.execute(dummySender, "greet", "greet", new String[] { "-o", "foo", "bar", "garply" }); Assert.assertEquals("Hello, bar\nWith option = foo!\n", out.toString()); out.delete(0, out.length()); // Positional argument that starts with - he.execute(dummySender, "greet", "greet", new String[] { "--", "-garply" }); Assert.assertEquals("Hello, -garply\n", out.toString()); out.delete(0, out.length()); // String[] parameter he.execute(dummySender, "say", "say", new String[] { "Hello", "there" }); Assert.assertEquals("Hello there\n", out.toString()); out.delete(0, out.length()); // No flags, so should not parse -Hello as one. he.execute(dummySender, "say", "say", new String[] { "-Hello", "there" }); Assert.assertEquals("-Hello there\n", out.toString()); out.delete(0, out.length()); // Sub-command good = false; try { he.execute(dummySender, "foo", "foo", new String[0]); } catch (ParseException e) { good = true; } Assert.assertTrue(good); he.execute(dummySender, "foo", "foo", new String[] { "hello" }); Assert.assertEquals("Hello from the foo sub-command!\n", out.toString()); out.delete(0, out.length()); good = false; try { he.execute(dummySender, "foo", "foo", new String[] { "hello", "extra" }); } catch (ParseException e) { good = true; } Assert.assertTrue(good); good = false; permissions.clear(); try { he.execute(dummySender, "secret", "secret", new String[0]); } catch (PermissionException e) { good = true; } Assert.assertTrue(good); permissions.add("foo.secret"); he.execute(dummySender, "secret", "secret", new String[0]); Assert.assertEquals("Spike has a crush on Rarity\nyay!\n", out.toString()); out.delete(0, out.length()); // Long/short flags he.execute(dummySender, "garply", "garply", new String[0]); // nothing Assert.assertEquals("no flag with option = null\n", out.toString()); out.delete(0, out.length()); he.execute(dummySender, "garply", "garply", new String[] { "-f" }); Assert.assertEquals("have flag with option = null\n", out.toString()); out.delete(0, out.length()); he.execute(dummySender, "garply", "garply", new String[] { "--flag" }); Assert.assertEquals("have flag with option = null\n", out.toString()); out.delete(0, out.length()); good = false; try { he.execute(dummySender, "garply", "garply", new String[] { "-o" }); } catch (MissingValueException e) { good = e.getOptionMetaData().getName().equals("-o"); } Assert.assertTrue(good); good = false; try { he.execute(dummySender, "garply", "garply", new String[] { "--option" }); } catch (MissingValueException e) { good = e.getOptionMetaData().getName().equals("-o"); } Assert.assertTrue(good); he.execute(dummySender, "garply", "garply", new String[] { "-o", "blah" }); Assert.assertEquals("no flag with option = blah\n", out.toString()); out.delete(0, out.length()); he.execute(dummySender, "garply", "garply", new String[] { "--option", "blah" }); Assert.assertEquals("no flag with option = blah\n", out.toString()); out.delete(0, out.length()); // Multi-flags he.execute(dummySender, "garply", "garply", new String[] { "-f", "-o", "blah" }); Assert.assertEquals("have flag with option = blah\n", out.toString()); out.delete(0, out.length()); he.execute(dummySender, "garply", "garply", new String[] { "-o", "blah", "-f"}); Assert.assertEquals("have flag with option = blah\n", out.toString()); out.delete(0, out.length()); good = false; try { he.execute(dummySender, "garply", "garply", new String[] { "-fo" }); } catch (MissingValueException e) { good = e.getOptionMetaData().getName().equals("-o"); } Assert.assertTrue(good); good = false; try { he.execute(dummySender, "garply", "garply", new String[] { "-of" }); } catch (MissingValueException e) { good = e.getOptionMetaData().getName().equals("-o"); } Assert.assertTrue(good); he.execute(dummySender, "garply", "garply", new String[] { "-fo", "blah" }); Assert.assertEquals("have flag with option = blah\n", out.toString()); out.delete(0, out.length()); he.execute(dummySender, "garply", "garply", new String[] { "-of", "blah" }); Assert.assertEquals("have flag with option = blah\n", out.toString()); out.delete(0, out.length()); // Dual multi-args he.execute(dummySender, "garply", "garply", new String[] { "-fo", "blah", "-t", "garply" }); Assert.assertEquals("have flag with option = blah\ngarply\n", out.toString()); out.delete(0, out.length()); he.execute(dummySender, "garply", "garply", new String[] { "-o", "blah", "-tf", "garply" }); Assert.assertEquals("have flag with option = blah\ngarply\n", out.toString()); out.delete(0, out.length()); good = false; try { he.execute(dummySender, "garply", "garply", new String[] { "-oft", "blah" }); } catch (MissingValueException e) { good = e.getOptionMetaData().getName().equals("-t"); } Assert.assertTrue(good); he.execute(dummySender, "garply", "garply", new String[] { "-oft", "blah", "garply" }); Assert.assertEquals("have flag with option = blah\ngarply\n", out.toString()); out.delete(0, out.length()); he.execute(dummySender, "garply", "garply", new String[] { "-tfo", "blah", "garply" }); Assert.assertEquals("have flag with option = garply\nblah\n", out.toString()); out.delete(0, out.length()); } @Test public void testTabCompletion() throws Throwable { TypeCompleter myTypeCompleter = new TypeCompleter() { @Override public List<String> complete(Class<?> clazz, String arg, CommandSender sender, String partial) { if (clazz == String.class) { if (StringUtil.startsWithIgnoreCase("ZerothAngel", partial)) { return Collections.singletonList("ZerothAngel"); } } return Collections.emptyList(); } }; Map<String, TypeCompleter> typeCompleterRegistry = Collections.singletonMap("myCompleter", myTypeCompleter); // No further args testCompletions(he.getTabCompletions(dummySender, "hello", "hello", new String[] { "" }, null, null, typeCompleterRegistry)); // Flag testCompletions(he.getTabCompletions(dummySender, "hello", "hello", new String[] { "-" }, null, null, typeCompleterRegistry), "--", "-f"); // Custom TestCompleter, empty query testCompletions(he.getTabCompletions(dummySender, "greet", "greet", new String[] { "" }, null, null, typeCompleterRegistry), "ZerothAngel"); // Custom TestCompleter, query doesn't match testCompletions(he.getTabCompletions(dummySender, "greet", "greet", new String[] { "a" }, null, null, typeCompleterRegistry)); // Custom TestCompleter, query match testCompletions(he.getTabCompletions(dummySender, "greet", "greet", new String[] { "zero" }, null, null, typeCompleterRegistry), "ZerothAngel"); // Flag testCompletions(he.getTabCompletions(dummySender, "greet", "greet", new String[] { "-" }, null, null, typeCompleterRegistry), "--", "-o"); // Flag value testCompletions(he.getTabCompletions(dummySender, "greet", "greet", new String[] { "-o", "" }, null, null, typeCompleterRegistry), "<value>"); // Flag after positional testCompletions(he.getTabCompletions(dummySender, "greet", "greet", new String[] { "ZerothAngel", "-" }, null, null, typeCompleterRegistry)); // Sub-command argument testCompletions(he.getTabCompletions(dummySender, "bar", "bar", new String[] { "" }, null, null, typeCompleterRegistry), "<name>"); // Sub-command itself testCompletions(he.getTabCompletions(dummySender, "bar", "bar", new String[] { "foo", "" }, null, null, typeCompleterRegistry), "greet"); testCompletions(he.getTabCompletions(dummySender, "bar", "bar", new String[] { "foo", "gr" }, null, null, typeCompleterRegistry), "greet"); testCompletions(he.getTabCompletions(dummySender, "bar", "bar", new String[] { "foo", "b" }, null, null, typeCompleterRegistry)); // Sub-command testCompletions(he.getTabCompletions(dummySender, "bar", "bar", new String[] { "foo", "greet", "" }, null, null, typeCompleterRegistry)); // Sub-command flag testCompletions(he.getTabCompletions(dummySender, "bar", "bar", new String[] { "foo", "greet", "-" }, null, null, typeCompleterRegistry), "--", "-o"); // Sub-command flag value testCompletions(he.getTabCompletions(dummySender, "bar", "bar", new String[] { "foo", "greet", "-o", "" }, null, null, typeCompleterRegistry), "<option>"); // Varargs testCompletions(he.getTabCompletions(dummySender, "say", "say", new String[] { "" }, null, null, typeCompleterRegistry), "ZerothAngel"); testCompletions(he.getTabCompletions(dummySender, "say", "say", new String[] { "a" }, null, null, typeCompleterRegistry)); testCompletions(he.getTabCompletions(dummySender, "say", "say", new String[] { "ZerothAngel", "" }, null, null, typeCompleterRegistry), "ZerothAngel"); } private void testCompletions(List<String> actual, String... expected) throws Throwable { Set<String> actualSet = new HashSet<>(actual); Set<String> expectedSet = new HashSet<>(Arrays.asList(expected)); Assert.assertEquals(expectedSet, actualSet); } }