diff options
author | Jeff Johnston | 2014-03-12 21:25:54 +0000 |
---|---|---|
committer | Alexander Kurtakov | 2014-04-05 05:51:56 +0000 |
commit | d42f1aa8fc3ef77ee436f65da8b38c1023f234ac (patch) | |
tree | 9bee07651e3c5032048cc7bbf912eef92c75188a /libhover/org.eclipse.linuxtools.cdt.libhover.tests | |
parent | 4f497bdc9479160a8be9a3f1eefdae4d7b9dfbc1 (diff) | |
download | org.eclipse.linuxtools-d42f1aa8fc3ef77ee436f65da8b38c1023f234ac.tar.gz org.eclipse.linuxtools-d42f1aa8fc3ef77ee436f65da8b38c1023f234ac.tar.xz org.eclipse.linuxtools-d42f1aa8fc3ef77ee436f65da8b38c1023f234ac.zip |
Fix for Bug 428037 - Libhover doesn't respect qualified names
- Added check for C++ and then if it is a completion context,
if the location is qualified
Change-Id: Id55d78f9fa944066e4f457338038077078db021c
Reviewed-on: https://git.eclipse.org/r/23282
Tested-by: Hudson CI
Reviewed-by: Torbjörn Svensson <azoff@svenskalinuxforeningen.se>
Reviewed-by: Alexander Kurtakov <akurtako@redhat.com>
IP-Clean: Alexander Kurtakov <akurtako@redhat.com>
Tested-by: Alexander Kurtakov <akurtako@redhat.com>
Diffstat (limited to 'libhover/org.eclipse.linuxtools.cdt.libhover.tests')
17 files changed, 2905 insertions, 0 deletions
diff --git a/libhover/org.eclipse.linuxtools.cdt.libhover.tests/.classpath b/libhover/org.eclipse.linuxtools.cdt.libhover.tests/.classpath new file mode 100644 index 0000000000..098194ca4b --- /dev/null +++ b/libhover/org.eclipse.linuxtools.cdt.libhover.tests/.classpath @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="UTF-8"?> +<classpath> + <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.7"/> + <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/> + <classpathentry kind="src" path="src"/> + <classpathentry kind="output" path="bin"/> +</classpath> diff --git a/libhover/org.eclipse.linuxtools.cdt.libhover.tests/.gitignore b/libhover/org.eclipse.linuxtools.cdt.libhover.tests/.gitignore new file mode 100644 index 0000000000..934e0e06ff --- /dev/null +++ b/libhover/org.eclipse.linuxtools.cdt.libhover.tests/.gitignore @@ -0,0 +1,2 @@ +/bin +/target diff --git a/libhover/org.eclipse.linuxtools.cdt.libhover.tests/.project b/libhover/org.eclipse.linuxtools.cdt.libhover.tests/.project new file mode 100644 index 0000000000..007a253318 --- /dev/null +++ b/libhover/org.eclipse.linuxtools.cdt.libhover.tests/.project @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="UTF-8"?> +<projectDescription> + <name>org.eclipse.linuxtools.cdt.libhover.tests</name> + <comment></comment> + <projects> + </projects> + <buildSpec> + <buildCommand> + <name>org.eclipse.jdt.core.javabuilder</name> + <arguments> + </arguments> + </buildCommand> + <buildCommand> + <name>org.eclipse.pde.ManifestBuilder</name> + <arguments> + </arguments> + </buildCommand> + <buildCommand> + <name>org.eclipse.pde.SchemaBuilder</name> + <arguments> + </arguments> + </buildCommand> + </buildSpec> + <natures> + <nature>org.eclipse.pde.PluginNature</nature> + <nature>org.eclipse.jdt.core.javanature</nature> + </natures> +</projectDescription> diff --git a/libhover/org.eclipse.linuxtools.cdt.libhover.tests/.settings/org.eclipse.jdt.core.prefs b/libhover/org.eclipse.linuxtools.cdt.libhover.tests/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 0000000000..f42de363af --- /dev/null +++ b/libhover/org.eclipse.linuxtools.cdt.libhover.tests/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,7 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.7 +org.eclipse.jdt.core.compiler.compliance=1.7 +org.eclipse.jdt.core.compiler.problem.assertIdentifier=error +org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.source=1.7 diff --git a/libhover/org.eclipse.linuxtools.cdt.libhover.tests/META-INF/MANIFEST.MF b/libhover/org.eclipse.linuxtools.cdt.libhover.tests/META-INF/MANIFEST.MF new file mode 100644 index 0000000000..2e61baacb7 --- /dev/null +++ b/libhover/org.eclipse.linuxtools.cdt.libhover.tests/META-INF/MANIFEST.MF @@ -0,0 +1,20 @@ +Manifest-Version: 1.0 +Bundle-ManifestVersion: 2 +Bundle-Name: Tests +Bundle-SymbolicName: org.eclipse.linuxtools.cdt.libhover.tests +Bundle-Version: 1.0.0.qualifier +Bundle-RequiredExecutionEnvironment: JavaSE-1.7 +Require-Bundle: org.eclipse.cdt.core;bundle-version="5.7.0", + org.eclipse.swt;bundle-version="3.103.0", + org.eclipse.core.commands;bundle-version="3.6.100", + org.eclipse.core.resources;bundle-version="3.9.0", + org.eclipse.core.runtime;bundle-version="3.10.0", + org.eclipse.ui;bundle-version="3.106.0", + org.eclipse.cdt.ui;bundle-version="5.8.0", + org.junit;bundle-version="4.11.0", + org.eclipse.jface.text;bundle-version="3.9.0", + org.eclipse.ui.ide;bundle-version="3.10.0", + org.eclipse.ui.workbench.texteditor;bundle-version="3.9.0", + org.eclipse.ui.editors;bundle-version="3.8.100", + org.eclipse.linuxtools.cdt.libhover;bundle-version="1.2.0", + org.eclipse.linuxtools.cdt.libhover.glibc;bundle-version="1.2.0" diff --git a/libhover/org.eclipse.linuxtools.cdt.libhover.tests/about.html b/libhover/org.eclipse.linuxtools.cdt.libhover.tests/about.html new file mode 100644 index 0000000000..c258ef55d8 --- /dev/null +++ b/libhover/org.eclipse.linuxtools.cdt.libhover.tests/about.html @@ -0,0 +1,28 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" + "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml"> +<head> +<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"/> +<title>About</title> +</head> +<body lang="EN-US"> +<h2>About This Content</h2> + +<p>June 5, 2006</p> +<h3>License</h3> + +<p>The Eclipse Foundation makes available all content in this plug-in ("Content"). Unless otherwise +indicated below, the Content is provided to you under the terms and conditions of the +Eclipse Public License Version 1.0 ("EPL"). A copy of the EPL is available +at <a href="http://www.eclipse.org/legal/epl-v10.html">http://www.eclipse.org/legal/epl-v10.html</a>. +For purposes of the EPL, "Program" will mean the Content.</p> + +<p>If you did not receive this Content directly from the Eclipse Foundation, the Content is +being redistributed by another party ("Redistributor") and different terms and conditions may +apply to your use of any object code in the Content. Check the Redistributor's license that was +provided with the Content. If no such license exists, contact the Redistributor. Unless otherwise +indicated below, the terms and conditions of the EPL still apply to any source code in the Content +and such source code may be obtained at <a href="http://www.eclipse.org/">http://www.eclipse.org</a>.</p> + +</body> +</html>
\ No newline at end of file diff --git a/libhover/org.eclipse.linuxtools.cdt.libhover.tests/build.properties b/libhover/org.eclipse.linuxtools.cdt.libhover.tests/build.properties new file mode 100644 index 0000000000..17daa5b49c --- /dev/null +++ b/libhover/org.eclipse.linuxtools.cdt.libhover.tests/build.properties @@ -0,0 +1,5 @@ +source.. = src/ +output.. = bin/ +bin.includes = META-INF/,\ + .,\ + about.html diff --git a/libhover/org.eclipse.linuxtools.cdt.libhover.tests/pom.xml b/libhover/org.eclipse.linuxtools.cdt.libhover.tests/pom.xml new file mode 100644 index 0000000000..9794f34852 --- /dev/null +++ b/libhover/org.eclipse.linuxtools.cdt.libhover.tests/pom.xml @@ -0,0 +1,47 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2014, Red Hat, Inc. + + 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 +--> +<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> + <modelVersion>4.0.0</modelVersion> + + <parent> + <artifactId>linuxtools-libhover-parent</artifactId> + <groupId>org.eclipse.linuxtools.cdt.libhover</groupId> + <version>3.0.0-SNAPSHOT</version> + </parent> + + <artifactId>org.eclipse.linuxtools.cdt.libhover.tests</artifactId> + <version>1.0.0-SNAPSHOT</version> + <packaging>eclipse-test-plugin</packaging> + + <name>Linux Tools Libhover Core Tests Plug-in</name> + + <build> + <plugins> + <plugin> + <groupId>org.eclipse.tycho</groupId> + <artifactId>tycho-surefire-plugin</artifactId> + <version>${tycho-version}</version> + <configuration> + <excludes> + <!-- test mojo matches TestProject be default and treats it as PojoTest --> + <exclude>**/Test*.class</exclude> + </excludes> + <testSuite>org.eclipse.linuxtools.cdt.libhover.tests</testSuite> + <testClass>org.eclipse.linuxtools.cdt.libhover.tests.ContentAssistTests</testClass> + <useUIHarness>true</useUIHarness> + <useUIThread>true</useUIThread> + <product>org.eclipse.platform.ide</product> + </configuration> + </plugin> + </plugins> + </build> + +</project> diff --git a/libhover/org.eclipse.linuxtools.cdt.libhover.tests/src/org/eclipse/linuxtools/cdt/libhover/tests/BaseTestCase.java b/libhover/org.eclipse.linuxtools.cdt.libhover.tests/src/org/eclipse/linuxtools/cdt/libhover/tests/BaseTestCase.java new file mode 100644 index 0000000000..db87e6384d --- /dev/null +++ b/libhover/org.eclipse.linuxtools.cdt.libhover.tests/src/org/eclipse/linuxtools/cdt/libhover/tests/BaseTestCase.java @@ -0,0 +1,323 @@ +/******************************************************************************* + * Copyright (c) 2006, 2013 Wind River Systems, Inc. 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: + * Markus Schorn - initial API and implementation + * Andrew Ferguson (Symbian) + * Sergey Prigogin (Google) + *******************************************************************************/ +package org.eclipse.linuxtools.cdt.libhover.tests; + +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import junit.framework.AssertionFailedError; +import junit.framework.Test; +import junit.framework.TestCase; +import junit.framework.TestFailure; +import junit.framework.TestResult; +import junit.framework.TestSuite; + +import org.eclipse.cdt.core.CCorePlugin; +import org.eclipse.cdt.core.index.IIndex; +import org.eclipse.cdt.core.model.CoreModel; +import org.eclipse.cdt.core.model.ElementChangedEvent; +import org.eclipse.cdt.core.model.ICProject; +import org.eclipse.cdt.core.model.IElementChangedListener; +import org.eclipse.cdt.internal.core.CCoreInternals; +import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPASTNameBase; +import org.eclipse.cdt.internal.core.pdom.CModelListener; +import org.eclipse.cdt.internal.core.pdom.PDOMManager; +import org.eclipse.core.resources.IFile; +import org.eclipse.core.resources.IResourceStatus; +import org.eclipse.core.resources.ResourcesPlugin; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.ILogListener; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.MultiStatus; +import org.eclipse.core.runtime.NullProgressMonitor; +import org.eclipse.core.runtime.jobs.Job; + +@SuppressWarnings("restriction") +public class BaseTestCase extends TestCase { + private static final String DEFAULT_INDEXER_TIMEOUT_SEC = "10"; + private static final String INDEXER_TIMEOUT_PROPERTY = "indexer.timeout"; + /** + * Indexer timeout used by tests. To avoid this timeout expiring during debugging add + * -Dindexer.timeout=some_large_number to VM arguments of the test launch configuration. + */ + protected static final int INDEXER_TIMEOUT_SEC = + Integer.parseInt(System.getProperty(INDEXER_TIMEOUT_PROPERTY, DEFAULT_INDEXER_TIMEOUT_SEC)); + protected static final int INDEXER_TIMEOUT_MILLISEC= INDEXER_TIMEOUT_SEC * 1000; + + private boolean fExpectFailure; + private int fBugNumber; + private int fExpectedLoggedNonOK; + + public BaseTestCase() { + super(); + } + + public BaseTestCase(String name) { + super(name); + } + + public static NullProgressMonitor npm() { + return new NullProgressMonitor(); + } + + @Override + protected void setUp() throws Exception { + CPPASTNameBase.sAllowRecursionBindings= false; + CPPASTNameBase.sAllowNameComputation= false; + CModelListener.sSuppressUpdateOfLastRecentlyUsed= true; + } + + @Override + protected void tearDown() throws Exception { + ResourceHelper.cleanUp(); + TestScannerProvider.clear(); + } + + protected static TestSuite suite(Class<?> clazz) { + return suite(clazz, null); + } + + protected static TestSuite suite(Class<?> clazz, String failingTestPrefix) { + TestSuite suite= new TestSuite(clazz); + Test failing= getFailingTests(clazz, failingTestPrefix); + if (failing != null) { + suite.addTest(failing); + } + return suite; + } + + private static Test getFailingTests(Class<?> clazz, String prefix) { + TestSuite suite= new TestSuite("Failing Tests"); + HashSet<String> names= new HashSet<String>(); + Class<?> superClass= clazz; + while (Test.class.isAssignableFrom(superClass) && !TestCase.class.equals(superClass)) { + Method[] methods= superClass.getDeclaredMethods(); + for (Method method : methods) { + addFailingMethod(suite, method, names, clazz, prefix); + } + superClass= superClass.getSuperclass(); + } + if (suite.countTestCases() == 0) { + return null; + } + return suite; + } + + private static void addFailingMethod(TestSuite suite, Method m, Set<String> names, Class<?> clazz, String prefix) { + String name= m.getName(); + if (!names.add(name)) { + return; + } + if (name.startsWith("test") || (prefix != null && !name.startsWith(prefix))) { + return; + } + if (name.equals("tearDown") || name.equals("setUp") || name.equals("runBare")) { + return; + } + if (Modifier.isPublic(m.getModifiers())) { + @SuppressWarnings("rawtypes") + Class[] parameters= m.getParameterTypes(); + Class<?> returnType= m.getReturnType(); + if (parameters.length == 0 && returnType.equals(Void.TYPE)) { + Test test= TestSuite.createTest(clazz, name); + ((BaseTestCase) test).setExpectFailure(0); + suite.addTest(test); + } + } + } + + @Override + public void runBare() throws Throwable { + final List<IStatus> statusLog= Collections.synchronizedList(new ArrayList<IStatus>()); + ILogListener logListener= new ILogListener() { + @Override + public void logging(IStatus status, String plugin) { + if (!status.isOK() && status.getSeverity() != IStatus.INFO) { + switch (status.getCode()) { + case IResourceStatus.NOT_FOUND_LOCAL: + case IResourceStatus.NO_LOCATION_LOCAL: + case IResourceStatus.FAILED_READ_LOCAL: + case IResourceStatus.RESOURCE_NOT_LOCAL: + // Logged by the resources plugin. + return; + } + statusLog.add(status); + } + } + }; + final CCorePlugin corePlugin = CCorePlugin.getDefault(); + if (corePlugin != null) { //Iif we don't run a JUnit Plugin Test + corePlugin.getLog().addLogListener(logListener); + } + + Throwable testThrowable= null; + try { + try { + super.runBare(); + } catch (Throwable e) { + testThrowable= e; + } + + if (statusLog.size() != fExpectedLoggedNonOK) { + StringBuffer msg= new StringBuffer("Expected number (" + fExpectedLoggedNonOK + ") of "); + msg.append("non-OK status objects in log differs from actual (" + statusLog.size() + ").\n"); + Throwable cause= null; + if (!statusLog.isEmpty()) { + synchronized (statusLog) { + for (IStatus status : statusLog) { + IStatus[] ss= {status}; + ss= status instanceof MultiStatus ? ((MultiStatus) status).getChildren() : ss; + for (IStatus s : ss) { + msg.append("\t" + s.getMessage() + " "); + + Throwable t= s.getException(); + cause= cause != null ? cause : t; + if (t != null) { + msg.append(t.getMessage() != null ? t.getMessage() : t.getClass().getCanonicalName()); + } + + msg.append("\n"); + } + } + } + } + cause= cause != null ? cause : testThrowable; + AssertionFailedError afe= new AssertionFailedError(msg.toString()); + afe.initCause(cause); + throw afe; + } + } finally { + if (corePlugin != null) { + corePlugin.getLog().removeLogListener(logListener); + } + } + + if (testThrowable != null) + throw testThrowable; + } + + @Override + public void run(TestResult result) { + if (!fExpectFailure || "true".equals(System.getProperty("SHOW_EXPECTED_FAILURES"))) { + super.run(result); + return; + } + + result.startTest(this); + + TestResult r = new TestResult(); + super.run(r); + if (r.failureCount() == 1) { + TestFailure failure= r.failures().nextElement(); + String msg= failure.exceptionMessage(); + if (msg != null && msg.startsWith("Method \"" + getName() + "\"")) { + result.addFailure(this, new AssertionFailedError(msg)); + } + } else if (r.errorCount() == 0 && r.failureCount() == 0) { + String err = "Unexpected success of " + getName(); + if (fBugNumber > 0) { + err += ", bug #" + fBugNumber; + } + result.addFailure(this, new AssertionFailedError(err)); + } + + result.endTest(this); + } + + public void setExpectFailure(int bugNumber) { + fExpectFailure= true; + fBugNumber= bugNumber; + } + + /** + * The last value passed to this method in the body of a testXXX method + * will be used to determine whether or not the presence of non-OK status objects + * in the log should fail the test. If the logged number of non-OK status objects + * differs from the last value passed, the test is failed. If this method is not called + * at all, the expected number defaults to zero. + * @param count the expected number of logged error and warning messages + */ + public void setExpectedNumberOfLoggedNonOKStatusObjects(int count) { + fExpectedLoggedNonOK= count; + } + + /** + * Some test steps need synchronizing against a CModel event. This class + * is a very basic means of doing that. + */ + static protected class ModelJoiner implements IElementChangedListener { + private final boolean[] changed= new boolean[1]; + + public ModelJoiner() { + CoreModel.getDefault().addElementChangedListener(this); + } + + public void clear() { + synchronized (changed) { + changed[0]= false; + changed.notifyAll(); + } + } + + public void join() throws CoreException { + try { + synchronized(changed) { + while (!changed[0]) { + changed.wait(); + } + } + } catch (InterruptedException e) { + throw new CoreException(CCorePlugin.createStatus("Interrupted", e)); + } + } + + public void dispose() { + CoreModel.getDefault().removeElementChangedListener(this); + } + + @Override + public void elementChanged(ElementChangedEvent event) { + // Only respond to post change events + if (event.getType() != ElementChangedEvent.POST_CHANGE) + return; + + synchronized (changed) { + changed[0]= true; + changed.notifyAll(); + } + } + } + + public static void waitForIndexer(ICProject project) throws InterruptedException { + Job.getJobManager().join(ResourcesPlugin.FAMILY_AUTO_REFRESH, null); + + final PDOMManager indexManager = CCoreInternals.getPDOMManager(); + assertTrue(indexManager.joinIndexer(INDEXER_TIMEOUT_SEC * 1000, npm())); + long waitms= 1; + while (waitms < 2000 && !indexManager.isProjectRegistered(project)) { + Thread.sleep(waitms); + waitms *= 2; + } + assertTrue(indexManager.isProjectRegistered(project)); + assertTrue(indexManager.joinIndexer(INDEXER_TIMEOUT_SEC * 1000, npm())); + } + + public static void waitUntilFileIsIndexed(IIndex index, IFile file) throws Exception { + TestSourceReader.waitUntilFileIsIndexed(index, file, INDEXER_TIMEOUT_SEC * 1000); + } +} diff --git a/libhover/org.eclipse.linuxtools.cdt.libhover.tests/src/org/eclipse/linuxtools/cdt/libhover/tests/BaseUITestCase.java b/libhover/org.eclipse.linuxtools.cdt.libhover.tests/src/org/eclipse/linuxtools/cdt/libhover/tests/BaseUITestCase.java new file mode 100644 index 0000000000..7a23b046c8 --- /dev/null +++ b/libhover/org.eclipse.linuxtools.cdt.libhover.tests/src/org/eclipse/linuxtools/cdt/libhover/tests/BaseUITestCase.java @@ -0,0 +1,355 @@ +/******************************************************************************* + * Copyright (c) 2006, 2010 Wind River Systems, Inc. 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: + * Markus Schorn - initial API and implementation + *******************************************************************************/ +package org.eclipse.linuxtools.cdt.libhover.tests; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import org.eclipse.cdt.core.dom.ast.IASTTranslationUnit; +import org.eclipse.cdt.core.index.IIndex; +import org.eclipse.cdt.core.model.CModelException; +import org.eclipse.cdt.core.model.ICProject; +import org.eclipse.cdt.utils.Platform; +import org.eclipse.core.commands.ExecutionException; +import org.eclipse.core.commands.NotEnabledException; +import org.eclipse.core.commands.NotHandledException; +import org.eclipse.core.commands.common.NotDefinedException; +import org.eclipse.core.resources.IContainer; +import org.eclipse.core.resources.IFile; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.Path; +import org.eclipse.swt.SWT; +import org.eclipse.swt.SWTException; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Event; +import org.eclipse.swt.widgets.Tree; +import org.eclipse.swt.widgets.TreeItem; +import org.eclipse.ui.IEditorPart; +import org.eclipse.ui.IEditorReference; +import org.eclipse.ui.IViewPart; +import org.eclipse.ui.IViewReference; +import org.eclipse.ui.IWorkbenchPage; +import org.eclipse.ui.IWorkbenchPartSite; +import org.eclipse.ui.IWorkbenchWindow; +import org.eclipse.ui.PartInitException; +import org.eclipse.ui.PlatformUI; +import org.eclipse.ui.WorkbenchException; +import org.eclipse.ui.handlers.IHandlerService; +import org.eclipse.ui.internal.WorkbenchPartReference; + +@SuppressWarnings("restriction") +public class BaseUITestCase extends BaseTestCase { + + public BaseUITestCase() { + super(); + } + + public BaseUITestCase(String name) { + super(name); + } + + @Override + protected void setUp() throws Exception { + super.setUp(); + final IWorkbenchPage activePage = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage(); + IViewPart view= activePage.findView("org.eclipse.cdt.ui.tests.DOMAST.DOMAST"); + if (view != null) { + activePage.hideView(view); + } + } + + @Override + protected void tearDown() throws Exception { + runEventQueue(0); + super.tearDown(); + } + + /** + * Reads a section in comments form the source of the given class. Fully + * equivalent to <code>readTaggedComment(getClass(), tag)</code> + * @since 4.0 + */ + protected String readTaggedComment(final String tag) throws IOException { + return TestSourceReader.readTaggedComment(Platform.getBundle(CProjectHelper.PLUGIN_ID), "ui", getClass(), tag); + } + + /** + * Reads multiple sections in comments from the source of the given class. + * @since 4.0 + */ + public StringBuilder[] getContentsForTest(int sections) throws IOException { + return TestSourceReader.getContentsForTest(Platform.getBundle(CProjectHelper.PLUGIN_ID), "ui", + getClass(), getName(), sections); + } + + public String getAboveComment() throws IOException { + return getContentsForTest(1)[0].toString(); + } + + protected IFile createFile(IContainer container, String fileName, String contents) throws Exception { + return TestSourceReader.createFile(container, new Path(fileName), contents); + } + + protected IASTTranslationUnit createIndexBasedAST(IIndex index, ICProject project, IFile file) throws CModelException, CoreException { + return TestSourceReader.createIndexBasedAST(index, project, file); + } + + protected void runEventQueue(int time) { + final long endTime= System.currentTimeMillis() + time; + while (true) { + while (Display.getCurrent().readAndDispatch()) + ; + long diff= endTime - System.currentTimeMillis(); + if (diff <= 0) { + break; + } + try { + Thread.sleep(Math.min(20, diff)); + } catch (InterruptedException e) { + return; + } + } + } + + protected void expandTreeItem(Tree tree, int idx) { + expandTreeItem(tree, new int[] { idx }); + } + + protected void expandTreeItem(Tree tree, int idx1, int idx2) { + expandTreeItem(tree, new int[] { idx1, idx2 }); + } + + protected void expandTreeItem(Tree tree, int[] idxs) { + TreeItem item= tree.getItem(idxs[0]); + assertNotNull(item); + expandTreeItem(item); + for (int i= 1; i < idxs.length; i++) { + item= item.getItem(idxs[i]); + assertNotNull(item); + expandTreeItem(item); + } + } + + protected void expandTreeItem(TreeItem item) { + Event event = new Event(); + event.item = item; + item.getParent().notifyListeners(SWT.Expand, event); + item.setExpanded(true); + runEventQueue(0); + } + + protected void selectTreeItem(Tree tree, int idx) { + selectTreeItem(tree, new int[] {idx}); + } + + protected void selectTreeItem(Tree tree, int idx1, int idx2) { + selectTreeItem(tree, new int[] {idx1, idx2}); + } + + protected void selectTreeItem(Tree tree, int[] idxs) { + TreeItem item= tree.getItem(idxs[0]); + assertNotNull(item); + for (int i= 1; i < idxs.length; i++) { + item= item.getItem(idxs[i]); + assertNotNull(item); + } + tree.setSelection(item); + Event event = new Event(); + event.item = item; + item.getParent().notifyListeners(SWT.Selection, event); + runEventQueue(0); + } + + protected void closeEditor(IEditorPart editor) { + IWorkbenchPartSite site; + IWorkbenchPage page; + if (editor != null && (site= editor.getSite()) != null && (page= site.getPage()) != null) { + page.closeEditor(editor, false); + } + } + + protected void closeAllEditors() { + IWorkbenchWindow[] windows= PlatformUI.getWorkbench().getWorkbenchWindows(); + for (IWorkbenchWindow window : windows) { + IWorkbenchPage[] pages= window.getPages(); + for (IWorkbenchPage page : pages) { + page.closeAllEditors(false); + } + } + } + + protected void restoreAllParts() throws WorkbenchException { + IWorkbenchPage page= PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage(); + page.zoomOut(); + runEventQueue(0); + + IViewReference[] viewRefs= page.getViewReferences(); + for (IViewReference ref : viewRefs) { + page.setPartState(ref, IWorkbenchPage.STATE_RESTORED); + } + IEditorReference[] editorRefs= page.getEditorReferences(); + for (IEditorReference ref : editorRefs) { + page.setPartState(ref, IWorkbenchPage.STATE_RESTORED); + } + runEventQueue(0); + } + + protected IViewPart activateView(String id) throws PartInitException { + IViewPart view= PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage().showView(id); + assertNotNull(view); + runEventQueue(0); + return view; + } + + protected void executeCommand(IViewPart viewPart, String commandID) throws ExecutionException, NotDefinedException, NotEnabledException, NotHandledException { + IHandlerService hs= (IHandlerService)viewPart.getSite().getService(IHandlerService.class); + assertNotNull(hs); + hs.executeCommand(commandID, null); + } + + private Control[] findControls(Control w, Class<?> clazz) { + ArrayList<Control> result= new ArrayList<Control>(); + findControls(w, clazz, result); + return result.toArray(new Control[result.size()]); + } + + private void findControls(Control w, Class<?> clazz, List<Control> result) { + if (clazz.isInstance(w)) { + result.add(w); + } + if (w instanceof Composite) { + Composite comp= (Composite) w; + Control[] children= comp.getChildren(); + for (Control element : children) { + findControls(element, clazz, result); + } + } + } + + final protected TreeItem checkTreeNode(IViewPart part, int i0, String label) { + assertNotNull(label); // we don't handle testing for a base node to not appear; can be added if/when needed + IViewReference viewRef = part.getViewSite().getPage().findViewReference(part.getViewSite().getId()); + Control viewControl = ((WorkbenchPartReference) viewRef).getPane().getControl(); + + Tree tree= null; + TreeItem root= null; + StringBuilder cands= new StringBuilder(); + for (int i= 0; i < 400; i++) { + cands.setLength(0); + Control[] trees= findControls(viewControl, Tree.class); + for (int j = 0; j < trees.length; j++) { + try { + tree= (Tree) trees[j]; + root= tree.getItem(i0); + if (label.equals(root.getText())) { + return root; + } + if (j > 0) { + cands.append('|'); + } + cands.append(root.getText()); + } catch (SWTException e) { + // in case widget was disposed, item may be replaced + } catch (IllegalArgumentException e) { + // item does not yet exist. + } + } + runEventQueue(10); + } + assertNotNull("No tree in viewpart", tree); + assertNotNull("Tree node " + label + "{" + i0 + "} does not exist!", root); + assertEquals(label, cands.toString()); + return root; + } + + final protected TreeItem checkTreeNode(Tree tree, int i0, String label) { + assertNotNull(label); // we don't handle testing for a base node to not appear; can be added if/when needed + TreeItem root= null; + for (int millis= 0; millis < 5000; millis= millis == 0 ? 1 : millis * 2) { + runEventQueue(millis); + try { + root= tree.getItem(i0); + if (label.equals(root.getText())) { + return root; + } + } catch (SWTException e) { + // in case widget was disposed, item may be replaced + } catch (IllegalArgumentException e) { + // item does not yet exist. + } + } + fail("Tree node " + label + "{" + i0 + "} does not exist!"); + return null; + } + + /** + * Pass label=null to test that the {i0,i1} node doesn't exist + */ + final protected TreeItem checkTreeNode(Tree tree, int i0, int i1, String label) { + String firstItemText= null; + int timeout = (label == null) ? 1000 : 5000; // see footnote[0] + + // If {i0,i1} exists, whether or not it matches label (when label != null) + boolean nodePresent = false; + + for (int millis= 0; millis < timeout; millis= millis == 0 ? 1 : millis * 2) { + nodePresent = false; + runEventQueue(millis); + TreeItem i0Node= tree.getItem(i0); + if (!i0Node.getExpanded()) { + expandTreeItem(i0Node); + } + try { + TreeItem firstItem= i0Node.getItem(0); + firstItemText= firstItem.getText(); + if (firstItemText.length() > 0 && !firstItemText.equals("...")) { + TreeItem item = i0Node.getItem(i1); + nodePresent = true; + if (label != null && label.equals(item.getText())) { + return item; + } + } + } catch (SWTException e) { + // in case widget was disposed, item may be replaced + } catch (IllegalArgumentException e) { + // item does not yet exist. + } + } + + if (label == null) { + assertFalse("Tree node {" + i0 + "," + i1 + "} exists but shouldn't!", nodePresent); + } else { + fail("Tree node " + label + "{" + i0 + "," + i1 + "} does not exist!"); + } + return null; + } + + public static void assertEqualString(String actual, String expected) { + assertEqualString(actual, expected); + } +} + +// Footnotes +// [0] Waiting for something to appear is very efficient; waiting for it to not +// appear is very inefficient. In the former case, regardless of how much time +// is alloted, we stop waiting as soon as the item appears, whereas in the +// latter we have to wait the entire timeout. In test suites with thousands of +// tests, efficiency is critical. Thus, in testing that a tree node doesn't have +// an Nth child, we shoot for efficiency and accept the risk of a false +// negative. More specifically, we wait only one second for the item TO NOT +// appear, whereas we give an item up to five seconds TO appear. This compromise +// is better than not having that sort of test at all, which some would argue is +// the better approach. In practice, it takes about 60-150 ms for the item to +// appear (on my machine), but we give it up to five seconds. Waiting one second +// for it to not appear should be more than adequate diff --git a/libhover/org.eclipse.linuxtools.cdt.libhover.tests/src/org/eclipse/linuxtools/cdt/libhover/tests/CProjectHelper.java b/libhover/org.eclipse.linuxtools.cdt.libhover.tests/src/org/eclipse/linuxtools/cdt/libhover/tests/CProjectHelper.java new file mode 100644 index 0000000000..7ba4763d21 --- /dev/null +++ b/libhover/org.eclipse.linuxtools.cdt.libhover.tests/src/org/eclipse/linuxtools/cdt/libhover/tests/CProjectHelper.java @@ -0,0 +1,448 @@ +/******************************************************************************* + * Copyright (c) 2005, 2010 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 + * Andrew Ferguson (Symbian) + * Markus Schorn (Wind River Systems) + * Anton Leherbauer (Wind River Systems) + *******************************************************************************/ +package org.eclipse.linuxtools.cdt.libhover.tests; + +import java.io.File; +import java.io.IOException; +import java.lang.reflect.InvocationTargetException; +import java.util.zip.ZipFile; + +import static org.junit.Assert.*; + +import org.eclipse.cdt.core.CCProjectNature; +import org.eclipse.cdt.core.CCorePlugin; +import org.eclipse.cdt.core.CProjectNature; +import org.eclipse.cdt.core.model.CModelException; +import org.eclipse.cdt.core.model.CoreModel; +import org.eclipse.cdt.core.model.IArchive; +import org.eclipse.cdt.core.model.IArchiveContainer; +import org.eclipse.cdt.core.model.IBinary; +import org.eclipse.cdt.core.model.IBinaryContainer; +import org.eclipse.cdt.core.model.ICContainer; +import org.eclipse.cdt.core.model.ICElement; +import org.eclipse.cdt.core.model.ICProject; +import org.eclipse.cdt.core.model.ISourceRoot; +import org.eclipse.cdt.core.model.ITranslationUnit; +import org.eclipse.cdt.core.settings.model.ICConfigExtensionReference; +import org.eclipse.cdt.core.settings.model.ICConfigurationDescription; +import org.eclipse.cdt.core.settings.model.ICProjectDescription; +import org.eclipse.cdt.core.settings.model.ICProjectDescriptionManager; +import org.eclipse.cdt.core.settings.model.util.CDataUtil; +import org.eclipse.cdt.internal.core.pdom.indexer.IndexerPreferences; +import org.eclipse.core.resources.IFolder; +import org.eclipse.core.resources.IProject; +import org.eclipse.core.resources.IProjectDescription; +import org.eclipse.core.resources.IResource; +import org.eclipse.core.resources.IWorkspace; +import org.eclipse.core.resources.IWorkspaceRoot; +import org.eclipse.core.resources.IWorkspaceRunnable; +import org.eclipse.core.resources.ResourcesPlugin; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.FileLocator; +import org.eclipse.core.runtime.IPath; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.NullProgressMonitor; +import org.eclipse.core.runtime.Path; +import org.eclipse.core.runtime.Status; +import org.eclipse.ui.dialogs.IOverwriteQuery; +import org.eclipse.ui.wizards.datatransfer.FileSystemStructureProvider; +import org.eclipse.ui.wizards.datatransfer.ImportOperation; +import org.eclipse.ui.wizards.datatransfer.ZipFileStructureProvider; +import org.osgi.framework.Bundle; +/** + * Helper methods to set up a ICProject. + */ +@SuppressWarnings("restriction") +public class CProjectHelper { + + public final static String PLUGIN_ID = "org.eclipse.linuxtools.cdt.libhover.tests"; //$NON-NLS-1$ + + private final static IOverwriteQuery OVERWRITE_QUERY= new IOverwriteQuery() { + @Override + public String queryOverwrite(String file) { + return ALL; + } + }; + + public static ICProject createCProject(final String projectName, String binFolderName) throws CoreException { + return createCCProject(projectName, binFolderName, null); + } + + /** + * Creates a ICProject. + */ + public static ICProject createCProject(final String projectName, String binFolderName, final String indexerID) throws CoreException { + final IWorkspace ws = ResourcesPlugin.getWorkspace(); + final ICProject newProject[] = new ICProject[1]; + ws.run(new IWorkspaceRunnable() { + @Override + public void run(IProgressMonitor monitor) throws CoreException { + IWorkspaceRoot root = ws.getRoot(); + IProject project = root.getProject(projectName); + if (indexerID != null) { + IndexerPreferences.set(project, IndexerPreferences.KEY_INDEX_UNUSED_HEADERS_WITH_DEFAULT_LANG, "true"); + IndexerPreferences.set(project, IndexerPreferences.KEY_INDEXER_ID, indexerID); + } + if (!project.exists()) { + project.create(null); + } else { + project.refreshLocal(IResource.DEPTH_INFINITE, null); + } + if (!project.isOpen()) { + project.open(null); + } + if (!project.hasNature(CProjectNature.C_NATURE_ID)) { + String projectId = PLUGIN_ID + ".TestProject"; + addNatureToProject(project, CProjectNature.C_NATURE_ID, null); + CCorePlugin.getDefault().mapCProjectOwner(project, projectId, false); + } + addDefaultBinaryParser(project); + newProject[0] = CCorePlugin.getDefault().getCoreModel().create(project); + } + }, null); + + return newProject[0]; + } + + /** + * Add the default binary parser if no binary parser configured. + * + * @param project + * @throws CoreException + */ + public static boolean addDefaultBinaryParser(IProject project) throws CoreException { + ICConfigExtensionReference[] binaryParsers= CCorePlugin.getDefault().getDefaultBinaryParserExtensions(project); + if (binaryParsers == null || binaryParsers.length == 0) { + ICProjectDescription desc= CCorePlugin.getDefault().getProjectDescription(project); + if (desc == null) { + return false; + } + + desc.getDefaultSettingConfiguration().create(CCorePlugin.BINARY_PARSER_UNIQ_ID, CCorePlugin.DEFAULT_BINARY_PARSER_UNIQ_ID); + CCorePlugin.getDefault().setProjectDescription(project, desc); + } + return true; + } + + /** + * Creates a ICProject. + */ + public static ICProject createNewStileCProject(final String projectName, final String indexerID) throws CoreException { + return createNewStileCProject(projectName, indexerID, false); + } + + /** + * Creates a ICProject. + */ + public static ICProject createNewStileCProject(final String projectName, String providerId, final String indexerID) throws CoreException { + return createNewStileCProject(projectName, providerId, indexerID, false); + } + + /** + * Creates a ICProject. + */ + public static ICProject createNewStileCProject(final String projectName, final String indexerID, boolean markCreating) throws CoreException { + return createNewStileCProject(projectName, null, indexerID, markCreating); + } + + /** + * Creates a ICProject. + */ + public static ICProject createNewStileCProject(final String projectName, String cfgProviderId, final String indexerID, final boolean markCreating) throws CoreException { + final IWorkspace ws = ResourcesPlugin.getWorkspace(); + final ICProject newProject[] = new ICProject[1]; + if(cfgProviderId == null) + cfgProviderId = TestCfgDataProvider.PROVIDER_ID; + + final String finalCfgProviderId = cfgProviderId; + ws.run(new IWorkspaceRunnable() { + @Override + public void run(IProgressMonitor monitor) throws CoreException { + IWorkspaceRoot root = ws.getRoot(); + IProject project = root.getProject(projectName); + if (indexerID != null) { + IndexerPreferences.set(project, IndexerPreferences.KEY_INDEXER_ID, indexerID); + } + if (!project.exists()) { + project.create(null); + } else { + project.refreshLocal(IResource.DEPTH_INFINITE, null); + } + if (!project.isOpen()) { + project.open(null); + } + if (!project.hasNature(CProjectNature.C_NATURE_ID)) { + addNatureToProject(project, CProjectNature.C_NATURE_ID, null); + ICConfigurationDescription prefCfg = CCorePlugin.getDefault().getPreferenceConfiguration(finalCfgProviderId); + ICProjectDescriptionManager mngr = CCorePlugin.getDefault().getProjectDescriptionManager(); + ICProjectDescription projDes = mngr.createProjectDescription(project, false, markCreating); + projDes.createConfiguration(CDataUtil.genId(null), CDataUtil.genId("test"), prefCfg); + mngr.setProjectDescription(project, projDes); +// CCorePlugin.getDefault().mapCProjectOwner(project, projectId, false); + } + addDefaultBinaryParser(project); + newProject[0] = CCorePlugin.getDefault().getCoreModel().create(project); + } + }, null); + + return newProject[0]; + } + + + private static String getMessage(IStatus status) { + StringBuffer message = new StringBuffer("["); + message.append(status.getMessage()); + if (status.isMultiStatus()) { + IStatus children[] = status.getChildren(); + for( int i = 0; i < children.length; i++) { + message.append(getMessage(children[i])); + } + } + message.append("]"); + return message.toString(); + } + + public static ICProject createCCProject(final String projectName, final String binFolderName) throws CoreException { + return createCCProject(projectName, binFolderName, null); + } + + public static ICProject createCCProject(final String projectName, final String binFolderName, final String indexerID) throws CoreException { + final IWorkspace ws = ResourcesPlugin.getWorkspace(); + final ICProject newProject[] = new ICProject[1]; + ws.run(new IWorkspaceRunnable() { + @Override + public void run(IProgressMonitor monitor) throws CoreException { + ICProject cproject = createCProject(projectName, binFolderName, indexerID); + if (!cproject.getProject().hasNature(CCProjectNature.CC_NATURE_ID)) { + addNatureToProject(cproject.getProject(), CCProjectNature.CC_NATURE_ID, null); + } + newProject[0] = cproject; + } + }, null); + return newProject[0]; + } + + /** + * Removes a ICProject. + */ + public static void delete(ICProject cproject) { + try { + cproject.getProject().delete(true, true, null); + } catch (CoreException e) { + try { + Thread.sleep(1000); + } catch (InterruptedException e1) { + } finally { + try { + System.gc(); + System.runFinalization(); + cproject.getProject().delete(true, true, null); + } catch (CoreException e2) { + fail(getMessage(e2.getStatus())); + } + } + } + } + + /** + * Adds a folder container to a ICProject. + */ + public static ICContainer addCContainer(ICProject cproject, String containerName) throws CoreException { + IProject project = cproject.getProject(); + ICContainer container = null; + if (containerName == null || containerName.length() == 0) { + ICContainer[] conts = cproject.getSourceRoots(); + if (conts.length > 0) { + container = conts[0]; + } + } else { + IFolder folder = project.getFolder(containerName); + if (!folder.exists()) { + folder.create(false, true, null); + } + container = CoreModel.getDefault().create(folder); + } + return container; + } + + /** + * Adds a folder container to a ICProject and imports all files contained in the given Zip file. + */ + public static ICContainer addCContainerWithImport(ICProject cproject, String containerName, ZipFile zipFile) + throws InvocationTargetException, CoreException { + ICContainer root = addCContainer(cproject, containerName); + importFilesFromZip(zipFile, root.getPath(), null); + return root; + } + + /** + * Removes a folder from a ICProject. + */ + public static void removeCContainer(ICProject cproject, String containerName) throws CoreException { + IFolder folder = cproject.getProject().getFolder(containerName); + folder.delete(true, null); + } + + /** + * Attempts to find an archive with the given name in the workspace + */ + public static IArchive findArchive(ICProject testProject, String name) throws CModelException { + int x; + IArchive[] myArchives; + IArchiveContainer archCont; + /*************************************************************************************************************************** + * Since ArchiveContainer.getArchives does not wait until all the archives in the project have been parsed before returning + * the list, we have to do a sync ArchiveContainer.getChildren first to make sure we find all the archives. + */ + archCont = testProject.getArchiveContainer(); + myArchives = archCont.getArchives(); + if (myArchives.length < 1) + return null; + for (x = 0; x < myArchives.length; x++) { + if (myArchives[x].getElementName().equals(name)) + return (myArchives[x]); + } + return null; + } + + /** + * Attempts to find a binary with the given name in the workspace + */ + public static IBinary findBinary(ICProject testProject, String name) throws CModelException { + IBinaryContainer binCont; + int x; + IBinary[] myBinaries; + binCont = testProject.getBinaryContainer(); + myBinaries = binCont.getBinaries(); + if (myBinaries.length < 1) + return null; + for (x = 0; x < myBinaries.length; x++) { + if (myBinaries[x].getElementName().equals(name)) + return (myBinaries[x]); + } + return null; + } + + /** + * Attempts to find an object with the given name in the workspace + */ + public static IBinary findObject(ICProject testProject, String name) throws CModelException { + ICElement[] sourceRoots = testProject.getChildren(); + for (int i = 0; i < sourceRoots.length; i++) { + ISourceRoot root = (ISourceRoot) sourceRoots[i]; + ICElement[] myElements = root.getChildren(); + for (int x = 0; x < myElements.length; x++) { + if (myElements[x].getElementName().equals(name)) { + if (myElements[x] instanceof IBinary) { + return ((IBinary) myElements[x]); + } + } + } + } + return null; + } + + /** + * Attempts to find a TranslationUnit with the given name in the workspace + * @throws InterruptedException + */ + public static ITranslationUnit findTranslationUnit(ICProject testProject, String name) throws CModelException, InterruptedException { + for (int j=0; j<20; j++) { + ICElement[] sourceRoots = testProject.getChildren(); + for (int i = 0; i < sourceRoots.length; i++) { + ISourceRoot root = (ISourceRoot) sourceRoots[i]; + ICElement[] myElements = root.getChildren(); + for (int x = 0; x < myElements.length; x++) { + if (myElements[x].getElementName().equals(name)) { + if (myElements[x] instanceof ITranslationUnit) { + return ((ITranslationUnit) myElements[x]); + } + } + } + } + Thread.sleep(100); + } + return null; + } + + /** + * Attempts to find an element with the given name in the workspace + */ + public static ICElement findElement(ICProject testProject, String name) throws CModelException { + ICElement[] sourceRoots = testProject.getChildren(); + for (int i = 0; i < sourceRoots.length; i++) { + ISourceRoot root = (ISourceRoot) sourceRoots[i]; + ICElement[] myElements = root.getChildren(); + for (int x = 0; x < myElements.length; x++) { + if (myElements[x].getElementName().equals(name)) { + return myElements[x]; + } + } + } + return null; + } + + public static void addNatureToProject(IProject proj, String natureId, IProgressMonitor monitor) throws CoreException { + IProjectDescription description = proj.getDescription(); + String[] prevNatures = description.getNatureIds(); + String[] newNatures = new String[prevNatures.length + 1]; + System.arraycopy(prevNatures, 0, newNatures, 0, prevNatures.length); + newNatures[prevNatures.length] = natureId; + description.setNatureIds(newNatures); + proj.setDescription(description, monitor); + } + + private static void importFilesFromZip(ZipFile srcZipFile, IPath destPath, IProgressMonitor monitor) + throws InvocationTargetException { + ZipFileStructureProvider structureProvider = new ZipFileStructureProvider(srcZipFile); + try { + ImportOperation op = new ImportOperation(destPath, structureProvider.getRoot(), structureProvider, + OVERWRITE_QUERY); + op.run(monitor); + } catch (InterruptedException e) { + // should not happen + } + } + + + public static void importSourcesFromPlugin(ICProject project, Bundle bundle, String sources) throws CoreException { + try { + String baseDir= FileLocator.toFileURL(FileLocator.find(bundle, new Path(sources), null)).getFile(); + ImportOperation importOp = new ImportOperation(project.getProject().getFullPath(), + new File(baseDir), FileSystemStructureProvider.INSTANCE, OVERWRITE_QUERY); + importOp.setCreateContainerStructure(false); + importOp.run(new NullProgressMonitor()); + } + catch (Exception e) { + throw new CoreException(new Status(IStatus.ERROR, PLUGIN_ID, 0, "Import Interrupted", e)); + } + } + + /** + * @return the location of a newly created directory in temporary area. + * Note that cleanup should be done with {@link ResourceHelper#cleanUp()}. + * @throws IOException + * @throws CoreException + */ + public static File freshDir() throws IOException, CoreException { + IPath folderPath = ResourceHelper.createTemporaryFolder(); + File folder = new File(folderPath.toOSString()); + assertTrue(folder.exists()); + assertTrue(folder.isDirectory()); + assertTrue(folder.canWrite()); + + return folder; + } +}
\ No newline at end of file diff --git a/libhover/org.eclipse.linuxtools.cdt.libhover.tests/src/org/eclipse/linuxtools/cdt/libhover/tests/ContentAssistTests.java b/libhover/org.eclipse.linuxtools.cdt.libhover.tests/src/org/eclipse/linuxtools/cdt/libhover/tests/ContentAssistTests.java new file mode 100644 index 0000000000..7c70c8bfb4 --- /dev/null +++ b/libhover/org.eclipse.linuxtools.cdt.libhover.tests/src/org/eclipse/linuxtools/cdt/libhover/tests/ContentAssistTests.java @@ -0,0 +1,312 @@ +/******************************************************************************* + * Copyright (c) 2004, 2008, 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 + * Markus Schorn (Wind River Systems) + * Red Hat Inc. - modified to test Linux Tools libhover + *******************************************************************************/ +package org.eclipse.linuxtools.cdt.libhover.tests; + +import java.io.ByteArrayInputStream; +import java.io.InputStream; +import java.io.StringWriter; + +import junit.framework.Test; +import junit.framework.TestSuite; + +import org.eclipse.cdt.core.dom.IPDOMManager; +import org.eclipse.cdt.core.model.CModelException; +import org.eclipse.cdt.core.model.CoreModel; +import org.eclipse.cdt.core.model.ICProject; +import org.eclipse.cdt.core.model.ITranslationUnit; +import org.eclipse.cdt.core.model.IWorkingCopy; +import org.eclipse.cdt.internal.ui.CHelpProviderManager; +import org.eclipse.cdt.internal.ui.editor.CEditor; +import org.eclipse.cdt.internal.ui.text.CHelpBookDescriptor; +import org.eclipse.cdt.internal.ui.text.contentassist.CContentAssistProcessor; +import org.eclipse.cdt.ui.text.ICHelpInvocationContext; +import org.eclipse.core.resources.IFile; +import org.eclipse.core.resources.IProject; +import org.eclipse.core.resources.IResource; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.NullProgressMonitor; +import org.eclipse.jface.action.IAction; +import org.eclipse.jface.text.contentassist.ContentAssistant; +import org.eclipse.jface.text.contentassist.ICompletionProposal; +import org.eclipse.ui.IEditorPart; +import org.eclipse.ui.IWorkbenchPage; +import org.eclipse.ui.PlatformUI; +import org.eclipse.ui.part.FileEditorInput; + +/** + * @author aniefer + */ +@SuppressWarnings("restriction") +public class ContentAssistTests extends BaseUITestCase { + private final NullProgressMonitor monitor= new NullProgressMonitor(); + static IProject project; + static ICProject cproject; + static boolean disabledHelpContributions = false; + + @Override + public void setUp() throws InterruptedException { + //(CCorePlugin.getDefault().getCoreModel().getIndexManager()).reset(); + + if (project == null) { + try { + cproject = CProjectHelper.createCCProject("ContentAssistTestProject", "bin", IPDOMManager.ID_FAST_INDEXER); //$NON-NLS-1$ //$NON-NLS-2$ + project = cproject.getProject(); + waitForIndexer(cproject); + } catch ( CoreException e ) { + /*boo*/ + } + if (project == null) + fail("Unable to create project"); //$NON-NLS-1$ + } + } + public ContentAssistTests() + { + super(); + } + /** + * @param name + */ + public ContentAssistTests(String name) + { + super(name); + } + + private void disableContributions (){ + //disable the help books so we don't get proposals we weren't expecting + CHelpBookDescriptor helpBooks[]; + helpBooks = CHelpProviderManager.getDefault().getCHelpBookDescriptors(new ICHelpInvocationContext(){ + @Override + public IProject getProject(){return project;} + @Override + public ITranslationUnit getTranslationUnit(){return null;} + } + ); + for (CHelpBookDescriptor helpBook : helpBooks) { + if( helpBook != null && !helpBook.getCHelpBook().getTitle().contains("glibc") ) //$NON-NLS-1$ + helpBook.enable( false ); + } + } + + public static Test suite() { + TestSuite suite= suite(ContentAssistTests.class, "_"); + suite.addTest( new ContentAssistTests("cleanupProject") ); //$NON-NLS-1$ + return suite; + } + + public void cleanupProject() throws Exception { + closeAllEditors(); + try{ + project.delete( true, false, monitor ); + project = null; + } catch( Throwable e ){ + /*boo*/ + } + } + + @Override + protected void tearDown() throws Exception { + if( project == null || !project.exists() ) + return; + + closeAllEditors(); + + // wait for indexer before deleting project to avoid errors in the log + waitForIndexer(cproject); + + IResource [] members = project.members(); + for (IResource member : members) { + if( member.getName().equals( ".project" ) || member.getName().equals( ".cproject" ) ) //$NON-NLS-1$ //$NON-NLS-2$ + continue; + if (member.getName().equals(".settings")) + continue; + try{ + member.delete( false, monitor ); + } catch( Throwable e ){ + /*boo*/ + } + } + + } + + protected IFile importFile(String fileName, String contents ) throws Exception{ + //Obtain file handle + IFile file = project.getProject().getFile(fileName); + + InputStream stream = new ByteArrayInputStream( contents.getBytes() ); + //Create file input stream + if( file.exists() ) + file.setContents( stream, false, false, monitor ); + else + file.create( stream, false, monitor ); + + return file; + } + + protected ICompletionProposal[] getResults( IFile file, int offset ) throws Exception { + disableContributions(); + ITranslationUnit tu = (ITranslationUnit)CoreModel.getDefault().create( file ); + @SuppressWarnings("unused") + String buffer = tu.getBuffer().getContents(); + @SuppressWarnings("unused") + IWorkingCopy wc = null; + try{ + wc = tu.getWorkingCopy(); + }catch (CModelException e){ + fail("Failed to get working copy"); //$NON-NLS-1$ + } + + // call the ContentAssistProcessor + IWorkbenchPage page = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage(); + FileEditorInput editorInput = new FileEditorInput(file); + IEditorPart editorPart = page.openEditor(editorInput, "org.eclipse.cdt.ui.editor.CEditor"); + CEditor editor = (CEditor) editorPart ; + @SuppressWarnings("unused") + IAction completionAction = editor.getAction("ContentAssistProposal"); + + String contentType = editor.getViewer().getDocument().getContentType(offset); + ContentAssistant assistant = new ContentAssistant(); + CContentAssistProcessor processor = new CContentAssistProcessor(editor, assistant, contentType); + return processor.computeCompletionProposals(editor.getViewer(), offset); + } + + public void testBug69334a() throws Exception { + importFile( "test.h", "class Test{ public : Test( int ); }; \n" ); //$NON-NLS-1$//$NON-NLS-2$ + StringWriter writer = new StringWriter(); + writer.write( "#include \"test.h\" \n"); //$NON-NLS-1$ + writer.write( "Test::Test( int i ) { return; } \n"); //$NON-NLS-1$ + writer.write( "int main() { \n"); //$NON-NLS-1$ + writer.write( " int veryLongName = 1; \n"); //$NON-NLS-1$ + writer.write( " Test * ptest = new Test( very \n"); //$NON-NLS-1$ + + String code = writer.toString(); + IFile cu = importFile( "test.cpp", code ); //$NON-NLS-1$ + + ICompletionProposal [] results = getResults( cu, code.indexOf( "very " ) + 4 ); //$NON-NLS-1$ + + assertEquals( 1, results.length ); + assertEquals( "veryLongName : int", results[0].getDisplayString() ); //$NON-NLS-1$ + } + + public void testBug69334b() throws Exception { + importFile( "test.h", "class Test{ public : Test( int ); }; \n" ); //$NON-NLS-1$//$NON-NLS-2$ + StringWriter writer = new StringWriter(); + writer.write( "#include \"test.h\" \n"); //$NON-NLS-1$ + writer.write( "Test::Test( int i ) { return; } \n"); //$NON-NLS-1$ + writer.write( "int main() { \n"); //$NON-NLS-1$ + writer.write( " int veryLongName = 1; \n"); //$NON-NLS-1$ + writer.write( " Test test( very \n"); //$NON-NLS-1$ + + String code = writer.toString(); + IFile cu = importFile( "test.cpp", code ); //$NON-NLS-1$ + + ICompletionProposal [] results = getResults( cu, code.indexOf( "very " ) + 4 ); //$NON-NLS-1$ + + assertEquals( 1, results.length ); + // We should not match anything in glibc and only see something from the source code + assertEquals( "veryLongName : int", results[0].getDisplayString() ); //$NON-NLS-1$ + } + + public void testBug428037() throws Exception { + StringWriter writer = new StringWriter(); + writer.write( "class Strategy { \n"); //$NON-NLS-1$ + writer.write( "public : \n"); //$NON-NLS-1$ + writer.write( " enum _Ability { IDIOT, NORMAL, CHEAT } ; \n"); //$NON-NLS-1$ + writer.write( " Strategy( _Ability a ) { } \n"); //$NON-NLS-1$ + writer.write( " _Ability getAbility(); \n"); //$NON-NLS-1$ + writer.write( "}; \n"); //$NON-NLS-1$ + writer.write( "int main(){ \n"); //$NON-NLS-1$ + + String code = writer.toString(); + String c2 = code + " Strategy *p[3] = { new Strategy( Str \n"; //$NON-NLS-1$ + + IFile cu = importFile( "strategy.cpp", c2 ); //$NON-NLS-1$ + + ICompletionProposal [] results = getResults( cu, c2.indexOf( "Str " ) + 3 ); //$NON-NLS-1$ + assertEquals( 1, results.length ); + assertEquals( "Strategy", results[0].getDisplayString() ); //$NON-NLS-1$ + + c2 = code + " Strategy *p[3] = { new Strategy( Strategy:: \n"; //$NON-NLS-1$ + + cu = importFile( "strategy.cpp", c2 ); //$NON-NLS-1$ + + results = getResults( cu, c2.indexOf( "::" ) + 2 ); //$NON-NLS-1$ + + // Verify we only get back completions from the source code and not glibc function completions + assertEquals( 4, results.length ); + assertEquals( "CHEAT", results[0].getDisplayString() ); //$NON-NLS-1$ + assertEquals( "IDIOT", results[1].getDisplayString() ); //$NON-NLS-1$ + assertEquals( "NORMAL", results[2].getDisplayString() ); //$NON-NLS-1$ + // "_Ability" is here due to fix for bug 199598 + // Difficult to differentiate between declaration and expression context + assertEquals( "_Ability", results[3].getDisplayString() ); //$NON-NLS-1$ + + // in a method definition context, constructors and methods should be proposed + + c2 = code + "return 0;}\nStrategy::\n"; //$NON-NLS-1$ + + cu = importFile( "strategy.cpp", c2 ); //$NON-NLS-1$ + + results = getResults( cu, c2.indexOf( "::" ) + 2 ); //$NON-NLS-1$ + assertEquals( 3, results.length ); + assertEquals( "getAbility(void) : enum _Ability", results[1].getDisplayString() ); //$NON-NLS-1$ + assertEquals( "Strategy(enum _Ability a)", results[0].getDisplayString() ); //$NON-NLS-1$ + assertEquals( "_Ability", results[2].getDisplayString() ); //$NON-NLS-1$ +} + + public void testBug72559() throws Exception { + StringWriter writer = new StringWriter(); + writer.write("void foo(){ \n"); //$NON-NLS-1$ + writer.write(" int var; \n"); //$NON-NLS-1$ + writer.write(" { \n"); //$NON-NLS-1$ + writer.write(" float var; \n"); //$NON-NLS-1$ + writer.write(" v \n"); //$NON-NLS-1$ + writer.write(" } \n"); //$NON-NLS-1$ + writer.write("} \n"); //$NON-NLS-1$ + + String code = writer.toString(); + IFile cu = importFile( "t.cpp", code ); //$NON-NLS-1$ + ICompletionProposal [] results = getResults( cu, code.indexOf( "v " ) + 1 ); //$NON-NLS-1$ + + assertTrue( results.length >= 5 ); + + // Verify first 2 suggestions come from glibc + assertEquals( results[0].getDisplayString(), "valloc (size_t size) void *" ); //$NON-NLS-1$ + assertEquals( results[1].getDisplayString(), "vasprintf (char **ptr, const char *template, va_list ap) int" ); //$NON-NLS-1$ + + + // Verify end of list contains completions from indexer + assertEquals( results[results.length - 3].getDisplayString(), "var : float" ); //$NON-NLS-1$ + assertEquals( results[results.length - 2].getDisplayString(), "virtual" ); //$NON-NLS-1$ + assertEquals( results[results.length - 1].getDisplayString(), "volatile" ); //$NON-NLS-1$ + } + + public void testCfunc() throws Exception { + StringWriter writer = new StringWriter(); + writer.write("void foo(){ \n"); //$NON-NLS-1$ + writer.write(" int var; \n"); //$NON-NLS-1$ + writer.write(" var = strle \n"); //$NON-NLS-1$ + writer.write("} \n"); //$NON-NLS-1$ + + String code = writer.toString(); + IFile cu = importFile( "t.c", code ); //$NON-NLS-1$ + ICompletionProposal [] results = getResults( cu, code.indexOf( "strle " ) + 5 ); //$NON-NLS-1$ + + assertTrue( results.length >= 1 ); + + // Verify we find the glibc strlen function + assertEquals( results[0].getDisplayString(), "strlen (const char *s) size_t" ); //$NON-NLS-1$ + + if (results.length > 1) // if newlib book also enabled, verify it is correct too + assertEquals( results[1].getDisplayString(), "strlen (const char *str) size_t " ); //$NON-NLS-1$ + } +} diff --git a/libhover/org.eclipse.linuxtools.cdt.libhover.tests/src/org/eclipse/linuxtools/cdt/libhover/tests/ResourceHelper.java b/libhover/org.eclipse.linuxtools.cdt.libhover.tests/src/org/eclipse/linuxtools/cdt/libhover/tests/ResourceHelper.java new file mode 100644 index 0000000000..7705d01888 --- /dev/null +++ b/libhover/org.eclipse.linuxtools.cdt.libhover.tests/src/org/eclipse/linuxtools/cdt/libhover/tests/ResourceHelper.java @@ -0,0 +1,745 @@ +/******************************************************************************* + * Copyright (c) 2009, 2011 Andrew Gvozdev 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: + * Andrew Gvozdev - Initial API and implementation + * James Blackburn (Broadcom Corp.) + * Liviu Ionescu - bug 392416 + *******************************************************************************/ +package org.eclipse.linuxtools.cdt.libhover.tests; + +import java.io.BufferedReader; +import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.Reader; +import java.net.URI; +import java.net.URISyntaxException; +import java.nio.charset.Charset; +import java.util.HashSet; +import java.util.Set; +import java.util.UUID; + +import org.eclipse.cdt.core.CCorePlugin; +import org.eclipse.cdt.core.dom.IPDOMManager; +import org.eclipse.cdt.core.settings.model.ICConfigurationDescription; +import org.eclipse.cdt.core.settings.model.ICProjectDescription; +import org.eclipse.cdt.core.settings.model.ICProjectDescriptionManager; +import org.eclipse.cdt.internal.core.Cygwin; +import org.eclipse.cdt.internal.core.pdom.indexer.IndexerPreferences; +import org.eclipse.core.resources.IContainer; +import org.eclipse.core.resources.IFile; +import org.eclipse.core.resources.IFolder; +import org.eclipse.core.resources.IProject; +import org.eclipse.core.resources.IProjectDescription; +import org.eclipse.core.resources.IResource; +import org.eclipse.core.resources.IWorkspace; +import org.eclipse.core.resources.IWorkspaceRoot; +import org.eclipse.core.resources.ResourcesPlugin; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IPath; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.NullProgressMonitor; +import org.eclipse.core.runtime.OperationCanceledException; +import org.eclipse.core.runtime.Path; +import org.eclipse.core.runtime.Platform; +import org.eclipse.core.runtime.jobs.Job; +import org.junit.Assert; + +/** + * This class contains utility methods for creating resources + * such as projects, files, folders etc. which are being used + * in test fixture of unit tests. + * + * Some classes with similar idea worth to look at: + * org.eclipse.core.filebuffers.tests.ResourceHelper, + * org.eclipse.cdt.ui.tests.text.ResourceHelper. + * + * @since 6.0 + */ +@SuppressWarnings("restriction") +public class ResourceHelper { + private final static IProgressMonitor NULL_MONITOR = new NullProgressMonitor(); + private static final int MAX_RETRY= 5; + + private final static Set<String> externalFilesCreated = new HashSet<String>(); + private final static Set<IResource> resourcesCreated = new HashSet<IResource>(); + + /** + * Creates CDT project in a specific path in workspace and opens it. + * + * @param projectName - project name. + * @param pathInWorkspace - path relative to workspace root. + * @return - new {@link IProject}. + * @throws CoreException - if the project can't be created. + * @throws OperationCanceledException... + */ + public static IProject createCDTProject(String projectName, String pathInWorkspace) throws OperationCanceledException, CoreException { + return createCDTProject(projectName, pathInWorkspace, null); + } + + /** + * Creates CDT project in a specific path in workspace adding specified configurations and opens it. + * + * @param projectName - project name. + * @param pathInWorkspace - path relative to workspace root. + * @param configurationIds - array of configuration IDs. + * @return - new {@link IProject}. + * @throws CoreException - if the project can't be created. + * @throws OperationCanceledException... + */ + public static IProject createCDTProject(String projectName, String pathInWorkspace, String[] configurationIds) throws OperationCanceledException, CoreException { + CCorePlugin cdtCorePlugin = CCorePlugin.getDefault(); + IWorkspace workspace = ResourcesPlugin.getWorkspace(); + IWorkspaceRoot root = workspace.getRoot(); + + IProject project = root.getProject(projectName); + IndexerPreferences.set(project, IndexerPreferences.KEY_INDEXER_ID, IPDOMManager.ID_NO_INDEXER); + resourcesCreated.add(project); + + IProjectDescription prjDescription = workspace.newProjectDescription(projectName); + if(pathInWorkspace != null) { + IPath absoluteLocation = root.getLocation().append(pathInWorkspace); + prjDescription.setLocation(absoluteLocation); + } + + if (configurationIds != null && configurationIds.length > 0) { + ICProjectDescriptionManager prjDescManager = cdtCorePlugin.getProjectDescriptionManager(); + + project.create(NULL_MONITOR); + project.open(NULL_MONITOR); + + ICProjectDescription icPrjDescription = prjDescManager.createProjectDescription(project, false); + ICConfigurationDescription baseConfiguration = cdtCorePlugin.getPreferenceConfiguration(TestCfgDataProvider.PROVIDER_ID); + + for (String cfgId : configurationIds) { + icPrjDescription.createConfiguration(cfgId, cfgId + " Name", baseConfiguration); + } + prjDescManager.setProjectDescription(project, icPrjDescription); + } + project = cdtCorePlugin.createCDTProject(prjDescription, project, NULL_MONITOR); + waitForProjectRefreshToFinish(); + Assert.assertNotNull(project); + + project.open(null); + Assert.assertTrue(project.isOpen()); + + return project; + } + + /** + * Creates CDT project in a specific location and opens it. + * + * @param projectName - project name. + * @param locationURI - location. + * @return - new {@link IProject}. + * @throws CoreException - if the project can't be created. + * @throws OperationCanceledException... + */ + public static IProject createCDTProject(String projectName, URI locationURI) throws OperationCanceledException, CoreException { + IWorkspace workspace = ResourcesPlugin.getWorkspace(); + IWorkspaceRoot root = workspace.getRoot(); + + IProject project = root.getProject(projectName); + IndexerPreferences.set(project, IndexerPreferences.KEY_INDEXER_ID, IPDOMManager.ID_NO_INDEXER); + resourcesCreated.add(project); + + IProjectDescription description = workspace.newProjectDescription(projectName); + description.setLocationURI(locationURI); + project = CCorePlugin.getDefault().createCDTProject(description, project, NULL_MONITOR); + waitForProjectRefreshToFinish(); + Assert.assertNotNull(project); + + project.open(null); + Assert.assertTrue(project.isOpen()); + + return project; + } + + /** + * Creates a project in the workspace and opens it. + * + * @param projectName - project name. + * @return - new {@link IProject}. + * @throws CoreException - if the project can't be created. + * @throws OperationCanceledException... + */ + public static IProject createCDTProject(String projectName) throws OperationCanceledException, CoreException { + return createCDTProject(projectName, null, null); + } + + /** + * Creates a project with 1 test configuration and opens it. + * + * @param projectName - project name. + * @return - new {@link IProject}. + * @throws CoreException - if the project can't be created. + * @throws OperationCanceledException... + */ + public static IProject createCDTProjectWithConfig(String projectName) throws Exception { + IProject project = createCDTProject(projectName, null, + new String[] {"org.eclipse.cdt.core.tests.configuration"}); + resourcesCreated.add(project); + return project; + } + + /** + * Creates a plain Eclipse project. + * + * @param projectName + * @return the project handle + * @throws CoreException if project could not be created + */ + public static IProject createProject(String projectName) throws CoreException { + IWorkspaceRoot root= ResourcesPlugin.getWorkspace().getRoot(); + IProject project= root.getProject(projectName); + if (!project.exists()) { + project.create(NULL_MONITOR); + } else { + project.refreshLocal(IResource.DEPTH_INFINITE, null); + } + + if (!project.isOpen()) + project.open(NULL_MONITOR); + + resourcesCreated.add(project); + return project; + } + + /** + * Deletes project by name. + * + * @param projectName + * @throws CoreException + */ + public static void deleteProject(String projectName) throws CoreException { + IWorkspaceRoot root= ResourcesPlugin.getWorkspace().getRoot(); + IProject project= root.getProject(projectName); + if (project.exists()) + delete(project); + } + + /** + * Deletes given project with content. + * + * @param project + * @throws CoreException + */ + public static void delete(final IProject project) throws CoreException { + delete(project, true); + } + + /** + * Deletes project. + * + * @param project + * @param deleteContent whether to delete project content + * @throws CoreException + */ + public static void delete(final IProject project, boolean deleteContent) throws CoreException { + for (int i= 0; i < MAX_RETRY; i++) { + try { + project.delete(deleteContent, true, NULL_MONITOR); + i= MAX_RETRY; + } catch (CoreException x) { + if (i == MAX_RETRY - 1) { + x.printStackTrace(); + } + try { + Thread.sleep(1000); // sleep a second + } catch (InterruptedException e) { + } + } + } + } + + /** + * Creates a file with specified content. + * + * @param file - file name. + * @param contents - contents of the file. + * @return file handle. + * @throws CoreException - if the file can't be created. + */ + public static IFile createFile(IFile file, String contents) throws CoreException { + if (contents == null) { + contents= ""; + } + + InputStream inputStream = new ByteArrayInputStream(contents.getBytes()); + file.create(inputStream, true, NULL_MONITOR); + resourcesCreated.add(file); + return file; + } + + /** + * Creates new file from project root with empty content. The filename + * can include relative path as a part of the name but the the path + * has to be present on disk. + * + * @param project - project where to create the file. + * @param name - filename. + * @return file handle. + * @throws CoreException if something goes wrong. + */ + public static IFile createFile(IProject project, String name) throws CoreException { + if (new Path(name).segmentCount() > 1) + createFolder(project, new Path(name).removeLastSegments(1).toString()); + return createFile(project.getFile(name), null); + } + + /** + * Creates new file from workspace root with empty content. The filename + * can include relative path as a part of the name but the the path + * has to be present on disk. + * The intention of the method is to create files which do not belong to any project. + * + * @param name - filename. + * @return full path of the created file. + * + * @throws CoreException... + * @throws IOException... + */ + public static IPath createWorkspaceFile(String name) throws CoreException, IOException { + IWorkspaceRoot workspaceRoot = ResourcesPlugin.getWorkspace().getRoot(); + IPath fullPath = workspaceRoot.getLocation().append(name); + java.io.File file = new java.io.File(fullPath.toOSString()); + if (!file.exists()) { + boolean result = file.createNewFile(); + Assert.assertTrue(result); + } + Assert.assertTrue(file.exists()); + + externalFilesCreated.add(fullPath.toOSString()); + workspaceRoot.refreshLocal(IResource.DEPTH_INFINITE, new NullProgressMonitor()); + return fullPath; + } + + /** + * Creates new folder from project root. The folder name + * can include relative path as a part of the name. + * Nonexistent parent directories are being created. + * + * @param project - project where to create the folder. + * @param name - folder name. + * @return folder handle. + * @throws CoreException if something goes wrong. + */ + public static IFolder createFolder(IProject project, String name) throws CoreException { + final IPath p = new Path(name); + IContainer folder = project; + for (String seg : p.segments()) { + folder = folder.getFolder(new Path(seg)); + if (!folder.exists()) + ((IFolder)folder).create(true, true, NULL_MONITOR); + } + resourcesCreated.add(folder); + return (IFolder)folder; + } + + /** + * Creates new folder from workspace root. The folder name + * can include relative path as a part of the name. + * Nonexistent parent directories are being created as per {@link File#mkdirs()}. + * The intention of the method is to create folders which do not belong to any project. + * + * @param name - folder name. + * @return absolute location of the folder on the file system. + * @throws IOException if something goes wrong. + */ + public static IPath createWorkspaceFolder(String name) throws CoreException, IOException { + IWorkspaceRoot workspaceRoot = ResourcesPlugin.getWorkspace().getRoot(); + IPath fullPath = workspaceRoot.getLocation().append(name); + java.io.File folder = new java.io.File(fullPath.toOSString()); + if (!folder.exists()) { + boolean result = folder.mkdirs(); + Assert.assertTrue(result); + } + Assert.assertTrue(folder.exists()); + + externalFilesCreated.add(fullPath.toOSString()); + workspaceRoot.refreshLocal(IResource.DEPTH_INFINITE, NULL_MONITOR); + return fullPath; + } + + /** + * Creates new temporary folder with generated name from workspace root. + * + * @return absolute location of the folder on the file system. + * @throws IOException if something goes wrong. + */ + public static IPath createTemporaryFolder() throws CoreException, IOException { + return ResourceHelper.createWorkspaceFolder("tmp/" + System.currentTimeMillis() + '.' + UUID.randomUUID()); + } + + /** + * Creates new eclipse file-link from project root to file system file. The filename + * can include relative path as a part of the name but the the path + * has to be present on disk. + * + * @param project - project where to create the file. + * @param fileLink - filename of the link being created. + * @param realFile - file on the file system, the target of the link. + * @return file handle. + * @throws CoreException if something goes wrong. + */ + public static IFile createLinkedFile(IProject project, String fileLink, IPath realFile) throws CoreException { + IFile file = project.getFile(fileLink); + file.createLink(realFile, IResource.REPLACE, null); + Assert.assertTrue(file.exists()); + resourcesCreated.add(file); + return file; + } + + /** + * Creates new eclipse file-link from project root to file system file. The filename + * can include relative path as a part of the name but the the path + * has to be present on disk. + * + * @param project - project where to create the file. + * @param fileLink - filename of the link being created. + * @param realFile - file on the file system, the target of the link. + * @return file handle. + * @throws CoreException if something goes wrong. + */ + public static IFile createLinkedFile(IProject project, String fileLink, String realFile) throws CoreException { + return createLinkedFile(project, fileLink, new Path(realFile)); + } + + /** + * Creates new eclipse file-link from project root to EFS file. + * + * @param project - project where to create the file. + * @param fileLink - filename of the link being created. + * @param realFile - file on the EFS file system, the target of the link. + * @return file handle. + * @throws CoreException if something goes wrong. + */ + public static IFile createEfsFile(IProject project, String fileLink, URI realFile) throws CoreException { + IFile file= project.getFile(fileLink); + file.createLink(realFile, IResource.ALLOW_MISSING_LOCAL, NULL_MONITOR); + resourcesCreated.add(file); + return file; + } + + /** + * Creates new eclipse file-link from project root to EFS file. + * + * @param project - project where to create the file. + * @param fileLink - filename of the link being created. + * @param realFile - file on the EFS file system, the target of the link. + * @return file handle. + * @throws CoreException if something goes wrong. + * @throws URISyntaxException if wrong URI syntax + */ + public static IFile createEfsFile(IProject project, String fileLink, String realFile) throws CoreException, URISyntaxException { + return createEfsFile(project,fileLink,new URI(realFile)); + } + + /** + * Creates new eclipse folder-link from project root to file system folder. The folder name + * can include relative path as a part of the name but the the path + * has to be present on disk. + * + * @param project - project where to create the file. + * @param folderLink - name of the link being created. + * @param realFolder - folder on the file system, the target of the link. + * @return file handle. + * @throws CoreException if something goes wrong. + */ + public static IFolder createLinkedFolder(IProject project, String folderLink, IPath realFolder) throws CoreException { + IFolder folder = project.getFolder(folderLink); + folder.createLink(realFolder, IResource.REPLACE | IResource.ALLOW_MISSING_LOCAL, null); + Assert.assertTrue(folder.exists()); + resourcesCreated.add(folder); + return folder; + } + + /** + * Creates new eclipse folder-link from project root to file system folder. The folder name + * can include relative path as a part of the name but the the path + * has to be present on disk. + * + * @param project - project where to create the file. + * @param folderLink - name of the link being created. + * @param realFolder - folder on the file system, the target of the link. + * @return file handle. + * @throws CoreException if something goes wrong. + */ + public static IFolder createLinkedFolder(IProject project, String folderLink, String realFolder) throws CoreException { + return createLinkedFolder(project, folderLink, new Path(realFolder)); + } + + /** + * Creates new eclipse folder-link from project root to EFS folder. + * + * @param project - project where to create the folder. + * @param folderLink - folder name of the link being created. + * @param realFolder - folder on the EFS file system, the target of the link. + * @return folder handle. + * @throws CoreException if something goes wrong. + */ + public static IFolder createEfsFolder(IProject project, String folderLink, URI realFolder) throws CoreException { + IFolder folder= project.getFolder(folderLink); + if (folder.exists()) { + Assert.assertEquals("Folder with the same name but different location already exists", + realFolder, folder.getLocationURI()); + return folder; + } + + folder.createLink(realFolder, IResource.ALLOW_MISSING_LOCAL, new NullProgressMonitor()); + resourcesCreated.add(folder); + return folder; + } + + /** + * Creates new eclipse folder-link from project root to EFS folder. + * + * @param project - project where to create the folder. + * @param folderLink - folder name of the link being created. + * @param realFolder - folder on the EFS file system, the target of the link. + * @return folder handle. + * @throws CoreException if something goes wrong. + * @throws URISyntaxException if wrong URI syntax + */ + public static IFolder createEfsFolder(IProject project, String folderLink, String realFolder) throws CoreException, URISyntaxException { + return createEfsFolder(project,folderLink,new URI(realFolder)); + } + + /** + * Checks if symbolic links are supported on the system. + * Used in particular by method {@link #createSymbolicLink(IPath, IPath)} + * and other flavors to create symbolic links. + * + * Note that Windows links .lnk are not supported here. + * @return {@code true} if symbolic links are suppoted, {@code false} otherwise. + */ + public static boolean isSymbolicLinkSupported() { + return ! Platform.getOS().equals(Platform.OS_WIN32); + } + + /** + * Creates new symbolic file system link from file or folder on project root + * to another file system file. The filename can include relative path + * as a part of the name but the the path has to be present on disk. + * + * @param project - project where to create the file. + * @param linkName - name of the link being created. + * @param realPath - file or folder on the file system, the target of the link. + * @return file handle. + * + * @throws UnsupportedOperationException on Windows where links are not supported. + * @throws IOException... + * @throws CoreException... + */ + public static IResource createSymbolicLink(IProject project, String linkName, IPath realPath) + throws IOException, CoreException, UnsupportedOperationException { + if (!isSymbolicLinkSupported()) { + throw new UnsupportedOperationException("Windows links .lnk are not supported."); + } + + Assert.assertTrue("Path for symbolic link does not exist: [" + realPath.toOSString() + "]", + new File(realPath.toOSString()).exists()); + + IPath linkedPath = project.getLocation().append(linkName); + createSymbolicLink(linkedPath, realPath); + + IResource resource = project.getFile(linkName); + resource.refreshLocal(IResource.DEPTH_ZERO, null); + + if (!resource.exists()) { + resource = project.getFolder(linkName); + resource.refreshLocal(IResource.DEPTH_ZERO, null); + } + Assert.assertTrue("Failed to create resource form symbolic link", resource.exists()); + + externalFilesCreated.add(linkedPath.toOSString()); + ResourcesPlugin.getWorkspace().getRoot().refreshLocal(IResource.DEPTH_INFINITE, NULL_MONITOR); + return resource; + } + + /** + * Creates new symbolic file system link from file or folder to another filesystem file. + * The target path has to be present on disk. + * + * @param linkPath - filesystem path of the link being created. + * @param realPath - file or folder on the file system, the target of the link. + * + * @throws UnsupportedOperationException on Windows where links are not supported. + * @throws IOException if execution of the command fails. + */ + public static void createSymbolicLink(IPath linkPath, IPath realPath) throws IOException { + if (!isSymbolicLinkSupported()) { + throw new UnsupportedOperationException("Windows links .lnk are not supported."); + } + + String command[] = { "ln", "-s", realPath.toOSString(), linkPath.toOSString()}; + Process process = Runtime.getRuntime().exec(command); + + // Wait for up to 2.5s... + for (int i = 0; i < 5; i++) { + try { + Assert.assertTrue("ln process exited with non-zero status", process.waitFor() == 0); + // If exitValue succeeded, then the process has exited successfully. + break; + } catch (InterruptedException e) { + // Clear interrupted state, see Java bug http://bugs.sun.com/view_bug.do?bug_id=6420270 + Thread.interrupted(); + } + // Wait for a 500ms before checking again. + try { Thread.sleep(500); } catch (InterruptedException e) {/*don't care*/} + } + Assert.assertTrue("Symbolic link not created, command=[" + command + "]", linkPath.toFile().exists()); + } + + /** + * Creates new symbolic file system link from file or folder on project root + * to another file system file. The filename can include relative path + * as a part of the name but the the path has to be present on disk. + * + * @param project - project where to create the file. + * @param linkName - name of the link being created. + * @param realPath - file or folder on the file system, the target of the link. + * @return file handle. + * + * @throws UnsupportedOperationException on Windows where links are not supported. + * @throws IOException... + * @throws CoreException... + */ + public static IResource createSymbolicLink(IProject project, String linkName, String realPath) + throws IOException, CoreException, UnsupportedOperationException { + return createSymbolicLink(project, linkName, new Path(realPath)); + } + + /** + * Conversion from Windows path to Cygwin path. + * + * @param windowsPath - Windows path. + * @return Cygwin style converted path. + * @throws UnsupportedOperationException if Cygwin is unavailable. + * @throws IOException on IO problem. + */ + public static String windowsToCygwinPath(String windowsPath) throws IOException, UnsupportedOperationException { + return Cygwin.windowsToCygwinPath(windowsPath); + } + + /** + * Conversion from Cygwin path to Windows path. + * + * @param cygwinPath - Cygwin path. + * @return Windows style converted path. + * @throws UnsupportedOperationException if Cygwin is unavailable. + * @throws IOException on IO problem. + */ + public static String cygwinToWindowsPath(String cygwinPath) throws IOException, UnsupportedOperationException { + return Cygwin.cygwinToWindowsPath(cygwinPath); + } + + /** + * Get contents of file on file-system. + * + * @param fullPath - full path to the file on the file-system. + * @return contents of the file. + * @throws IOException on IO problem. + */ + public static String getContents(IPath fullPath) throws IOException { + FileInputStream stream = new FileInputStream(fullPath.toFile()); + try { + // Avoid using java.nio.channels.FileChannel, + // see http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4715154 + Reader reader = new BufferedReader(new InputStreamReader(stream, Charset.defaultCharset())); + StringBuilder builder = new StringBuilder(); + char[] buffer = new char[8192]; + int read; + while ((read = reader.read(buffer, 0, buffer.length)) > 0) { + builder.append(buffer, 0, read); + } + return builder.toString(); + } finally { + stream.close(); + } + } + + /** + * Get contents of file on file-system. + * + * @param fullPath - full path to the file on the file-system. + * @return contents of the file. + * @throws IOException on IO problem. + */ + public static String getContents(String fullPath) throws IOException { + return getContents(new Path(fullPath)); + } + + /** + * Clean-up any files created as part of a unit test. + * This method removes *all* Workspace IResources and any external + * files / folders created with the #createWorkspaceFile #createWorkspaceFolder + * methods in this class + */ + public static void cleanUp() throws CoreException, IOException { + IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot(); + root.refreshLocal(IResource.DEPTH_INFINITE, NULL_MONITOR); + + // Delete all external files & folders created using ResourceHelper + for (String loc : externalFilesCreated) { + File f = new File(loc); + if (f.exists()) + deleteRecursive(f); + } + externalFilesCreated.clear(); + + // Remove IResources created by this helper + for (IResource r : resourcesCreated) { + if (r.exists()) { + try { + r.delete(true, NULL_MONITOR); + } catch (CoreException e) { + // Ignore + } + } + } + resourcesCreated.clear(); + } + + private static void waitForProjectRefreshToFinish() { + try { + // CDT opens the Project with BACKGROUND_REFRESH enabled which causes the + // refresh manager to refresh the project 200ms later. This Job interferes + // with the resource change handler firing see: bug 271264 + Job.getJobManager().join(ResourcesPlugin.FAMILY_AUTO_REFRESH, null); + } catch (Exception e) { + // Ignore + } + } + + /** + * Recursively delete a directory / file + * + * For safety this method only deletes files created under the workspace + * + * @param file + */ + private static final void deleteRecursive(File f) throws IllegalArgumentException { + // Ensure that the file being deleted is a child of the workspace + // root to prevent anything nasty happening + if (!f.getAbsolutePath().startsWith( + ResourcesPlugin.getWorkspace().getRoot().getLocation().toFile().getAbsolutePath())) { + throw new IllegalArgumentException("File must exist within the workspace!"); + } + + if (f.isDirectory()) { + for (File f1 : f.listFiles()) { + deleteRecursive(f1); + } + } + f.delete(); + } +} diff --git a/libhover/org.eclipse.linuxtools.cdt.libhover.tests/src/org/eclipse/linuxtools/cdt/libhover/tests/TestCfgDataProvider.java b/libhover/org.eclipse.linuxtools.cdt.libhover.tests/src/org/eclipse/linuxtools/cdt/libhover/tests/TestCfgDataProvider.java new file mode 100644 index 0000000000..41a31507da --- /dev/null +++ b/libhover/org.eclipse.linuxtools.cdt.libhover.tests/src/org/eclipse/linuxtools/cdt/libhover/tests/TestCfgDataProvider.java @@ -0,0 +1,68 @@ +/******************************************************************************* + * Copyright (c) 2007, 2009 Intel 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: + * Intel Corporation - Initial API and implementation + *******************************************************************************/ +package org.eclipse.linuxtools.cdt.libhover.tests; + +import org.eclipse.cdt.core.settings.model.ICSettingEntry; +import org.eclipse.cdt.core.settings.model.extension.CConfigurationData; +import org.eclipse.cdt.core.settings.model.extension.CLanguageData; +import org.eclipse.cdt.core.settings.model.extension.CResourceData; +import org.eclipse.cdt.core.settings.model.extension.impl.CDataFactory; +import org.eclipse.cdt.core.settings.model.extension.impl.CDefaultConfigurationDataProvider; +import org.eclipse.cdt.core.settings.model.extension.impl.CDefaultLanguageData; +import org.eclipse.cdt.core.settings.model.util.CDataUtil; + +/** + * Basic CDefaultConfigurationDataProvider + * + * This is extended to allow persisting Library path + Library file language + * settings which aren't supported by the base default configuration data provider + * i.e. cdt.core without managedbuild... + */ +public class TestCfgDataProvider extends CDefaultConfigurationDataProvider { + public static final String PROVIDER_ID = CProjectHelper.PLUGIN_ID + ".testCfgDataProvider"; + + + // Overriden so it supports saving Library file and Library path entries + private static final class TestDataFactory extends CDataFactory { + + @Override + public CLanguageData createLanguageData(CConfigurationData cfg, + CResourceData rcBase, + CLanguageData base, + String id, + boolean clone){ + if(id == null) + id = clone ? base.getId() : CDataUtil.genId(rcBase.getId()); + return new CDefaultLanguageData(id, base); + } + + @Override + public CLanguageData createLanguageData(CConfigurationData cfg, + CResourceData rcBase, + String id, + String name, + String languageId, + int supportedEntryKinds, + String[] rcTypes, + boolean isContentTypes) { + return super.createLanguageData(cfg, rcBase, id, name, languageId, + supportedEntryKinds | ICSettingEntry.LIBRARY_FILE | ICSettingEntry.LIBRARY_PATH, + rcTypes, isContentTypes); + } + + } + + @Override + protected CDataFactory getDataFactory(){ + return new TestDataFactory(); + } + +} diff --git a/libhover/org.eclipse.linuxtools.cdt.libhover.tests/src/org/eclipse/linuxtools/cdt/libhover/tests/TestScannerInfo.java b/libhover/org.eclipse.linuxtools.cdt.libhover.tests/src/org/eclipse/linuxtools/cdt/libhover/tests/TestScannerInfo.java new file mode 100644 index 0000000000..bbcc3076e1 --- /dev/null +++ b/libhover/org.eclipse.linuxtools.cdt.libhover.tests/src/org/eclipse/linuxtools/cdt/libhover/tests/TestScannerInfo.java @@ -0,0 +1,58 @@ +/******************************************************************************* + * Copyright (c) 2000, 2006 QNX Software Systems 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: + * QNX Software Systems - Initial API and implementation + * Markus Schorn (Wind River Systems) + *******************************************************************************/ +package org.eclipse.linuxtools.cdt.libhover.tests; + +import java.util.Collections; +import java.util.Map; + +import org.eclipse.cdt.core.parser.ExtendedScannerInfo; + +public class TestScannerInfo extends ExtendedScannerInfo { + private static final String[] EMPTY = {}; + private String[] fIncludes; + private String[] fIncludeFiles; + private String[] fMacroFiles; + private Map<String, String> fDefinedSymbols; + + public TestScannerInfo(String[] includes, String[] macroFiles, String[] includeFiles, + Map<String, String> definedSymbols) { + fIncludes= includes; + fIncludeFiles= includeFiles; + fMacroFiles= macroFiles; + fDefinedSymbols= definedSymbols; + } + + @Override + public Map getDefinedSymbols() { + return fDefinedSymbols == null ? Collections.emptyMap() : fDefinedSymbols; + } + + @Override + public String[] getIncludePaths() { + return fIncludes == null ? EMPTY : fIncludes; + } + + @Override + public String[] getIncludeFiles() { + return fIncludeFiles == null ? EMPTY: fIncludeFiles; + } + + @Override + public String[] getLocalIncludePath() { + return null; + } + + @Override + public String[] getMacroFiles() { + return fMacroFiles == null ? EMPTY: fMacroFiles; + } +} diff --git a/libhover/org.eclipse.linuxtools.cdt.libhover.tests/src/org/eclipse/linuxtools/cdt/libhover/tests/TestScannerProvider.java b/libhover/org.eclipse.linuxtools.cdt.libhover.tests/src/org/eclipse/linuxtools/cdt/libhover/tests/TestScannerProvider.java new file mode 100644 index 0000000000..847aca4605 --- /dev/null +++ b/libhover/org.eclipse.linuxtools.cdt.libhover.tests/src/org/eclipse/linuxtools/cdt/libhover/tests/TestScannerProvider.java @@ -0,0 +1,47 @@ +/******************************************************************************* + * Copyright (c) 2000, 2007 QNX Software Systems 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: + * QNX Software Systems - Initial API and implementation + * Markus Schorn (Wind River Systems) + *******************************************************************************/ +package org.eclipse.linuxtools.cdt.libhover.tests; + +import java.util.HashMap; +import java.util.Map; + +import org.eclipse.cdt.core.AbstractCExtension; +import org.eclipse.cdt.core.parser.IScannerInfo; +import org.eclipse.cdt.core.parser.IScannerInfoChangeListener; +import org.eclipse.cdt.core.parser.IScannerInfoProvider; +import org.eclipse.core.resources.IResource; + +public class TestScannerProvider extends AbstractCExtension implements IScannerInfoProvider { + public static String[] sIncludes; + public static String[] sIncludeFiles; + public static String[] sMacroFiles; + public static Map<String, String> sDefinedSymbols = new HashMap<String, String>(); + public final static String SCANNER_ID = CProjectHelper.PLUGIN_ID + ".TestScanner"; + + public static void clear() { + sIncludes= sIncludeFiles= sMacroFiles= null; + sDefinedSymbols.clear(); + } + + @Override + public IScannerInfo getScannerInformation(IResource resource) { + return new TestScannerInfo(sIncludes, sMacroFiles, sIncludeFiles, sDefinedSymbols); + } + + @Override + public void subscribe(IResource resource, IScannerInfoChangeListener listener) { + } + + @Override + public void unsubscribe(IResource resource, IScannerInfoChangeListener listener) { + } +} diff --git a/libhover/org.eclipse.linuxtools.cdt.libhover.tests/src/org/eclipse/linuxtools/cdt/libhover/tests/TestSourceReader.java b/libhover/org.eclipse.linuxtools.cdt.libhover.tests/src/org/eclipse/linuxtools/cdt/libhover/tests/TestSourceReader.java new file mode 100644 index 0000000000..b9b68ad529 --- /dev/null +++ b/libhover/org.eclipse.linuxtools.cdt.libhover.tests/src/org/eclipse/linuxtools/cdt/libhover/tests/TestSourceReader.java @@ -0,0 +1,405 @@ +/******************************************************************************* + * Copyright (c) 2006, 2012 Wind River Systems, Inc. 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: + * Markus Schorn - initial API and implementation + * Andrew Ferguson (Symbian) + * Sergey Prigogin (Google) + *******************************************************************************/ +package org.eclipse.linuxtools.cdt.libhover.tests; + +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import java.io.BufferedReader; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.LineNumberReader; +import java.io.Reader; +import java.io.UnsupportedEncodingException; +import java.util.ArrayList; +import java.util.List; + +import org.eclipse.cdt.core.CCorePlugin; +import org.eclipse.cdt.core.dom.ILinkage; +import org.eclipse.cdt.core.dom.ast.IASTTranslationUnit; +import org.eclipse.cdt.core.index.IIndex; +import org.eclipse.cdt.core.index.IIndexFile; +import org.eclipse.cdt.core.index.IIndexFileLocation; +import org.eclipse.cdt.core.index.IndexLocationFactory; +import org.eclipse.cdt.core.model.CModelException; +import org.eclipse.cdt.core.model.ICElement; +import org.eclipse.cdt.core.model.ICProject; +import org.eclipse.cdt.core.model.ITranslationUnit; +import org.eclipse.core.resources.IContainer; +import org.eclipse.core.resources.IFile; +import org.eclipse.core.resources.IFolder; +import org.eclipse.core.resources.IResource; +import org.eclipse.core.resources.IWorkspace; +import org.eclipse.core.resources.IWorkspaceRunnable; +import org.eclipse.core.resources.ResourcesPlugin; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.FileLocator; +import org.eclipse.core.runtime.IPath; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.NullProgressMonitor; +import org.eclipse.core.runtime.Path; +import org.eclipse.core.runtime.Status; +import org.osgi.framework.Bundle; + +/** + * Utilities for reading test source code from plug-in .java sources + */ +public class TestSourceReader { + private final Bundle bundle; + private final String srcRoot; + private final Class<?> clazz; + private final int numSections; + + /** + * @param bundle the bundle containing the source, if {@code null} can try to load using + * classpath (source folder has to be in the classpath for this to work) + * @param srcRoot the directory inside the bundle containing the packages + * @param clazz the name of the class containing the test + */ + public TestSourceReader(Bundle bundle, String srcRoot, Class<?> clazz) { + this(bundle, srcRoot, clazz, 0); + } + + /** + * @param bundle the bundle containing the source, if {@code null} can try to load using + * classpath (source folder has to be in the classpath for this to work) + * @param srcRoot the directory inside the bundle containing the packages + * @param clazz the name of the class containing the test + * @param numSections the number of comment sections preceding the named test to return. + * Pass zero to get all available sections. + */ + public TestSourceReader(Bundle bundle, String srcRoot, Class<?> clazz, int numSections) { + this.bundle = bundle; + this.srcRoot = srcRoot; + this.clazz = clazz; + this.numSections = numSections; + } + + public StringBuilder[] getContentsForTest(final String testName) throws IOException { + return getContentsForTest(bundle, srcRoot, clazz, testName, numSections); + } + + public String readTaggedComment(String tag) throws IOException { + return readTaggedComment(bundle, tag, clazz, tag); + } + + /** + * Returns an array of StringBuilder objects for each comment section found preceding the named + * test in the source code. + * + * @param bundle the bundle containing the source, if {@code null} can try to load using + * classpath (source folder has to be in the classpath for this to work) + * @param srcRoot the directory inside the bundle containing the packages + * @param clazz the name of the class containing the test + * @param testName the name of the test + * @param numSections the number of comment sections preceding the named test to return. + * Pass zero to get all available sections. + * @return an array of StringBuilder objects for each comment section found preceding the named + * test in the source code. + * @throws IOException + */ + public static StringBuilder[] getContentsForTest(Bundle bundle, String srcRoot, Class<?> clazz, + final String testName, int numSections) throws IOException { + // Walk up the class inheritance chain until we find the test method. + try { + while (clazz.getMethod(testName).getDeclaringClass() != clazz) { + clazz = clazz.getSuperclass(); + } + } catch (SecurityException e) { + fail(e.getMessage()); + } catch (NoSuchMethodException e) { + fail(e.getMessage()); + } + + while (true) { + // Find and open the .java file for the class clazz. + String fqn = clazz.getName().replace('.', '/'); + fqn = fqn.indexOf("$") == -1 ? fqn : fqn.substring(0, fqn.indexOf("$")); + String classFile = fqn + ".java"; + IPath filePath= new Path(srcRoot + '/' + classFile); + + InputStream in; + Class<?> superclass = clazz.getSuperclass(); + try { + if (bundle != null) { + in = FileLocator.openStream(bundle, filePath, false); + } else { + in = clazz.getResourceAsStream('/' + classFile); + } + } catch (IOException e) { + if (superclass == null || !superclass.getPackage().equals(clazz.getPackage())) { + throw e; + } + clazz = superclass; + continue; + } + + BufferedReader br = new BufferedReader(new InputStreamReader(in)); + try { + // Read the java file collecting comments until we encounter the test method. + List<StringBuilder> contents = new ArrayList<StringBuilder>(); + StringBuilder content = new StringBuilder(); + for (String line = br.readLine(); line != null; line = br.readLine()) { + line = line.replaceFirst("^\\s*", ""); // Replace leading whitespace, preserve trailing + if (line.startsWith("//")) { + content.append(line.substring(2) + "\n"); + } else { + if (!line.startsWith("@") && content.length() > 0) { + contents.add(content); + if (numSections > 0 && contents.size() == numSections + 1) + contents.remove(0); + content = new StringBuilder(); + } + if (line.length() > 0 && !contents.isEmpty()) { + int idx= line.indexOf(testName); + if (idx != -1 && !Character.isJavaIdentifierPart(line.charAt(idx + testName.length()))) { + return contents.toArray(new StringBuilder[contents.size()]); + } + if (!line.startsWith("@")) { + contents.clear(); + } + } + } + } + } finally { + br.close(); + } + + if (superclass == null || !superclass.getPackage().equals(clazz.getPackage())) { + throw new IOException("Test data not found for " + clazz.getName() + "." + testName); + } + clazz = superclass; + } + } + + /** + * Searches for the offset of the first occurrence of a string in a workspace file. + * @param lookfor string to be searched for + * @param fullPath full path of the workspace file + * @return the offset or -1 + * @throws Exception + * @throws UnsupportedEncodingException + * @since 4.0 + */ + public static int indexOfInFile(String lookfor, Path fullPath) throws Exception { + IFile file= ResourcesPlugin.getWorkspace().getRoot().getFile(fullPath); + Reader reader= new BufferedReader(new InputStreamReader(file.getContents(), file.getCharset())); + assertTrue(lookfor.indexOf('\n') == -1); + try { + int c= 0; + int offset= 0; + StringBuilder buf= new StringBuilder(); + while ((c = reader.read()) >= 0) { + buf.append((char) c); + if (c == '\n') { + int idx= buf.indexOf(lookfor); + if (idx >= 0) { + return idx + offset; + } + offset += buf.length(); + buf.setLength(0); + } + } + int idx= buf.indexOf(lookfor); + if (idx >= 0) { + return idx + offset; + } + return -1; + } finally { + reader.close(); + } + } + + public static int getLineNumber(int offset, Path fullPath) throws Exception { + IFile file= ResourcesPlugin.getWorkspace().getRoot().getFile(fullPath); + Reader reader= new BufferedReader(new InputStreamReader(file.getContents(), file.getCharset())); + try { + int line = 1; + for (int i = 0; i < offset; i++) { + int c= reader.read(); + assertTrue(c >= 0); + if (c == '\n') + line++; + } + return line; + } finally { + reader.close(); + } + } + + /** + * Reads a section in comments form the source of the given class. The section + * is started with '// {tag}' and ends with the first line not started by '//' + * @since 4.0 + */ + public static String readTaggedComment(Bundle bundle, String srcRoot, Class<?> clazz, final String tag) throws IOException { + IPath filePath= new Path(srcRoot + '/' + clazz.getName().replace('.', '/') + ".java"); + + InputStream in= FileLocator.openStream(bundle, filePath, false); + LineNumberReader reader= new LineNumberReader(new InputStreamReader(in)); + boolean found= false; + final StringBuilder content= new StringBuilder(); + try { + String line= reader.readLine(); + while (line != null) { + line= line.trim(); + if (line.startsWith("//")) { + line= line.substring(2); + if (found) { + content.append(line); + content.append('\n'); + } else { + line= line.trim(); + if (line.startsWith("{" + tag)) { + if (line.length() == tag.length() + 1 || + !Character.isJavaIdentifierPart(line.charAt(tag.length() + 1))) { + found= true; + } + } + } + } else if (found) { + break; + } + line= reader.readLine(); + } + } finally { + reader.close(); + } + assertTrue("Tag '" + tag + "' is not defined inside of '" + filePath + "'.", found); + return content.toString(); + } + + /** + * Creates a file with content at the given path inside the given container. + * If the file exists its content is replaced. + * @param container a container to create the file in + * @param filePath the path relative to the container to create the file at + * @param contents the content for the file + * @return a file object. + * @throws CoreException + * @since 4.0 + */ + public static IFile createFile(final IContainer container, final IPath filePath, + final CharSequence contents) throws CoreException { + final IWorkspace ws = ResourcesPlugin.getWorkspace(); + final IFile result[] = new IFile[1]; + ws.run(new IWorkspaceRunnable() { + @Override + public void run(IProgressMonitor monitor) throws CoreException { + // Obtain file handle + IFile file = container.getFile(filePath); + + InputStream stream; + try { + stream = new ByteArrayInputStream(contents.toString().getBytes("UTF-8")); + } catch (UnsupportedEncodingException e) { + throw new CoreException(new Status(IStatus.ERROR, CProjectHelper.PLUGIN_ID, null, e)); + } + // Create file input stream + if (file.exists()) { + long timestamp= file.getLocalTimeStamp(); + file.setContents(stream, false, false, new NullProgressMonitor()); + if (file.getLocalTimeStamp() == timestamp) { + file.setLocalTimeStamp(timestamp + 1000); + } + } else { + createFolders(file); + file.create(stream, true, new NullProgressMonitor()); + } + result[0]= file; + } + + private void createFolders(IResource res) throws CoreException { + IContainer container= res.getParent(); + if (!container.exists() && container instanceof IFolder) { + createFolders(container); + ((IFolder) container).create(true, true, new NullProgressMonitor()); + } + } + }, null); + return result[0]; + } + + /** + * Creates a file with content at the given path inside the given container. + * If the file exists its content is replaced. + * @param container a container to create the file in + * @param filePath the path relative to the container to create the file at + * @param contents the content for the file + * @return a file object. + * @since 4.0 + */ + public static IFile createFile(IContainer container, String filePath, String contents) throws CoreException { + return createFile(container, new Path(filePath), contents); + } + + /** + * Waits until the given file is indexed. Fails if this does not happen within the + * given time. + * @param file + * @param maxmillis + * @throws Exception + * @since 4.0 + */ + public static void waitUntilFileIsIndexed(IIndex index, IFile file, int maxmillis) throws Exception { + long fileTimestamp = file.getLocalTimeStamp(); + IIndexFileLocation indexFileLocation = IndexLocationFactory.getWorkspaceIFL(file); + + long endTime= System.currentTimeMillis() + maxmillis; + int timeLeft= maxmillis; + while (timeLeft >= 0) { + assertTrue(CCorePlugin.getIndexManager().joinIndexer(timeLeft, new NullProgressMonitor())); + index.acquireReadLock(); + try { + IIndexFile[] files= index.getFiles(ILinkage.CPP_LINKAGE_ID, indexFileLocation); + if (files.length > 0 && areAllFilesNotOlderThan(files, fileTimestamp)) { + assertTrue(CCorePlugin.getIndexManager().joinIndexer(timeLeft, new NullProgressMonitor())); + return; + } + files= index.getFiles(ILinkage.C_LINKAGE_ID, indexFileLocation); + if (files.length > 0 && areAllFilesNotOlderThan(files, fileTimestamp)) { + assertTrue(CCorePlugin.getIndexManager().joinIndexer(timeLeft, new NullProgressMonitor())); + return; + } + } finally { + index.releaseReadLock(); + } + + Thread.sleep(50); + timeLeft= (int) (endTime - System.currentTimeMillis()); + } + fail("Indexing of " + file.getFullPath() + " did not complete in " + maxmillis / 1000. + " sec"); + } + + private static boolean areAllFilesNotOlderThan(IIndexFile[] files, long timestamp) throws CoreException { + for (IIndexFile file : files) { + if (file.getTimestamp() < timestamp) { + return false; + } + } + return true; + } + + public static IASTTranslationUnit createIndexBasedAST(IIndex index, ICProject project, IFile file) throws CModelException, CoreException { + ICElement elem= project.findElement(file.getFullPath()); + if (elem instanceof ITranslationUnit) { + ITranslationUnit tu= (ITranslationUnit) elem; + return tu.getAST(index, ITranslationUnit.AST_SKIP_INDEXED_HEADERS); + } + fail("Could not create AST for " + file.getFullPath()); + return null; + } +} |