/*******************************************************************************
* Copyright (c) 2004, 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:
* IBM Rational Software - Initial API and implementation
* Anton Leherbauer (Wind River Systems)
* Bryan Wilkinson (QNX)
* Markus Schorn (Wind River Systems)
* Thomas Corbat (IFS)
* Sergey Prigogin (Google)
* Mohamed Azab (Mentor Graphics)
*******************************************************************************/
package org.eclipse.cdt.ui.tests.text.contentassist2;
import static org.eclipse.cdt.core.formatter.DefaultCodeFormatterConstants.FORMATTER_INSERT_SPACE_AFTER_COMMA_IN_METHOD_DECLARATION_PARAMETERS;
import static org.eclipse.cdt.core.formatter.DefaultCodeFormatterConstants.FORMATTER_INSERT_SPACE_AFTER_COMMA_IN_TEMPLATE_PARAMETERS;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.TextUtilities;
import org.eclipse.jface.text.contentassist.ContentAssistant;
import org.eclipse.jface.text.contentassist.ICompletionProposal;
import org.eclipse.jface.text.contentassist.IContextInformation;
import org.eclipse.jface.text.source.ISourceViewer;
import org.eclipse.jface.text.templates.TemplateProposal;
import org.eclipse.ui.texteditor.AbstractTextEditor;
import org.eclipse.ui.texteditor.ITextEditor;
import org.eclipse.cdt.core.dom.IPDOMManager;
import org.eclipse.cdt.core.model.CModelException;
import org.eclipse.cdt.core.model.ICProject;
import org.eclipse.cdt.core.testplugin.CProjectHelper;
import org.eclipse.cdt.ui.CUIPlugin;
import org.eclipse.cdt.ui.testplugin.CTestPlugin;
import org.eclipse.cdt.ui.testplugin.EditorTestHelper;
import org.eclipse.cdt.ui.tests.BaseUITestCase;
import org.eclipse.cdt.ui.text.ICCompletionProposal;
import org.eclipse.cdt.ui.text.ICPartitions;
import org.eclipse.cdt.ui.text.contentassist.ContentAssistInvocationContext;
import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPASTNameBase;
import org.eclipse.cdt.internal.ui.text.contentassist.CCompletionProposal;
import org.eclipse.cdt.internal.ui.text.contentassist.CContentAssistProcessor;
import org.eclipse.cdt.internal.ui.text.contentassist.ContentAssistPreference;
import org.eclipse.cdt.internal.ui.text.contentassist.ParameterGuessingProposal;
import org.eclipse.cdt.internal.ui.text.contentassist.RelevanceConstants;
public abstract class AbstractContentAssistTest extends BaseUITestCase {
public static enum CompareType { ID, DISPLAY, REPLACEMENT, CONTEXT, INFORMATION }
private class ContentAssistResult {
long startTime;
long endTime;
Object[] results;
public ContentAssistResult(long startTime, long endTime, Object[] results) {
this.startTime = startTime;
this.endTime = endTime;
this.results = results;
}
}
protected ICProject fCProject;
private IFile fCFile;
protected ITextEditor fEditor;
private final boolean fIsCpp;
protected boolean fProcessorNeedsConfiguring;
public AbstractContentAssistTest(String name, boolean isCpp) {
super(name);
fIsCpp= isCpp;
}
@Override
protected void setUp() throws Exception {
super.setUp();
if (fIsCpp) {
fCProject= CProjectHelper.createCCProject(getName(), "unused", IPDOMManager.ID_FAST_INDEXER);
} else {
fCProject= CProjectHelper.createCProject(getName(), "unused", IPDOMManager.ID_FAST_INDEXER);
}
fCFile= setUpProjectContent(fCProject.getProject());
assertNotNull(fCFile);
waitForIndexer(fCProject);
fEditor= (ITextEditor) EditorTestHelper.openInEditor(fCFile, true);
assertNotNull(fEditor);
CPPASTNameBase.sAllowNameComputation= true;
// EditorTestHelper.joinBackgroundActivities((AbstractTextEditor) fEditor);
}
/**
* Sets up the project's content.
* @param project
* @return the file to be opened in the editor
*/
protected abstract IFile setUpProjectContent(IProject project) throws Exception;
@Override
protected void tearDown() throws Exception {
ContentAssistInvocationContext.assertNoUndisposedContexts();
EditorTestHelper.closeEditor(fEditor);
fEditor= null;
CProjectHelper.delete(fCProject);
fCProject= null;
fCFile= null;
super.tearDown();
}
protected static IPreferenceStore getPreferenceStore() {
return CUIPlugin.getDefault().getPreferenceStore();
}
private ContentAssistResult invokeContentAssist(int offset, int length, boolean isCompletion,
boolean isTemplate, boolean filterResults) throws Exception {
if (CTestPlugin.getDefault().isDebugging()) {
System.out.println("\n\n\n\n\nTesting " + this.getClass().getName());
}
// Call the CContentAssistProcessor
ISourceViewer sourceViewer= EditorTestHelper.getSourceViewer((AbstractTextEditor)fEditor);
String contentType= TextUtilities.getContentType(sourceViewer.getDocument(), ICPartitions.C_PARTITIONING, offset, true);
boolean isCode= IDocument.DEFAULT_CONTENT_TYPE.equals(contentType);
ContentAssistant assistant = new ContentAssistant();
CContentAssistProcessor processor = new CContentAssistProcessor(fEditor, assistant, contentType);
assistant.setContentAssistProcessor(processor, contentType);
if (fProcessorNeedsConfiguring) {
ContentAssistPreference.configure(assistant, getPreferenceStore());
}
long startTime= System.currentTimeMillis();
sourceViewer.setSelectedRange(offset, length);
Object[] results = isCompletion ?
(Object[]) processor.computeCompletionProposals(sourceViewer, offset) :
(Object[]) processor.computeContextInformation(sourceViewer, offset);
long endTime= System.currentTimeMillis();
assertTrue(results != null);
if (filterResults) {
if (isTemplate) {
results= filterResultsKeepTemplates(results);
} else {
results= filterResults(results, isCode);
}
}
return new ContentAssistResult(startTime, endTime, results);
}
protected void assertContentAssistResults(int offset, int length, String[] expected, boolean isCompletion,
boolean isTemplate, boolean filterResults, CompareType compareType) throws Exception {
ContentAssistResult r = invokeContentAssist(offset, length, isCompletion, isTemplate, filterResults);
String[] resultStrings= toStringArray(r.results, compareType);
Arrays.sort(expected);
Arrays.sort(resultStrings);
if (CTestPlugin.getDefault().isDebugging()) {
System.out.println("Time: " + (r.endTime - r.startTime) + " ms");
for (String proposal : resultStrings) {
System.out.println("Result: " + proposal);
}
}
boolean allFound = true; // For the time being, let's be optimistic.
for (String element : expected) {
boolean found = false;
for (String proposal : resultStrings) {
if(element.equals(proposal)){
found = true;
if (CTestPlugin.getDefault().isDebugging()) {
System.out.println("Lookup success for " + element);
}
break;
}
}
if (!found) {
allFound = false;
if (CTestPlugin.getDefault().isDebugging()) {
System.out.println( "Lookup failed for " + element); //$NON-NLS-1$
}
}
}
if (!allFound) {
assertEquals("Missing results!", toString(expected), toString(resultStrings));
} else if (doCheckExtraResults()) {
assertEquals("Extra results!", toString(expected), toString(resultStrings));
}
}
protected void assertContentAssistResults(int offset, int length, Map<String, String[][]> expected,
boolean isCompletion, boolean isTemplate, boolean filterResults, CompareType compareType)
throws Exception {
ContentAssistResult r = invokeContentAssist(offset, length, isCompletion, isTemplate, filterResults);
Map<String, String[][]> resultMap = toMap(r.results, compareType);
if (CTestPlugin.getDefault().isDebugging()) {
System.out.println("Time : " + (r.endTime - r.startTime) + " ms");
for (String proposal : resultMap.keySet()) {
System.out.println("Result: " + proposal);
String[][] result = resultMap.get(proposal);
for (String[] row : result) {
for (String s : row) {
System.out.print(s + " ");
}
System.out.println();
}
}
System.out.println();
}
for (String proposal : expected.keySet()) {
String[][] result = resultMap.get(proposal);
assertNotNull(result);
String[][] expectedGuesses = expected.get(proposal);
String exp = "";
String guess = "";
assertEquals(expectedGuesses.length, result.length);
for (int i = 0; i < result.length; i++) {
String[] tmp = expectedGuesses[i];
Arrays.sort(tmp);
exp += toString(tmp) + "\n";
tmp = result[i];
Arrays.sort(tmp);
guess += toString(tmp) + "\n";
}
assertEquals(exp, guess);
}
}
private Map<String, String[][]> toMap(Object[] results, CompareType compareType) throws CModelException {
Map<String, String[][]> resultsMap = new HashMap<>();
for (Object result : results) {
switch (compareType) {
case REPLACEMENT:
if (result instanceof ParameterGuessingProposal) {
ParameterGuessingProposal proposal = (ParameterGuessingProposal) result;
proposal.generateParameterGuesses();
String pName = proposal.getReplacementString();
ICompletionProposal[][] pProposals = proposal
.getParametersGuesses();
String[][] p;
if (pProposals != null) {
p = new String[pProposals.length][];
for (int i = 0; i < pProposals.length; i++) {
p[i] = new String[pProposals[i].length];
for (int j = 0; j < pProposals[i].length; j++) {
p[i][j] = pProposals[i][j].getDisplayString();
}
}
} else {
p = new String[0][];
}
resultsMap.put(pName, p);
}
break;
}
}
return resultsMap;
}
protected void assertContentAssistResults(int offset, int length, String[] expected, boolean isCompletion,
boolean isTemplate, CompareType compareType) throws Exception {
assertContentAssistResults(offset, length, expected, isCompletion, isTemplate, true, compareType);
}
protected void assertContentAssistResults(int offset, String[] expected, boolean isCompletion,
CompareType compareType) throws Exception {
assertContentAssistResults(offset, 0, expected, isCompletion, false, compareType);
}
/**
* Filter out template and keyword proposals.
*
* @param results
* @param isCodeCompletion completion is in code, not preprocessor, etc.
* @return filtered proposals
*/
private Object[] filterResults(Object[] results, boolean isCodeCompletion) {
List<Object> filtered= new ArrayList<>();
for (Object result : results) {
if (result instanceof TemplateProposal) {
continue;
}
if (result instanceof ICCompletionProposal) {
if (isCodeCompletion) {
// Check for keywords proposal.
int relevance = ((ICCompletionProposal)result).getRelevance();
if (relevance >= RelevanceConstants.CASE_MATCH_RELEVANCE) {
relevance -= RelevanceConstants.CASE_MATCH_RELEVANCE;
}
if (relevance <= RelevanceConstants.KEYWORD_TYPE_RELEVANCE) {
continue;
}
}
filtered.add(result);
} else if (result instanceof IContextInformation) {
filtered.add(result);
}
}
return filtered.toArray();
}
/**
* Filter out proposals, keep only templates
*/
private Object[] filterResultsKeepTemplates(Object[] results) {
List<Object> filtered= new ArrayList<>();
for (Object result : results) {
if (result instanceof TemplateProposal) {
filtered.add(result);
}
}
return filtered.toArray();
}
private String[] toStringArray(Object[] results, CompareType type) {
String[] strings = new String[results.length];
for (int i = 0; i < results.length; i++) {
Object result = results[i];
switch (type) {
case ID:
if (result instanceof ICCompletionProposal) {
strings[i] = ((ICCompletionProposal) result).getIdString();
}
break;
case DISPLAY:
if (result instanceof ICompletionProposal) {
strings[i] = ((ICompletionProposal) result).getDisplayString();
}
break;
case REPLACEMENT:
if (result instanceof CCompletionProposal) {
strings[i] = ((CCompletionProposal) result).getReplacementString();
} else if (result instanceof ICCompletionProposal) {
strings[i] = ((ICCompletionProposal) result).getDisplayString();
}
break;
case CONTEXT:
if (result instanceof ICompletionProposal) {
result = ((CCompletionProposal) result).getContextInformation();
}
if (result instanceof IContextInformation) {
strings[i] = ((IContextInformation) result).getContextDisplayString();
}
break;
case INFORMATION:
if (result instanceof IContextInformation) {
strings[i] = ((IContextInformation) result).getInformationDisplayString();
}
break;
}
}
return strings;
}
private String toString(String[] strings) {
StringBuilder buf= new StringBuilder();
for (String string : strings) {
buf.append(string).append('\n');
}
return buf.toString();
}
/**
* Override to relax checking of extra results
*/
protected boolean doCheckExtraResults() {
return true;
}
/**
* Returns the content of the editor buffer
*/
protected String getBuffer() {
return getDocument().get();
}
/**
* Returns the editor document
*/
protected IDocument getDocument() {
return EditorTestHelper.getDocument(fEditor);
}
protected void setCommaAfterFunctionParameter(String value) {
fCProject.setOption(
FORMATTER_INSERT_SPACE_AFTER_COMMA_IN_METHOD_DECLARATION_PARAMETERS, value);
}
protected void setCommaAfterTemplateParameter(String value) {
fCProject.setOption(FORMATTER_INSERT_SPACE_AFTER_COMMA_IN_TEMPLATE_PARAMETERS, value);
}
}