/******************************************************************************* * Copyright (c) 2009, 2014 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 Corporation - initial API and implementation * Zend Technologies * Dawid PakuĊ‚a - convert to JUnit4 *******************************************************************************/ package org.eclipse.php.core.tests.codeassist; import static org.junit.Assert.fail; import java.nio.file.Paths; import java.util.ArrayList; import java.util.LinkedHashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IProject; import org.eclipse.dltk.compiler.problem.IProblem; import org.eclipse.dltk.core.CompletionProposal; import org.eclipse.dltk.core.CompletionRequestor; import org.eclipse.dltk.core.DLTKCore; import org.eclipse.dltk.core.IModelElement; import org.eclipse.dltk.core.ISourceModule; import org.eclipse.dltk.core.ModelException; import org.eclipse.jface.text.IDocument; import org.eclipse.php.core.tests.PdttFile; import org.eclipse.php.core.tests.TestSuiteWatcher; import org.eclipse.php.core.tests.TestUtils; import org.eclipse.php.core.tests.codeassist.CodeAssistPdttFile.ExpectedProposal; import org.eclipse.php.core.tests.runner.PDTTList; import org.eclipse.php.core.tests.runner.PDTTList.AfterList; import org.eclipse.php.core.tests.runner.PDTTList.BeforeList; import org.eclipse.php.core.tests.runner.PDTTList.Parameters; import org.eclipse.php.internal.core.Logger; import org.eclipse.php.core.PHPVersion; import org.eclipse.php.internal.core.codeassist.AliasType; import org.eclipse.php.internal.core.codeassist.IPHPCompletionRequestor; import org.eclipse.php.internal.core.documentModel.loader.PHPDocumentLoader; import org.eclipse.php.internal.core.typeinference.FakeConstructor; import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocument; import org.junit.ClassRule; import org.junit.Test; import org.junit.rules.TestWatcher; import org.junit.runner.RunWith; @SuppressWarnings("all") @RunWith(PDTTList.class) public class CodeAssistTests { @ClassRule public static TestWatcher watcher = new TestSuiteWatcher(); private static abstract class TestCompletionRequestor extends CompletionRequestor implements IPHPCompletionRequestor { private IStructuredDocument document; private int offset; public TestCompletionRequestor(IStructuredDocument document, int offset) { this.document = document; this.offset = offset; } @Override public IDocument getDocument() { return document; } @Override public boolean isExplicit() { return true; } @Override public int getOffset() { return offset; } @Override public void setOffset(int offset) { this.offset = offset; } @Override public boolean filter(int flag) { return false; } @Override public void addFlag(int flag) { // ignore } } @Parameters public static final Map<PHPVersion, String[]> TESTS = new LinkedHashMap<PHPVersion, String[]>(); public static final String DEFAULT_CURSOR = "|"; static { TESTS.put(PHPVersion.PHP5, new String[] { "/workspace/codeassist/php5/exclusive", "/workspace/codeassist/php5", "/workspace/codeassist/php5/classExclusive" }); TESTS.put(PHPVersion.PHP5_3, new String[] { "/workspace/codeassist/php5", "/workspace/codeassist/php5/classExclusive", "/workspace/codeassist/php53/exclusive", "/workspace/codeassist/php53/classExclusive", "/workspace/codeassist/php53" }); TESTS.put(PHPVersion.PHP5_4, new String[] { "/workspace/codeassist/php5", "/workspace/codeassist/php5/classExclusive", "/workspace/codeassist/php53", "/workspace/codeassist/php53/classExclusive", "/workspace/codeassist/php54", "/workspace/codeassist/php54/classExclusive" }); TESTS.put(PHPVersion.PHP5_5, new String[] { "/workspace/codeassist/php5", "/workspace/codeassist/php53", "/workspace/codeassist/php54", "/workspace/codeassist/php55" }); TESTS.put(PHPVersion.PHP5_6, new String[] { "/workspace/codeassist/php5", "/workspace/codeassist/php53", "/workspace/codeassist/php54", "/workspace/codeassist/php55", "/workspace/codeassist/php56" }); TESTS.put(PHPVersion.PHP7_0, new String[] { "/workspace/codeassist/php5", "/workspace/codeassist/php53", "/workspace/codeassist/php54", "/workspace/codeassist/php55", "/workspace/codeassist/php56", "/workspace/codeassist/php7" }); TESTS.put(PHPVersion.PHP7_1, new String[] { "/workspace/codeassist/php5", "/workspace/codeassist/php53", "/workspace/codeassist/php54", "/workspace/codeassist/php55", "/workspace/codeassist/php56", "/workspace/codeassist/php7", "/workspace/codeassist/php71" }); }; private IProject project; private IFile testFile; private List<IFile> otherFiles = null; private PHPVersion version; public CodeAssistTests(PHPVersion version, String[] fileNames) { this.version = version; } @BeforeList public void setUpSuite() throws Exception { project = TestUtils.createProject("CodeAssistTests_" + version.toString()); TestUtils.setProjectPhpVersion(project, version); } @AfterList public void tearDownSuite() throws Exception { TestUtils.deleteProject(project); } @Test public void assist(final String fileName) throws Exception { final CodeAssistPdttFile pdttFile = new CodeAssistPdttFile(fileName); pdttFile.applyPreferences(); final int offset = createFiles(pdttFile); CompletionProposal[] proposals = getProposals(DLTKCore.createSourceModuleFrom(testFile), offset); compareProposals(proposals, pdttFile); deleteFiles(); } private void deleteFiles() { if (testFile != null) { TestUtils.deleteFile(testFile); } if (otherFiles != null) { for (IFile file : otherFiles) { if (file != null) TestUtils.deleteFile(file); } } } /** * Creates test file with the specified content and calculates the offset at * OFFSET_CHAR. Offset character itself is stripped off. * * @param data * File data * @return offset where's the offset character set. * @throws Exception */ private int createFiles(PdttFile pdttFile) throws Exception { final String cursor = getCursor(pdttFile) != null ? getCursor(pdttFile) : DEFAULT_CURSOR; String data = pdttFile.getFile(); String[] otherFiles = pdttFile.getOtherFiles(); int offset = data.lastIndexOf(cursor); if (offset == -1) { throw new IllegalArgumentException("Offset character is not set"); } // Replace the offset character data = data.substring(0, offset) + data.substring(offset + 1); String fileName = Paths.get(pdttFile.getFileName()).getFileName().toString(); fileName = fileName.substring(0, fileName.indexOf('.')); testFile = TestUtils.createFile(project, fileName + ".php", data); this.otherFiles = new ArrayList<IFile>(otherFiles.length); int i = 0; for (String otherFileContent : otherFiles) { IFile tmp = TestUtils.createFile(project, String.format("test%s.php", i), otherFileContent); this.otherFiles.add(i, tmp); i++; } TestUtils.waitForIndexer(); return offset; } private CompletionProposal[] getProposals(ISourceModule sourceModule, int offset) throws ModelException { IStructuredDocument document = (IStructuredDocument) new PHPDocumentLoader().createNewStructuredDocument(); String content = new String(sourceModule.getSourceAsCharArray()); document.set(content); final List<CompletionProposal> proposals = new LinkedList<CompletionProposal>(); sourceModule.codeComplete(offset, new TestCompletionRequestor(document, offset) { public void accept(CompletionProposal proposal) { proposals.add(proposal); } @Override public void completionFailure(IProblem problem) { Logger.log(Logger.ERROR, problem.getMessage()); } }, 60000); return proposals.toArray(new CompletionProposal[proposals.size()]); } private void compareProposals(CompletionProposal[] proposals, CodeAssistPdttFile pdttFile) throws Exception { ExpectedProposal[] expectedProposals = pdttFile.getExpectedProposals(); boolean proposalsEqual = true; if (proposals.length == expectedProposals.length) { for (ExpectedProposal expectedProposal : pdttFile.getExpectedProposals()) { boolean found = false; for (CompletionProposal proposal : proposals) { IModelElement modelElement = proposal.getModelElement(); if (modelElement == null) { if (new String(proposal.getName()).equalsIgnoreCase(expectedProposal.name)) { // keyword found = true; break; } } else if (modelElement.getElementType() == expectedProposal.type) { if (modelElement instanceof AliasType) { if (((AliasType) modelElement).getAlias().equals(expectedProposal.name)) { found = true; break; } } else if ((modelElement instanceof FakeConstructor) && (modelElement.getParent() instanceof AliasType)) { if (((AliasType) modelElement.getParent()).getAlias().equals(expectedProposal.name)) { found = true; break; } } else { if (modelElement.getElementName().equalsIgnoreCase(expectedProposal.name)) { found = true; break; } } } else if (modelElement.getElementType() == expectedProposal.type && new String(proposal.getName()).equalsIgnoreCase(expectedProposal.name)) { // for phar include found = true; break; } } if (!found) { proposalsEqual = false; break; } } } else { proposalsEqual = false; } if (!proposalsEqual) { StringBuilder errorBuf = new StringBuilder(); errorBuf.append("\nEXPECTED COMPLETIONS LIST:\n-----------------------------\n"); errorBuf.append(pdttFile.getExpected()); errorBuf.append("\nACTUAL COMPLETIONS LIST:\n-----------------------------\n"); for (CompletionProposal p : proposals) { IModelElement modelElement = p.getModelElement(); if (modelElement == null || modelElement.getElementName() == null) { errorBuf.append("keyword(").append(p.getName()).append(")\n"); } else { switch (modelElement.getElementType()) { case IModelElement.FIELD: errorBuf.append("field"); break; case IModelElement.METHOD: errorBuf.append("method"); break; case IModelElement.TYPE: errorBuf.append("type"); break; } if (modelElement instanceof AliasType) { errorBuf.append('(').append(((AliasType) modelElement).getAlias()).append(")\n"); } else { errorBuf.append('(').append(modelElement.getElementName()).append(")\n"); } } } fail(errorBuf.toString()); } } private String getCursor(PdttFile pdttFile) { Map<String, String> config = pdttFile.getConfig(); return config.get("cursor"); } }