/*
* $Id$
*
* SARL is an general-purpose agent programming language.
* More details on http://www.sarl.io
*
* Copyright (C) 2014-2017 the original authors or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.sarl.tests.api;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import com.google.inject.Binder;
import com.google.inject.Inject;
import com.google.inject.Injector;
import com.google.inject.Module;
import com.google.inject.Singleton;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.jdt.core.IClasspathEntry;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.internal.ui.viewsupport.JavaElementImageProvider;
import org.eclipse.jdt.ui.JavaElementImageDescriptor;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.xtext.testing.InjectWith;
import org.eclipse.xtext.ui.XtextProjectHelper;
import org.eclipse.xtext.ui.util.JREContainerProvider;
import org.eclipse.xtext.ui.util.PluginProjectFactory;
import org.eclipse.xtext.util.JavaVersion;
import org.eclipse.xtext.util.Strings;
import org.junit.ComparisonFailure;
import org.junit.Rule;
import org.junit.rules.TestWatcher;
import org.junit.runner.Description;
import io.sarl.lang.sarl.SarlScript;
import io.sarl.lang.ui.tests.SARLUiInjectorProvider;
import io.sarl.tests.api.WorkbenchTestHelper.ProjectCreator;
/** This class is inspired from AbstractXbaseUITestCase of Xtext.
*
* @author $Author: sgalland$
* @version $FullVersion$
* @mavengroupid $GroupId$
* @mavenartifactid $ArtifactId$
*/
@InjectWith(SARLUiInjectorProvider.class)
@SuppressWarnings("all")
public abstract class AbstractSarlUiTest extends AbstractSarlTest {
/** This rule permits to tear down the workbench helper.
*/
@Rule
public TestWatcher rootSarlUiWatchter = new TestWatcher() {
@Override
protected void starting(Description description) {
try {
helper().setUp();
} catch (Exception e) {
throw new RuntimeException(e);
}
TestProject projectAnnotation = description.getAnnotation(TestProject.class);
if (projectAnnotation == null) {
Class<?> type = description.getTestClass();
while (projectAnnotation == null && type != null) {
projectAnnotation = type.getAnnotation(TestProject.class);
type = type.getDeclaringClass();
}
}
TestClasspath classPathAnnotation = description.getAnnotation(TestClasspath.class);
if (classPathAnnotation == null) {
Class<?> type = description.getTestClass();
while (classPathAnnotation == null && type != null) {
classPathAnnotation = type.getAnnotation(TestClasspath.class);
type = type.getDeclaringClass();
}
}
String[] buildPath;
if (classPathAnnotation != null) {
String[] addedBundles = classPathAnnotation.value();
if (classPathAnnotation.includeDefaultBundles()) {
buildPath = new String[WorkbenchTestHelper.DEFAULT_REQ_BUNDLES.size() + addedBundles.length];
WorkbenchTestHelper.DEFAULT_REQ_BUNDLES.toArray(buildPath);
for (int i = WorkbenchTestHelper.DEFAULT_REQ_BUNDLES.size(), j = 0;
i < buildPath.length && j < addedBundles.length; ++i, ++j) {
buildPath[i] = addedBundles[j];
}
} else {
buildPath = addedBundles;
}
} else {
buildPath = null;
}
if (buildPath == null) {
AbstractSarlUiTest.this.initialClasspath = new String[WorkbenchTestHelper.DEFAULT_REQ_BUNDLES.size()];
WorkbenchTestHelper.DEFAULT_REQ_BUNDLES.toArray(AbstractSarlUiTest.this.initialClasspath);
} else {
AbstractSarlUiTest.this.initialClasspath = buildPath;
}
if (projectAnnotation != null && projectAnnotation.clearWorkspaceAtStartup()) {
helper().clearWorkspace();
}
if (projectAnnotation == null || projectAnnotation.automaticProjectCreation()) {
try {
createDefaultTestProject(AbstractSarlUiTest.this.initialClasspath);
} catch (CoreException e) {
throw new RuntimeException(e);
}
}
}
@Override
protected void finished(Description description) {
helper().clearWorkspace();
try {
helper().tearDown();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
};
@Inject
private Injector injectedInjector;
/** Helper for interaction with the Eclipse workbench.
*/
private WorkbenchTestHelper workbenchHelper;
private volatile String[] initialClasspath;
/** Replies the classpath that is specified by {@link TestClasspath}.
*
* @return the classpath.
* @since 0.3.0
*/
protected String[] getTestClasspath() {
return this.initialClasspath;
}
/** Create the default test project with the given classpath.
*
* @param classpath - the bundles on the classpath.
* @return the project.
* @throws CoreException if the project cannot be created.
* @since 0.3.0
*/
protected IProject createDefaultTestProject(String[] classpath) throws CoreException {
return createDefaultTestProject(classpath, WorkbenchTestHelper.TESTPROJECT_NAME);
}
/** Create the default test project with the given classpath.
*
* @param classpath - the bundles on the classpath.
* @param projectName - the name of the project.
* @return the project.
* @throws CoreException if the project cannot be created.
* @since 0.3.0
*/
protected IProject createDefaultTestProject(String[] classpath, String projectName) throws CoreException {
ProjectCreator creator = getInjector().getInstance(ProjectCreator.class);
if (classpath == null) {
return this.workbenchHelper.createPluginProject(projectName, creator);
}
return this.workbenchHelper.createPluginProject(projectName, creator, classpath);
}
/** Replies the injected injector.
*
* @return the injector.
*/
public final Injector getInjectedInjector() {
return this.injectedInjector;
}
/** Replies the injection modules to be used.
*
* @return the injection modules.
*/
protected Module[] getInjectionModules() {
return new Module[] {
new Module() {
@Override
public void configure(Binder binder) {
final JavaVersion version = JavaVersion.JAVA8;
binder.bind(JavaVersion.class).toProvider(() -> version);
binder.bind(ProjectCreator.class).toProvider(() -> {
return new JavaProjectCreator(version);
}).asEagerSingleton();
}
},
};
}
/** Replies the injector.
*
* @return the injector.
*/
public Injector getInjector() {
return getInjectedInjector().createChildInjector(getInjectionModules());
}
/** Assert the given image descriptor is for an image in a bundle.
*
* @param filename - the name of the image file.
* @param desc - the image descriptor to test.
*/
protected static void assertBundleImage(String filename, ImageDescriptor desc) {
assertNotNull(desc);
String s = desc.toString();
String regex = Pattern.quote("URLImageDescriptor(bundleentry://") //$NON-NLS-1$
+ "[^/]+" //$NON-NLS-1$
+ Pattern.quote("/icons/") //$NON-NLS-1$
+ "([^/]+[/])*" //$NON-NLS-1$
+ Pattern.quote(filename + ")"); //$NON-NLS-1$
if (!Pattern.matches(regex, s)) {
if (desc instanceof JavaElementImageDescriptor) {
JavaElementImageDescriptor jeid = (JavaElementImageDescriptor) desc;
try {
Field field = JavaElementImageDescriptor.class.getDeclaredField("fBaseImage");
boolean isAcc = field.isAccessible();
field.setAccessible(true);
try {
ImageDescriptor id = (ImageDescriptor) field.get(jeid);
s = id.toString();
assertTrue("Invalid image: " + filename //$NON-NLS-1$
+ ". Actual: " + s, Pattern.matches(regex, s)); //$NON-NLS-1$
} finally {
field.setAccessible(isAcc);
}
} catch (NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException e) {
fail("Invalid background image descriptor: " + jeid.getClass().getName());
}
}
}
}
private static String getImage(ImageDescriptor d) throws Exception {
String regex = Pattern.quote("URLImageDescriptor(bundleentry://") //$NON-NLS-1$
+ "[^/]+" //$NON-NLS-1$
+ Pattern.quote("/icons/") //$NON-NLS-1$
+ "(?:[^/]+[/])*" //$NON-NLS-1$
+ "(.+?)" //$NON-NLS-1$
+ Pattern.quote(")"); //$NON-NLS-1$
Pattern pattern = Pattern.compile(regex);
if (d instanceof JavaElementImageDescriptor) {
JavaElementImageDescriptor expectedDescriptor = (JavaElementImageDescriptor) d;
Field field = JavaElementImageDescriptor.class.getDeclaredField("fBaseImage");
boolean isAcc = field.isAccessible();
field.setAccessible(true);
try {
ImageDescriptor id = (ImageDescriptor) field.get(expectedDescriptor);
Matcher matcher = pattern.matcher(id.toString());
if (matcher.find()) {
return matcher.group(1);
}
} finally {
field.setAccessible(isAcc);
}
}
Matcher matcher = pattern.matcher(d.toString());
if (matcher.find()) {
return matcher.group(1);
}
return "";
}
/** Assert the given image descriptors are the equal.
*
* @param expected - the expected image descriptor.
* @param actual - the current image descriptor.
* @throws Exception if the test cannot be done.
*/
protected static void assertImageDescriptors(ImageDescriptor expected, ImageDescriptor actual) throws Exception {
String expectedImage = getImage(expected);
String actualImage = getImage(actual);
if (!Strings.equal(expectedImage, actualImage)) {
throw new ComparisonFailure("Not same image descriptors", expectedImage, actualImage);
}
}
/** Assert the given image descriptor is for an image in the platform.
*
* @param filename - the name of the image file.
* @param desc - the image descriptor to test.
*/
protected static void assertPlaformImage(String filename, ImageDescriptor desc) {
assertNotNull(desc);
String s = desc.toString();
String regex = Pattern.quote("URLImageDescriptor(platform:") //$NON-NLS-1$
+ "([/][^/]+)*" //$NON-NLS-1$
+ Pattern.quote("/icons/") //$NON-NLS-1$
+ "([^/]+[/])*" //$NON-NLS-1$
+ Pattern.quote(filename + ")"); //$NON-NLS-1$
assertTrue("Image not found: " + filename //$NON-NLS-1$
+ ". Actual: " + s, Pattern.matches(regex, s)); //$NON-NLS-1$
}
/** Assert the given image descriptor is for an image given by JDT.
*
* @param expected - the expected base image descriptor.
* @param expectedFlags - the additional expected flags.
* @param actual - the image descriptor to test.
* @throws Exception if the test cannot be done.
*/
protected static void assertJdtImage(ImageDescriptor expected, int expectedFlags, ImageDescriptor actual) throws Exception {
assertNotNull(actual);
assertTrue(actual instanceof JavaElementImageDescriptor);
assertImageDescriptors(expected, actual);
assertEquals("Not the same flags", expectedFlags,
((JavaElementImageDescriptor) actual).getAdronments());
assertEquals("Not the same size.", JavaElementImageProvider.BIG_SIZE,
((JavaElementImageDescriptor) actual).getImageSize());
}
@Override
protected SarlScript file(String string, boolean validate) throws Exception {
return helper().sarlScript(
helper().generateFilename("io", "sarl", "tests", getClass().getSimpleName()),
string,
validate);
}
/** Replies the workspace test helper.
*
* @return the helper.
*/
protected synchronized WorkbenchTestHelper helper() {
if (this.workbenchHelper == null) {
this.workbenchHelper = getInjector().getInstance(WorkbenchTestHelper.class);
}
return this.workbenchHelper;
}
/** Factory of a Java project.
*
* @author $Author: sgalland$
* @version $FullVersion$
* @mavengroupid $GroupId$
* @mavenartifactid $ArtifactId$
*/
@Singleton
private class JavaProjectCreator implements ProjectCreator {
@Nullable
private final JavaVersion javaVersion;
JavaProjectCreator(JavaVersion version) {
this.javaVersion = version;
}
@Override
public Injector getInjector() {
return getInjectedInjector();
}
@Override
public PluginProjectFactory getProjectFactory() {
return getInjector().getInstance(PluginProjectFactory.class);
}
@Override
public JavaVersion getJavaVersion() {
return this.javaVersion;
}
@Override
public List<String> getSourceFolders() {
return Arrays.asList("src", "src-gen"); //$NON-NLS-1$//$NON-NLS-2$
}
@Override
public String getGenerationFolder() {
return "src-gen"; //$NON-NLS-1$
}
@Override
public String[] getBuilderIds() {
return new String[] {
XtextProjectHelper.BUILDER_ID,
JavaCore.BUILDER_ID
};
}
@Override
public String[] getNatures() {
return new String[] {
XtextProjectHelper.NATURE_ID,
JavaCore.NATURE_ID,
WorkbenchTestHelper.NATURE_ID
};
}
@Override
public void addJreClasspathEntry(IJavaProject javaProject) throws JavaModelException {
IClasspathEntry existingJreContainerClasspathEntry = JREContainerProvider.getJREContainerEntry(javaProject);
if (existingJreContainerClasspathEntry == null) {
addToClasspath(javaProject, JREContainerProvider.getDefaultJREContainerEntry());
}
}
@Override
public void addToClasspath(IJavaProject javaProject, IClasspathEntry newClassPathEntry) throws JavaModelException {
IClasspathEntry[] newClassPath;
IClasspathEntry[] classPath = javaProject.getRawClasspath();
for (IClasspathEntry classPathEntry : classPath) {
if (classPathEntry.equals(newClassPathEntry)) {
return;
}
}
newClassPath = new IClasspathEntry[classPath.length + 1];
System.arraycopy(classPath, 0, newClassPath, 1, classPath.length);
newClassPath[0] = newClassPathEntry;
javaProject.setRawClasspath(newClassPath, null);
helper().awaitAutoBuild();
}
@Override
public void addToClasspath(IJavaProject javaProject,
boolean autobuild,
Iterable<IClasspathEntry> newClassPathEntries) throws JavaModelException {
List<IClasspathEntry> newClassPath = new ArrayList<>(Arrays.asList(javaProject.getRawClasspath()));
for (IClasspathEntry classPathEntry : newClassPathEntries) {
if (!newClassPath.contains(classPathEntry)) {
newClassPath.add(classPathEntry);
}
}
IClasspathEntry[] classPath = new IClasspathEntry[newClassPath.size()];
newClassPath.toArray(classPath);
javaProject.setRawClasspath(classPath, null);
if (autobuild) {
helper().awaitAutoBuild();
}
}
}
}