/*==========================================================================*\
| $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.io.File;
import java.io.IOException;
import java.text.MessageFormat;
import java.util.Map;
import net.sf.webcat.eclipse.cxxtest.i18n.Messages;
import net.sf.webcat.eclipse.cxxtest.internal.MutableBoolean;
import net.sf.webcat.eclipse.cxxtest.internal.generator.TestCaseVisitor;
import net.sf.webcat.eclipse.cxxtest.internal.generator.TestRunnerGenerator;
import net.sf.webcat.eclipse.cxxtest.internal.generator.TestSuiteCollection;
import net.sf.webcat.eclipse.cxxtest.options.IExtraOptionsUpdater;
import org.eclipse.cdt.core.CCorePlugin;
import org.eclipse.cdt.core.model.ICProject;
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.IncrementalProjectBuilder;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.SubProgressMonitor;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.swt.widgets.Display;
//------------------------------------------------------------------------
/**
* This builder iterates through the classes in the C++ project, determines
* which ones represent CxxTest test suites, and generates the test runner
* source file.
*
* @author Tony Allevato (Virginia Tech Computer Science)
* @author Stephen Edwards (Virginia Tech Computer Science)
* @author latest changes by: $Author$
* @version $Revision$ $Date$
*/
public class CxxTestDriverBuilder extends IncrementalProjectBuilder
{
//~ Methods ...............................................................
// ----------------------------------------------------------
/**
* Called by the IDE when the project is built. This method traverses the
* project DOM, collects the test cases, and builds the test runner source
* file.
*/
@SuppressWarnings("rawtypes")
protected IProject[] build(int kind, Map args, IProgressMonitor monitor)
throws CoreException
{
final IProject project = getProject();
ICProject cproject = CCorePlugin.getDefault().getCoreModel().create(project);
// Check to see if the latest version of project options handlers
// currently loaded is more recent than the version of project options
// recorded in the project. If so, ask the user if the project should
// be upgraded.
final IExtraOptionsUpdater updater = CxxTestPlugin.getDefault().getExtraOptionsUpdater();
final MutableBoolean alreadyUpdated = new MutableBoolean();
if (updater.isUpdateNeeded(project))
{
Display.getDefault().syncExec(new Runnable() {
public void run()
{
boolean result = MessageDialog.openQuestion(null,
Messages.CxxTestDriverBuilder_UpgradeProjectSettingsTitle,
MessageFormat.format(
Messages.CxxTestDriverBuilder_UpgradeProjectSettingsMessage,
project.getName()));
if(result)
{
updater.updateOptions(project);
alreadyUpdated.setValue(true);
}
}
});
}
if(!checkForRebuild())
{
// We don't need to rebuild the test case runner, so bail out.
monitor.done();
return null;
}
monitor.beginTask(Messages.CxxTestDriverBuilder_GeneratingDriverTaskDescription, 3);
// Delete the existing test runner source file.
String outputFile = getDriverFileName();
IFile outputFileRsrc = project.getFile(outputFile);
outputFileRsrc.delete(true, monitor);
// Walk down the project DOM tree and find all the classes that
// are CxxTest test suites.
TestCaseVisitor visitor = new TestCaseVisitor(outputFile);
cproject.accept(visitor);
monitor.worked(1);
TestSuiteCollection suites = visitor.getSuites();
IPreferenceStore store = CxxTestPlugin.getDefault().getPreferenceStore();
try
{
// Generate the .cpp file that will run all the tests.
File projectDir = getProject().getLocation().toFile();
String fullPath = projectDir.toString() + "/" + outputFile; //$NON-NLS-1$
//IPath exePath = getExecutableFile().getProjectRelativePath();
boolean trackHeap = store.getBoolean(
CxxTestPlugin.CXXTEST_PREF_TRACK_HEAP);
boolean trapSignals = store.getBoolean(
CxxTestPlugin.CXXTEST_PREF_TRAP_SIGNALS);
boolean traceStack = store.getBoolean(
CxxTestPlugin.CXXTEST_PREF_TRACE_STACK);
IManagedBuildInfo buildInfo = ManagedBuildManager.getBuildInfo(project);
IConfiguration config = buildInfo.getDefaultConfiguration();
String[] extraIncludes = CxxTestPlugin.getDefault().
getExtraOptionsUpdater().getLatestCxxTestRunnerIncludes(config);
TestRunnerGenerator generator = new TestRunnerGenerator(
cproject, fullPath, suites, null);
generator.setTrackHeap(trackHeap);
generator.setTrapSignals(trapSignals);
generator.setTraceStack(traceStack);
generator.setPossibleTestFiles(
visitor.getSuites().getPossibleTestFiles());
generator.setExtraIncludes(extraIncludes);
generator.generate();
}
catch(IOException e)
{
setProblemMarker(ICxxTestConstants.MARKER_INVOCATION_PROBLEM,
MessageFormat.format(
Messages.CxxTestDriverBuilder_ErrorGeneratingDriver,
e.getMessage()));
}
monitor.worked(1);
// Refresh the project so it recognizes the new source file and
// rebuilds appropriately.
outputFileRsrc.refreshLocal(IResource.DEPTH_ZERO,
new SubProgressMonitor(monitor, 1));
monitor.done();
return null;
}
// ----------------------------------------------------------
@SuppressWarnings("unused")
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;
}
// ----------------------------------------------------------
public boolean checkForRebuild()
{
IProject project = getProject();
IResourceDelta delta = getDelta(project);
boolean changeRequiresRebuild = false;
// If the delta is null, the documentation says to assume
// "unspecified changes" have occurred and do something
// appropriate, so we'll go ahead and do a rebuild.
if(delta == null)
{
changeRequiresRebuild = true;
}
else
{
IManagedBuildInfo buildInfo = ManagedBuildManager
.getBuildInfo(project);
String[] configurations = buildInfo.getConfigurationNames();
IResourceDelta[] children = delta.getAffectedChildren();
for(int i = 0; i < children.length; i++)
{
// Now, search through the binary build dirs for a match
int j;
for (j = 0; j < configurations.length; j++)
{
IPath configPath = project.getFolder(configurations[j])
.getFullPath();
if (configPath.equals(children[i].getFullPath()))
break;
}
// Next, check for pesky stackdump files
IResource resource = children[i].getResource();
String fileExt = resource.getFileExtension();
boolean isStackDump = (fileExt != null && fileExt
.endsWith("stackdump")) //$NON-NLS-1$
|| (resource.getType() == IResource.FILE && resource
.getName().startsWith("core")); //$NON-NLS-1$
boolean notInConfigBuildDir = j == configurations.length;
// Finally, ignore dot-files.
boolean isDotFile = resource.getName().startsWith("."); //$NON-NLS-1$
// If the child isn't in any of the configuration-based
// binary build directories in the project and isn't a
// stack dump file ...
if(notInConfigBuildDir && !isStackDump && !isDotFile)
{
changeRequiresRebuild = true;
break;
}
}
}
return changeRequiresRebuild;
}
// ----------------------------------------------------------
/**
* Gets the name of the test runner source file as specified in the
* workbench preferences.
*
* @return the name of the test runner source file that will be generated.
*/
private String getDriverFileName()
{
IPreferenceStore store = CxxTestPlugin.getDefault().getPreferenceStore();
String outputFile = store.getString(
CxxTestPlugin.CXXTEST_PREF_DRIVER_FILENAME);
if (outputFile != null)
outputFile = outputFile.trim();
if (outputFile == null || outputFile.length() == 0)
outputFile = ICxxTestConstants.DEFAULT_DRIVER_FILENAME;
return outputFile;
}
// ----------------------------------------------------------
/**
* Creates a generic problem marker in the project with the specified
* id and message.
*
* @param id the id of the marker to create.
* @param message the message to create with the marker.
*
* @throws CoreException if creating the marker fails
*/
private void setProblemMarker(String id, String message)
throws CoreException
{
IMarker marker = getProject().createMarker(id);
marker.setAttribute(IMarker.MESSAGE, message);
marker.setAttribute(IMarker.SEVERITY, IMarker.SEVERITY_ERROR);
}
// ----------------------------------------------------------
/**
* Called when the user cleans the project. Here we want to delete any
* markers that may have been generated by the test runner.
*/
protected void clean(IProgressMonitor monitor) throws CoreException
{
super.clean(monitor);
}
}