/*******************************************************************************
* 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
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* VMWare, Inc. - initial API and implementation
*******************************************************************************/
package org.grails.ide.eclipse.test;
import static org.grails.ide.eclipse.core.launch.GrailsLaunchConfigurationDelegate.getScript;
import java.io.File;
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.NullProgressMonitor;
import org.eclipse.core.runtime.Path;
import org.eclipse.debug.core.ILaunch;
import org.eclipse.debug.core.ILaunchConfiguration;
import org.eclipse.debug.core.ILaunchManager;
import org.eclipse.jdt.internal.junit.model.JUnitModel;
import org.eclipse.jdt.internal.junit.model.TestCaseElement;
import org.eclipse.jdt.internal.junit.model.TestRoot;
import org.eclipse.jdt.internal.junit.model.TestRunSession;
import org.eclipse.jdt.internal.junit.model.TestSuiteElement;
import org.eclipse.jdt.junit.model.ITestCaseElement;
import org.eclipse.jdt.junit.model.ITestElement;
import org.eclipse.jdt.junit.model.ITestElementContainer;
import org.eclipse.jdt.junit.model.ITestRunSession;
import org.eclipse.jdt.junit.model.ITestSuiteElement;
import org.grails.ide.eclipse.core.model.GrailsVersion;
import org.grails.ide.eclipse.test.util.GrailsTest;
import org.grails.ide.eclipse.ui.internal.launch.GrailsTestLaunchShortcut;
import org.grails.ide.eclipse.ui.internal.launch.OpenInterestingNewResourceListener;
import org.springsource.ide.eclipse.commons.frameworks.core.ExceptionUtil;
import org.springsource.ide.eclipse.commons.frameworks.test.util.ACondition;
import org.springsource.ide.eclipse.commons.tests.util.StsTestUtil;
/**
* Tests for Eclipse / Junit integration features in the Grails tooling.
* @author Kris De Volder
* @author Andrew Eisenberg
* @created 2010-08-20
*/
public final class GrailsRunAsTestAppTests extends GrailsTest {
private static final String G_TUNES = "bTunes"; //Use a different name for this 'gTunes' app to avoid confusing grails
// with lingering state of the other gTunes test app
//This class made final because at the oment it uses static fields in a way that
// would break subclasses.
public static final long TIMEOUT_TEST_APP = 5 * 60 * 1000;
private static IProject project;
private static Throwable projectInitError = null;
GrailsTestLaunchShortcut shortCut = new GrailsTestLaunchShortcut();
@Override
public void setUp() throws Exception {
super.setUp();
if (projectInitError!=null) {
//Project setup failed in another test. No point continuing with broken project.
//So fail quickly with the same error.
throw ExceptionUtil.exception(projectInitError);
}
ensureDefaultGrailsVersion(GrailsVersion.MOST_RECENT);
OpenInterestingNewResourceListener.testMode(true);
StsTestUtil.setAutoBuilding(false);
setJava17Compliance();
if (project==null) {
try {
//Only first time when creating the project
project = ResourcesPlugin.getWorkspace().getRoot().getProject(G_TUNES);
if (project.exists()) {
project.delete(true, true, new NullProgressMonitor());
}
project = ensureProject(G_TUNES);
createResource(project, "grails-app/domain/"+G_TUNES+"/domain/Song.groovy",
"package "+G_TUNES+".domain\n" +
"\n" +
"class Song {\n" +
"\n" +
" static constraints = {\n" +
" title(blank:false)\n" +
" artist(blank:false)\n" +
" }\n" +
" \n" +
" String title\n" +
" String artist\n" +
" \n" +
"}\n");
createResource(project, "test/unit/"+G_TUNES+"/domain/SongTests.groovy",
"package "+G_TUNES+".domain\n" +
"\n" +
"import grails.test.*\n" +
"\n" +
"class SongTests extends GrailsUnitTestCase {\n" +
" protected void setUp() {\n" +
" super.setUp()\n" +
" }\n" +
"\n" +
" protected void tearDown() {\n" +
" super.tearDown()\n" +
" }\n" +
"\n" +
" void testSomething() {\n" +
" }\n" +
"}\n");
createResource(project, "test/integration/"+G_TUNES+"/SongITests.groovy",
"package "+G_TUNES+"\n" +
"\n" +
"import "+G_TUNES+".domain.Song;\n" +
"import grails.test.*\n" +
"\n" +
"class SongITests extends GrailsUnitTestCase {\n" +
" protected void setUp() {\n" +
" super.setUp()\n" +
" }\n" +
"\n" +
" protected void tearDown() {\n" +
" super.tearDown()\n" +
" }\n" +
"\n" +
" void testSomething() {\n" +
" Song song = new Song()\n" +
" song.title = \"foo\"\n" +
" song.artist = \"Kung\"\n" +
" if (song.validate()) {\n" +
" //OK\n" +
" }\n" +
" else {\n" +
" fail \"Validation should be ok!\"\n" +
" }\n" +
" }\n" +
" \n" +
"}\n");
StsTestUtil.assertNoErrors(project);
} catch (Throwable e) {
projectInitError = e;
throw ExceptionUtil.exception(e);
}
}
//Every test run:
deleteOldTestReports();
}
@Override
protected void tearDown() throws Exception {
OpenInterestingNewResourceListener.testMode(false);
super.tearDown();
}
private void deleteOldTestReports() throws CoreException {
IFolder testReports = getTestReportsFolder();
if (testReports.exists()) {
testReports.delete(true, new NullProgressMonitor());
}
}
private IFolder getTestReportsFolder() {
return project.getFolder(new Path("target/test-reports"));
}
private static abstract class TestVisitor {
protected abstract void doit(ITestElement e);
public void visit(ITestElement e) {
doit(e);
if (e instanceof TestSuiteElement) {
ITestElementContainer container = (ITestElementContainer) e;
ITestElement[] cs = container.getChildren();
if (cs!=null){
for (ITestElement c : cs) {
visit(c);
}
}
}
}
}
public static String getName(ITestElement e) {
if (e instanceof TestCaseElement) {
ITestCaseElement tce = (ITestCaseElement) e;
return tce.getTestClassName() + "."+tce.getTestMethodName();
} else if (e instanceof ITestSuiteElement) {
ITestSuiteElement se = (ITestSuiteElement) e;
return se.getSuiteTypeName();
} else if (e instanceof ITestRunSession) {
ITestRunSession trs = (ITestRunSession) e;
return trs.getTestRunName();
}
return null; //Don't know how to obtain a name for this kind of thing.
}
private void dump(TestRunSession testResult) {
new TestVisitor() {
protected void doit(ITestElement e) {
System.out.println("testNode: "+getName(e));
}
}.visit(testResult.getTestRoot());
}
private void assertNodeStartingWith(TestRunSession testResult, final String prefix) {
TestRoot root = testResult.getTestRoot();
final StringBuilder summary = new StringBuilder();
final boolean[] found = {false};
new TestVisitor() {
protected void doit(ITestElement e) {
String name = getName(e);
summary.append(name+"\n");
if (name.startsWith(prefix)) {
found[0] = true;
}
}
}.visit(root);
assertTrue(prefix + " not found in ... \n"+summary, found[0]);
}
static class MockTestOpener extends OpenInterestingNewResourceListener {
public MockTestOpener(IProject project) {
super(project);
}
TestRunSession getTestResults() throws CoreException {
if (interestingResource!=null) {
System.out.println("Most interesting report: "+interestingResource);
File testReport = interestingResource.getLocation().toFile();
return JUnitModel.importTestRunSession(testReport);
}
throw new Error("No test reports");
}
}
/**
* Loads the 'most interesting' new test report
* @return
* @throws Exception
*/
private TestRunSession loadTestResult() throws Exception {
IFolder reportsFolder = getTestReportsFolder();
reportsFolder.refreshLocal(IResource.DEPTH_INFINITE, new NullProgressMonitor());
MockTestOpener opener = new MockTestOpener(project);
IResource[] reports = reportsFolder.members();
for (IResource r : reports) {
System.out.println("Found test report: "+r);
opener.newResource(r);
}
return opener.getTestResults();
}
/**
* Test that does nothing, we run this test just to ensure that all the setup / scaffolding code works.
*/
public void testScaffolding() {
assertTrue(project.getFile("test/unit/"+G_TUNES+"/domain/SongTests.groovy").exists());
}
public void testTestAppOnProject() throws Exception {
IResource target = project;
ILaunchConfiguration launchConf = shortCut.findLaunchConfiguration(target);
assertEquals("test-app", getScript(launchConf));
TestRunSession testResult = run(launchConf);
assertEquals(2, testResult.getStartedCount());
assertEquals(0, testResult.getErrorCount());
assertEquals(0, testResult.getFailureCount());
assertNodeStartingWith(testResult, ""+G_TUNES+".SongITests");
assertNodeStartingWith(testResult, ""+G_TUNES+".domain.SongTests");
}
private TestRunSession run(ILaunchConfiguration launchConf)
throws CoreException, Exception {
final ILaunch launch = launchConf.launch(ILaunchManager.RUN_MODE, new NullProgressMonitor());
new ACondition("test process terminated") {
public boolean test() throws Exception {
return launch.isTerminated();
}
}.waitFor(TIMEOUT_TEST_APP);
TestRunSession testResult = loadTestResult();
dump(testResult); //So we can see something interesting in the output.
return testResult;
}
public void testTestAppOnIntegrationTestFolder() throws Exception {
IFolder target = project.getFolder(new Path("test/integration"));
ILaunchConfiguration launchConf = shortCut.findLaunchConfiguration(target);
assertEquals("test-app -integration", getScript(launchConf));
TestRunSession testResult = run(launchConf);
assertEquals(1, testResult.getStartedCount());
assertEquals(0, testResult.getErrorCount());
assertEquals(0, testResult.getFailureCount());
assertNodeStartingWith(testResult, ""+G_TUNES+".SongITests");
}
public void testTestAppOnUnitTestFolder() throws Exception {
IFolder target = project.getFolder(new Path("test/unit"));
ILaunchConfiguration launchConf = shortCut.findLaunchConfiguration(target);
assertEquals("test-app -unit", getScript(launchConf));
TestRunSession testResult = run(launchConf);
assertEquals(1, testResult.getStartedCount());
assertEquals(0, testResult.getFailureCount());
assertEquals(0, testResult.getErrorCount());
assertNodeStartingWith(testResult, ""+G_TUNES+".domain.SongTests");
}
public void testTestAppOnTestFile() throws Exception {
IFile target = project.getFile(new Path("test/integration/"+G_TUNES+"/SongITests.groovy"));
ILaunchConfiguration launchConf = shortCut.findLaunchConfiguration(target);
assertEquals("test-app -integration "+G_TUNES+".SongITests", getScript(launchConf));
TestRunSession testResult = run(launchConf);
assertEquals(1, testResult.getStartedCount());
assertEquals(0, testResult.getFailureCount());
assertEquals(0, testResult.getErrorCount());
assertNodeStartingWith(testResult, ""+G_TUNES+".SongITests");
}
public void testTestAppOnPackage() throws Exception {
IFolder target = project.getFolder(new Path("grails-app/domain/"+G_TUNES+"/domain"));
ILaunchConfiguration launchConf = shortCut.findLaunchConfiguration(target);
assertEquals("test-app "+G_TUNES+".domain.*", getScript(launchConf));
TestRunSession testResult = run(launchConf);
assertEquals(1, testResult.getStartedCount());
assertEquals(0, testResult.getFailureCount());
assertEquals(0, testResult.getErrorCount());
assertNodeStartingWith(testResult, ""+G_TUNES+".domain.SongTests");
}
public void testTestAppOnPackageInUnitTests() throws Exception {
IFolder target = project.getFolder(new Path("test/unit/"+G_TUNES+"/domain"));
ILaunchConfiguration launchConf = shortCut.findLaunchConfiguration(target);
assertEquals("test-app -unit "+G_TUNES+".domain.*", getScript(launchConf));
TestRunSession testResult = run(launchConf);
assertEquals(1, testResult.getStartedCount());
assertEquals(0, testResult.getFailureCount());
assertEquals(0, testResult.getErrorCount());
assertNodeStartingWith(testResult, ""+G_TUNES+".domain.SongTests");
}
public void testTestAppOnSourceFile() throws Exception {
IFile target = project.getFile(new Path("grails-app/domain/"+G_TUNES+"/domain/Song.groovy"));
ILaunchConfiguration launchConf = shortCut.findLaunchConfiguration(target);
assertEquals("test-app "+G_TUNES+".domain.Song", getScript(launchConf));
TestRunSession testResult = run(launchConf);
assertEquals(1, testResult.getStartedCount());
assertEquals(0, testResult.getFailureCount());
assertEquals(0, testResult.getErrorCount());
assertNodeStartingWith(testResult, ""+G_TUNES+".domain.SongTests");
}
}