org.grails.ide.eclipse.test.util.GrailsTest.java Source code

Java tutorial

Introduction

Here is the source code for org.grails.ide.eclipse.test.util.GrailsTest.java

Source

/*******************************************************************************
 * Copyright (c) 2012 VMWare, 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
 * https://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     VMWare, Inc. - initial API and implementation
 *******************************************************************************/
package org.grails.ide.eclipse.test.util;

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.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import junit.framework.Assert;
import junit.framework.TestCase;

import org.apache.commons.io.FileUtils;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IFolder;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
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.core.runtime.jobs.Job;
import org.eclipse.core.runtime.preferences.IEclipsePreferences;
import org.eclipse.core.runtime.preferences.InstanceScope;
import org.eclipse.jdt.core.IClasspathEntry;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.JavaModelException;
import org.grails.ide.eclipse.commands.GrailsCommand;
import org.grails.ide.eclipse.commands.GrailsCommandFactory;
import org.grails.ide.eclipse.commands.GrailsCommandUtils;
import org.grails.ide.eclipse.core.GrailsCoreActivator;
import org.grails.ide.eclipse.core.internal.GrailsNature;
import org.grails.ide.eclipse.core.internal.classpath.SourceFolderJob;
import org.grails.ide.eclipse.core.internal.model.DefaultGrailsInstall;
import org.grails.ide.eclipse.core.launch.SynchLaunch.ILaunchResult;
import org.grails.ide.eclipse.core.model.GrailsInstallManager;
import org.grails.ide.eclipse.core.model.GrailsVersion;
import org.grails.ide.eclipse.core.model.IGrailsInstall;
import org.grails.ide.eclipse.longrunning.LongRunningProcessGrailsExecutor;
import org.grails.ide.eclipse.ui.internal.importfixes.GrailsProjectVersionFixer;
import org.osgi.service.prefs.BackingStoreException;
import org.springsource.ide.eclipse.commons.core.ZipFileUtil;
import org.springsource.ide.eclipse.commons.frameworks.test.util.ACondition;
import org.springsource.ide.eclipse.commons.tests.util.DownloadManager;
import org.springsource.ide.eclipse.commons.tests.util.DownloadManager.DownloadRequestor;
import org.springsource.ide.eclipse.commons.tests.util.StsTestUtil;

/**
 * @author Kris De Volder
 */
public class GrailsTest extends TestCase {

    public abstract class TearDownAction {
        public abstract void doit() throws CoreException;
    }

    public List<TearDownAction> tearDownActions = new ArrayList<TearDownAction>();

    @Override
    protected void setUp() throws Exception {
        super.setUp();
        GrailsProjectVersionFixer.testMode();
    }

    public static void clearGrailsState() {
        File home = GrailsCoreActivator.getDefault().getUserHome();
        //deleteDir(new File(home, ".ivy2"));
        deleteDir(new File(home, ".grails"));
        //Long running Grails processes may hold on to state as well if they are still running.
        // Plus.... they will break if the files in .grails are all deleted. So kill em all :-)
        LongRunningProcessGrailsExecutor.shutDownIfNeeded();
    }

    private static void deleteDir(File dir) {
        try {
            FileUtils.deleteDirectory(dir);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Override
    protected void tearDown() throws Exception {
        super.tearDown();
        for (TearDownAction td : tearDownActions) {
            td.doit();
        }
    }

    protected void assertPluginSourceFolder(IProject proj, String plugin, String... pathElements)
            throws JavaModelException {
        IFolder folder = sourceFolderFor(proj, plugin, pathElements);
        assertClassPathEntry(IClasspathEntry.CPE_SOURCE, folder.getFullPath().toString(), getRawClassPath(proj));
        assertTrue("Source folder not found: " + folder, folder.exists());
    }

    protected IClasspathEntry[] getRawClassPath(IProject proj) throws JavaModelException {
        return JavaCore.create(proj).getRawClasspath();
    }

    protected void assertAbsentPluginSourceFolder(IProject proj, String plugin, String... pathElements)
            throws JavaModelException {
        IFolder folder = sourceFolderFor(proj, plugin, pathElements);
        assertFalse("Source folder exists but shouldn't: " + folder, folder.exists());
        assertAbsentClassPathEntry(IClasspathEntry.CPE_SOURCE, folder.getFullPath().toString(),
                getRawClassPath(proj));
    }

    protected IFolder sourceFolderFor(IProject proj, String plugin, String... pathElements) {
        IFolder folder = proj.getFolder(SourceFolderJob.PLUGINS_FOLDER_LINK);
        folder = folder.getFolder(plugin);
        for (String e : pathElements) {
            folder = folder.getFolder(e);
        }
        return folder;
    }

    /**
     * Verify that an expected classPath entry with given kind and path exists in the classpath.
     */
    protected void assertClassPathEntry(int kind, String path, IClasspathEntry[] classPath) {
        StringBuffer found = new StringBuffer();
        for (IClasspathEntry entry : classPath) {
            found.append(kindString(entry.getEntryKind()) + ": " + entry.getPath() + "\n");
            if (entry.getEntryKind() == kind) {
                if (entry.getEntryKind() == IClasspathEntry.CPE_CONTAINER) {
                    if (entry.getPath().equals(Path.EMPTY.append(path))) {
                        return; //OK
                    }
                } else if (entry.getEntryKind() == IClasspathEntry.CPE_LIBRARY) {
                    // Library paths will vary a lot in location and version numbers, so use an 'approximate' 
                    // check to see if it contains some key string.
                    if (entry.getPath().toString().contains(path)) {
                        return; //OK
                    }
                } else {
                    if (entry.getPath().toString().endsWith(path)) {
                        return; //OK
                    }
                }
            }
        }
        fail("No classpath entry found for: " + kindString(kind) + ": " + path + "\n" + "found entries are: \n"
                + found.toString());
    }

    protected void assertClassPathEntry(int kind, Pattern path, IClasspathEntry[] classPath) {
        StringBuffer found = new StringBuffer();
        for (IClasspathEntry entry : classPath) {
            found.append(kindString(entry.getEntryKind()) + ": " + entry.getPath() + "\n");
            if (entry.getEntryKind() == kind) {
                Matcher matcher = path.matcher(entry.getPath().toString());
                if (matcher.find()) {
                    return; //OK
                }
            }
        }
        fail("No classpath entry found for: " + kindString(kind) + ": " + path + "\n" + "found entries are: \n"
                + found.toString());
    }

    /**
     * Verify that an expected classPath entry with given kind and path exists in the classpath.
     */
    protected void assertAbsentClassPathEntry(int kind, String path, IClasspathEntry[] classPath) {
        for (IClasspathEntry entry : classPath) {
            if (entry.getEntryKind() == kind) {
                if (entry.getEntryKind() == IClasspathEntry.CPE_CONTAINER) {
                    if (entry.getPath().equals(Path.EMPTY.append(path))) {
                        fail("Class path entry should not be there: " + entry);
                    }
                } else if (entry.getEntryKind() == IClasspathEntry.CPE_LIBRARY) {
                    // Library paths will vary a lot in location and version numbers, so use an 'approximate' 
                    // check to see if it contains some key string.
                    if (entry.getPath().toString().contains(path)) {
                        fail("Class path entry should not be there: " + entry);
                    }
                } else {
                    if (entry.getPath().toString().endsWith(path)) {
                        fail("Class path entry should not be there: " + entry);
                    }
                }
            }
        }
        // OK
    }

    /**
     * @param kind
     * @return
     */
    protected String kindString(int kind) {
        switch (kind) {
        case IClasspathEntry.CPE_SOURCE:
            return "src";
        case IClasspathEntry.CPE_CONTAINER:
            return "con";
        case IClasspathEntry.CPE_LIBRARY:
            return "lib";
        default:
            return "" + kind;
        }
    }

    /**
     * Ensure a project with the given name exists, so we can use it as a test Fixture.
     */
    public static IProject ensureProject(String name) throws Exception {
        return ensureProject(name, false);
    }

    /**
     * Ensure a project with the given name exists, so we can use it as a test Fixture.
     * Note that when creating a plugin project, this method expects a camel case name.
     * @param name name of the project
     * @param isPluginProject whether this project should be a plugin project
     * @return the created Grails projet
     * @throws Throwable 
     * @throws CoreException
     */
    protected static IProject ensureProject(final String name, final boolean isPluginProject) throws Exception {
        try {
            final IProject project = StsTestUtil.getProject(name);
            if (project.exists()) {
                System.out.println("Reusing test project '" + name + "'");
                return project;
            }

            final Job job = new Job("EnsureProject " + name) {
                @Override
                protected IStatus run(IProgressMonitor monitor) {
                    try {
                        System.out.println(
                                "Creating test project '" + name + "' with Grails " + GrailsVersion.getDefault());
                        GrailsCommand cmd;
                        if (isPluginProject) {
                            cmd = GrailsCommandFactory.createPlugin(name);
                        } else {
                            cmd = GrailsCommandFactory.createApp(name);
                        }
                        ILaunchResult result = cmd.synchExec();
                        IGrailsInstall install = cmd.getGrailsInstall();
                        try {
                            GrailsCommandUtils.eclipsifyProject(install, project);
                        } catch (Exception e) {
                            System.err.println(
                                    "Ugh...tried to Eclipsify project, but failed.  Maybe a network error.  Retrying");
                            System.err.println("Project is: " + project.getName());
                            // try again maybe something from the server
                            GrailsCommandUtils.eclipsifyProject(install, project);
                        }
                        if (!isPluginProject) {
                            assertContains("Created Grails Application at " + workspacePath() + "/" + name,
                                    result.getOutput());
                        } else {
                            String pluginName = GrailsNature.createPluginName(name);
                            String projectName = pluginName.substring(0, pluginName.indexOf("GrailsPlugin.groovy"));
                            assertContains("Created plugin " + projectName, result.getOutput());
                            assertTrue("Plugin file " + pluginName + " does not exist",
                                    GrailsNature.isGrailsPluginProject(project));
                        }
                        return Status.OK_STATUS;
                    } catch (Throwable e) {
                        return new Status(IStatus.ERROR, GrailsCoreActivator.PLUGIN_ID,
                                "problem creating test project", e);
                    }
                }
            };
            waitForWorkspaceJob(job);
            assertTrue(project.exists());
            assertNoErrors(project);
            assertTrue(project.hasNature(GrailsNature.NATURE_ID));

            System.out.println("Created project '" + project.getName() + " uses Grails "
                    + GrailsVersion.getEclipseGrailsVersion(project));

            return project;
        } catch (Throwable e) {
            throw new Error(e);
        }
    }

    /**
     * Helper method to Schedule a Job with a 'buildRule' and then wait for it to complete
     * while keeping the UI thread alive.
     */
    public static void waitForWorkspaceJob(final Job job) throws Exception {
        job.setRule(ResourcesPlugin.getWorkspace().getRuleFactory().buildRule());
        job.schedule();
        new ACondition(job.getName()) {
            @Override
            public boolean test() throws Exception {
                return job.getResult() != null;
            }
        }.waitFor(600000);
        assertStatusOK(job.getResult());
    }

    public static void assertNoErrors(IProject project) throws CoreException {
        try {
            StsTestUtil.assertNoErrors(project);
        } catch (Throwable e) {
            //This retry is compensating for https://jira.grails.org/browse/GRAILS-9263
            GrailsCommandUtils.refreshDependencies(JavaCore.create(project), false);
            StsTestUtil.assertNoErrors(project);
        }
    }

    public static void assertContains(String needle, String haystack) {
        if (!haystack.contains(needle)) {
            fail("Couldn't find expected substring: '" + needle + "'\n in '" + haystack + "'");
        }
    }

    public static String workspacePath() {
        return ResourcesPlugin.getWorkspace().getRoot().getLocation().toString();
    }

    /**
     * Creates a 'temporary' resource for testing purpose. The resource is deleted during the 
     * test tearDown.
     */
    public void createTmpResource(IProject project, String path, String contents) throws CoreException {
        final IFile file = project.getFile(new Path(path));
        assertFalse("Can't create temporary resource because it already exists: " + file, file.exists());
        createResource(project, path, contents);
        tearDownActions.add(new TearDownAction() {
            @Override
            public void doit() throws CoreException {
                file.delete(true, new NullProgressMonitor());
            };
        });
    }

    /**
     * Replace a file 'temporarily' with a different contents. The original contents of the file will
     * be reinstated in the test teardown.
     * @throws IOException 
     * @throws CoreException 
     */
    protected void tmpReplaceResource(final IProject project, final String path, String newContents)
            throws IOException, CoreException {
        final IFile file = project.getFile(new Path(path));
        File realFile = file.getLocation().toFile();
        final byte[] oldContents = file.exists() ? getBytesFromFile(realFile) : null;
        createResource(project, path, newContents);
        tearDownActions.add(new TearDownAction() {
            @Override
            public void doit() throws CoreException {
                if (oldContents != null) {
                    //File existed before:
                    createResource(project, path, new String(oldContents));
                } else {
                    //File didn't exist before:
                    file.delete(true, new NullProgressMonitor());
                }
            }
        });
    }

    /**
     * Check that a set contains exactly the expected elements, no more no less.
     */
    public static void assertElements(Set<Object> actualSet, Object... expecteds) {
        HashSet<Object> expectedSet = new HashSet<Object>(Arrays.asList(expecteds));
        StringBuilder msg = new StringBuilder();
        for (Object expected : expectedSet) {
            if (!actualSet.contains(expected)) {
                msg.append("Expected but not found: " + expected);
            }
        }
        for (Object actual : actualSet) {
            if (!expectedSet.contains(actual)) {
                msg.append("Found but not expected: " + actual);
            }
        }
        if (!"".equals(msg.toString())) {
            fail(msg.toString());
        }
    }

    /**
     * Check that some expected elements are in a set. Extra elements in the set are tolerated.
     */
    public static void assertExpectedElements(Set<Object> actualSet, Object... expecteds) {
        HashSet<Object> expectedSet = new HashSet<Object>(Arrays.asList(expecteds));
        StringBuilder msg = new StringBuilder();
        for (Object expected : expectedSet) {
            if (!actualSet.contains(expected)) {
                msg.append("Expected but not found: '" + expected + "'\n");
            }
        }
        if (!"".equals(msg.toString())) {
            //Better message. E.g. could be something missing because spelled wrong. So include elements 
            //that were found but not expected in the message.
            for (Object actual : actualSet) {
                if (!expectedSet.contains(actual)) {
                    msg.append("Found but not expected: '" + actual + "'\n");
                }
            }
            fail(msg.toString());
        }
    }

    public static void assertExpectedElements(Object[] actualElements, Object... expecteds) {
        assertExpectedElements(new HashSet<Object>(Arrays.asList(actualElements)), expecteds);
    }

    public static String[] join(String[] base, String... extra) {
        String[] result = new String[base.length + extra.length];
        for (int i = 0; i < base.length; i++) {
            result[i] = base[i];
        }
        for (int i = 0; i < extra.length; i++) {
            result[base.length + i] = extra[i];
        }
        return result;
    }

    public static String[] defaultPlugins() {
        return defaultPlugins(false);
    }

    private static String[] defaultPlugins(boolean isPlugin) {
        if (GrailsVersion.MOST_RECENT.compareTo(GrailsVersion.V_2_0_0) < 0) {
            //Grails 1.3
            return new String[] { "tomcat", "hibernate" };
        } else if (GrailsVersion.MOST_RECENT.compareTo(GrailsVersion.V_2_1_0) < 0) {
            //Grails 2.0
            if (isPlugin) {
                //Default plugins for plugin projects
                return new String[] { "tomcat", "release", "svn" };
            } else {
                //Default plugins for regular projects
                return new String[] { "tomcat", "hibernate", "jquery", "resources", "webxml" };
            }
        } else if (GrailsVersion.MOST_RECENT.compareTo(GrailsVersion.V_2_3_) < 0) {
            //Grails 2.1, 2.2
            if (isPlugin) {
                //Default plugins for plugin projects
                return new String[] { "tomcat", "release", "rest-client-builder" };
            } else {
                //Default plugins for regular projects
                return new String[] { "cache", "database-migration", "hibernate", "jquery", "resources", "tomcat",
                        "webxml" };
            }
        } else {
            //Grails 2.3
            if (isPlugin) {
                //Default plugins for plugin projects
                return new String[] { "tomcat", "release", "rest-client-builder" };
            } else {
                //Default plugins for regular projects
                return new String[] { "scaffolding", "cache", "database-migration", "hibernate", "jquery",
                        "resources", "tomcat", "webxml" };
            }
        }
    }

    public static String[] defaultPlugins(IProject project) {
        return defaultPlugins(GrailsNature.isGrailsPluginProject(project));
    }

    public static void assertStatusOK(IStatus status) {
        if (!status.isOK()) {
            fail("Expecting OK Status, but was:\n" + status);
        }
    }

    /**
     * If Java compiler compliance is set below 1.5 will get Groovy compilation errors 
     * having to do with generics. Solution: call this method in the test setup.
     */
    /*   public static void setJava15Compliance() {
          @SuppressWarnings("rawtypes")
          Hashtable options = JavaCore.getDefaultOptions();
           options.put(JavaCore.COMPILER_COMPLIANCE, "1.5");
           options.put(JavaCore.COMPILER_SOURCE, "1.5");
           options.put(JavaCore.COMPILER_CODEGEN_TARGET_PLATFORM, "1.5");
           JavaCore.setOptions(options);
       }
     */
    //   public static void setJava16Compliance() {
    //      @SuppressWarnings("rawtypes")
    //      Hashtable options = JavaCore.getDefaultOptions();
    //       options.put(JavaCore.COMPILER_COMPLIANCE, "1.6");
    //       options.put(JavaCore.COMPILER_SOURCE, "1.6");
    //       options.put(JavaCore.COMPILER_CODEGEN_TARGET_PLATFORM, "1.6");
    //       JavaCore.setOptions(options);
    //   }

    public static void setJava17Compliance() {
        @SuppressWarnings("rawtypes")
        Hashtable options = JavaCore.getDefaultOptions();
        options.put(JavaCore.COMPILER_COMPLIANCE, "1.7");
        options.put(JavaCore.COMPILER_SOURCE, "1.7");
        options.put(JavaCore.COMPILER_CODEGEN_TARGET_PLATFORM, "1.7");
        JavaCore.setOptions(options);
    }

    //   /**
    //    * If Java compiler compliance is set below 1.5 will get Groovy compilation errors 
    //    * having to do with generics. Solution: call this method in the test setup.
    //    */
    //   public static void setJava16Compliance() {
    //      @SuppressWarnings("rawtypes")
    //      Hashtable options = JavaCore.getDefaultOptions();
    //       options.put(JavaCore.COMPILER_COMPLIANCE, "1.5");
    //       options.put(JavaCore.COMPILER_SOURCE, "1.5");
    //       options.put(JavaCore.COMPILER_CODEGEN_TARGET_PLATFORM, "1.5");
    //       JavaCore.setOptions(options);
    //   }   

    public static URL getProjectZip(String name, GrailsVersion version) {
        final String location = "projects/" + name + "-" + version + ".zip";
        URL result = GrailsTest.class.getClassLoader().getResource(location);
        assertNotNull("Couldn't find zipped test project: " + location, result);
        return result;
    }

    public static IResource createResource(IProject project, String pathStr, String contents) throws CoreException {
        Path path = new Path(pathStr);
        for (int i = 0; i < path.segmentCount() - 1; i++) {
            IFolder folder = project.getFolder(path.removeLastSegments(path.segmentCount() - i - 1));
            if (!folder.exists()) {
                folder.create(true, true, null);
            }
        }
        IFile file = project.getFile(path);
        if (!file.exists()) {
            file.create(new ByteArrayInputStream(contents.getBytes()), true, null);
        } else {
            setContents(file, contents);
        }
        return file;
    }

    public static void setContents(IFile file, String newContents) throws CoreException {
        file.setContents(new ByteArrayInputStream(newContents.getBytes()), true, false, null);
    }

    public static void assertRegexp(String regexpExpect, String actual) {
        Matcher matcher = Pattern.compile(regexpExpect).matcher(actual);
        if (!matcher.find()) {
            fail("Expected (regexp) '" + regexpExpect + ":\n" + actual);
        }
    }

    /**
     * When a test requires a specific version of Grails to be the default Grails install in the
     * workspace, call this method to ensure the workspace is in fact setup with a matching
     * install.
     */
    public static void ensureDefaultGrailsVersion(final GrailsVersion version) throws Exception {
        //      waitForGrailsIntall();
        if (version.equals(GrailsVersion.getDefault())) {
            return; // OK!
        }
        GrailsInstallManager manager = GrailsCoreActivator.getDefault().getInstallManager();
        Collection<IGrailsInstall> available = manager.getAllInstalls();

        boolean matchingInstallFound = false;
        final Set<IGrailsInstall> newInstalls = new LinkedHashSet<IGrailsInstall>();
        for (IGrailsInstall candidate : available) {
            if (candidate.getVersion().equals(version)) {
                matchingInstallFound = true;
                newInstalls.add(new DefaultGrailsInstall(candidate.getHome(), candidate.getName(), true));
            } else {
                newInstalls.add(new DefaultGrailsInstall(candidate.getHome(), candidate.getName(), false));
            }
        }
        if (!matchingInstallFound) {
            final String installName = "grails-" + version.getVersionString();
            //We'll try to install one from a downloaded zip.
            URI distro = getDistributionZipURI(version);
            DownloadManager.getDefault().doWithDownload(distro, new DownloadRequestor() {
                public void exec(File downloadedFile) throws Exception {
                    File unzipDir = StsTestUtil.createTempDirectory(installName + "-", ".home");
                    File grailsHome = new File(unzipDir, installName);
                    ZipFileUtil.unzip(downloadedFile.toURI().toURL(), unzipDir, new NullProgressMonitor());
                    DefaultGrailsInstall install = new DefaultGrailsInstall(grailsHome.getCanonicalPath(),
                            installName, true);
                    IStatus status = install.verify();
                    if (!status.isOK()) {
                        throw new CoreException(status);
                    }
                    assertEquals(version, install.getVersion());
                    newInstalls.add(install);
                }
            });
            matchingInstallFound = true;
        }
        Assert.assertTrue("Couldn't find a Grails install of the required version " + version,
                matchingInstallFound);
        manager.setGrailsInstalls(newInstalls);
    }

    /**
     * Computes a URI from where it should be possible to download a Grails distribution.
     */
    public static URI getDistributionZipURI(GrailsVersion version) throws URISyntaxException {
        final String installName = "grails-" + version.getVersionString();

        if (version.getDownloadLocation() != null) {
            return version.getDownloadLocation();
        }

        if (version.equals(GrailsVersion.BUILDSNAPHOT_2_0_2)) {
            //For build snapshot we assume that you built them locally, so they could be anywhere. Add locations to look for it
            // in the String array below.
            for (String buildDirStr : new String[] {
                    "/home/kdvolder/commandline-dev/grails-core/build/distributions" }) {
                File buildDir = new File(buildDirStr);
                if (buildDir.exists()) {
                    File grailsZip = new File(buildDir, installName + ".zip");
                    if (grailsZip.exists()) {
                        return grailsZip.toURI();
                    }
                }
            }
        }

        String releaseType = version.isRelease() ? "release" : "milestone";
        String downloadUrl = "https://dist.springframework.org.s3.amazonaws.com/" + releaseType + "/GRAILS/grails-"
                + version + ".zip";

        return new URI(downloadUrl);
    }

    /** 
     * Flag to record whether the 'wait' has timed out before. If it has, don't try it again. I will just fail again anyway.
     * This flag provides the flexibility for waitForGrailsInstall and ensureDefaultGrailsVersion to both call eachother,
     * without getting stuck in an infinite loop.
     */
    private static boolean waitForGrailsInstallHasFailed = false;

    /**
     * When Eclipse starts, it sets up configuration stuff in a Job, we may start running the tests
     * before this Job is finished. So wait until we know that Grails was configured properly.
     */
    public static void waitForGrailsIntall() throws Exception {
        if (!waitForGrailsInstallHasFailed) {
            long time = System.currentTimeMillis();
            long endTime = time + 30000;
            while (System.currentTimeMillis() <= endTime) {
                GrailsInstallManager installMan = GrailsCoreActivator.getDefault().getInstallManager();
                IGrailsInstall install = installMan.getDefaultGrailsInstall();
                if (install != null) {
                    System.out.println("Found grails install: " + install.getHome());
                    return;
                }
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                }
                System.out.println("Waiting for Grails to be configured...");
            }
            waitForGrailsInstallHasFailed = true;
            ensureDefaultGrailsVersion(GrailsVersion.MOST_RECENT);
        }
        //throw new Error("No Grails install was found");
    }

    public GrailsTest(String name) {
        super(name);
    }

    public GrailsTest() {
        super();
    }

    public static byte[] getBytesFromFile(File file) throws IOException {
        InputStream is = new FileInputStream(file);

        // Get the size of the file
        long length = file.length();

        // You cannot create an array using a long type.
        // It needs to be an int type.
        // Before converting to an int type, check
        // to ensure that file is not larger than Integer.MAX_VALUE.
        if (length > Integer.MAX_VALUE) {
            // File is too large
        }

        // Create the byte array to hold the data
        byte[] bytes = new byte[(int) length];

        // Read in the bytes
        int offset = 0;
        int numRead = 0;
        while (offset < bytes.length && (numRead = is.read(bytes, offset, bytes.length - offset)) >= 0) {
            offset += numRead;
        }

        // Ensure all the bytes have been read in
        if (offset < bytes.length) {
            throw new IOException("Could not completely read file " + file.getName());
        }

        // Close the input stream and return bytes
        is.close();
        return bytes;
    }

    public static String getContents(IFile file) throws CoreException, IOException {
        InputStream is = file.getContents();
        BufferedReader br = new BufferedReader(new InputStreamReader(is));
        StringBuffer buffer = new StringBuffer();
        char[] readBuffer = new char[2048];
        int n = br.read(readBuffer);
        while (n > 0) {
            buffer.append(readBuffer, 0, n);
            n = br.read(readBuffer);
        }
        return buffer.toString();
    }

    /**
     * Set a bunch of preferences so that m2eclipse hopefully isn't doing a lot of time consuming stuff in the
     * background.
     */
    public static void mavenOffline() throws Error {
        System.out.println("Pacifying m2eclipse...");
        IEclipsePreferences m2EclipsePrefs = new InstanceScope().getNode("org.eclipse.m2e.core");
        m2EclipsePrefs.putBoolean("eclipse.m2.offline", true);
        m2EclipsePrefs.putBoolean("eclipse.m2.globalUpdatePolicy", false);
        m2EclipsePrefs.putBoolean("eclipse.m2.updateIndexes", false);
        try {
            m2EclipsePrefs.flush();
        } catch (BackingStoreException e) {
            throw new Error(e);
        }

        //      LegacyProjectChecker.NON_BLOCKING = true;
        System.out.println("Pacifying m2eclipse...DONE");
    }

}