blob: 32f6b0f11652b3e510704667e4f10641348251be [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2007, 2021 IBM Corporation and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.pde.api.tools.model.tests;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.net.URL;
import java.nio.file.Files;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.FileLocator;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.Status;
import org.eclipse.jdt.core.compiler.batch.BatchCompiler;
import org.eclipse.osgi.service.resolver.ResolverError;
import org.eclipse.pde.api.tools.internal.model.ApiModelFactory;
import org.eclipse.pde.api.tools.internal.model.ApiType;
import org.eclipse.pde.api.tools.internal.provisional.IApiDescription;
import org.eclipse.pde.api.tools.internal.provisional.IApiFilterStore;
import org.eclipse.pde.api.tools.internal.provisional.IRequiredComponentDescription;
import org.eclipse.pde.api.tools.internal.provisional.descriptors.IElementDescriptor;
import org.eclipse.pde.api.tools.internal.provisional.model.ApiTypeContainerVisitor;
import org.eclipse.pde.api.tools.internal.provisional.model.IApiBaseline;
import org.eclipse.pde.api.tools.internal.provisional.model.IApiComponent;
import org.eclipse.pde.api.tools.internal.provisional.model.IApiElement;
import org.eclipse.pde.api.tools.internal.provisional.model.IApiType;
import org.eclipse.pde.api.tools.internal.provisional.model.IApiTypeContainer;
import org.eclipse.pde.api.tools.internal.provisional.model.IApiTypeRoot;
import org.eclipse.pde.api.tools.internal.search.IReferenceCollection;
import org.eclipse.pde.api.tools.internal.search.UseScanReferences;
import org.eclipse.pde.api.tools.internal.util.Util;
import org.eclipse.pde.api.tools.tests.util.ProjectUtils;
import org.junit.Assert;
import org.osgi.framework.Bundle;
/**
* Helper methods to set up baselines, etc.
*
* @since 1.0.0
*/
public class TestSuiteHelper {
/**
* Computes the compile options to use. Currently this only changes if we
* are running the tests on Java 8.
*
* @return the array of compiler options to use
* @since 1.0.400
*/
public static String[] getCompilerOptions() {
ArrayList<String> args = new ArrayList<>();
if (ProjectUtils.isJava8Compatible()) {
args.add("-1.8"); //$NON-NLS-1$
} else {
args.add("-1.5"); //$NON-NLS-1$
}
args.add("-preserveAllLocals"); //$NON-NLS-1$
args.add("-nowarn"); //$NON-NLS-1$
return args.toArray(new String[] {});
}
/**
* Creates a baseline from all bundles in the specified directory.
*
* @param rootDirectory directory to collect bundles from
* @return API baseline
* @throws CoreException
*/
public static IApiBaseline createBaseline(String name, File rootDirectory) throws CoreException {
File eeFile = getEEDescriptionFile();
IApiBaseline baseline = newApiBaseline(name, eeFile);
// create a component for each jar/directory in the folder
File[] files = rootDirectory.listFiles();
List<IApiComponent> components = new ArrayList<>();
Set<String> requiredComponents = new HashSet<>();
for (File bundle : files) {
IApiComponent component = ApiModelFactory.newApiComponent(baseline, bundle.getAbsolutePath());
if (component != null) {
components.add(component);
requiredComponents.add(component.getSymbolicName());
}
}
// collect required components
IApiComponent[] base = components.toArray(new IApiComponent[components.size()]);
for (IApiComponent component : base) {
addAllRequired(baseline, requiredComponents, component, components);
}
baseline.addApiComponents(components.toArray(new IApiComponent[components.size()]));
return baseline;
}
/**
* Creates a testing API baseline
*
* @param testDirectory
* @return
* @throws CoreException
*/
public static IApiBaseline createTestingBaseline(String testDirectory) throws CoreException {
return createTestingBaseline(null, new Path(testDirectory));
}
/**
* Creates a testing {@link IApiComponent} that has a testing baseline
* created for it
*
* @param baselinename the name for the owning testing {@link IApiBaseline}
* or <code>null</code>
* @param name the name for the component
* @param id the id for the component
* @param description an {@link IApiDescription} for the component or
* <code>null</code>
*
* @return a new {@link IApiComponent} for testing purposes only
*/
public static IApiComponent createTestingApiComponent(final String baselinename, final String name, final String id, final IApiDescription description) {
return new IApiComponent() {
private IReferenceCollection fReferences;
@Override
public String[] getPackageNames() throws CoreException {
return null;
}
@Override
public IApiTypeRoot findTypeRoot(String qualifiedName) throws CoreException {
return null;
}
@Override
public void close() throws CoreException {
}
@Override
public void accept(ApiTypeContainerVisitor visitor) throws CoreException {
}
@Override
public String getVersion() {
return null;
}
@Override
public IRequiredComponentDescription[] getRequiredComponents() {
return null;
}
@Override
public String getName() {
return name;
}
@Override
public String getLocation() {
return null;
}
@Override
public String getSymbolicName() {
return id;
}
@Override
public String[] getExecutionEnvironments() {
return null;
}
@Override
public IApiTypeContainer[] getApiTypeContainers() {
return null;
}
@Override
public IApiDescription getApiDescription() {
return description;
}
@Override
public boolean hasApiDescription() {
return false;
}
@Override
public boolean isSystemComponent() {
return false;
}
@Override
public void dispose() {
}
@Override
public IApiBaseline getBaseline() {
return ApiModelFactory.newApiBaseline(baselinename);
}
@Override
public IApiFilterStore getFilterStore() {
return null;
}
@Override
public boolean isSourceComponent() {
return false;
}
@Override
public boolean isFragment() {
return false;
}
@Override
public boolean hasFragments() {
return false;
}
@Override
public IApiTypeContainer[] getApiTypeContainers(String id) {
return null;
}
@Override
public IApiTypeRoot findTypeRoot(String qualifiedName, String id) throws CoreException {
return null;
}
@Override
public IApiElement getAncestor(int ancestorType) {
return null;
}
@Override
public IApiElement getParent() {
return null;
}
@Override
public int getType() {
return IApiElement.COMPONENT;
}
@Override
public String[] getLowestEEs() {
return null;
}
@Override
public ResolverError[] getErrors() throws CoreException {
return null;
}
@Override
public IApiComponent getApiComponent() {
return this;
}
@Override
public IElementDescriptor getHandle() {
return null;
}
@Override
public IApiComponent getHost() throws CoreException {
return null;
}
@Override
public int getContainerType() {
return 0;
}
@Override
public IReferenceCollection getExternalDependencies() {
if (fReferences == null) {
fReferences = new UseScanReferences();
}
return fReferences;
}
@Override
public boolean isDisposed() {
return false;
}
};
}
/**
* Creates a testing {@link IApiComponent}
*
* @param name
* @param id
* @param description
* @return a new {@link IApiComponent} for testing
*/
public static IApiComponent createTestingApiComponent(final String name, final String id, final IApiDescription description) {
return createTestingApiComponent(null, name, id, description);
}
/**
* Creates a simple baseline from bundles in the specified directory of the
* test plug-in project.
*
* @param baselineid the name for the testing baseline
* @param testDirectory the dir the test baseline resides in
*
* @return Testing API baseline. If for some reason the testing directory is
* not available <code>null</code> is returned
* @throws CoreException
*/
public static IApiBaseline createTestingBaseline(String baselineid, IPath testDirectory) throws CoreException {
IPath path = TestSuiteHelper.getPluginDirectoryPath();
path = path.append(testDirectory);
File file = path.toFile();
String name = baselineid == null ? "test" : baselineid; //$NON-NLS-1$
if (file.exists()) {
File eeFile = getEEDescriptionFile();
IApiBaseline baseline = newApiBaseline(name, eeFile);
// create a component for each jar/directory in the folder
File[] files = file.listFiles();
List<IApiComponent> components = new ArrayList<>();
Set<String> requiredComponents = new HashSet<>();
for (File bundle : files) {
IApiComponent component = ApiModelFactory.newApiComponent(baseline, bundle.getAbsolutePath());
if (component != null) {
components.add(component);
requiredComponents.add(component.getSymbolicName());
}
}
// collect required components
IApiComponent[] base = components.toArray(new IApiComponent[components.size()]);
for (IApiComponent component : base) {
addAllRequired(baseline, requiredComponents, component, components);
}
baseline.addApiComponents(components.toArray(new IApiComponent[components.size()]));
return baseline;
}
return null;
}
/**
* Constructs a new {@link IApiProfile} with the given name, id, version,
* and environment.
* <p>
* Attempts to locate OSGi execution environment baseline when not running
* in an OSGi framework.
* </p>
*
* @param name
* @param ee execution environment description file
* @return API baseline
* @exception CoreException if unable to create a baseline
*/
public static IApiBaseline newApiBaseline(String name, File eeFile) throws CoreException {
return ApiModelFactory.newApiBaseline(name, eeFile);
}
/**
* Creates a new {@link IApiType} for testing
*
* @param componentid
* @param name
* @param sig
* @param genericsig
* @param flags
* @param enclosingname
* @return a new testing {@link IApiType}
*/
public static IApiType createTestingApiType(String baselineid, String componentid, String name, String sig, String genericsig, int flags, String enclosingname) {
return new ApiType(TestSuiteHelper.createTestingApiComponent(baselineid, componentid, componentid, null), name, sig, genericsig, flags, enclosingname, null);
}
/**
* Gets the .ee file supplied to run tests based on system property.
*
* @return
*/
public static File getEEDescriptionFile() {
String eePath = System.getProperty("ee.file"); //$NON-NLS-1$
if (eePath == null) {
// generate a fake 11 ee file
File fakeEEFile = null;
try {
fakeEEFile = Util.createTempFile("eefile", ".ee"); //$NON-NLS-1$ //$NON-NLS-2$
try (PrintWriter writer = new PrintWriter(new BufferedWriter(new FileWriter(fakeEEFile)))) {
writer.print("-Djava.home="); //$NON-NLS-1$
writer.println(System.getProperty("java.home")); //$NON-NLS-1$
writer.print("-Dee.bootclasspath="); //$NON-NLS-1$
writer.println(org.eclipse.pde.api.tools.internal.util.Util.getJavaClassLibsAsString());
writer.println("-Dee.language.level=11"); //$NON-NLS-1$
writer.println("-Dee.class.library.level=JavaSE-11"); //$NON-NLS-1$
writer.flush();
}
} catch (IOException e) {
// ignore
}
fakeEEFile.deleteOnExit();
eePath = fakeEEFile.getAbsolutePath();
}
Assert.assertNotNull("-Dee.file not specified", eePath); //$NON-NLS-1$
File eeFile = new File(eePath);
Assert.assertTrue("EE file does not exist: " + eePath, eeFile.exists()); //$NON-NLS-1$
return eeFile;
}
/**
* Returns a file to the root of the specified bundle or <code>null</code>
* if none. Searches for plug-ins based on the "requiredBundles" system
* property. If "requiredBundles" is not set, failback to resolution of
* plugin running under current Platform (using OSGi), if active and
* available.
*
* @param bundleName
* symbolic name
* @return bundle root or <code>null</code>
*/
public static File getBundle(String bundleName) {
String root = System.getProperty("requiredBundles"); //$NON-NLS-1$
if (root != null) {
File bundlesRoot = new File(root);
if (bundlesRoot.exists() && bundlesRoot.isDirectory()) {
File[] bundles = bundlesRoot.listFiles();
String key = bundleName + "_"; //$NON-NLS-1$
for (File file : bundles) {
if (file.getName().startsWith(key)) {
return file;
}
}
}
} else {
Bundle bundle = Platform.getBundle(bundleName);
if (bundle != null) {
try {
return FileLocator.getBundleFile(bundle);
} catch (IOException e) {
e.printStackTrace();
}
}
}
return null;
}
/**
* Adds all required components to the collection of components.
*
* @param done set of component id's that have already been collected
* @param component component to add all prerequisites for
* @param collection collection to add prerequisites to.
* @throws CoreException
*/
public static void addAllRequired(IApiBaseline baseline, Set<String> done, IApiComponent component, List<IApiComponent> collection) throws CoreException {
IRequiredComponentDescription[] descriptions = component.getRequiredComponents();
boolean error = false;
StringBuilder buffer = null;
for (IRequiredComponentDescription description : descriptions) {
if (!done.contains(description.getId())) {
File bundle = getBundle(description.getId());
if (bundle == null) {
if (!description.isOptional()) {
if (buffer == null) {
buffer = new StringBuilder();
}
buffer.append(description.getId()).append(',');
error = true;
}
} else {
IApiComponent apiComponent = ApiModelFactory.newApiComponent(baseline, bundle.getAbsolutePath());
collection.add(apiComponent);
done.add(apiComponent.getSymbolicName());
addAllRequired(baseline, done, apiComponent, collection);
}
}
}
if (error) {
throw new CoreException(Status.error("Check the property : -DrequiredBundles=...\nMissing required bundle(s): " + String.valueOf(buffer))); //$NON-NLS-1$
}
}
/**
* Compiles a single source file
*
* @param sourcename
* @param destinationpath
* @param compileroptions
* @return true if compilation succeeded false otherwise
*/
public static boolean compile(String sourcename, String destinationpath, String[] compileroptions) {
StringWriter out = new StringWriter();
PrintWriter outWriter = new PrintWriter(out);
StringWriter err = new StringWriter();
PrintWriter errWriter = new PrintWriter(err);
List<String> cmd = new ArrayList<>();
cmd.add("-noExit"); //$NON-NLS-1$
for (String compileroption : compileroptions) {
cmd.add(compileroption);
}
if (destinationpath != null) {
cmd.add("-d"); //$NON-NLS-1$
cmd.add(destinationpath);
}
cmd.add(sourcename);
String[] args = new String[cmd.size()];
cmd.toArray(args);
boolean result = false;
try {
result = BatchCompiler.compile(args, outWriter, errWriter, null);
} catch (RuntimeException e) {
e.printStackTrace();
}
if (!result) {
System.err.println(err.getBuffer());
}
return result;
}
/**
* Compiles all source files in the specified source paths to the specified
* destination path, with the given compiler options
*
* @param sourceFilePaths
* @param destinationPath
* @param compilerOptions
* @return true if the compilation succeeded false otherwise
*/
public static boolean compile(String[] sourceFilePaths, String destinationPath, String[] compilerOptions) {
StringWriter out = new StringWriter();
PrintWriter outWriter = new PrintWriter(out);
StringWriter err = new StringWriter();
PrintWriter errWriter = new PrintWriter(err);
List<String> cmd = new ArrayList<>();
cmd.add("-noExit"); //$NON-NLS-1$
for (String compilerOption : compilerOptions) {
cmd.add(compilerOption);
}
if (destinationPath != null) {
cmd.add("-d"); //$NON-NLS-1$
cmd.add(destinationPath);
}
Set<String> directories = new HashSet<>();
for (String sourceFilePath : sourceFilePaths) {
File file = new File(sourceFilePath);
if (!file.exists()) {
continue;
}
if (file.isDirectory()) {
directories.add(file.getAbsolutePath());
} else {
File parent = file.getParentFile();
directories.add(parent.getAbsolutePath());
}
cmd.add(sourceFilePath);
}
// add all directories as classpath entries
if (!directories.isEmpty()) {
StringBuilder classpathEntry = new StringBuilder();
int length = directories.size();
int counter = 0;
for (String path : directories) {
classpathEntry.append(path);
if (counter < length - 1) {
classpathEntry.append(File.pathSeparatorChar);
}
}
cmd.add("-classpath"); //$NON-NLS-1$
cmd.add(String.valueOf(classpathEntry));
}
String[] args = new String[cmd.size()];
cmd.toArray(args);
boolean result = false;
try {
result = BatchCompiler.compile(args, outWriter, errWriter, null);
} catch (RuntimeException e) {
e.printStackTrace();
}
if (!result) {
System.err.println(err.getBuffer());
}
return result;
}
/**
* Delete the file f and all sub-directories if f is a directory
*
* @param f the given file to delete
* @return true if the file was successfully deleted, false otherwise
*/
public static boolean delete(File f) {
if (!Util.delete(f)) {
System.err.println("Could not delete " + f.getAbsolutePath()); //$NON-NLS-1$
return false;
}
return true;
}
/**
* Copy file into the destination folder. If file is not a directory, it is
* copied into the destination folder. If file is a directory, all its files
* and subfolders are copied to the destination folder.
*
* <code>dest</code> has to be a directory.
*
* @param file the given file to copy
* @param dest the given destination folder
* @throws IllegalArgumentException if dest is not a directory or it doesn't
* exist or if the given file doesn't exist
*/
public static void copy(File file, File dest) {
if (!dest.exists()) {
if (!dest.mkdirs()) {
throw new IllegalArgumentException("could not create destination"); //$NON-NLS-1$
}
} else if (!dest.isDirectory()) {
throw new IllegalArgumentException("destination is not a directory"); //$NON-NLS-1$
}
if (!file.exists()) {
throw new IllegalArgumentException("The given file to copy doesn't exist"); //$NON-NLS-1$
}
copy0(file, dest);
}
private static void copy0(File f, File dest) {
dest.mkdirs();
if (f.isDirectory()) {
File[] files = f.listFiles();
for (File file : files) {
if (file.isDirectory()) {
String name = file.getName();
copy0(new File(f, name), new File(dest, name));
} else {
copy0(file, dest);
}
}
} else {
try {
Files.copy(f.toPath(), new File(dest, f.getName()).toPath(),
java.nio.file.StandardCopyOption.REPLACE_EXISTING);
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* Returns the OS path to the directory that contains this plugin.
*/
public static IPath getPluginDirectoryPath() {
if (Platform.isRunning()) {
try {
URL platformURL = Platform.getBundle("org.eclipse.pde.api.tools.tests").getEntry("/"); //$NON-NLS-1$ //$NON-NLS-2$
return new Path(new File(FileLocator.toFileURL(platformURL).getFile()).getAbsolutePath());
} catch (IOException e) {
e.printStackTrace();
}
}
return new Path(System.getProperty("user.dir")); //$NON-NLS-1$
}
/**
* @return the path for the system property <code>user.dir</code>
*/
public static IPath getUserDirectoryPath() {
return new Path(System.getProperty("user.dir")); //$NON-NLS-1$
}
}