blob: 56df24cb9e11f5c760c9422cd2146eab7ba7267e [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2015 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Genady Beryozkin, me@genady.org - initial API and implementation
* Fabio Zadrozny <fabiofz at gmail dot com> - [typing] HippieCompleteAction is slow ( Alt+/ ) - https://bugs.eclipse.org/bugs/show_bug.cgi?id=270385
*******************************************************************************/
package org.eclipse.ui.workbench.texteditor.tests;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import org.eclipse.core.runtime.AssertionFailedException;
import org.eclipse.text.tests.Accessor;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.Document;
import org.eclipse.jface.text.IDocument;
import org.eclipse.ui.internal.texteditor.HippieCompletionEngine;
import junit.framework.Test;
import junit.framework.TestCase;
import junit.framework.TestSuite;
/**
* Tests for the Hippie completion action of the text editor.
*
* @author Genady Beryozkin, me@genady.org
*/
public class HippieCompletionTest extends TestCase {
IDocument[] documents;
private HippieCompletionEngine fEngine;
public HippieCompletionTest(String name) {
super(name);
}
@Override
protected void setUp() throws Exception {
documents= new IDocument[5];
documents[0]= new Document("package ui.TestPackage;\n" +
"\n" +
"/**\n" +
" * This is a testing class that tests the hippie completion engine.\n" +
" * it has a simple main with a print method\n" +
" */\n" +
"public class TestClass1 {\n" +
"\n" +
" public static void main(String[] args) {\n" +
" System.out.println(\"I will be printing Hello world!\");\n" +
" }\n" +
"}");
documents[1]= new Document("This is a simple text file\n" +
"with some testssome test that is also used in the completion engine tests");
documents[2]= new Document("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +
"<plugin\n" +
" id=\"org.eclipse.ui.workbench.texteditor.tests\"\n" +
" name=\"%Plugin.name\"\n" +
" version=\"3.1.0\"\n" +
" provider-name=\"%Plugin.providerName\">\n" +
"\n" +
" <runtime>\n" +
" <library name=\"workbenchtexteditortests.jar\">\n" +
" <export name=\"*\"/>\n" +
" </library>\n" +
" </runtime>\n" +
" \n" +
" <requires>\n" +
" <import plugin=\"org.eclipse.core.runtime.compatibility\"/>\n" +
" <import plugin=\"org.junit\"/>\n" +
" <import plugin=\"org.eclipse.text.tests\"/>\n" +
" <import plugin=\"org.eclipse.jface.text\"/>\n" +
" <import plugin=\"org.eclipse.ui.workbench.texteditor\"/>\n" +
" <import plugin=\"org.eclipse.ui\"/>\n" +
" </requires>\n" +
" \n" +
"</plugin>\n" +
"");
documents[3]= new Document("###############################################################################\n" +
"# Copyright (c) 2000, 2004 IBM Corporation and others.\n" +
"# All rights reserved. This program and the accompanying materials \n" +
"# are made available under the terms of the Eclipse Public License v1.0\n" +
"# which accompanies this distribution, and is available at\n" +
"# http://www.eclipse.org/legal/epl-v10.html\n" +
"# \n" +
"# Contributors:\n" +
"# IBM Corporation - initial API and implementation\n" +
"###############################################################################\n" +
"bin.includes= plugin.xml,\\\n" +
" plugin.properties,\\\n" +
" test.xml,\\\n" +
" about.html,\\\n" +
" *.jar\n" +
"\n" +
"src.includes= about.html\n" +
" \n" +
"source.workbenchtexteditortests.jar= src/\n" +
"");
documents[4]= new Document("/**\n" +
" * This class tests the hippie completion functionality.\n" +
" * \u05D4\u05DE\u05D7\u05DC\u05E7\u05D4 \u05D4\u05D6\u05D5 \u05D1\u05D5\u05D3\u05E7\u05EA \u05D0\u05EA \u05DE\u05E0\u05D2\u05E0\u05D5\u05DF \u05D4\u05D4\u05E9\u05DC\u05DE\u05D5\u05EA\n" +
" * This is an arabic word - \u0647\u0630\u0627 \u0643\u0644\u0645\u0629 \u0639\u0631\u0628\u064A\u0651\u0629\n" +
" * Eclipse is the best IDE - Eclipse \u044D\u0442\u043E \u0441\u0430\u043C\u044B\u0439 \u043B\u0443\u0447\u0448\u0438\u0439 IDE.\n" +
" */\n" +
"\n" +
"public class HippieTester2 {\n" +
"\n" +
"\tpublic static void main(String[] args) {\n" +
"\t\tchar \u05DE\u05D7= '9';\n" + // hebrew text
"\t\tString $arabic\u20ACDigits= \"\u0661\u0662\u0663\u0664\u0665\u0666" + // Euro symbol in variable name, arabic digits from 1 to 6
"\u2021\u0667\u0668\u0669\u0660\";\n" + // double dagger, arabic digits 7-0
"\t\tString $arabic\u20AAWord= \"\u0628\u064E\u0627\u0628\u0650\";\n" + // shekel (israeli) currency symbol + arabic word
"\t\tString \u0628\u0627\u0628= \"\u044D\u0442\";\n" + // arabic var, russian string
"\t\tint \u20A31= 3;\n" + // frank currency symbol
"\t\tint \u00A3\u0661\u0662\u0663= \u20A31 + \u05DE\u05D7;\n" + // pound, arabic digits 1-3, partial hebrew word
"\t\tint a\u0300\u0301b= 18;\n" + // combining diactritical marks
"\t\t}\n" +
"\t\t\n" +
"\tpublic void \u05D4\u05D4\u05E9(int \u0441\u0430\u043C) {\n" + // hebrew word prexif, russian word prefix
"\t\tString \u043B\u0443\u0447\u0448= \"\u05D1\u05D5\u05D3\u05E7\";\n" +
"\t\tchar \u20AA129;\n" + // shekel (israeli) currency
"\t}\n" +
"}");
fEngine= new HippieCompletionEngine();
}
public void testSearchBackwards1() {
try {
List<String> list= fEngine.getCompletionsBackwards(documents[0],
"pri", documents[0].get().indexOf("println") + 10);
assertEquals(list.size(), 2);
assertEquals(list.get(0), "ntln");
assertEquals(list.get(1), "nt");
list= fEngine.getCompletionsBackwards(documents[0],
"pri", documents[0].getLength());
assertEquals(list.size(), 3);
assertEquals(list.get(0), "nting");
assertEquals(list.get(1), "ntln");
assertEquals(list.get(2), "nt");
list= fEngine.getCompletionsBackwards(documents[0],
"pri", documents[0].get().indexOf("println") + 1);
assertEquals(list.size(), 1);
assertEquals(list.get(0), "nt");
list= fEngine.getCompletionsBackwards(documents[0],
"pa", 2);
assertEquals(list.size(), 0);
} catch (BadLocationException e) {
assertTrue("Got out of document bounds", false);
}
}
public void testSearchBackwards2() {
try {
List<String> list= fEngine.getCompletionsBackwards(documents[2],
"plugi", documents[2].getLength());
assertEquals(8, list.size());
list= fEngine.makeUnique(list);
assertEquals(1, list.size());
assertEquals("n", list.get(0));
list= fEngine.getCompletionsBackwards(documents[2],
"plugin", documents[2].getLength());
assertEquals(0, list.size()); // empty completions discarded
} catch (BadLocationException e) {
assertTrue("Got out of document bounds", false);
}
}
public void testSearchBackwards3() {
try {
List<String> list= fEngine.getCompletionsBackwards(documents[1],
"test", documents[1].getLength());
assertEquals("Number of backwards suggestions does not match", 2, list.size());
list= fEngine.getCompletionsBackwards(documents[1],
"tests", documents[1].getLength());
assertEquals("Number of backwards suggestions does not match", 1, list.size());
list= fEngine.getCompletionsBackwards(documents[1],
"test", documents[1].getLength() - 1);
assertEquals("Number of backwards suggestions does not match", 1, list.size());
} catch (BadLocationException e) {
assertTrue("Got out of document bounds", false);
}
}
public void testSearch() {
ArrayList<IDocument> docsList= new ArrayList<>(Arrays.asList(this.documents));
List<String> result= createSuggestions("te", docsList);
assertEquals("Number of completions does not match", 15, result.size());
result= fEngine.makeUnique(result);
assertEquals("Number of completions does not match", 7, result.size());
result= createSuggestions("Plug", docsList);
assertEquals("Number of completions does not match", 2, result.size());
result= createSuggestions("p", docsList);
assertEquals("Number of completions does not match", 23, result.size());
result= fEngine.makeUnique(result);
assertEquals("Number of completions does not match", 10, result.size());
assertEquals("Incorrect completion", "ackage", result.get(0));
assertEquals("Incorrect completion", "rint", result.get(1));
assertEquals("Incorrect completion", "ublic", result.get(2));
assertEquals("Incorrect completion", "rintln", result.get(3));
assertEquals("Incorrect completion", "rinting", result.get(4));
assertEquals("Incorrect completion", "lugin", result.get(5));
assertEquals("Incorrect completion", "rovider", result.get(6));
assertEquals("Incorrect completion", "roviderName", result.get(7));
assertEquals("Incorrect completion", "rogram", result.get(8));
assertEquals("Incorrect completion", "roperties", result.get(9));
}
public void testSearch2() {
ArrayList<IDocument> docsList= new ArrayList<>(Arrays.asList(this.documents));
List<String> result= createSuggestions("printe", docsList);
assertEquals("Number of completions does not match", 0, result.size());
result= createSuggestions("s", docsList);
assertEquals("Number of completions does not match", 8, result.size());
result= createSuggestions("pack", documents[0]);
assertEquals("Number of completions does not match", 1, result.size());
}
public void testForwardSearch() {
try {
List<String> result= fEngine.getCompletionsForward(documents[0],
"cl", documents[0].get().indexOf("cl"), true);
assertEquals(2, result.size());
result= fEngine.getCompletionsForward(documents[0],
"cl", documents[0].get().indexOf("cl") + 1, true);
assertEquals(1, result.size());
result= fEngine.getCompletionsForward(documents[1],
"Thi", 0, true);
assertEquals(1, result.size());
result= fEngine.getCompletionsForward(documents[1],
"Thi", 1, true);
assertEquals(0, result.size());
} catch (BadLocationException e) {
assertTrue("Got out of document bounds", false);
}
}
public void testForwardSearchInternational() {
List<String> result;
try {
result= fEngine.getCompletionsForward(documents[4],
"$", documents[4].get().indexOf('$'), true);
assertEquals(2, result.size());
assertEquals("arabic\u20AAWord", result.get(0));
assertEquals("arabic\u20ACDigits", result.get(1));
result= fEngine.getCompletionsForward(documents[4],
"$", documents[4].get().indexOf('$'), false);
assertEquals(2, result.size());
assertEquals("arabic\u20ACDigits", result.get(0));
assertEquals("arabic\u20AAWord", result.get(1));
result= fEngine.getCompletionsForward(documents[4],
"$", documents[4].get().indexOf('$') + 1, true);
assertEquals(1, result.size());
assertEquals("arabic\u20AAWord", result.get(0));
} catch (BadLocationException e) {
assertTrue("Got out of document bounds", false);
}
}
public void testPrefix() {
try {
String prefix= fEngine.getPrefixString(documents[0],
documents[0].get().indexOf("testing") + 3);
assertEquals(prefix, "tes");
prefix= fEngine.getPrefixString(documents[0],
documents[0].get().indexOf("public") + 4);
assertEquals(prefix, "publ");
prefix= fEngine.getPrefixString(documents[0],
documents[0].get().indexOf("println") + 7);
assertEquals(prefix, "println");
prefix= fEngine.getPrefixString(documents[0],
documents[0].get().indexOf("println") + 8);
assertEquals(prefix, null);
prefix= fEngine.getPrefixString(documents[1], 3);
assertEquals(prefix, "Thi");
prefix= fEngine.getPrefixString(documents[1], 0);
assertEquals(prefix, null);
prefix= fEngine.getPrefixString(documents[1], documents[1].getLength());
assertEquals(prefix, "tests");
prefix= fEngine.getPrefixString(documents[3],
documents[3].get().indexOf("Copyright") - 2);
assertEquals(prefix, null);
prefix= fEngine.getPrefixString(documents[4],
documents[4].get().indexOf("IDE") + 2);
assertEquals(prefix, "ID");
prefix= fEngine.getPrefixString(documents[4],
documents[4].get().indexOf("$arabic\u20ACDigits") + 8);
assertEquals(prefix, "$arabic\u20AC");
prefix= fEngine.getPrefixString(documents[4],
documents[4].get().indexOf("$arabic\u20AAWord") + 8);
assertEquals(prefix, "$arabic\u20AA");
prefix= fEngine.getPrefixString(documents[4],
documents[4].get().indexOf("\u00A3\u0661\u0662\u0663") + 3);
assertEquals(prefix, "\u00A3\u0661\u0662");
prefix= fEngine.getPrefixString(documents[4],
documents[4].get().indexOf("a\u0300\u0301b") + 3);
assertEquals(prefix, "a\u0300\u0301");
prefix= fEngine.getPrefixString(documents[4],
documents[4].get().indexOf("\u0667\u0668\u0669\u0660") + 2);
assertEquals(prefix, "\u0667\u0668");
} catch (BadLocationException e) {
assertTrue("Got out of document bounds", false);
}
}
public void testInternational() {
IDocument intlDoc= documents[4];
List<String> result= createSuggestions("\u05D4", intlDoc); // hebrew letter heh
assertEquals("Number of completions does not match", 4, result.size());
assertEquals(result.get(0), "\u05DE\u05D7\u05DC\u05E7\u05D4");
assertEquals(result.get(1), "\u05D6\u05D5");
assertEquals(result.get(2), "\u05D4\u05E9\u05DC\u05DE\u05D5\u05EA");
assertEquals(result.get(3), "\u05D4\u05E9");
result= createSuggestions("\u0661", intlDoc); // arabic digit "1"
assertEquals("Number of completions does not match", 1, result.size());
assertEquals(result.get(0), "\u0662\u0663\u0664\u0665\u0666");
result= createSuggestions("\u0628\u064E", intlDoc); // arabic letter bah and fatha
assertEquals("Number of completions does not match", 1, result.size());
assertEquals(result.get(0), "\u0627\u0628\u0650");
result= createSuggestions("\u0628", intlDoc); // arabic letter bah
assertEquals("Number of completions does not match", 2, result.size());
assertEquals(result.get(0), "\u064E\u0627\u0628\u0650");
assertEquals(result.get(1), "\u0627\u0628");
result= createSuggestions("$ara", intlDoc);
assertEquals("Number of completions does not match", 2, result.size());
assertEquals(result.get(0), "bic\u20ACDigits");
assertEquals(result.get(1), "bic\u20AAWord");
result= createSuggestions("\u0441\u0430", intlDoc); // russian letters "s" and "a"
assertEquals("Number of completions does not match", 2, result.size());
assertEquals(result.get(0), "\u043C\u044B\u0439");
assertEquals(result.get(1), "\u043C");
result= createSuggestions("\u05D1\u05D5", intlDoc); // hebrew letters bet and vav
assertEquals("Number of completions does not match", 2, result.size());
assertEquals(result.get(0), "\u05D3\u05E7\u05EA");
assertEquals(result.get(1), "\u05D3\u05E7");
result= createSuggestions("a", intlDoc);
assertEquals("Number of completions does not match", 4, result.size());
assertEquals(result.get(0), "n");
assertEquals(result.get(1), "rabic");
assertEquals(result.get(2), "rgs");
assertEquals(result.get(3), "\u0300\u0301b");
result= createSuggestions("\u20AA", intlDoc); // israeli currency (shekel)
assertEquals("Number of completions does not match", 1, result.size());
assertEquals(result.get(0), "129");
result= createSuggestions("\u20A3", intlDoc); // french currency (frank)
assertEquals("Number of completions does not match", 2, result.size());
assertEquals(result.get(0), "1");
assertEquals(result.get(1), "1");
result= createSuggestions("\u044D", intlDoc); // russial letter "hard e"
assertEquals("Number of completions does not match", 2, result.size());
assertEquals(result.get(0), "\u0442\u043E");
assertEquals(result.get(1), "\u0442");
result= createSuggestions("\u00A3", intlDoc); // pound currency sign
assertEquals("Number of completions does not match", 1, result.size());
assertEquals(result.get(0), "\u0661\u0662\u0663");
result= createSuggestions("\u00A5", intlDoc); // yen currency sign
assertEquals("Number of completions does not match", 0, result.size());
}
public void testInternationalBackwards() {
IDocument intlDoc= documents[4];
try {
List<String> list= fEngine.getCompletionsBackwards(intlDoc,
"\u043B\u0443", intlDoc.get().indexOf("129"));
assertEquals(2, list.size());
assertEquals(list.get(0), "\u0447\u0448");
assertEquals(list.get(1), "\u0447\u0448\u0438\u0439");
list= fEngine.getCompletionsBackwards(intlDoc,
"\u05DE", intlDoc.get().lastIndexOf('+'));
assertEquals(2, list.size());
assertEquals(list.get(0), "\u05D7");
assertEquals(list.get(1), "\u05E0\u05D2\u05E0\u05D5\u05DF");
list= fEngine.getCompletionsBackwards(intlDoc,
"\u0667", intlDoc.get().indexOf("\u2021\u0667") + 1);
assertEquals(0, list.size());
list= fEngine.getCompletionsBackwards(intlDoc,
"\u0628", intlDoc.get().lastIndexOf("\u0628"));
assertEquals(1, list.size());
assertEquals(list.get(0), "\u064E\u0627\u0628\u0650");
} catch (BadLocationException e) {
assertTrue("Got out of document bounds", false);
}
}
private Accessor createAccessor(Iterator<String> suggestions, int startOffset) {
return new Accessor("org.eclipse.ui.texteditor.HippieCompleteAction$CompletionState",
getClass().getClassLoader(), new Class[] { Iterator.class, int.class }, new
Object[] { suggestions, new Integer(startOffset) });
}
private String next(Accessor state) {
return (String)state.invoke("next", new Object[] {});
}
/*
* Getting completions lazily
*/
public void testCompletionState() throws Exception {
ArrayList<String> list= new ArrayList<>();
Accessor state= null;
try {
state= createAccessor(list.iterator(), 0);
fail("Having no items is not valid (at least the empty completion must be there)");
} catch (AssertionFailedException ex) {
}
list.add("");
state= createAccessor(list.iterator(), 0);
assertTrue(state.getBoolean("hasOnly1EmptySuggestion"));
for (int i= 0; i < 3; i++) {
assertEquals("", next(state));
}
list.add("");
state= createAccessor(list.iterator(), 0);
assertTrue(state.getBoolean("hasOnly1EmptySuggestion"));
for (int i= 0; i < 3; i++) {
assertEquals("", next(state));
}
//only empty and aaaa
list.add(0, "aaaa");
state= createAccessor(list.iterator(), 0);
assertFalse(state.getBoolean("hasOnly1EmptySuggestion"));
for (int i= 0; i < 3; i++) {
assertEquals("aaaa", next(state));
assertEquals("", next(state));
}
//empty, aaaa and bbbb
list.add(1, "bbbb");
state= createAccessor(list.iterator(), 0);
assertFalse(state.getBoolean("hasOnly1EmptySuggestion"));
for (int i= 0; i < 3; i++) {
assertEquals("aaaa", next(state));
assertEquals("bbbb", next(state));
assertEquals("", next(state));
}
//empty, aaaa and 2 from 'bbbb' (should make unique)
list.add(2, "bbbb");
state= createAccessor(list.iterator(), 0);
assertFalse(state.getBoolean("hasOnly1EmptySuggestion"));
for (int i= 0; i < 3; i++) {
assertEquals("aaaa", next(state));
assertEquals("bbbb", next(state));
assertEquals("", next(state));
}
}
/*
* Getting completions lazily
*/
public void testIteration() throws Exception {
//Check only with current document
IDocument openDocument= new Document("" +
"bar\n" +
"bar1\n" +
"bar2\n" +
"");
Iterator<String> suggestions= fEngine.getMultipleDocumentsIterator(openDocument, new ArrayList<IDocument>(), "bar", 3);
assertEquals("1", suggestions.next());
assertEquals("2", suggestions.next());
assertEquals("", suggestions.next());
assertFalse(suggestions.hasNext());
//Check with 2 documents
List<IDocument> otherDocuments= new ArrayList<>();
otherDocuments.add(new Document("" +
"bar3\n" +
"bar4\n" +
""));
suggestions= fEngine.getMultipleDocumentsIterator(openDocument, otherDocuments, "bar", 3);
assertEquals("1", suggestions.next());
assertEquals("2", suggestions.next());
assertEquals("3", suggestions.next());
assertEquals("4", suggestions.next());
assertEquals("", suggestions.next());
assertFalse(suggestions.hasNext());
//Check with duplicates (duplicates are gotten at this level -- they're removed later -- at the CompletionState)
suggestions= fEngine.getMultipleDocumentsIterator(openDocument, otherDocuments, "bar", 3);
otherDocuments.add(new Document());
otherDocuments.add(new Document("" +
"bar3\n" +
"bar4\n" +
""));
assertEquals("1", suggestions.next());
assertEquals("2", suggestions.next());
assertEquals("3", suggestions.next());
assertEquals("4", suggestions.next());
assertEquals("3", suggestions.next());
assertEquals("4", suggestions.next());
assertEquals("", suggestions.next());
assertFalse(suggestions.hasNext());
//Check with current document with only backward matches
openDocument= new Document("" +
"bar0 bar1 bar" +
"");
suggestions= fEngine.getMultipleDocumentsIterator(openDocument, new ArrayList<IDocument>(), "bar", openDocument.getLength());
assertEquals("1", suggestions.next());
assertEquals("0", suggestions.next());
assertEquals("", suggestions.next());
assertFalse(suggestions.hasNext());
}
public static Test suite() {
return new TestSuite(HippieCompletionTest.class);
}
private List<String> createSuggestions(String prefix, IDocument doc) {
return createSuggestions(prefix, Arrays.asList(new IDocument[]{doc}));
}
private List<String> createSuggestions(String prefix, List<IDocument> docsList) {
ArrayList<String> results= new ArrayList<>();
for (Iterator<IDocument> i= docsList.iterator(); i.hasNext();) {
IDocument doc= i.next();
try {
results.addAll(fEngine.getCompletionsForward(doc, prefix, 0, false));
} catch (BadLocationException e) {
assertTrue("No exception should be thrown here", false);
}
}
return results;
}
}