| /*******************************************************************************
|
| * Copyright (c) 2011, 2017 IBM Corporation and others.
|
| * All rights reserved. This program and the accompanying materials
|
| * are made available under the terms of the Eclipse Public License 2.0
|
| * which accompanies this distribution, and is available at
|
| * https://www.eclipse.org/legal/epl-2.0/
|
| *
|
| * SPDX-License-Identifier: EPL-2.0
|
| *
|
| * Contributors:
|
| * IBM Corporation - initial API and implementation
|
| *******************************************************************************/ |
| package org.eclipse.wst.jsdt.web.ui.tests.contentassist;
|
|
|
| import java.io.IOException;
|
| import java.io.StringReader;
|
| import java.lang.reflect.Method;
|
| import java.util.HashSet;
|
| import java.util.Set;
|
|
|
| import junit.framework.Assert;
|
|
|
| import org.eclipse.core.resources.IFile;
|
| import org.eclipse.jface.text.IDocument;
|
| import org.eclipse.jface.text.contentassist.ContentAssistant;
|
| import org.eclipse.jface.text.contentassist.ICompletionProposal;
|
| import org.eclipse.jface.text.contentassist.IContentAssistProcessor;
|
| import org.eclipse.jface.text.contentassist.IContentAssistant;
|
| import org.eclipse.jface.text.source.ISourceViewer;
|
| import org.eclipse.ui.IEditorPart;
|
| import org.eclipse.ui.IWorkbenchPage;
|
| import org.eclipse.ui.IWorkbenchWindow;
|
| import org.eclipse.ui.PlatformUI;
|
| import org.eclipse.ui.ide.IDE;
|
| import org.eclipse.ui.texteditor.AbstractTextEditor;
|
| import org.eclipse.ui.texteditor.ITextEditor;
|
| import org.eclipse.wst.html.ui.StructuredTextViewerConfigurationHTML;
|
| import org.eclipse.wst.jsdt.internal.ui.JavaScriptPlugin;
|
| import org.eclipse.wst.jsdt.internal.ui.javaeditor.JavaEditor;
|
| import org.eclipse.wst.jsdt.internal.ui.text.html.HTML2TextReader;
|
| import org.eclipse.wst.jsdt.ui.text.IJavaScriptPartitions;
|
| import org.eclipse.wst.jsdt.ui.text.JavaScriptSourceViewerConfiguration;
|
| import org.eclipse.wst.jsdt.web.ui.tests.internal.TestProjectSetup;
|
| import org.eclipse.wst.sse.ui.StructuredTextEditor;
|
| import org.eclipse.wst.sse.ui.StructuredTextViewerConfiguration;
|
| import org.eclipse.wst.sse.ui.internal.StructuredTextViewer;
|
|
|
| /**
|
| * <p>
|
| * Helpful utilities for running content assist tests.
|
| * </p>
|
| *
|
| * @see org.ecliplse.wst.jsdt.ui.tests.contentassist.ContentAssistUtilities
|
| * @see org.eclipse.wst.jsdt.web.ui.tests.contentassist.ContentAssistUtilities
|
| */
|
| public class ContentAssistTestUtilities {
|
| /**
|
| * <p>
|
| * Run a proposal test by opening the given file and invoking content assist for each expected
|
| * proposal count at the given line number and line character offset and then compare the number
|
| * of proposals for each invocation (pages) to the expected number of proposals.
|
| * </p>
|
| *
|
| * @param testProject
|
| * @param filePath
|
| * @param lineNum
|
| * @param lineRelativeCharOffset
|
| * @param expectedProposalCounts
|
| * @throws Exception
|
| */
|
| public static void runProposalTest(TestProjectSetup testProject, String filePath, int lineNum, int lineRelativeCharOffset,
|
| String[][] expectedProposals) throws Exception {
|
|
|
| runProposalTest(testProject, filePath, lineNum, lineRelativeCharOffset, expectedProposals, false, true);
|
| }
|
|
|
| /**
|
| * <p>
|
| * Run a proposal test by opening the given file and invoking content assist for each expected
|
| * proposal count at the given line number and line character offset and then compare the number
|
| * of proposals for each invocation (pages) to the expected number of proposals.
|
| * </p>
|
| *
|
| * @param testProject
|
| * @param filePath
|
| * @param lineNum
|
| * @param lineRelativeCharOffset
|
| * @param expectedProposalCounts
|
| * @param negativeTest
|
| * <code>true</code> if expectedProposals should not be found, <code>false</code> if
|
| * they should be found
|
| * @param exactMatch
|
| * <code>true</code> if expectedProposals should be compared to actual proposals with
|
| * {@link String#equals(Object)}, <code>false</code> if {@link String#indexOf(int)}
|
| * should be used
|
| * @throws Exception
|
| */
|
| public static void runProposalTest(TestProjectSetup testProject, String filePath, int lineNum, int lineRelativeCharOffset,
|
| String[][] expectedProposals, boolean negativeTest, boolean exactMatch) throws Exception {
|
|
|
| ICompletionProposal[][] pages = getProposals(testProject, filePath, lineNum, lineRelativeCharOffset,
|
| expectedProposals.length);
|
| verifyExpectedProposal(pages, expectedProposals, negativeTest, exactMatch, false);
|
| }
|
|
|
| /**
|
| * <p>
|
| * Run a proposal test by opening the given file and invoking content assist for each expected
|
| * proposal count at the given line number and line character offset and then compare the number
|
| * of proposals for each invocation (pages) to the expected number of proposals.
|
| * </p>
|
| *
|
| * @param testProject
|
| * @param filePath
|
| * @param lineNum
|
| * @param lineRelativeCharOffset
|
| * @param expectedProposalCounts
|
| * @param negativeTest
|
| * <code>true</code> if expectedProposals should not be found, <code>false</code> if
|
| * they should be found
|
| * @param exactMatch
|
| * <code>true</code> if expectedProposals should be compared to actual proposals with
|
| * {@link String#equals(Object)}, <code>false</code> if {@link String#indexOf(int)}
|
| * should be used
|
| * @param inOrder
|
| * <code>true</code> if <code>expectedProposals</code> should be found in the order
|
| * they are given, <code>false</code> if order does not matter
|
| *
|
| * @throws Exception
|
| */
|
| public static void runProposalTest(TestProjectSetup testProject, String filePath, int lineNum, int lineRelativeCharOffset,
|
| String[][] expectedProposals, boolean negativeTest, boolean exactMatch, boolean inOrder) throws Exception {
|
|
|
| ICompletionProposal[][] pages = getProposals(testProject, filePath, lineNum, lineRelativeCharOffset,
|
| expectedProposals.length);
|
| verifyExpectedProposal(pages, expectedProposals, negativeTest, exactMatch, inOrder);
|
| }
|
|
|
| /**
|
| * <p>
|
| * Run a proposal info test by opening the given file and invoking content assist for each
|
| * expected proposal count at the given line number and line character offset and then compare
|
| * the proposal Info for each invocation (pages) with its expected Proposal Info .
|
| * </p>
|
| *
|
| * @param testProject
|
| * @param filePath
|
| * @param lineNum
|
| * @param lineRelativeCharOffset
|
| * @param expectedProposalInfo
|
| * @throws Exception
|
| */
|
| public static void runProposalInfoTest(TestProjectSetup testProject, String filePath, int lineNum, int lineRelativeCharOffset,
|
| String[][] expectedProposals, String[][] expectedProposalInfo) throws Exception {
|
|
|
| ICompletionProposal[][] pages = getProposals(testProject, filePath, lineNum, lineRelativeCharOffset,
|
| expectedProposals.length);
|
| verifyExpectedProposalInfo(pages, expectedProposals, expectedProposalInfo);
|
| }
|
|
|
| /**
|
| * <p>
|
| * Runs a test for finding whether duplicate proposals are generated when content assist is
|
| * invoked.
|
| * </p>
|
| *
|
| * @param testProject
|
| * @param filePath
|
| * @param lineNum
|
| * @param lineRelativeOffset
|
| */
|
| public static void verifyNoDuplicates(TestProjectSetup testProject, String filePath, int lineNum, int lineRelativeCharOffset) throws Exception {
|
|
|
| int numOfPages = 1;
|
| ICompletionProposal[][] pages = getProposals(testProject, filePath, lineNum, lineRelativeCharOffset, numOfPages);
|
| checkForDuplicates(pages, numOfPages);
|
| }
|
|
|
| /**
|
| * <p>
|
| * Run a proposal test by opening the given file and invoking content assist for each expected
|
| * proposal count at the given line number and line character offset and then compare the number
|
| * of proposals for each invocation (pages) to the expected number of proposals.
|
| * </p>
|
| *
|
| * @param testProject
|
| * @param filePath
|
| * @param lineNum
|
| * @param lineRelativeCharOffset
|
| * @param expectedProposalCounts
|
| * @param negativeTest
|
| * <code>true</code> if expectedProposals should not be found, <code>false</code> if
|
| * they should be found
|
| * @param exactMatch
|
| * <code>true</code> if expectedProposals should be compared to actual proposals with
|
| * {@link String#equals(Object)}, <code>false</code> if {@link String#indexOf(int)}
|
| * should be used
|
| * @throws Exception
|
| */
|
| public static void runHTMLProposalTest(TestProjectSetup testProject, String filePath, int lineNum, int lineRelativeCharOffset,
|
| String[][] expectedProposals, boolean negativeTest, boolean exactMatch) throws Exception {
|
|
|
| ICompletionProposal[][] pages = getHTMLProposals(testProject, filePath, lineNum, lineRelativeCharOffset,
|
| expectedProposals.length);
|
| verifyExpectedProposal(pages, expectedProposals, negativeTest, exactMatch, false);
|
| }
|
|
|
| /**
|
| * <p>
|
| * Returns the content assist proposals when invoked at the offset provided.
|
| * </p>
|
| *
|
| * @param fileNum
|
| * @param lineNum
|
| * @param lineRelativeCharOffset
|
| * @param numOfPages
|
| */
|
| private static ICompletionProposal[][] getProposals(TestProjectSetup testProject, String filePath, int lineNum, int lineRelativeCharOffset,
|
| int numOfPages) throws Exception {
|
|
|
| IFile file = testProject.getFile(filePath);
|
| AbstractTextEditor editor = testProject.getEditor(file);
|
| IDocument doc = editor.getDocumentProvider().getDocument(editor.getEditorInput());
|
| int offset = doc.getLineOffset(lineNum) + lineRelativeCharOffset;
|
|
|
| ICompletionProposal[][] pages = getProposals(editor, offset, numOfPages);
|
| return pages;
|
| }
|
|
|
| /**
|
| * <p>
|
| * Invoke content assist on the given editor at the given offset, for the given number of pages
|
| * and return the results of each page
|
| * </p>
|
| */
|
| private static ICompletionProposal[][] getProposals(AbstractTextEditor editor, int offset, int pageCount)
|
| throws Exception {
|
|
|
| ICompletionProposal[][] pages = null;
|
|
|
| /* if JS editor
|
| * else if HTML editor */
|
| if(editor instanceof JavaEditor) {
|
| // setup the viewer
|
| ISourceViewer viewer = ((JavaEditor) editor).getViewer();
|
| JavaScriptSourceViewerConfiguration configuration = new JavaScriptSourceViewerConfiguration(
|
| JavaScriptPlugin.getDefault().getJavaTextTools().getColorManager(),
|
| JavaScriptPlugin.getDefault().getCombinedPreferenceStore(), editor,
|
| IJavaScriptPartitions.JAVA_PARTITIONING);
|
|
|
| ContentAssistant contentAssistant = (ContentAssistant) configuration.getContentAssistant(viewer);
|
|
|
| // get the processor
|
| String partitionTypeID = viewer.getDocument().getPartition(offset).getType();
|
| IContentAssistProcessor processor = contentAssistant.getContentAssistProcessor(partitionTypeID);
|
|
|
| // fire content assist session about to start
|
| Method privateFireSessionBeginEventMethod = ContentAssistant.class.getDeclaredMethod(
|
| "fireSessionBeginEvent", new Class[] { boolean.class });
|
| privateFireSessionBeginEventMethod.setAccessible(true);
|
| privateFireSessionBeginEventMethod.invoke(contentAssistant, new Object[] { Boolean.TRUE });
|
|
|
| // get content assist suggestions
|
| pages = new ICompletionProposal[pageCount][];
|
| for(int p = 0; p < pageCount; ++p) {
|
| pages[p] = processor.computeCompletionProposals(viewer, offset);
|
| }
|
|
|
| // fire content assist session ending
|
| Method privateFireSessionEndEventMethod = ContentAssistant.class.getDeclaredMethod("fireSessionEndEvent",
|
| null);
|
| privateFireSessionEndEventMethod.setAccessible(true);
|
| privateFireSessionEndEventMethod.invoke(contentAssistant, null);
|
|
|
| } else if(editor instanceof StructuredTextEditor) {
|
| //setup the viewer
|
| StructuredTextViewer viewer = ((StructuredTextEditor) editor).getTextViewer();
|
| StructuredTextViewerConfigurationHTML configuration = new StructuredTextViewerConfigurationHTML();
|
| ContentAssistant contentAssistant = (ContentAssistant) configuration.getContentAssistant(viewer);
|
| viewer.configure(configuration);
|
| viewer.setSelectedRange(offset, 0);
|
|
|
| //get the processor
|
| String partitionTypeID = viewer.getDocument().getPartition(offset).getType();
|
| IContentAssistProcessor processor = contentAssistant.getContentAssistProcessor(partitionTypeID);
|
|
|
| //fire content assist session about to start
|
| Method privateFireSessionBeginEventMethod = ContentAssistant.class.getDeclaredMethod(
|
| "fireSessionBeginEvent", new Class[] { boolean.class });
|
| privateFireSessionBeginEventMethod.setAccessible(true);
|
| privateFireSessionBeginEventMethod.invoke(contentAssistant, new Object[] { Boolean.TRUE });
|
|
|
| //get content assist suggestions
|
| pages = new ICompletionProposal[pageCount][];
|
| for(int p = 0; p < pageCount; ++p) {
|
| pages[p] = processor.computeCompletionProposals(viewer, offset);
|
| }
|
|
|
| //fire content assist session ending
|
| Method privateFireSessionEndEventMethod = ContentAssistant.class.getDeclaredMethod("fireSessionEndEvent",
|
| null);
|
| privateFireSessionEndEventMethod.setAccessible(true);
|
| privateFireSessionEndEventMethod.invoke(contentAssistant, null);
|
| }
|
|
|
| return pages;
|
| }
|
|
|
| /**
|
| * @param pages
|
| * @param expectedProposals
|
| * @param negativeTest
|
| * <code>true</code> if <code>expectedProposals</code> should not be found,
|
| * <code>false</code> if
|
| * they should be found
|
| * @param exactMatch
|
| * <code>true</code> if expectedProposals should be compared to actual proposals with
|
| * {@link String#equals(Object)}, <code>false</code> if {@link String#indexOf(int)}
|
| * should be used
|
| * @param inOrder
|
| * <code>true</code> if <code>expectedProposals</code> should be found in the order
|
| * they are given, <code>false</code> if order does not matter
|
| */
|
| private static void verifyExpectedProposal(ICompletionProposal[][] pages, String[][] expectedProposals,
|
| boolean negativeTest, boolean exactMatch, boolean inOrder) {
|
|
|
| StringBuffer error = new StringBuffer();
|
| int lastFoundIndex = -1;
|
| String lastFoundProposal = "";
|
| for(int page = 0; page < expectedProposals.length; ++page) {
|
| for(int expected = 0; expected < expectedProposals[page].length; ++expected) {
|
| String expectedProposal = expectedProposals[page][expected];
|
| boolean found = false;
|
| int suggestion = 0;
|
| for( ; suggestion < pages[page].length && !found; ++suggestion) {
|
| String displayString = pages[page][suggestion].getDisplayString();
|
| found = exactMatch ?
|
| displayString.equals(expectedProposal) : displayString.indexOf(expectedProposal) != -1;
|
| }
|
|
|
| /* error if checking for in order and this expected proposal was
|
| * found at an index lower then the last found proposal */
|
| if(inOrder && found && suggestion < lastFoundIndex) {
|
| error.append("\nProposal was found out of order: " + expectedProposal + " found before " + lastFoundProposal);
|
| }
|
|
|
| if(found && negativeTest) {
|
| error.append("\nUnexpected proposal was found on page " + page + ": '" + expectedProposal + "'");
|
| } else if(!found && !negativeTest) {
|
| error.append("\n Expected proposal was not found on page " + page + ": '" + expectedProposal + "'");
|
| }
|
|
|
| if(found) {
|
| lastFoundProposal = expectedProposal;
|
| lastFoundIndex = suggestion;
|
| }
|
| }
|
| }
|
|
|
| // if errors report them
|
| if(error.length() > 0) {
|
| StringBuffer expected = new StringBuffer();
|
| for(int i = 0; i < expectedProposals.length; i++) {
|
| for(int j = 0; j < expectedProposals[i].length; j++) {
|
| expected.append(expectedProposals[i][j]);
|
| expected.append('\n');
|
| }
|
| }
|
| StringBuffer actual = new StringBuffer();
|
| for(int i = 0; i < pages.length; i++) {
|
| for(int j = 0; j < pages[i].length; j++) {
|
| actual.append(pages[i][j].getDisplayString());
|
| actual.append('\n');
|
| }
|
| }
|
| Assert.assertEquals(error.toString(), expected.toString(), actual.toString());
|
| }
|
| }
|
|
|
| /**
|
| *
|
| * <p>
|
| * Compares the expected proposal Info with the one generated from the JavaDoc
|
| * </p>
|
| *
|
| * @param pages
|
| * @param expectedProposalInfo
|
| */
|
|
|
| private static void verifyExpectedProposalInfo(ICompletionProposal[][] pages, String[][] expectedProposals,
|
| String[][] expectedProposalsInfo) {
|
| StringBuffer error = new StringBuffer();
|
| String proposalInfo = new String();
|
| for(int page = 0; page < expectedProposals.length; ++page) {
|
| for(int expected = 0; expected < expectedProposals[page].length; ++expected) {
|
| String expectedProposal = expectedProposals[page][expected];
|
| String expectedProposalInfo = expectedProposalsInfo[page][expected];
|
| boolean found = false;
|
| for(int suggestion = 0; suggestion < pages[page].length && !found; ++suggestion) {
|
| found = pages[page][suggestion].getDisplayString().equals(expectedProposal);
|
| if(found) {
|
| proposalInfo = pages[page][suggestion].getAdditionalProposalInfo();
|
|
|
| if(proposalInfo == null || proposalInfo.indexOf(expectedProposalInfo) < 0) {
|
| error.append("\n" + "Required proposal info for " + expectedProposal + " does not exist.");
|
| }
|
| break;
|
| }
|
|
|
| }
|
| }
|
|
|
| }
|
|
|
| // if errors report them
|
| if(error.length() > 0) {
|
| StringBuffer expected = new StringBuffer();
|
| for(int i = 0; i < expectedProposalsInfo.length; i++) {
|
| for(int j = 0; j < expectedProposalsInfo[i].length; j++) {
|
| expected.append(expectedProposalsInfo[i][j]);
|
| expected.append('\n');
|
| }
|
| }
|
| StringBuffer actual = new StringBuffer();
|
| for(int i = 0; i < pages.length; i++) {
|
| for(int j = 0; j < pages[i].length; j++) {
|
| try {
|
| actual.append(new HTML2TextReader(new StringReader(pages[i][j].getAdditionalProposalInfo()),
|
| null).getString().trim());
|
| } catch(IOException e) {
|
| e.printStackTrace();
|
| }
|
| actual.append('\n');
|
| }
|
| }
|
| Assert.assertEquals(error.toString(), expected.toString(), actual.toString());
|
| }
|
| }
|
|
|
| /**
|
| * <p>
|
| * Checks for Duplicates and reports an error if found.
|
| * </p>
|
| *
|
| * @param pages
|
| * @param numOfPages
|
| */
|
| private static void checkForDuplicates(ICompletionProposal[][] pages, int numOfPages) {
|
| Set set = new HashSet();
|
| StringBuffer error = new StringBuffer();
|
|
|
| for(int suggestion = 0; suggestion < pages[0].length; ++suggestion) {
|
| if(set.contains(pages[0][suggestion].toString())) {
|
| error.append("\nDuplicate proposal found: '" + pages[0][suggestion] + "'");
|
| } else {
|
| set.add(pages[0][suggestion].toString());
|
| }
|
| }
|
|
|
| if(error.length() > 0) {
|
| Assert.fail(error.toString());
|
| }
|
|
|
| set.clear();
|
| }
|
|
|
| /**
|
| * <p>
|
| * Returns the content assist proposals when invoked at the offset provided.
|
| * </p>
|
| *
|
| * @param fileNum
|
| * @param lineNum
|
| * @param lineRelativeCharOffset
|
| * @param numOfPages
|
| */
|
| private static ICompletionProposal[][] getHTMLProposals(TestProjectSetup testProject, String filePath, int lineNum, int lineRelativeCharOffset,
|
| int numOfPages) throws Exception {
|
|
|
| IFile file = testProject.getFile(filePath);
|
|
|
| IWorkbenchWindow workbenchWindow = PlatformUI.getWorkbench().getActiveWorkbenchWindow();
|
| IWorkbenchPage page = workbenchWindow.getActivePage();
|
| IEditorPart part = IDE.openEditor(page, file, "org.eclipse.wst.html.core.htmlsource.source");
|
| StructuredTextEditor editor = (StructuredTextEditor) part.getAdapter(ITextEditor.class);
|
|
|
| IDocument doc = editor.getDocumentProvider().getDocument(editor.getEditorInput());
|
| int offset = doc.getLineOffset(lineNum) + lineRelativeCharOffset;
|
|
|
| ICompletionProposal[][] pages = getHTMLProposals(editor, offset, numOfPages);
|
| return pages;
|
| }
|
|
|
| /**
|
| * <p>
|
| * Invoke content assist on the given editor at the given offset, for the given number of pages
|
| * and return the results of each page
|
| * </p>
|
| */
|
| private static ICompletionProposal[][] getHTMLProposals(StructuredTextEditor editor, int offset, int pageCount) throws Exception {
|
| // setup the viewer
|
| ISourceViewer viewer = editor.getTextViewer();
|
| Method getSourceViewerConfiguration = AbstractTextEditor.class.getDeclaredMethod("getSourceViewerConfiguration", new Class[0]);
|
| getSourceViewerConfiguration.setAccessible(true);
|
| StructuredTextViewerConfiguration configuration = (StructuredTextViewerConfiguration) getSourceViewerConfiguration.invoke(editor, null);
|
| IContentAssistant contentAssistant = configuration.getContentAssistant(viewer);
|
|
|
| // get the processor
|
| String partitionTypeID = viewer.getDocument().getPartition(offset).getType();
|
| IContentAssistProcessor processor = contentAssistant.getContentAssistProcessor(partitionTypeID);
|
|
|
| // fire content assist session about to start
|
| Method privateFireSessionBeginEventMethod = ContentAssistant.class.getDeclaredMethod("fireSessionBeginEvent",
|
| new Class[] { boolean.class });
|
| privateFireSessionBeginEventMethod.setAccessible(true);
|
| privateFireSessionBeginEventMethod.invoke(contentAssistant, new Object[] { Boolean.TRUE });
|
|
|
| // get content assist suggestions
|
| ICompletionProposal[][] pages = new ICompletionProposal[pageCount][];
|
| for(int p = 0; p < pageCount; ++p) {
|
| pages[p] = processor.computeCompletionProposals(viewer, offset);
|
| }
|
|
|
| // fire content assist session ending
|
| Method privateFireSessionEndEventMethod = ContentAssistant.class.getDeclaredMethod("fireSessionEndEvent", null);
|
| privateFireSessionEndEventMethod.setAccessible(true);
|
| privateFireSessionEndEventMethod.invoke(contentAssistant, null);
|
|
|
| editor.close(false);
|
| return pages;
|
| }
|
| } |