/*
* JBoss, Home of Professional Open Source.
* Copyright 2016, Red Hat Middleware LLC, and individual contributors
* as indicated by the @author tags. See the copyright.txt file in the
* distribution for a full listing of individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*
*/
package org.jboss.as.cli.completion.address.test;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.jboss.as.cli.CliInitializationException;
import org.jboss.as.cli.CommandFormatException;
import org.jboss.as.cli.CommandLineException;
import org.jboss.as.cli.completion.mock.MockNode;
import org.junit.Assert;
import org.junit.Test;
/**
* https://issues.jboss.org/browse/WFCORE-1971
*
* This test case is checking an offset and candidates returned by the OperationRequestCompleter when parsing
* an operation address.
*
* The offset determines where the candidate completions would be inserted in the buffer.
*
* @author Tomas Hofman (thofman@redhat.com)
*/
@SuppressWarnings("ArraysAsListWithZeroOrOneArgument")
public class CompletionWithQuotesTestCase extends AbstractAddressCompleterTest {
public CompletionWithQuotesTestCase() throws IOException, CliInitializationException {
MockNode root1 = addRoot("type");
MockNode root2 = addRoot("test");
root2.addChild("esca\'ped");
root2.addChild("esca\\ped");
root2.addChild("esca\"ped");
root2.addChild("esca/ped");
root2.addChild("esca:ped");
root2.addChild("esca=ped");
root2.addChild("multiple/escaped1");
root2.addChild("multiple/escaped2");
root2.addChild("multiple\"escaped1");
root2.addChild("multiple\"escaped2");
root2.addChild("test::test");
MockNode typeOne = root1.addChild("typeOne");
root1.addChild("typeTwo");
root1.addChild("type\"Three");
MockNode subtype = typeOne.addChild("subtype");
subtype.addChild("subtypeOne");
}
@Test
public void testNodeNameMultipleCandidates() throws CommandLineException {
String cmd = "/type=";
checkCompletion(cmd, cmd.length(), Arrays.asList("type\"Three", "typeOne", "typeTwo"));
cmd = "/type=type";
checkCompletion(cmd, cmd.indexOf('=') + 1, Arrays.asList("type\"Three", "typeOne", "typeTwo"));
cmd = "/type=\"";
checkCompletion(cmd, cmd.length(), Arrays.asList("type\"Three", "typeOne", "typeTwo"));
cmd = "/type=\"type";
checkCompletion(cmd, cmd.indexOf('"') + 1, Arrays.asList("type\"Three", "typeOne", "typeTwo"));
cmd = "/type=\"typeTwo\"";
checkCompletion(cmd, cmd.length(), Arrays.asList(":", "/"));
}
@Test
public void testNodeNameSingleCandidate() throws CommandFormatException {
String cmd = "/type=typeOne/subtype=";
checkCompletion(cmd, cmd.lastIndexOf('=') + 1, Arrays.asList("subtypeOne"));
cmd = "/type=typeOne/subtype=subtype";
checkCompletion(cmd, cmd.lastIndexOf('=') + 1, Arrays.asList("subtypeOne"));
cmd = "/type=typeOne/subtype=\"";
checkCompletion(cmd, cmd.lastIndexOf('"'), Arrays.asList("\"subtypeOne\""));
cmd = "/type=typeOne/subtype=\"sub";
checkCompletion(cmd, cmd.lastIndexOf('"'), Arrays.asList("\"subtypeOne\""));
cmd = "/type=typeOne/subtype=\"subtypeOne";
checkCompletion(cmd, cmd.length(), Arrays.asList("\""));
}
@Test
public void testNodeTypeMultipleCandidates() throws CommandFormatException {
String cmd = "/";
checkCompletion(cmd, 1, Arrays.asList("test", "type"));
cmd = "/t";
checkCompletion(cmd, 1, Arrays.asList("test", "type"));
}
@Test
public void testNodeTypeSingleCandidate() throws CommandFormatException {
String cmd = "/type=typeOne/";
checkCompletion(cmd, cmd.lastIndexOf('/') + 1, Arrays.asList("subtype="));
cmd = "/type=typeOne/sub";
checkCompletion(cmd, cmd.lastIndexOf('/') + 1, Arrays.asList("subtype="));
}
@Test
public void testOpenQuotesMultipleCandidates() throws CommandFormatException {
String cmd = "/\"";
checkCompletion(cmd, 2, Arrays.asList("test", "type"));
cmd = "/\"t";
checkCompletion(cmd, 2, Arrays.asList("test", "type"));
// escaped char
cmd = "/test=multiple\\/\"";
checkCompletion(cmd, cmd.indexOf('=') + 3, Arrays.asList("multiple/escaped1", "multiple/escaped2"));
cmd = "/test=multiple\"/";
checkCompletion(cmd, cmd.indexOf('=') + 2, Arrays.asList("multiple/escaped1", "multiple/escaped2"));
cmd = "/test=multiple\"\\/";
checkCompletion(cmd, cmd.indexOf('=') + 3, Arrays.asList("multiple/escaped1", "multiple/escaped2"));
cmd = "/test=multiple\\\"\"";
checkCompletion(cmd, cmd.indexOf('=') + 2, Arrays.asList("multiple\\\"escaped1", "multiple\\\"escaped2"));
cmd = "/test=multiple\"\\\"";
checkCompletion(cmd, cmd.indexOf('=') + 2, Arrays.asList("multiple\\\"escaped1", "multiple\\\"escaped2"));
}
@Test
public void testOpenQuotesSingleCandidate() throws CommandFormatException {
String cmd = "/type=typeOne/\"";
checkCompletion(cmd, cmd.lastIndexOf('"'), Arrays.asList("\"subtype\"="));
cmd = "/type=typeOne/\"sub";
checkCompletion(cmd, cmd.lastIndexOf('"'), Arrays.asList("\"subtype\"="));
cmd = "/type=typeOne/\"subtype";
checkCompletion(cmd, cmd.length(), Arrays.asList("\""));
cmd = "/type=typeOne/\"subtype\"";
checkCompletion(cmd, cmd.length(), Arrays.asList("="));
// escaped char
cmd = "/test=esca\\:\"";
checkCompletion(cmd, cmd.indexOf('=') + 2, Arrays.asList("\"esca:ped\""));
cmd = "/test=esca\":";
checkCompletion(cmd, cmd.indexOf('=') + 1, Arrays.asList("\"esca:ped\""));
cmd = "/test=esca\"\\:";
checkCompletion(cmd, cmd.indexOf('=') + 2, Arrays.asList("\"esca:ped\""));
}
@Test
public void testClosedQuotesMultipleCandidates() throws CommandFormatException {
String cmd = "/\"t\"";
checkCompletion(cmd, cmd.indexOf('"') + 2, Arrays.asList("test", "type"));
cmd = "/type=\"type\""; // single closed quotes
checkCompletion(cmd, cmd.indexOf('"') + 2, Arrays.asList("type\"Three", "typeOne", "typeTwo"));
cmd = "/type=\"ty\"\"pe\""; // multiple closed quotes
checkCompletion(cmd, cmd.indexOf('"') + 4, Arrays.asList("type\"Three", "typeOne", "typeTwo"));
// escaped char in quotes
cmd = "/test=\"multiple/\"";
checkCompletion(cmd, cmd.indexOf('"') + 1, Arrays.asList("multiple\\/escaped1", "multiple\\/escaped2"));
cmd = "/test=\"multiple\\/\"";
checkCompletion(cmd, cmd.indexOf('"') + 2, Arrays.asList("multiple\\/escaped1", "multiple\\/escaped2"));
cmd = "/test=\"multiple\"\\/";
checkCompletion(cmd, cmd.indexOf('"') + 2, Arrays.asList("multiple\\/escaped1", "multiple\\/escaped2"));
}
@Test
public void testClosedQuotesSingleCandidate() throws CommandFormatException {
String cmd = "/\"te\"";
checkCompletion(cmd, cmd.indexOf('"') + 2, Arrays.asList("test="));
cmd = "/type=\"typeO\"";
checkCompletion(cmd, cmd.indexOf('"') + 2, Arrays.asList("typeOne"));
cmd = "/type=\"type\\\"\"";
checkCompletion(cmd, cmd.indexOf('"') + 2, Arrays.asList("type\\\"Three"));
// escaped char in quotes
cmd = "/test=\"esca:\"";
checkCompletion(cmd, cmd.indexOf('"') + 1, Arrays.asList("esca\\:ped"));
cmd = "/test=\"esca\\:\"";
checkCompletion(cmd, cmd.indexOf('"') + 2, Arrays.asList("esca\\:ped"));
cmd = "/test=\"esca\"\\:";
checkCompletion(cmd, cmd.indexOf('"') + 2, Arrays.asList("esca\\:ped"));
}
@Test
public void testClosedAndOpenQuotesSingleCandidate() throws CommandFormatException {
String cmd = "/\"te\"\"";
checkCompletion(cmd, cmd.indexOf('"') + 2, Arrays.asList("\"test\"="));
cmd = "/\"ty\"\"";
checkCompletion(cmd, cmd.indexOf('"') + 2, Arrays.asList("\"type\"="));
cmd = "/\"ty\"\"p\"\"";
checkCompletion(cmd, cmd.indexOf('"') + 4, Arrays.asList("\"type\"="));
cmd = "/type=\"typeO\"\"";
checkCompletion(cmd, cmd.indexOf('"') + 2, Arrays.asList("\"typeOne\""));
cmd = "/type=\"type\"\"O\"\""; // two closed, last open
checkCompletion(cmd, cmd.indexOf('"') + 4, Arrays.asList("\"typeOne\""));
}
@Test
public void testLeadingSpacesSingleCandidate() throws CommandFormatException {
// leading space
String cmd = "/type=typeOne/ ";
checkCompletion(cmd, cmd.lastIndexOf('/') + 2, Arrays.asList("subtype="));
cmd = "/type=typeOne/subtype= ";
checkCompletion(cmd, cmd.lastIndexOf('=') + 2, Arrays.asList("subtypeOne"));
// combination of leading space and opening quote
cmd = "/type=typeOne/ \"";
checkCompletion(cmd, cmd.lastIndexOf('"'), Arrays.asList("\"subtype\"="));
cmd = "/type=typeOne/subtype= \"";
checkCompletion(cmd, cmd.lastIndexOf('"'), Arrays.asList("\"subtypeOne\""));
// combination of leading space and starting characters
cmd = "/type=typeOne/ sub";
checkCompletion(cmd, cmd.lastIndexOf("sub"), Arrays.asList("subtype="));
cmd = "/type=typeOne/subtype= sub";
checkCompletion(cmd, cmd.lastIndexOf("sub"), Arrays.asList("subtypeOne"));
// combination of leading space, opening quote and starting characters
cmd = "/type=typeOne/ \"sub";
checkCompletion(cmd, cmd.lastIndexOf('"'), Arrays.asList("\"subtype\"="));
cmd = "/type=typeOne/subtype= \"sub";
checkCompletion(cmd, cmd.lastIndexOf('"'), Arrays.asList("\"subtypeOne\""));
}
@Test
public void testLeadingSpacesMultipleCandidates() throws CommandFormatException {
// leading space
String cmd = "/ ";
checkCompletion(cmd, cmd.lastIndexOf('/') + 2, Arrays.asList("test", "type"));
cmd = "/type= ";
checkCompletion(cmd, cmd.lastIndexOf('=') + 2, Arrays.asList("type\"Three", "typeOne", "typeTwo"));
// combination of leading space and opening quote
cmd = "/ \"";
checkCompletion(cmd, cmd.lastIndexOf('/') + 3, Arrays.asList("test", "type"));
cmd = "/type= \"";
checkCompletion(cmd, cmd.lastIndexOf('=') + 3, Arrays.asList("type\"Three", "typeOne", "typeTwo"));
// combination of leading space and starting characters
cmd = "/ t";
checkCompletion(cmd, cmd.lastIndexOf('/') + 2, Arrays.asList("test", "type"));
cmd = "/type= type";
checkCompletion(cmd, cmd.lastIndexOf('=') + 2, Arrays.asList("type\"Three", "typeOne", "typeTwo"));
// combination of leading space, opening quote and starting characters
cmd = "/ \"t";
checkCompletion(cmd, cmd.lastIndexOf('/') + 3, Arrays.asList("test", "type"));
cmd = "/type= \"type";
checkCompletion(cmd, cmd.lastIndexOf('=') + 3, Arrays.asList("type\"Three", "typeOne", "typeTwo"));
}
@Test
public void testEscapedCharactersWithoutQuotesSingleCandidate() throws CommandFormatException {
String cmd = "/test=esca\\\"";
checkCompletion(cmd, cmd.indexOf('=') + 1, Arrays.asList("esca\\\"ped"));
cmd = "/test=esca\\'";
checkCompletion(cmd, cmd.indexOf('=') + 1, Arrays.asList("esca\\'ped"));
cmd = "/test=esca\\\\";
checkCompletion(cmd, cmd.indexOf('=') + 1, Arrays.asList("esca\\\\ped"));
cmd = "/test=esca\\/";
checkCompletion(cmd, cmd.indexOf('=') + 1, Arrays.asList("esca\\/ped"));
cmd = "/test=esca\\:";
checkCompletion(cmd, cmd.indexOf('=') + 1, Arrays.asList("esca\\:ped"));
cmd = "/test=esca\\=";
checkCompletion(cmd, cmd.indexOf('=') + 1, Arrays.asList("esca\\=ped"));
}
@Test
public void testEscapedCharactersWithoutQuotesMultipleCandidates() throws CommandFormatException {
String cmd = "/test=multiple\\/";
checkCompletion(cmd, cmd.indexOf('=') + 1, Arrays.asList("multiple\\/escaped1", "multiple\\/escaped2"));
cmd = "/test=multiple\\\"";
checkCompletion(cmd, cmd.indexOf('=') + 1, Arrays.asList("multiple\\\"escaped1", "multiple\\\"escaped2"));
}
@Test
public void testEscapedCharactersWithQuotesSingleCandidate() throws CommandFormatException {
// only " and \ should be escaped inside quotes
String cmd = "/test=\"esca\\\"";
checkCompletion(cmd, cmd.indexOf('"'), Arrays.asList("\"esca\\\"ped\""));
cmd = "/test=\"esca'";
checkCompletion(cmd, cmd.indexOf('"'), Arrays.asList("\"esca'ped\""));
cmd = "/test=\"esca\\'"; // unnecessarily escaped
checkCompletion(cmd, cmd.indexOf('"') + 1, Arrays.asList("\"esca'ped\""));
cmd = "/test=\"esca\\\\";
checkCompletion(cmd, cmd.indexOf('"'), Arrays.asList("\"esca\\\\ped\""));
cmd = "/test=\"esca/";
checkCompletion(cmd, cmd.indexOf('"'), Arrays.asList("\"esca/ped\""));
cmd = "/test=\"esca\\/"; // unnecessarily escaped
checkCompletion(cmd, cmd.indexOf('"') + 1, Arrays.asList("\"esca/ped\""));
cmd = "/test=\"esca:";
checkCompletion(cmd, cmd.indexOf('"'), Arrays.asList("\"esca:ped\""));
cmd = "/test=\"esca\\:"; // unnecessarily escaped
checkCompletion(cmd, cmd.indexOf('"') + 1, Arrays.asList("\"esca:ped\""));
cmd = "/test=\"esca=";
checkCompletion(cmd, cmd.indexOf('"'), Arrays.asList("\"esca=ped\""));
cmd = "/test=\"esca\\="; // unnecessarily escaped
checkCompletion(cmd, cmd.indexOf('"') + 1, Arrays.asList("\"esca=ped\""));
}
@Test
public void testEscapedCharactersWithQuotesMultipleCandidates() throws CommandFormatException {
String cmd = "/test=\"multiple/";
checkCompletion(cmd, cmd.indexOf('"') + 1, Arrays.asList("multiple/escaped1", "multiple/escaped2"));
cmd = "/test=\"multiple\\/";
checkCompletion(cmd, cmd.indexOf('"') + 2, Arrays.asList("multiple/escaped1", "multiple/escaped2"));
cmd = "/test=\"multiple\\\"";
checkCompletion(cmd, cmd.indexOf('"') + 1, Arrays.asList("multiple\\\"escaped1", "multiple\\\"escaped2"));
}
private void checkCompletion(String cmd, int expectedOffset, List<String> expectedCandidates) throws CommandFormatException {
ArrayList<String> candidates = new ArrayList<>();
int offset = complete(cmd, candidates);
Assert.assertEquals("Wrong offset", expectedOffset, offset);
Assert.assertEquals("Expected different candidates", expectedCandidates, candidates);
}
private int complete(String buffer, List<String> candidates) throws CommandFormatException {
ctx.parseCommandLine(buffer, false);
return completer.complete(ctx, buffer, 0, candidates);
}
}