/*
* Copyright 2015 Julien Viet
*
* 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 io.termd.core.readline;
import io.termd.core.TestBase;
import io.termd.core.util.Helper;
import org.junit.Test;
import java.util.Arrays;
import java.util.Collections;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Supplier;
/**
* @author <a href="mailto:julien@julienviet.com">Julien Viet</a>
*/
public class CompletionTest extends TestBase {
@Test
public void testCompletion() {
TestTerm term = new TestTerm(this);
term.readline(line -> {
}, completion -> {
assertEquals(0, completion.prefix().length);
term.assertScreen("% ");
term.assertAt(0, 2);
testComplete();
});
term.read('\t');
await();
}
@Test
public void testIllegalCompletion() {
TestTerm term = new TestTerm(this);
AtomicReference<Completion> completion = new AtomicReference<>();
term.readline(line -> {
}, completion::set);
term.read('a');
term.read('\t');
completion.get().suggest(new int[]{'b'});
try {
completion.get().suggest(new int[]{'c'});
fail("Was expecting an IllegalStateException");
} catch (IllegalStateException ignore) {
}
}
@Test
public void testEmptyCompletion() {
TestTerm term = new TestTerm(this);
AtomicBoolean completed = new AtomicBoolean();
Supplier<String> line = term.readlineComplete(completion -> {
completion.end();
completed.set(true);
});
term.read('\t');
assertTrue(completed.get());
term.assertScreen("% ");
term.assertAt(0, 2);
term.read('a');
term.assertScreen("% a");
term.assertAt(0, 3);
term.read('\r');
term.assertScreen("% a");
term.assertAt(1, 0);
assertEquals("a", line.get());
}
@Test
public void testEmptyCompletionAsync() throws Exception {
TestTerm term = new TestTerm(this);
CompletableFuture<Completion> completed = new CompletableFuture<>();
Supplier<String> line = term.readlineComplete(completed::complete);
term.read('\t');
term.assertScreen("% ");
term.assertAt(0, 2);
term.read('a');
term.assertScreen("% ");
term.assertAt(0, 2);
Completion completion = completed.get();
completion.end();
term.executeTasks();
term.assertScreen("% a");
term.assertAt(0, 3);
term.read('\r');
term.assertScreen("% a");
term.assertAt(1, 0);
assertEquals("a", line.get());
}
@Test
public void testSingleTerminalCompletion() {
TestTerm term = new TestTerm(this);
AtomicBoolean completed = new AtomicBoolean();
Supplier<String> line = term.readlineComplete(completion -> {
completion.complete(new int[]{'b','c','d','e','f'}, true);
completed.set(true);
});
term.read('a');
term.assertScreen("% a");
term.assertAt(0, 3);
term.read('\t');
assertTrue(completed.get());
term.assertScreen("% abcdef ");
term.assertAt(0, 9);
term.read('g');
term.assertScreen("% abcdef g");
term.assertAt(0, 10);
term.read('\r');
term.assertScreen("% abcdef g");
term.assertAt(1, 0);
assertEquals("abcdef g", line.get());
}
@Test
public void testSingleTerminalCompletionAsync() throws Exception {
TestTerm term = new TestTerm(this);
CompletableFuture<Completion> completed = new CompletableFuture<>();
Supplier<String> line = term.readlineComplete(completed::complete);
term.read('a');
term.assertScreen("% a");
term.assertAt(0, 3);
term.read('\t');
Completion completion = completed.get();
term.assertScreen("% a");
term.assertAt(0, 3);
term.read('g');
term.assertScreen("% a");
term.assertAt(0, 3);
completion.complete(new int[]{'b', 'c', 'd', 'e', 'f'}, true);
term.executeTasks();
term.assertScreen("% abcdef g");
term.assertAt(0, 10);
term.read('h');
term.assertScreen("% abcdef gh");
term.assertAt(0, 11);
term.read('\r');
term.assertScreen("% abcdef gh");
term.assertAt(1, 0);
assertEquals("abcdef gh", line.get());
}
@Test
public void testSingleNonTerminalCompletion() {
TestTerm term = new TestTerm(this);
AtomicBoolean completed = new AtomicBoolean();
Supplier<String> line = term.readlineComplete(completion -> {
completion.complete(new int[]{'b','c','d','e','f'});
completed.set(true);
});
term.read('a');
term.assertScreen("% a");
term.assertAt(0, 3);
term.read('\t');
assertTrue(completed.get());
term.assertScreen("% abcdef");
term.assertAt(0, 8);
term.read('g');
term.assertScreen("% abcdefg");
term.assertAt(0, 9);
term.read('\r');
term.assertScreen("% abcdefg");
term.assertAt(1, 0);
assertEquals("abcdefg", line.get());
}
@Test
public void testSingleEmptyCompletion() {
TestTerm term = new TestTerm(this);
AtomicBoolean completed = new AtomicBoolean();
Supplier<String> line = term.readlineComplete(completion -> {
completion.complete(new int[0], true);
completed.set(true);
});
term.read('a', 'b');
term.assertScreen("% ab");
term.assertAt(0, 4);
term.read('\t');
assertTrue(completed.get());
term.assertScreen("% ab ");
term.assertAt(0, 5);
term.read('\r');
term.assertScreen("% ab ");
term.assertAt(1, 0);
assertEquals("ab ", line.get());
}
@Test
public void testNoCommonPrefixCompletion() {
TestTerm term = new TestTerm(this);
AtomicBoolean completed = new AtomicBoolean();
Supplier<String> line = term.readlineComplete(completion -> {
completion.suggest(Arrays.asList(
new int[]{'f', 'o', 'o', 'a'},
new int[]{'f', 'o', 'o', 'b'},
new int[]{'f', 'o', 'o', 'c'}
));
completed.set(true);
});
term.read('f', 'o', 'o');
term.assertScreen("% foo");
term.assertAt(0, 5);
term.read('\t');
assertTrue(completed.get());
term.assertScreen("% foo", "fooa foob fooc ", "% foo");
term.assertAt(2, 5);
term.read('g');
term.assertScreen("% foo", "fooa foob fooc ", "% foog");
term.assertAt(2, 6);
term.read('\r');
term.assertScreen("% foo", "fooa foob fooc ", "% foog");
term.assertAt(3, 0);
assertEquals("foog", line.get());
}
@Test
public void testEmptyCompletionCollectionBlock() throws Exception {
TestTerm term = new TestTerm(this);
AtomicBoolean completed = new AtomicBoolean();
Supplier<String> line = term.readlineComplete(completion -> {
completion.suggest(Collections.emptyList());
completed.set(true);
});
term.read('a', 'b');
term.read(BACKWARD_KEY);
term.assertScreen("% ab");
term.assertAt(0, 3);
term.read('\t');
assertTrue(completed.get());
term.assertScreen("% ab", "% ab");
term.assertAt(1, 3);
term.read('c');
term.assertScreen("% ab", "% acb");
term.assertAt(1, 4);
term.read('\r');
term.assertScreen("% ab", "% acb");
term.assertAt(2, 0);
assertEquals("acb", line.get());
}
@Test
public void testCompletionBlock1() throws Exception {
TestTerm term = new TestTerm(this);
AtomicBoolean completed = new AtomicBoolean();
Supplier<String> line = term.readlineComplete(completion -> {
completion.suggest(Helper.toCodePoints("a\r\nb\r\nc\r\n"));
completed.set(true);
});
term.read('a', 'b');
term.read(BACKWARD_KEY);
term.assertScreen("% ab");
term.assertAt(0, 3);
term.read('\t');
assertTrue(completed.get());
term.assertScreen("% ab", "a", "b", "c", "% ab");
term.assertAt(4, 3);
term.read('c');
term.assertScreen("% ab", "a", "b", "c", "% acb");
term.assertAt(4, 4);
term.read('\r');
term.assertScreen("% ab", "a", "b", "c", "% acb");
term.assertAt(5, 0);
assertEquals("acb", line.get());
}
@Test
public void testCompletionBlock2() throws Exception {
TestTerm term = new TestTerm(this);
AtomicBoolean completed = new AtomicBoolean();
Supplier<String> line = term.readlineComplete(completion -> {
completion.suggest(Helper.toCodePoints("a\r\nb\r\nc\r\n"));
completed.set(true);
});
term.read('a', '\\', '\r', 'b', 'c');
term.read(BACKWARD_KEY);
term.assertScreen("% a\\", "> bc");
term.assertAt(1, 3);
term.read('\t');
assertTrue(completed.get());
term.assertScreen("% a\\", "> bc", "a", "b", "c", "> bc");
term.assertAt(5, 3);
term.read('d');
term.assertScreen("% a\\", "> bc", "a", "b", "c", "> bdc");
term.assertAt(5, 4);
term.read('\r');
term.assertScreen("% a\\", "> bc", "a", "b", "c", "> bdc");
term.assertAt(6, 0);
assertEquals("abdc", line.get());
}
@Test
public void testCompletionBlock3() throws Exception {
TestTerm term = new TestTerm(this);
AtomicBoolean completed = new AtomicBoolean();
Supplier<String> line = term.readlineComplete(completion -> {
completion.suggest(Helper.toCodePoints("a\r\nb\r\nc\r\n"));
completed.set(true);
});
term.read('a', '"', '\r', 'b', 'c');
term.read(BACKWARD_KEY);
term.assertScreen("% a\"", "> bc");
term.assertAt(1, 3);
term.read('\t');
assertTrue(completed.get());
term.assertScreen("% a\"", "> bc", "a", "b", "c", "> bc");
term.assertAt(5, 3);
term.read('d');
term.assertScreen("% a\"", "> bc", "a", "b", "c", "> bdc");
term.assertAt(5, 4);
term.read('"', '\r');
term.assertScreen("% a\"", "> bc", "a", "b", "c", "> bd\"c");
term.assertAt(6, 0);
assertEquals("a\"\nbd\"c", line.get());
}
@Test
public void testEscape() throws Exception {
assertPrefix("\\", "\\");
assertPrefix("\"", "\"");
assertPrefix("'", "'");
assertPrefix("\\a", "\\a");
assertPrefix("\"a", "\"a");
assertPrefix("'a", "'a");
}
@Test
public void testCompleteEscape() throws Exception {
assertCompleteInline("", "a", "% a");
assertCompleteInline("", "a", true, "% a ");
assertCompleteInline("", " ", "% \\ ");
assertCompleteInline("", " ", true, "% \\ ");
assertCompleteInline("", "\\", "% \\\\");
assertCompleteInline("", "\\", true, "% \\\\ ");
assertCompleteInline("", "'", "% \\'");
assertCompleteInline("", "'", true, "% \\' ");
assertCompleteInline("", "\"", "% \\\"");
assertCompleteInline("", "\"", true, "% \\\" ");
assertCompleteInline("\"", " ", "% \" ");
assertCompleteInline("\"", " ", true, "% \" \" ");
assertCompleteInline("\"", "\\", "% \"\\\\");
assertCompleteInline("\"", "\\", true, "% \"\\\\\" ");
assertCompleteInline("\"", "'", "% \"'");
assertCompleteInline("\"", "'", true, "% \"'\" ");
assertCompleteInline("\"", "\"", "% \"\\\"");
assertCompleteInline("\"", "\"", true, "% \"\\\"\" ");
assertCompleteInline("\"\\", "\"", "% \"\\\"");
assertCompleteInline("\"\\", "\"", true, "% \"\\\"\" ");
assertCompleteInline("\"\\", "\\", "% \"\\\\");
assertCompleteInline("\"\\", "\\", true, "% \"\\\\\" ");
assertCompleteInline("\"\\", "a", "% \"\\");
assertCompleteInline("\"\\", "a", true, "% \"\\");
assertCompleteInline("'", " ", "% ' ");
assertCompleteInline("'", " ", true, "% ' ' ");
assertCompleteInline("'", "\\", "% '\\");
assertCompleteInline("'", "\\", true, "% '\\' ");
assertCompleteInline("'", "'", "% ''\\''");
assertCompleteInline("'", "'", true, "% ''\\''' ");
assertCompleteInline("'", "\"", "% '\"");
assertCompleteInline("'", "\"", true, "% '\"' ");
assertCompleteInline("\\", "a", "% \\a");
assertCompleteInline("\\", "a", true, "% \\a ");
}
private void assertPrefix(String line, String expected) {
TestTerm term = new TestTerm(this);
AtomicReference<String> prefix = new AtomicReference<>();
term.readlineComplete(comp -> {
prefix.set(Helper.fromCodePoints(comp.prefix()));
});
term.read(Helper.toCodePoints(line));
term.read('\t');
assertEquals(expected, prefix.get());
}
private void assertCompleteInline(String line, String inline, String... expected) {
assertCompleteInline(line, inline, false, expected);
}
private void assertCompleteInline(String line, String inline, boolean terminate, String... expected) {
TestTerm term = new TestTerm(this);
AtomicReference<Completion> completion = new AtomicReference<>();
term.readlineComplete(completion::set);
term.read(Helper.toCodePoints(line));
term.read('\t');
completion.get().complete(Helper.toCodePoints(inline), terminate);
term.assertScreen(expected);
}
}