/*==========================================================================*\
| $Id$
|*-------------------------------------------------------------------------*|
| Copyright (C) 2006-2009 Virginia Tech
|
| This file is part of Web-CAT Eclipse Plugins.
|
| Web-CAT is free software; you can redistribute it and/or modify
| it under the terms of the GNU General Public License as published by
| the Free Software Foundation; either version 2 of the License, or
| (at your option) any later version.
|
| Web-CAT is distributed in the hope that it will be useful,
| but WITHOUT ANY WARRANTY; without even the implied warranty of
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
| GNU General Public License for more details.
|
| You should have received a copy of the GNU General Public License
| along with Web-CAT; if not, see <http://www.gnu.org/licenses/>.
\*==========================================================================*/
package net.sf.webcat.eclipse.cxxtest;
import java.util.Map;
import net.sf.webcat.eclipse.cxxtest.i18n.Messages;
import net.sf.webcat.eclipse.cxxtest.ui.TestRunnerViewPart;
import org.eclipse.cdt.core.CCorePlugin;
import org.eclipse.cdt.core.model.ICProject;
import org.eclipse.cdt.debug.core.ICDTLaunchConfigurationConstants;
import org.eclipse.cdt.managedbuilder.core.IConfiguration;
import org.eclipse.cdt.managedbuilder.core.IManagedBuildInfo;
import org.eclipse.cdt.managedbuilder.core.ManagedBuildManager;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IMarker;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IResourceDelta;
import org.eclipse.core.resources.IResourceDeltaVisitor;
import org.eclipse.core.resources.IWorkspace;
import org.eclipse.core.resources.IWorkspaceRunnable;
import org.eclipse.core.resources.IncrementalProjectBuilder;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.debug.core.DebugPlugin;
import org.eclipse.debug.core.ILaunch;
import org.eclipse.debug.core.ILaunchConfigurationType;
import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy;
import org.eclipse.debug.core.ILaunchManager;
import org.eclipse.debug.ui.IDebugUIConstants;
import org.eclipse.swt.widgets.Display;
//------------------------------------------------------------------------
/**
* This builder executes the generates test-case executable and adds problem
* markers to the source file for any failed test cases. It also populates the
* CxxTest view with information about the executed tests.
*
* @author Tony Allevato (Virginia Tech Computer Science)
* @author latest changes by: $Author$
* @version $Revision$ $Date$
*/
public class CxxTestDriverRunner extends IncrementalProjectBuilder
{
private class RunnerLaunchThread extends Thread
{
private IProject project;
public RunnerLaunchThread(IProject project)
{
this.project = project;
start();
}
public void run()
{
try
{
final ICProject cproject =
CCorePlugin.getDefault().getCoreModel().create(project);
IPath exePath = getExecutableFile().getProjectRelativePath();
ILaunchManager launchManager = DebugPlugin.getDefault().getLaunchManager();
ILaunchConfigurationType type = launchManager.getLaunchConfigurationType(
ICDTLaunchConfigurationConstants.ID_LAUNCH_C_APP);
final ILaunchConfigurationWorkingCopy config = type.newInstance(null, "CxxTestRunner"); //$NON-NLS-1$
config.setAttribute(IDebugUIConstants.ATTR_PRIVATE, true);
config.setAttribute(IDebugUIConstants.ATTR_LAUNCH_IN_BACKGROUND, false);
config.setAttribute(ICDTLaunchConfigurationConstants.ATTR_PROJECT_NAME,
project.getName());
config.setAttribute(ICDTLaunchConfigurationConstants.ATTR_PROGRAM_NAME,
exePath.toString());
IWorkspaceRunnable runnable = new IWorkspaceRunnable() {
public void run(IProgressMonitor monitor) throws CoreException {
final ILaunch launch = config.launch(ILaunchManager.RUN_MODE, null);
Display.getDefault().syncExec(new Runnable() {
public void run()
{
TestRunnerViewPart runnerPart =
CxxTestPlugin.getDefault().getTestRunnerView();
runnerPart.testRunStarted(cproject, launch);
}
});
// Wait for the launched process to complete.
waitForProcess(launch);
// Parse the CxxTest results file and enter the results in the
// CxxTest view.
Display.getDefault().syncExec(new Runnable() {
public void run()
{
TestRunnerViewPart runnerPart =
CxxTestPlugin.getDefault().getTestRunnerView();
runnerPart.testRunEnded();
}
});
}
};
project.getWorkspace().run(runnable, project, IWorkspace.AVOID_UPDATE, null);
}
catch(CoreException e)
{
}
}
private void waitForProcess(ILaunch launch)
{
try
{
while(!launch.isTerminated())
Thread.sleep(250);
}
catch(InterruptedException e) { }
}
}
private class ExecutableChangedVisitor implements IResourceDeltaVisitor
{
private boolean exeChanged;
public ExecutableChangedVisitor()
{
exeChanged = false;
}
public boolean visit(IResourceDelta delta) throws CoreException
{
if(delta.getResource().equals(getExecutableFile()))
exeChanged = true;
return true;
}
public boolean isExecutableChanged()
{
return exeChanged;
}
}
private IFile getExecutableFile()
{
IProject project = getProject();
IManagedBuildInfo buildInfo =
ManagedBuildManager.getBuildInfo(project);
IConfiguration configuration = buildInfo.getDefaultConfiguration();
String exeName = buildInfo.getBuildArtifactName();
String exeExtension = buildInfo.getBuildArtifactExtension();
if(exeExtension.length() > 0)
exeName += "." + exeExtension; //$NON-NLS-1$
IFile file = project.getFile(configuration.getName() + "/" + exeName); //$NON-NLS-1$
return file;
}
/* (non-Javadoc)
* @see org.eclipse.core.internal.events.InternalBuilder#build(int, java.util.Map, org.eclipse.core.runtime.IProgressMonitor)
*/
@SuppressWarnings("rawtypes")
protected IProject[] build(int kind, Map args, IProgressMonitor monitor)
throws CoreException
{
IProject project = getProject();
// Since the test runner generates files in the project directory,
// this will cause Eclipse to try to rebuild the project, and this
// builder will execute again. To prevent an infinite loop, we only
// run the test runner when the executable has changed (and so is
// found in the resource delta).
IResourceDelta delta = getDelta(project);
ExecutableChangedVisitor visitor = new ExecutableChangedVisitor();
if(delta != null)
{
delta.accept(visitor);
if(!visitor.isExecutableChanged())
{
// We don't need to rebuild the test case runner, so bail out.
monitor.done();
return null;
}
}
else
{
monitor.done();
return null;
}
monitor.beginTask(Messages.CxxTestDriverRunner_RunningDriverTaskDescription, 1);
deleteMarkers();
// If there are any problems in the project after it has been
// built, we probably shouldn't try to run the driver code.
// UPDATE: We need to be more specific here, even warnings were
// preventing the runner from executing.
IMarker[] problems = project.findMarkers(IMarker.PROBLEM, true,
IResource.DEPTH_INFINITE);
if(problems.length > 0)
{
for(int i = 0; i < problems.length; i++)
{
if(problems[i].getAttribute(IMarker.SEVERITY,
IMarker.SEVERITY_INFO) == IMarker.SEVERITY_ERROR)
{
monitor.done();
return null;
}
}
}
monitor.worked(1);
new RunnerLaunchThread(project);
monitor.done();
forgetLastBuiltState();
return null;
}
protected void clean(IProgressMonitor monitor) throws CoreException
{
super.clean(monitor);
deleteMarkers();
}
/**
* Delete any markers that were generated by the test runner.
*
* @throws CoreException if a problem occurs when deleting the markers.
*/
private void deleteMarkers() throws CoreException
{
final IProject project = getProject();
IWorkspaceRunnable runnable = new IWorkspaceRunnable() {
public void run(IProgressMonitor monitor)
{
try
{
project.deleteMarkers(ICxxTestConstants.MARKER_INVOCATION_PROBLEM,
true, IResource.DEPTH_INFINITE);
project.deleteMarkers(ICxxTestConstants.MARKER_FAILED_TEST, true,
IResource.DEPTH_INFINITE);
}
catch (CoreException e) { }
}
};
project.getWorkspace().run(runnable, new NullProgressMonitor());
}
}