package com.siberika.idea.pascal;
import com.intellij.codeInsight.completion.CompletionType;
import com.intellij.codeInsight.lookup.LookupElement;
import com.intellij.openapi.editor.Caret;
import com.intellij.openapi.editor.CaretModel;
import com.intellij.psi.PsiElement;
import com.intellij.testFramework.fixtures.CodeInsightTestFixture;
import com.intellij.testFramework.fixtures.LightPlatformCodeInsightFixtureTestCase;
import com.intellij.xdebugger.XDebuggerUtil;
import com.siberika.idea.pascal.lang.parser.NamespaceRec;
import com.siberika.idea.pascal.lang.psi.PasEntityScope;
import com.siberika.idea.pascal.lang.psi.impl.PasField;
import com.siberika.idea.pascal.lang.references.PasReferenceUtil;
import com.siberika.idea.pascal.util.PsiUtil;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.EnumSet;
import java.util.List;
import java.util.TreeSet;
public class CompletionTest extends LightPlatformCodeInsightFixtureTestCase {
@Override
protected String getTestDataPath() {
return "testData/completion";
}
public void testCompletion() {
myFixture.configureByFiles("completionTest.pas");
checkCompletion(myFixture, "r1");
}
public void testUnitCompletion() {
myFixture.configureByFiles("usesCompletion.pas", "unit1.pas", "completionTest.pas");
checkCompletion(myFixture, "unit1");
}
public void testModuleHeadCompletion() {
myFixture.configureByFiles("empty.pas");
checkCompletion(myFixture, "unit", "program", "library", "package", "begin ");
myFixture.type('p');
checkCompletion(myFixture, "program", "package");
}
private void checkCompletion(CodeInsightTestFixture myFixture, String...expected) {
myFixture.complete(CompletionType.BASIC, 1);
List<String> strings = myFixture.getLookupElementStrings();
assertTrue(strings != null);
assertEquals(new TreeSet<String>(Arrays.asList(expected)), new TreeSet<String>(strings));
}
public static void checkCompletionContains(CodeInsightTestFixture myFixture, String...expected) {
myFixture.completeBasic();
List<String> strings = myFixture.getLookupElementStrings();
assertTrue(strings != null);
checkContains(strings, expected, "\nMust present: ");
}
private void checkCompletionContainsAllCarets(CodeInsightTestFixture myFixture, String...expected) {
List<List<String>> strings = completeBasicAllCarets(myFixture);
for (int i = 0; i < strings.size(); i++) {
checkContains(strings.get(i), expected, String.format("\nCaret #%d, must present: ", strings.size()-i));
}
}
private static void checkContains(List<String> strings, String[] expected, String prefix) {
List<String> exp = Arrays.asList(expected);
ArrayList<String> lacking = new ArrayList<String>(exp);
lacking.removeAll(strings);
assertTrue(String.format(prefix + "%s\nLack of: %s", exp, lacking), strings.containsAll(exp));
}
private void checkCompletionNotContains(CodeInsightTestFixture myFixture, String...unexpected) {
completeBasicAllCarets(myFixture);
List<String> strings = myFixture.getLookupElementStrings();
assertTrue(strings != null);
List<String> unexp = Arrays.asList(unexpected);
StringBuilder sb = new StringBuilder();
for (String string : strings) {
if (unexp.contains(string)) {
sb.append(String.format("\nUnexpected but present: %s", string));
}
}
assertTrue(sb.toString(), sb.length() == 0);
}
public final List<List<String>> completeBasicAllCarets(CodeInsightTestFixture myFixture) {
final CaretModel caretModel = myFixture.getEditor().getCaretModel();
final List<Caret> carets = caretModel.getAllCarets();
final List<Integer> originalOffsets = new ArrayList<Integer>(carets.size());
for (final Caret caret : carets) {
originalOffsets.add(caret.getOffset());
}
caretModel.removeSecondaryCarets();
// We do it in reverse order because completions would affect offsets
// i.e.: when you complete "spa" to "spam", next caret offset increased by 1
Collections.reverse(originalOffsets);
final List<List<String>> result = new ArrayList<List<String>>(originalOffsets.size());
for (final int originalOffset : originalOffsets) {
caretModel.moveToOffset(originalOffset);
final LookupElement[] lookupElements = myFixture.completeBasic();
if (lookupElements != null) {
List<String> res = new ArrayList<String>(lookupElements.length);
result.add(res);
for (LookupElement lookupElement : lookupElements) {
res.add(lookupElement.getLookupString());
}
} else {
result.add(Collections.<String>emptyList());
}
}
return result;
}
public void testNoModuleHeadCompletion() {
myFixture.configureByFiles("unit1.pas");
checkCompletion(myFixture);
}
public void testUnitSection() {
myFixture.configureByFiles("unitSections.pas");
checkCompletion(myFixture, "interface", "implementation", "initialization", "finalization");
myFixture.type('f');
checkCompletion(myFixture, "interface", "finalization");
}
public void testUnitDeclSection() {
myFixture.configureByFiles("unitDeclSection.pas");
checkCompletion(myFixture, "const", "type", "var", "threadvar", "resourcestring",
"procedure", "function", "constructor", "destructor",
"uses", "begin");
myFixture.type('v');
checkCompletion(myFixture, "var", "threadvar");
}
public void testUnitDeclSectionImpl() {
myFixture.configureByFiles("unitDeclSectionImpl.pas");
checkCompletion(myFixture, "const", "type", "var", "threadvar", "resourcestring",
"procedure", "function", "constructor", "destructor",
"uses", "begin", "initialization", "finalization");
myFixture.type('v');
checkCompletion(myFixture, "var", "threadvar");
}
public void testModuleSection() {
myFixture.configureByFiles("moduleSection.pas");
checkCompletion(myFixture, "const", "type", "var", "threadvar", "resourcestring",
"procedure", "function", "constructor", "destructor",
"uses", "begin ");
myFixture.type('d');
checkCompletion(myFixture, "destructor", "procedure", "threadvar");
}
public void testModuleSectionWithUses() {
myFixture.configureByFiles("moduleSectionWithUses.pas");
checkCompletion(myFixture, "const", "type", "var", "threadvar", "resourcestring",
"procedure", "function", "constructor", "destructor", "begin ");
myFixture.type('i');
checkCompletion(myFixture, "function", "resourcestring", "begin ");
}
public void testRoutineHead() {
myFixture.configureByFiles("routineHead.pas");
checkCompletion(myFixture, "assembler", "cdecl", "deprecated", "experimental",
"export", "inline", "library", "overload", "pascal", "platform",
"register", "safecall", "stdcall", "begin");
}
public void testRoutineBlock() {
myFixture.configureByFiles("routineBlock.pas");
checkCompletion(myFixture, "const", "type", "var", "procedure", "function", "begin");
}
public void testRoutineParams1() {
myFixture.configureByFiles("routineParams1.pas");
checkCompletionContainsAllCarets(myFixture, "const ", "var ", "out ");
}
public void testRoutineParams2() {
myFixture.configureByFiles("routineParams2.pas");
checkCompletion(myFixture, "const ", "var ", "out ");
myFixture.type('o');
checkCompletion(myFixture, "const ", "out ");
}
public void testRoutineParams3() {
myFixture.configureByFiles("routineParams3.pas");
checkCompletionNotContains(myFixture, "const ", "var ", "out ");
}
public void testMethodDirectivesIntf() {
myFixture.configureByFiles("methodDirectivesIntf.pas");
checkCompletion(myFixture, "assembler", "cdecl", "deprecated", "dispid", "experimental",
"export", "final", "inline", "library", "message", "overload", "pascal", "platform",
"register", "safecall", "static", "stdcall",
"abstract", "dynamic", "override", "reintroduce", "virtual");
myFixture.type('v');
checkCompletionContains(myFixture, "virtual", "overload", "override");
}
public void testStructIntf() {
myFixture.configureByFiles("structIntf.pas");
checkCompletion(myFixture, "const", "type", "var", "procedure", "function", "constructor", "destructor",
"strict", "private", "protected", "public", "published", "automated",
"class ", "operator", "property", "end");
myFixture.type('v');
checkCompletion(myFixture, "var", "private");
}
public void testMethodDeclImplHead() {
myFixture.configureByFiles("methodImplHead.pas");
checkCompletion(myFixture, "begin");
}
public void testMethodDeclImpl() {
myFixture.configureByFiles("methodImpl.pas");
checkCompletion(myFixture, "const", "type", "var", "procedure", "function", "begin");
myFixture.type('t');
checkCompletion(myFixture, "type", "const", "function");
}
public void testStructured() {
myFixture.configureByFiles("structured.pas");
checkCompletion(myFixture, "strict", "private", "protected", "public", "published", "automated",
"const", "type", "var", "procedure", "function", "constructor", "destructor",
"class ", "operator", "property", "end");
myFixture.type('a');
checkCompletion(myFixture, "automated", "private", "class ", "operator", "var");
}
public void testTypeId() {
myFixture.configureByFiles("typeId.pas");
checkCompletionContains(myFixture, "TTest", "TRec2", "typeId",
"type", "class", "dispinterface", "interface ", "record", "object", "packed", "set", "file", "helper", "array");
myFixture.type("te");
checkCompletionContains(myFixture, "TTest", "dispinterface", "interface ");
}
public void testParent() {
myFixture.configureByFiles("parent.pas");
checkCompletion(myFixture, "ParentConstructor", "parentMethod", "ChildConstructor", "ChildMethod");
}
public void testStatement() {
myFixture.configureByFiles("statement.pas");
checkCompletionContains(myFixture, "a", "b", "s1",
"for", "while", "repeat", "if", "case", "with",
"goto", "exit", "try", "raise", "end");
myFixture.type("i");
checkCompletionContains(myFixture, "while", "if", "with", "exit", "raise");
}
public void testConsts() {
myFixture.configureByFiles("consts.pas");
checkCompletionContainsAllCarets(myFixture, "a", "b", "CONST_1", "CONST_2");
//checkCompletionContains(myFixture, "a", "b", "CONST_1", "CONST_2");
}
public void testStatementInStmt() {
myFixture.configureByFiles("statementInStmt.pas");
/*checkCompletionNotContains(myFixture,
"for", "while", "repeat", "if", "case", "with",
"goto", "exit", "try", "raise", "begin", "end");*/
checkCompletionContainsAllCarets(myFixture, "do", "then", "of");
}
public void testStatementInExpr() {
myFixture.configureByFiles("statementInExpr.pas");
checkCompletionNotContains(myFixture,
"for", "while", "repeat", "if", "case", "with",
"goto", "exit", "try", "raise", "end");
}
public void testDcu() {
myFixture.configureByFiles("dcu.pas");
checkCompletionContains(myFixture, "spec", "v", "test", "proc");
}
public void testBeginend() {
myFixture.configureByFiles("beginend.pas");
checkCompletionContainsAllCarets(myFixture, "begin");
}
public void testElse() {
myFixture.configureByFiles("else.pas");
checkCompletionContainsAllCarets(myFixture, "else");
}
public void testProp() {
myFixture.configureByFiles("prop.pas");
checkCompletionContains(myFixture, "X", "Y");
}
public void testContext() {
myFixture.configureByFiles("empty.pas", "contextTest.pas");
PsiElement el = XDebuggerUtil.getInstance().findContextElement(myFixture.findFileInTempDir("contextTest.pas"), 42, myFixture.getProject(), false);
PasEntityScope scope = PsiUtil.getNearestAffectingScope(el);
NamespaceRec fqn = NamespaceRec.fromFQN(myFixture.getFile(), "");
fqn.setIgnoreVisibility(true);
Collection<PasField> fields = PasReferenceUtil.resolve(null, scope, fqn, EnumSet.of(PasField.FieldType.VARIABLE), false, 0);
assertTrue(fields.iterator().hasNext());
assertTrue("local".equals(fields.iterator().next().name));
}
public void testForwardStructure() {
myFixture.configureByFiles("forwardStructure.pas");
checkCompletionContains(myFixture, "Bar");
}
}