org.eclipse.wb.tests.utils.ProjectClassLoaderTest.java Source code

Java tutorial

Introduction

Here is the source code for org.eclipse.wb.tests.utils.ProjectClassLoaderTest.java

Source

/*******************************************************************************
 * Copyright (c) 2011 Google, 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
 *
 * Contributors:
 *    Google, Inc. - initial API and implementation
 *******************************************************************************/
package org.eclipse.wb.tests.utils;

import com.google.common.collect.Lists;
import com.google.common.collect.Sets;

import org.eclipse.wb.internal.core.utils.jdt.core.ProjectUtils;
import org.eclipse.wb.internal.core.utils.reflect.ProjectClassLoader;
import org.eclipse.wb.internal.core.utils.reflect.ReflectionUtils;
import org.eclipse.wb.internal.swing.model.component.ContainerInfo;
import org.eclipse.wb.tests.designer.core.PdeProjectConversionUtils;
import org.eclipse.wb.tests.designer.core.TestProject;
import org.eclipse.wb.tests.designer.core.annotations.DisposeProjectAfter;
import org.eclipse.wb.tests.designer.swing.SwingModelTest;

import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IProjectDescription;
import org.eclipse.core.resources.IWorkspace;
import org.eclipse.core.resources.IWorkspaceRoot;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.Path;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.dom.CompilationUnit;

import static org.fest.assertions.Assertions.assertThat;

import org.apache.commons.io.FileUtils;

import java.io.File;
import java.util.List;
import java.util.Set;

/**
 * Test for {@link ProjectClassLoader}.
 * 
 * @author scheglov_ke
 */
public class ProjectClassLoaderTest extends SwingModelTest {
    private static IWorkspace workspace = ResourcesPlugin.getWorkspace();
    private static IWorkspaceRoot workspaceRoot = workspace.getRoot();
    private static final String workspaceLocation = workspaceRoot.getLocation().toPortableString();

    ////////////////////////////////////////////////////////////////////////////
    //
    // Exit zone :-) XXX
    //
    ////////////////////////////////////////////////////////////////////////////
    public void _test_exit() throws Exception {
        System.exit(0);
    }

    ////////////////////////////////////////////////////////////////////////////
    //
    // Tests
    //
    ////////////////////////////////////////////////////////////////////////////
    /**
     * Test that {@link ProjectClassLoader} defines packages.
     */
    public void test_getPackage() throws Exception {
        setFileContentSrc("test/SuperPanel.java", getTestSource("// filler filler filler filler filler",
                "// filler filler filler filler filler", "abstract public class SuperPanel extends JPanel {", "}"));
        waitForAutoBuild();
        ContainerInfo panel = parseContainer("// filler filler filler", "abstract class Test extends SuperPanel {",
                "  public Test() {", "  }", "}");
        //
        Class<?> superPanelClass = panel.getDescription().getComponentClass();
        assertNotNull(superPanelClass.getPackage());
        assertEquals("test", superPanelClass.getPackage().getName());
    }

    /**
     * Test that we can inherit from abstract classes with declared abstract method.
     */
    public void test_inheritanceWithAbstractMethod_noInvocation() throws Exception {
        setFileContentSrc("test/AbstractPanel.java",
                getTestSource("abstract public class AbstractPanel extends JPanel {",
                        "  protected abstract int myAbstractMethod();", "}"));
        waitForAutoBuild();
        //
        parseContainer("// filler filler filler", "abstract class Test extends AbstractPanel {",
                "  public Test() {", "  }", "}");
    }

    /**
     * If abstract method is invoked from binary, but not implemented in AST, return default value.
     */
    public void test_inheritanceWithAbstractMethod_withInvocation() throws Exception {
        setFileContentSrc("test/MyPanel.java",
                getTestSource("public abstract class MyPanel extends JPanel {", "  private final String m_foo;",
                        "  public MyPanel() {", "    m_foo = getFoo();", "  }",
                        "  protected abstract String getFoo();", "}"));
        waitForAutoBuild();
        // parse
        ContainerInfo panel = parseContainer("public abstract class Test extends MyPanel {", "  public Test() {",
                "  }", "}");
        panel.refresh();
        assertNoErrors(panel);
        assertEquals("<dynamic>", ReflectionUtils.getFieldObject(panel.getObject(), "m_foo"));
    }

    /**
     * If abstract void method is invoked, we ignore this, because we don't need its result.
     * <p>
     * Now we switch to AST interpretation it this case, if method is implemented in parsed
     * {@link CompilationUnit}.
     */
    public void test_inheritanceWithAbstractMethod_voidInvocation() throws Exception {
        setFileContentSrc("test/AbstractPanel.java",
                getTestSource("abstract public class AbstractPanel extends JPanel {", "  public AbstractPanel() {",
                        "    myAbstractMethod();", "  }", "  protected abstract void myAbstractMethod();", "}"));
        waitForAutoBuild();
        //
        parseContainer("// filler filler filler", "abstract class Test extends AbstractPanel {",
                "  public Test() {", "  }", "}");
    }

    /**
     * Test that we can inherit from abstract classes with declared, but not implemented interfaces.
     */
    public void test_inheritanceWithInterfaces() throws Exception {
        setFileContentSrc("test/ValueProvider2.java",
                getTestSource("// filler filler filler filler filler", "// filler filler filler filler filler",
                        "public interface ValueProvider2 {", "  int getValue();", "}"));
        setFileContentSrc("test/AbstractPanel.java", getTestSource(
                "abstract public class AbstractPanel extends JPanel implements ValueProvider2 {", "}"));
        waitForAutoBuild();
        //
        parseContainer("// filler filler filler", "abstract class Test extends AbstractPanel {",
                "  public Test() {", "  }", "}");
    }

    /**
     * Test for case when we call re-implemented abstract method.
     */
    public void test_inheritanceImplementCall() throws Exception {
        setFileContentSrc("test/ValueProvider.java",
                getTestSource("// filler filler filler filler filler", "// filler filler filler filler filler",
                        "public interface ValueProvider {", "  int getValue();", "}"));
        setFileContentSrc("test/MyAbstractButton.java",
                getTestSource("public abstract class MyAbstractButton extends JButton implements ValueProvider {",
                        "  public void setHorizontalAlignment(int alignment) {",
                        "    super.setHorizontalAlignment( getValue() );", "  }", "}"));
        setFileContentSrc("test/MyButton.java", getTestSource("public class MyButton extends MyAbstractButton {",
                "  public int getValue() {", "    return 0;", "  }", "}"));
        waitForAutoBuild();
        //
        ContainerInfo panel = parseContainer("abstract class Test extends JPanel {", "  public Test() {",
                "  MyButton button = new MyButton();", "    add( button );",
                "    button.setHorizontalAlignment(0);", "  }", "}");
        assertNoErrors(panel);
    }

    ////////////////////////////////////////////////////////////////////////////
    //
    // Import-Package
    //
    ////////////////////////////////////////////////////////////////////////////
    /**
     * Test for "Import-Package" statement support in manifest.
     * <p>
     * Note: right now only directly imported packages.
     */
    @DisposeProjectAfter
    public void test_importPackage() throws Exception {
        PdeProjectConversionUtils.convertToPDE(m_project, null);
        // create plugin with needed Class
        TestProject projectWithPackage = new TestProject("SomeProject");
        try {
            PdeProjectConversionUtils.convertToPDE(projectWithPackage.getProject(), null);
            setFileContentSrc(projectWithPackage.getProject(), "my/classes/MyClass.java", getSource(
                    "// filler filler filler filler filler", "package my.classes;", "public class MyClass {", "}"));
            // export/import package
            {
                IFile manifestFile = getFile(projectWithPackage.getProject(), "META-INF/MANIFEST.MF");
                String manifest = getFileContent(manifestFile);
                manifest = manifest.trim() + "\nExport-Package: my.classes\n\n";
                setFileContent(manifestFile, manifest);
            }
            {
                IFile manifestFile = getFile(m_testProject.getProject(), "META-INF/MANIFEST.MF");
                String manifest = getFileContent(manifestFile);
                manifest = manifest.trim() + "\nImport-Package: my.classes\n\n";
                setFileContent(manifestFile, manifest);
            }
            waitForAutoBuild();
            // prepare ClassLoader
            ProjectClassLoader classLoader = ProjectClassLoader.create(null, m_javaProject);
            // no exception
            classLoader.loadClass("my.classes.MyClass");
        } finally {
            projectWithPackage.dispose();
        }
    }

    ////////////////////////////////////////////////////////////////////////////
    //
    // Fragments
    //
    ////////////////////////////////////////////////////////////////////////////
    /**
     * Fragments should be included into {@link ClassLoader} of main plugin.
     */
    @DisposeProjectAfter
    public void test_fragments() throws Exception {
        PdeProjectConversionUtils.convertToPDE(m_testProject.getProject(), null);
        // create fragment
        TestProject fragmentProject = new TestProject("TestProject_ru");
        try {
            PdeProjectConversionUtils.convertToPDE(fragmentProject.getProject(), "TestProject");
            {
                setFileContent(m_testProject.getProject(), "src/com/test", "Application.properties",
                        getSourceDQ("shell.text=Hello!"));
                setFileContent(fragmentProject.getProject(), "src/com/test", "Application_ru.properties",
                        getSourceDQ("shell.text=Privet!"));
            }
            waitForAutoBuild();
            // prepare ClassLoader
            ProjectClassLoader classLoader = ProjectClassLoader.create(null, m_testProject.getJavaProject());
            assertNotNull(classLoader.getResource("com/test/Application.properties"));
            assertNotNull(classLoader.getResource("com/test/Application_ru.properties"));
        } finally {
            fragmentProject.dispose();
        }
    }

    /**
     * Fragments should be included into {@link ClassLoader} of main plugin.
     */
    @DisposeProjectAfter
    public void test_fragments_cycle() throws Exception {
        PdeProjectConversionUtils.convertToPDE(m_testProject.getProject(), null);
        // create fragment
        TestProject fragmentProject = new TestProject("TestProject_ru");
        try {
            PdeProjectConversionUtils.convertToPDE(fragmentProject.getProject(), "TestProject");
            {
                setFileContent(m_testProject.getProject(), "src/com/test", "Application.properties",
                        getSourceDQ("shell.text=Hello!"));
                setFileContent(fragmentProject.getProject(), "src/com/test", "Application_ru.properties",
                        getSourceDQ("shell.text=Privet!"));
            }
            waitForAutoBuild();
            // bad thing: add fragment into classpath of its host
            m_testProject.addRequiredProject(fragmentProject);
            // prepare ClassLoader
            ProjectClassLoader classLoader = ProjectClassLoader.create(null, m_testProject.getJavaProject());
            assertNotNull(classLoader.getResource("com/test/Application.properties"));
            assertNotNull(classLoader.getResource("com/test/Application_ru.properties"));
        } finally {
            fragmentProject.dispose();
        }
    }

    /**
     * Test for fragment that is not Java project. Not sure if this makes sense, but one user has such
     * project.
     */
    @DisposeProjectAfter
    public void test_fragments_notJavaFragment() throws Exception {
        PdeProjectConversionUtils.convertToPDE(m_testProject.getProject(), null);
        // create fragment
        TestProject fragmentProject = new TestProject("TestProject_ru");
        try {
            PdeProjectConversionUtils.convertToPDE(fragmentProject.getProject(), "TestProject");
            ProjectUtils.removeNature(fragmentProject.getProject(), JavaCore.NATURE_ID);
            {
                setFileContent(m_testProject.getProject(), "src/com/test", "Application.properties",
                        getSourceDQ("shell.text=Hello!"));
                setFileContent(fragmentProject.getProject(), "com/test", "Application_ru.properties",
                        getSourceDQ("shell.text=Privet!"));
            }
            waitForAutoBuild();
            // prepare ClassLoader
            ProjectClassLoader classLoader = ProjectClassLoader.create(null, m_testProject.getJavaProject());
            assertNotNull(classLoader.getResource("com/test/Application.properties"));
            assertNotNull(classLoader.getResource("com/test/Application_ru.properties"));
        } finally {
            fragmentProject.dispose();
        }
    }

    /**
     * Fragments should be included into {@link ClassLoader} of main plugin and required plugins.
     */
    @DisposeProjectAfter
    public void test_fragments_ofRequiredProject() throws Exception {
        PdeProjectConversionUtils.convertToPDE(m_testProject.getProject(), null);
        // create projects
        TestProject requiredProject = new TestProject("RequiredProject");
        TestProject fragmentProject = new TestProject("RequiredProject_ru");
        try {
            PdeProjectConversionUtils.convertToPDE(requiredProject.getProject(), null);
            PdeProjectConversionUtils.convertToPDE(fragmentProject.getProject(), "RequiredProject");
            {
                setFileContent(requiredProject.getProject(), "src/com/test", "Application.properties",
                        getSourceDQ("shell.text=Hello!"));
                setFileContent(fragmentProject.getProject(), "src/com/test", "Application_ru.properties",
                        getSourceDQ("shell.text=Privet!"));
            }
            m_testProject.addRequiredProject(requiredProject);
            waitForAutoBuild();
            // prepare ClassLoader
            ProjectClassLoader classLoader = ProjectClassLoader.create(null, m_testProject.getJavaProject());
            assertNotNull(classLoader.getResource("com/test/Application.properties"));
            assertNotNull(classLoader.getResource("com/test/Application_ru.properties"));
        } finally {
            fragmentProject.dispose();
            requiredProject.dispose();
        }
    }

    ////////////////////////////////////////////////////////////////////////////
    //
    // addSourceLocations()
    //
    ////////////////////////////////////////////////////////////////////////////
    public void test_addSourceLocations_normalProject() throws Exception {
        List<String> locations = getSourceLocations();
        assertThat(locations).containsExactly(workspaceLocation + "/TestProject/src");
    }

    @DisposeProjectAfter
    public void test_addSourceLocations_noSuchProject() throws Exception {
        m_project.delete(true, null);
        // check locations
        List<String> locations = getSourceLocations();
        assertThat(locations).isEmpty();
    }

    @DisposeProjectAfter
    public void test_addSourceLocations_notJavaProject() throws Exception {
        ProjectUtils.removeNature(m_project, JavaCore.NATURE_ID);
        // check locations
        List<String> locations = getSourceLocations();
        assertThat(locations).isEmpty();
    }

    @DisposeProjectAfter
    public void test_addSourceLocations_projectNotInWorkspace() throws Exception {
        String newProjectLocation = moveProjectIntoWorkspaceSubFolder();
        // check locations
        List<String> locations = getSourceLocations();
        assertThat(locations).containsExactly(newProjectLocation + "/src");
    }

    @DisposeProjectAfter
    public void test_addSourceLocations_recursion() throws Exception {
        // create new project "myProject"
        TestProject myProject = new TestProject("myProject");
        IJavaProject myJavaProject = myProject.getJavaProject();
        // reference "myProject" from "TestProject"
        try {
            // create circular dependency
            ProjectUtils.requireProject(m_javaProject, myJavaProject);
            ProjectUtils.requireProject(myJavaProject, m_javaProject);
            // check locations
            List<String> locations = getSourceLocations();
            assertThat(locations).containsExactly(workspaceLocation + "/TestProject/src",
                    workspaceLocation + "/myProject/src");
        } finally {
            myProject.dispose();
        }
    }

    /**
     * Old Java project style, when source and output is project itself.
     * <p>
     * https://groups.google.com/forum/#!topic/google-web-toolkit/r0Klxfkd7qA
     */
    @DisposeProjectAfter
    public void test_addSourceLocations_oldProjectStyle() throws Exception {
        setFileContent(".classpath",
                getSourceDQ("<classpath>",
                        "  <classpathentry kind='con' path='org.eclipse.jdt.launching.JRE_CONTAINER'/>",
                        "  <classpathentry kind='src' path=''/>", "  <classpathentry kind='output' path='bin'/>",
                        "</classpath>"));
        // check locations
        List<String> locations = getSourceLocations();
        assertThat(locations).containsExactly(workspaceLocation + "/TestProject");
    }

    /**
     * Move existing {@link IProject} into "subFolder" in workspace.
     * 
     * @return the new absolute location of project.
     */
    public static String moveProjectIntoWorkspaceSubFolder() throws Exception {
        String newProjectLocation = workspaceLocation + "/subFolder/Test";
        // move project content
        FileUtils.moveDirectory(new File(m_project.getLocation().toPortableString()), new File(newProjectLocation));
        // delete old project
        m_project.delete(true, null);
        // create new project, in workspace sub-folder
        {
            IProjectDescription projectDescription = workspace.newProjectDescription("Test");
            projectDescription.setLocation(new Path(newProjectLocation));
            m_project = workspaceRoot.getProject("Test");
            m_project.create(projectDescription, null);
            m_project.open(null);
            // update Java project
            m_testProject = new TestProject(m_project);
            m_javaProject = m_testProject.getJavaProject();
        }
        return newProjectLocation;
    }

    /**
     * @return result of {@link ProjectClassLoader#addSourceLocations(Set, List, IProject)}.
     */
    private List<String> getSourceLocations() throws Exception {
        List<String> locations = Lists.newArrayList();
        ProjectClassLoader.addSourceLocations(Sets.<IProject>newHashSet(), locations, m_project);
        return locations;
    }

    ////////////////////////////////////////////////////////////////////////////
    //
    // addOutputLocations()
    //
    ////////////////////////////////////////////////////////////////////////////
    public void test_addOutputLocations_normalProject() throws Exception {
        List<String> locations = getOutputLocations();
        assertThat(locations).containsExactly(workspaceLocation + "/TestProject/bin");
    }

    @DisposeProjectAfter
    public void test_addOutputLocations_noSuchProject() throws Exception {
        m_project.delete(true, null);
        // check locations
        List<String> locations = getOutputLocations();
        assertThat(locations).isEmpty();
    }

    @DisposeProjectAfter
    public void test_addOutputLocations_notJavaProject() throws Exception {
        ProjectUtils.removeNature(m_project, JavaCore.NATURE_ID);
        // check locations
        List<String> locations = getOutputLocations();
        assertThat(locations).isEmpty();
    }

    @DisposeProjectAfter
    public void test_addOutputLocations_projectNotInWorkspace() throws Exception {
        String newProjectLocation = moveProjectIntoWorkspaceSubFolder();
        // check locations
        List<String> locations = getOutputLocations();
        assertThat(locations).containsExactly(newProjectLocation + "/bin");
    }

    @DisposeProjectAfter
    public void test_addOutputLocations_recursion() throws Exception {
        // create new project "myProject"
        TestProject myProject = new TestProject("myProject");
        IJavaProject myJavaProject = myProject.getJavaProject();
        // reference "myProject" from "TestProject"
        try {
            // create circular dependency
            ProjectUtils.requireProject(m_javaProject, myJavaProject);
            ProjectUtils.requireProject(myJavaProject, m_javaProject);
            // check locations
            List<String> locations = getOutputLocations();
            assertThat(locations).containsExactly(workspaceLocation + "/TestProject/bin",
                    workspaceLocation + "/myProject/bin");
        } finally {
            myProject.dispose();
        }
    }

    /**
     * @return result of {@link ProjectClassLoader#addOutputLocations(Set, List, IProject)}.
     */
    private List<String> getOutputLocations() throws Exception {
        List<String> locations = Lists.newArrayList();
        ProjectClassLoader.addOutputLocations(Sets.<IProject>newHashSet(), locations, m_project);
        return locations;
    }
}