1
0
Fork 0
mirror of https://github.com/eclipse-cdt/cdt synced 2025-07-24 01:15:29 +02:00

Bug 303953 - Deleted linked resource in managed project is still being built.

- Ensure that subdir.mk is re-generated when last file has been removed from a directory + test
  - Add scaffolding for managedbuilder.core regression tests
This commit is contained in:
James Blackburn 2011-01-24 16:07:59 +00:00
parent 834cc7b829
commit 094a51f44a
8 changed files with 1276 additions and 21 deletions

View file

@ -0,0 +1,4 @@
helloworldC.zip:
Basic standard Linux GCC C HelloWorld ManagedBuild Project template.
Contains Debug + Release configurations.

View file

@ -0,0 +1,273 @@
/*******************************************************************************
* Copyright (c) 2011 Broadcom Corporation and others.
* 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:
* Broadcom Corporation - Initial API and implementation
*******************************************************************************/
package org.eclipse.cdt.managedbuilder.testplugin;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.LinkedHashSet;
import java.util.List;
import junit.framework.TestCase;
import org.eclipse.cdt.core.CCorePlugin;
import org.eclipse.cdt.core.model.CoreModel;
import org.eclipse.cdt.core.model.ICModelMarker;
import org.eclipse.cdt.core.settings.model.ICConfigurationDescription;
import org.eclipse.cdt.core.settings.model.ICProjectDescription;
import org.eclipse.cdt.core.settings.model.ICProjectDescriptionManager;
import org.eclipse.core.resources.IBuildConfiguration;
import org.eclipse.core.resources.IFolder;
import org.eclipse.core.resources.IMarker;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IWorkspace;
import org.eclipse.core.resources.IWorkspaceDescription;
import org.eclipse.core.resources.IWorkspaceRunnable;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.jobs.Job;
/**
* Abstract builder test which provides utility methods for:
* <ul>
* <li>Importing projects into the workspace</li>
* <li>Adding expected resources to the delta verifier</li>
* <li>Verifying the delta</li>
* <li>Printing markers</li>
* <li>Cleaning up the workspace at the end</li>
* </ul>
*/
public abstract class AbstractBuilderTest extends TestCase {
static final String PATH = "builderTests";
private String workspace;
private List<IProject> projects;
@Override
protected void setUp() throws Exception {
super.setUp();
setAutoBuilding(false);
}
@Override
protected void tearDown() throws Exception {
super.tearDown();
// Bug 327126 Stop the indexer before tearing down so we don't deadlock
Job.getJobManager().cancel(CCorePlugin.getPDOMManager());
Job.getJobManager().join(CCorePlugin.getPDOMManager(), null);
// Clean-up any projects we were using
for (IProject project : projects) {
project.delete(true, null);
}
projects.clear();
}
/**
* Run a build of the specified kind, and verify that the resource changes that
* result match the expectations of the given verifier.
*/
protected void verifyBuild(final IProject project, final int kind, ResourceDeltaVerifier verifier) throws CoreException {
verifyBuild(new IBuildConfiguration[]{project.getActiveBuildConfig()}, kind, verifier);
}
/**
* Build the specified configurations, and verify that the resource changes that
* result match the expectations of the given verifier.
*/
protected void verifyBuild(final IBuildConfiguration[] configs, final int kind, ResourceDeltaVerifier verifier) throws CoreException {
getWorkspace().getRoot().refreshLocal(IResource.DEPTH_INFINITE, null);
getWorkspace().addResourceChangeListener(verifier);
try {
// batch changes
IWorkspaceRunnable body = new IWorkspaceRunnable() {
public void run(IProgressMonitor monitor) throws CoreException {
getWorkspace().build(configs, kind, true, monitor);
}
};
getWorkspace().run(body, null);
} finally {
assertTrue(verifier.getMessage(), verifier.isDeltaValid());
getWorkspace().removeResourceChangeListener(verifier);
printAllMarkers();
}
}
/**
* Set the active configuration of a project by configuration name
*/
protected void setActiveConfigurationByName(IProject project, String cfgName) throws CoreException {
ICProjectDescriptionManager mngr = CoreModel.getDefault().getProjectDescriptionManager();
ICProjectDescription desc = mngr.getProjectDescription(project, true);
ICConfigurationDescription cfg = desc.getConfigurationByName(cfgName);
assertNotNull(cfg);
desc.setActiveConfiguration(cfg);
mngr.setProjectDescription(project, desc);
// FIXME: enable when cdt.core knows about core.resources build configurations
// assertTrue(project.getActiveBuildConfig().getName().equals(cfg.getName()));
}
protected Collection<IResource> getProjectBuildExeResources(String projectName, String cfgName, String obj) throws CoreException {
return getProjectBuildExeResources(projectName, cfgName, new String[]{obj});
}
protected Collection<IResource> getProjectBuildLibResources(String projectName, String cfgName, String obj) throws CoreException {
return getProjectBuildLibResources(projectName, cfgName, new String[]{obj});
}
protected Collection<IResource> getProjectBuildSharedLibResources(String projectName, String cfgName, String obj) throws CoreException {
return getProjectBuildSharedLibResources(projectName, cfgName, new String[]{obj});
}
protected Collection<IResource> getProjectBuildExeResources(String projectName, String cfgName, String[] objs) throws CoreException {
Collection<IResource> resources = getProjectBuildResources(projectName, cfgName, objs);
IProject project = getWorkspace().getRoot().getProject(projectName);
IFolder buildDir = project.getFolder(cfgName);
resources.add(buildDir.getFile(projectName));
return resources;
}
protected Collection<IResource> getProjectBuildLibResources(String projectName, String cfgName, String[] objs) throws CoreException {
Collection<IResource> resources = getProjectBuildResources(projectName, cfgName, objs);
IProject project = getWorkspace().getRoot().getProject(projectName);
IFolder buildDir = project.getFolder(cfgName);
resources.add(buildDir.getFile("lib" + projectName + ".a"));
return resources;
}
protected Collection<IResource> getProjectBuildSharedLibResources(String projectName, String cfgName, String[] objs) throws CoreException {
Collection<IResource> resources = getProjectBuildResources(projectName, cfgName, objs);
IProject project = getWorkspace().getRoot().getProject(projectName);
IFolder buildDir = project.getFolder(cfgName);
resources.add(buildDir.getFile("lib" + projectName + ".so"));
return resources;
}
/**
* Returns an array of resources expected to be generated by building a project configuration.
* The object files expected to be output can also be specified.
*/
protected Collection<IResource> getProjectBuildResources(String projectName, String cfgName, String[] objs) throws CoreException {
IProject project = getWorkspace().getRoot().getProject(projectName);
IFolder buildDir = project.getFolder(cfgName);
Collection<IResource> resources = new LinkedHashSet<IResource>();
resources.add(buildDir);
resources.add(buildDir.getFile("makefile"));
resources.add(buildDir.getFile("objects.mk"));
resources.add(buildDir.getFile("sources.mk"));
for (String obj : objs) {
resources.add(buildDir.getFile(obj + ".d"));
resources.add(buildDir.getFile(obj + ".o"));
// Add subdir.mk in the same directory
resources.add(buildDir.getFile(new Path(obj).removeLastSegments(1).append("subdir.mk")));
// If the parent of the obj doesn't exist, then ensure we're expecting that too...
IPath p = new Path(obj).removeLastSegments(1);
while (p.segmentCount() > 0) {
IFolder folder = buildDir.getFolder(p);
resources.add(folder);
p = p.removeLastSegments(1);
}
}
return resources;
}
public AbstractBuilderTest() {
super();
}
public AbstractBuilderTest(String name) {
super(name);
}
protected void setWorkspace(String name) {
workspace = name;
projects = new ArrayList<IProject>();
}
protected IProject loadProject(String name) throws CoreException {
ManagedBuildTestHelper.loadProject(name, PATH + "/" + workspace);
IProject project = getWorkspace().getRoot().getProject(name);
assertTrue(project.exists());
projects.add(project);
project.refreshLocal(IResource.DEPTH_INFINITE, null);
return project;
}
private List<IMarker> getAllMarkers() throws CoreException {
List<IMarker> markers = new ArrayList<IMarker>();
for (IProject project : projects)
markers.addAll(Arrays.asList(project.findMarkers(ICModelMarker.C_MODEL_PROBLEM_MARKER, true, IResource.DEPTH_INFINITE)));
return markers;
}
protected void printAllMarkers() throws CoreException {
List<IMarker> markers = getAllMarkers();
String[] attributes = new String[] {IMarker.LINE_NUMBER, IMarker.SEVERITY, IMarker.MESSAGE, IMarker.LOCATION /*, ICModelMarker.C_MODEL_MARKER_CONFIGURATION_NAME*/};
StringBuilder sb = new StringBuilder();
for (IMarker m : markers) {
// Project
if (m.getResource().getProject() == null)
sb.append("?");
else
sb.append(m.getResource().getProject().getName());
// Resource workspace path
sb.append(", "); //$NON-NLS-1$
sb.append(m.getResource().getFullPath());
Object[] attrs = m.getAttributes(attributes);
sb.append(":"); //$NON-NLS-1$
int i = 0;
// line number
if (attrs[i] != null)
sb.append(" line " + attrs[i]);
// severity
if (attrs[++i] != null) {
switch ((Integer)attrs[i++]) {
case IMarker.SEVERITY_ERROR:
sb.append(" ERROR");
break;
case IMarker.SEVERITY_WARNING:
sb.append(" WARNING");
break;
case IMarker.SEVERITY_INFO:
sb.append(" INFO");
break;
}
}
// append the rest of the string fields
do {
if (attrs[i] != null)
sb.append(" " + attrs[i]);
} while (++i < attrs.length);
// Finally print the string
System.err.println(sb.toString());
sb.setLength(0);
}
}
protected void setAutoBuilding(boolean value) throws CoreException {
IWorkspace workspace = getWorkspace();
if (workspace.isAutoBuilding() == value)
return;
IWorkspaceDescription desc = workspace.getDescription();
desc.setAutoBuilding(value);
workspace.setDescription(desc);
}
protected IWorkspace getWorkspace() throws CoreException {
return ResourcesPlugin.getWorkspace();
}
}

View file

@ -0,0 +1,850 @@
/*******************************************************************************
* Copyright (c) 2000, 2011 IBM Corporation and others.
* 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:
* IBM Corporation - initial API and implementation
* Alex Collins (Broadcom Corp.)
*******************************************************************************/
package org.eclipse.cdt.managedbuilder.testplugin;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.Set;
import junit.framework.Assert;
import org.eclipse.core.resources.IContainer;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IResourceChangeEvent;
import org.eclipse.core.resources.IResourceChangeListener;
import org.eclipse.core.resources.IResourceDelta;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.Path;
/**
* Based on org.eclipse.core.tests.resources.ResourceDeltaVerifier with
* additional support for ignoring changes in certain resources.
*
* Verifies the state of an <code>IResourceDelta</code> by comparing
* it with a client's expectations. The delta is considered valid
* if it contains exactly the set of changes expected by the client,
* and parents of those changes (having ignore filtered resources).
*
* <p>Example usage:
* <code>
* ResourceDeltaVerifier verifier = new ResourceDeltaVerifier();
* IResourceChangeListener listener = (IResourceChangeListener)verifier;
* IWorkspace workspace = ResourcesPlugin.getWorkspace();
* IProject proj = workspace.getRoot().getProject("MyProject");
* // Assume the project is accessible
* workspace.addResourceChangeListener(listener);
* verifier.addExpectedChange(proj, REMOVED, 0);
* try {
* proj.delete(true, true, null);
* } catch(CoreException e){
* fail("1.0", e);
* }
* assert("2.0 "+verifier.getMessage(), verifier.isDeltaValid());
* </code>
*/
public class ResourceDeltaVerifier extends Assert implements IResourceChangeListener {
private class ExpectedChange {
IResource fResource;
IPath movedFromPath;
IPath movedToPath;
int fKind;
int fChangeFlags;
public ExpectedChange(IResource resource, int kind, int changeFlags, IPath movedFromPath, IPath movedToPath) {
fResource = resource;
fKind = kind;
fChangeFlags = changeFlags;
this.movedFromPath = movedFromPath;
this.movedToPath = movedToPath;
}
public int getChangeFlags() {
return fChangeFlags;
}
public IPath getMovedFromPath() {
if ((fChangeFlags & IResourceDelta.MOVED_FROM) != 0) {
return movedFromPath;
}
return null;
}
public IPath getMovedToPath() {
if ((fChangeFlags & IResourceDelta.MOVED_TO) != 0) {
return movedToPath;
}
return null;
}
public int getKind() {
return fKind;
}
@Override
public String toString() {
StringBuffer buf = new StringBuffer("ExpectedChange(");
buf.append(fResource);
buf.append(", ");
buf.append(convertKind(fKind));
buf.append(", ");
buf.append(convertChangeFlags(fChangeFlags));
buf.append(")");
return buf.toString();
}
}
/**
* Table of IPath -> ExpectedChange
*/
private Hashtable fExpectedChanges = new Hashtable();
boolean fIsDeltaValid = true;
private StringBuffer fMessage = new StringBuffer();
/**
* The verifier can be in one of three states. In the initial
* state, the verifier is still receiving inputs via the
* addExpectedChange() methods, and the state is RECEIVING_INPUTS.
* After a call to verifyDelta(), the state becomes DELTA_VERIFIED
* The verifier remains in the second state for any number of delta
* verifications. When a getMessage() or isDeltaValid() method is
* called, the verification completes, and the state becomes
* VERIFICATION_COMPLETE. While in this state, any number of
* getMessage() and isDeltaValid() methods can be called.
* While in the third state, any call to addExpectedChange()
* resets the verifier and puts it back in its RECEIVING_INPUTS state.
*/
private static final int RECEIVING_INPUTS = 0;
private static final int DELTA_VERIFIED = 1;
private static final int VERIFICATION_COMPLETE = 2;
private int fState = RECEIVING_INPUTS;
private Set<IResource> fIgnoreResources = new HashSet<IResource>();
/**
* @see #addExpectedChange
*/
public void addExpectedChange(IResource[] resources, int status, int changeFlags) {
for (int i = 0; i < resources.length; i++)
addExpectedChange(resources[i], null, status, changeFlags, null, null);
}
/**
* Adds an expected deletion for the given resource and all children.
*/
public void addExpectedDeletion(IResource resource) {
addExpectedChange(resource, IResourceDelta.REMOVED, 0);
if (resource instanceof IContainer) {
try {
IResource[] children = ((IContainer) resource).members(IContainer.INCLUDE_PHANTOMS | IContainer.INCLUDE_TEAM_PRIVATE_MEMBERS | IContainer.INCLUDE_HIDDEN);
for (int i = 0; i < children.length; i++) {
addExpectedDeletion(children[i]);
}
} catch (CoreException e) {
e.printStackTrace();
fail("Failed to get children in addExpectedDeletion");
}
}
}
/**
* Signals to the comparer that the given resource is expected to
* change in the specified way. The change flags should be set to
* zero if no change is expected.
* @param resource the resource that is expected to change
* @param status the type of change (ADDED, REMOVED, CHANGED)
* @param changeFlags the type of change (CONTENT, SYNC, etc)
* @see IResourceConstants
*/
public void addExpectedChange(IResource resource, int status, int changeFlags) {
addExpectedChange(resource, null, status, changeFlags, null, null);
}
/**
* Signals to the comparer that the given resource is expected to
* change in the specified way. The change flags should be set to
* zero if no change is expected.
* @param resource the resource that is expected to change
* @param status the type of change (ADDED, REMOVED, CHANGED)
* @param changeFlags the type of change (CONTENT, SYNC, etc)
* @param movedPath or null
* @see IResourceConstants
*/
public void addExpectedChange(IResource resource, int status, int changeFlags, IPath movedFromPath, IPath movedToPath) {
addExpectedChange(resource, null, status, changeFlags, movedFromPath, movedToPath);
}
/**
* Signals to the comparer that the given resource is expected to
* change in the specified way. The change flags should be set to
* zero if no change is expected.
* @param resource the resource that is expected to change
* @param topLevelParent Do not added expected changes above this parent
* @param status the type of change (ADDED, REMOVED, CHANGED)
* @param changeFlags the type of change (CONTENT, SYNC, etc)
* @param movedPath or null
* @see IResourceConstants
*/
public void addExpectedChange(IResource resource, IResource topLevelParent, int status, int changeFlags) {
addExpectedChange(resource, topLevelParent, status, changeFlags, null, null);
}
/**
* Signals to the comparer that the given resource is expected to
* change in the specified way. The change flags should be set to
* zero if no change is expected.
* @param resource the resource that is expected to change
* @param topLevelParent Do not added expected changes above this parent
* @param status the type of change (ADDED, REMOVED, CHANGED)
* @param changeFlags the type of change (CONTENT, SYNC, etc)
* @param movedPath or null
* @see IResourceConstants
*/
public void addExpectedChange(IResource resource, IResource topLevelParent, int status, int changeFlags, IPath movedFromPath, IPath movedToPath) {
resetIfNecessary();
ExpectedChange expectedChange = new ExpectedChange(resource, status, changeFlags, movedFromPath, movedToPath);
fExpectedChanges.put(resource.getFullPath(), expectedChange);
// Add changes for all resources above this one and limited by the topLevelParent
IResource parentResource = resource.getParent();
IResource limit = (topLevelParent == null) ? null : topLevelParent.getParent();
while (parentResource != null && !parentResource.equals(limit)) {
//change table is keyed by resource path
IPath key = parentResource.getFullPath();
if (fExpectedChanges.get(key) == null) {
ExpectedChange parentExpectedChange = new ExpectedChange(parentResource, IResourceDelta.CHANGED, 0, null, null);
fExpectedChanges.put(key, parentExpectedChange);
}
parentResource = parentResource.getParent();
}
}
private void checkChanges(IResourceDelta delta) {
IResource resource = delta.getResource();
ExpectedChange expectedChange = (ExpectedChange) fExpectedChanges.remove(resource.getFullPath());
int status = delta.getKind();
int changeFlags = delta.getFlags();
if (status == IResourceDelta.NO_CHANGE)
return;
if (expectedChange == null) {
recordMissingExpectedChange(status, changeFlags);
} else {
int expectedStatus = expectedChange.getKind();
int expectedChangeFlags = expectedChange.getChangeFlags();
if (status != expectedStatus || changeFlags != expectedChangeFlags) {
recordConflictingChange(expectedStatus, status, expectedChangeFlags, changeFlags);
}
}
}
private void checkChildren(IResourceDelta delta) {
IResourceDelta[] affectedChildren = delta.getAffectedChildren(IResourceDelta.ALL_WITH_PHANTOMS, IContainer.INCLUDE_TEAM_PRIVATE_MEMBERS | IContainer.INCLUDE_HIDDEN);
IResourceDelta[] addedChildren = delta.getAffectedChildren(IResourceDelta.ADDED, IContainer.INCLUDE_TEAM_PRIVATE_MEMBERS | IContainer.INCLUDE_HIDDEN);
IResourceDelta[] changedChildren = delta.getAffectedChildren(IResourceDelta.CHANGED, IContainer.INCLUDE_TEAM_PRIVATE_MEMBERS | IContainer.INCLUDE_HIDDEN);
IResourceDelta[] removedChildren = delta.getAffectedChildren(IResourceDelta.REMOVED, IContainer.INCLUDE_TEAM_PRIVATE_MEMBERS | IContainer.INCLUDE_HIDDEN);
Hashtable h = new Hashtable(affectedChildren.length + 1);
for (int i = 0; i < addedChildren.length; ++i) {
IResourceDelta childDelta1 = addedChildren[i];
IResource childResource = childDelta1.getResource();
IResourceDelta childDelta2 = (IResourceDelta) h.get(childResource);
if (childDelta2 != null) {
recordDuplicateChild(childResource.getFullPath(), childDelta2.getKind(), childDelta1.getKind(), IResourceDelta.ADDED);
} else {
h.put(childResource, childDelta1);
}
if (childDelta1.getKind() != IResourceDelta.ADDED) {
recordIllegalChild(childResource.getFullPath(), IResourceDelta.ADDED, childDelta1.getKind());
}
}
for (int i = 0; i < changedChildren.length; ++i) {
IResourceDelta childDelta1 = changedChildren[i];
IResource childResource = childDelta1.getResource();
IResourceDelta childDelta2 = (IResourceDelta) h.get(childResource);
if (childDelta2 != null) {
recordDuplicateChild(childResource.getFullPath(), childDelta2.getKind(), childDelta1.getKind(), IResourceDelta.CHANGED);
} else {
h.put(childResource, childDelta1);
}
if (childDelta1.getKind() != IResourceDelta.CHANGED) {
recordIllegalChild(childResource.getFullPath(), IResourceDelta.CHANGED, childDelta1.getKind());
}
}
for (int i = 0; i < removedChildren.length; ++i) {
IResourceDelta childDelta1 = removedChildren[i];
IResource childResource = childDelta1.getResource();
IResourceDelta childDelta2 = (IResourceDelta) h.get(childResource);
if (childDelta2 != null) {
recordDuplicateChild(childResource.getFullPath(), childDelta2.getKind(), childDelta1.getKind(), IResourceDelta.REMOVED);
} else {
h.put(childResource, childDelta1);
}
if (childDelta1.getKind() != IResourceDelta.REMOVED) {
recordIllegalChild(childResource.getFullPath(), IResourceDelta.REMOVED, childDelta1.getKind());
}
}
for (int i = 0; i < affectedChildren.length; ++i) {
IResourceDelta childDelta1 = affectedChildren[i];
IResource childResource = childDelta1.getResource();
IResourceDelta childDelta2 = (IResourceDelta) h.remove(childResource);
if (childDelta2 == null) {
int kind = childDelta1.getKind();
//these kinds should have been added to h earlier
if (kind == IResourceDelta.ADDED || kind == IResourceDelta.REMOVED || kind == IResourceDelta.CHANGED) {
recordMissingChild(childResource.getFullPath(), childDelta1.getKind(), false);
}
}
}
Enumeration keys = h.keys();
while (keys.hasMoreElements()) {
IResource childResource = (IResource) keys.nextElement();
IResourceDelta childDelta = (IResourceDelta) h.get(childResource);
recordMissingChild(childResource.getFullPath(), childDelta.getKind(), true);
}
for (int i = 0; i < affectedChildren.length; ++i) {
internalVerifyDelta(affectedChildren[i]);
}
keys = h.keys();
while (keys.hasMoreElements()) {
IResource childResource = (IResource) keys.nextElement();
IResourceDelta childDelta = (IResourceDelta) h.get(childResource);
internalVerifyDelta(childDelta);
}
}
private void checkPaths(IResourceDelta delta) {
IResource resource = delta.getResource();
IPath expectedFullPath = resource.getFullPath();
IPath actualFullPath = delta.getFullPath();
if (!expectedFullPath.equals(actualFullPath)) {
recordConflictingFullPaths(expectedFullPath, actualFullPath);
}
IPath expectedProjectRelativePath = resource.getProjectRelativePath();
IPath actualProjectRelativePath = delta.getProjectRelativePath();
if (expectedProjectRelativePath != actualProjectRelativePath) {
if (expectedProjectRelativePath == null || !expectedProjectRelativePath.equals(actualProjectRelativePath)) {
recordConflictingProjectRelativePaths(expectedProjectRelativePath, actualProjectRelativePath);
}
}
ExpectedChange expectedChange = (ExpectedChange) fExpectedChanges.get(resource.getFullPath());
if (expectedChange != null) {
IPath expectedMovedFromPath = expectedChange.getMovedFromPath();
IPath actualMovedFromPath = delta.getMovedFromPath();
if (expectedMovedFromPath != actualMovedFromPath) {
if (expectedMovedFromPath == null || !expectedMovedFromPath.equals(actualMovedFromPath)) {
recordConflictingMovedFromPaths(expectedMovedFromPath, actualMovedFromPath);
}
}
IPath expectedMovedToPath = expectedChange.getMovedToPath();
IPath actualMovedToPath = delta.getMovedToPath();
if (expectedMovedToPath != actualMovedToPath) {
if (expectedMovedToPath == null || !expectedMovedToPath.equals(actualMovedToPath)) {
recordConflictingMovedToPaths(expectedMovedToPath, actualMovedToPath);
}
}
}
}
String convertChangeFlags(int changeFlags) {
if (changeFlags == 0) {
return "0";
}
StringBuffer buf = new StringBuffer();
if ((changeFlags & IResourceDelta.CONTENT) != 0) {
changeFlags ^= IResourceDelta.CONTENT;
buf.append("CONTENT | ");
}
if ((changeFlags & IResourceDelta.MOVED_FROM) != 0) {
changeFlags ^= IResourceDelta.MOVED_FROM;
buf.append("MOVED_FROM | ");
}
if ((changeFlags & IResourceDelta.MOVED_TO) != 0) {
changeFlags ^= IResourceDelta.MOVED_TO;
buf.append("MOVED_TO | ");
}
if ((changeFlags & IResourceDelta.OPEN) != 0) {
changeFlags ^= IResourceDelta.OPEN;
buf.append("OPEN | ");
}
if ((changeFlags & IResourceDelta.TYPE) != 0) {
changeFlags ^= IResourceDelta.TYPE;
buf.append("TYPE | ");
}
if ((changeFlags & IResourceDelta.MARKERS) != 0) {
changeFlags ^= IResourceDelta.MARKERS;
buf.append("MARKERS | ");
}
if ((changeFlags & IResourceDelta.REPLACED) != 0) {
changeFlags ^= IResourceDelta.REPLACED;
buf.append("REPLACED | ");
}
if ((changeFlags & IResourceDelta.ENCODING) != 0) {
changeFlags ^= IResourceDelta.ENCODING;
buf.append("ENCODING | ");
}
if ((changeFlags & IResourceDelta.DERIVED_CHANGED) != 0) {
changeFlags ^= IResourceDelta.DERIVED_CHANGED;
buf.append("DERIVED_CHANGED | ");
}
if ((changeFlags & IResourceDelta.DESCRIPTION) != 0) {
changeFlags ^= IResourceDelta.DESCRIPTION;
buf.append("DESCRIPTION | ");
}
if ((changeFlags & IResourceDelta.SYNC) != 0) {
changeFlags ^= IResourceDelta.SYNC;
buf.append("SYNC | ");
}
if (changeFlags != 0) {
buf.append(changeFlags);
buf.append(" | ");
}
String result = buf.toString();
if (result.length() != 0) {
result = result.substring(0, result.length() - 3);
}
return result;
}
String convertKind(int kind) {
switch (kind) {
case IResourceDelta.ADDED :
return "ADDED";
case IResourceDelta.CHANGED :
return "CHANGED";
case IResourceDelta.REMOVED :
return "REMOVED";
case IResourceDelta.ADDED_PHANTOM :
return "ADDED_PHANTOM";
case IResourceDelta.REMOVED_PHANTOM :
return "REMOVED_PHANTOM";
default :
return "Unknown(" + kind + ")";
}
}
/**
* Called to cleanup internal state and make sure expectations
* are met after iterating over a resource delta.
*/
private void finishVerification() {
Hashtable resourcePaths = new Hashtable();
Enumeration keys = fExpectedChanges.keys();
while (keys.hasMoreElements()) {
Object key = keys.nextElement();
resourcePaths.put(key, key);
}
keys = resourcePaths.keys();
while (keys.hasMoreElements()) {
IPath resourcePath = (IPath) keys.nextElement();
fMessage.append("Checking expectations for ");
fMessage.append(resourcePath);
fMessage.append("\n");
ExpectedChange expectedChange = (ExpectedChange) fExpectedChanges.remove(resourcePath);
if (expectedChange != null) {
// List an ignored resource
if (fIgnoreResources.contains(expectedChange.fResource))
fMessage.append("\tIgnored\n");
else
recordMissingActualChange(expectedChange.getKind(), expectedChange.getChangeFlags());
}
}
}
/**
* Returns a message that describes the result of the resource
* delta verification checks.
*/
public String getMessage() {
if (fState == RECEIVING_INPUTS) {
if (hasExpectedChanges()) {
fail("Verifier has not yet been given a resource delta");
} else {
fState = DELTA_VERIFIED;
}
}
if (fState == DELTA_VERIFIED) {
finishVerification();
fState = VERIFICATION_COMPLETE;
}
return fMessage.toString();
}
/**
* Returns true if this verifier has received a delta notification
* since the last reset, and false otherwise.
*/
public boolean hasBeenNotified() {
return fState == DELTA_VERIFIED;
}
/**
* Returns true if this verifier currently has an expected
* changes, and false otherwise.
*/
public boolean hasExpectedChanges() {
return !fExpectedChanges.isEmpty();
}
/**
* Compares the given delta with the expected changes. Recursively
* compares child deltas.
*/
void internalVerifyDelta(IResourceDelta delta) {
try {
// FIXME: bogus
if (delta == null)
return;
fMessage.append("Verifying delta for ");
fMessage.append(delta.getFullPath());
fMessage.append("\n");
// Don't check changes for the workspace
// or for ignored resources
if (delta.getResource() != null && !fIgnoreResources.contains(delta.getResource())) {
checkPaths(delta);
checkChanges(delta);
}
checkChildren(delta);
} catch (Exception e) {
e.printStackTrace();
fMessage.append("Exception during event notification:" + e.getMessage());
fIsDeltaValid = false;
}
}
/**
* Returns whether the resource delta passed all verification
* checks.
*/
public boolean isDeltaValid() {
if (fState == RECEIVING_INPUTS) {
if (hasExpectedChanges()) {
fail("Verifier has not yet been given a resource delta");
} else {
fState = DELTA_VERIFIED;
}
}
if (fState == DELTA_VERIFIED) {
finishVerification();
fState = VERIFICATION_COMPLETE;
}
return fIsDeltaValid;
}
/**
* Tests message formatting. This main method does not represent the
* intended use of the ResourceDeltaVerifier. See the class comment
* for instructions on using the verifier.
*/
public static void main(String[] args) {
ResourceDeltaVerifier comparer = new ResourceDeltaVerifier();
int status = IResourceDelta.CHANGED;
int changeFlags = IResourceDelta.CONTENT;
int expectedStatus = IResourceDelta.CHANGED;
int actualStatus = IResourceDelta.REMOVED;
int expectedChangeFlags = IResourceDelta.OPEN;
int actualChangeFlags = 0;
int formerChildStatus = expectedStatus;
int latterChildStatus = actualStatus;
IPath path = new Path("/a/b/c");
IPath path2 = new Path("/a/b/d");
IPath expectedFullPath = path;
IPath actualFullPath = path2;
IPath expectedMovedFromPath = path;
IPath actualMovedFromPath = path2;
IPath expectedMovedToPath = path;
IPath actualMovedToPath = path2;
IPath expectedProjectRelativePath = new Path("b/c");
IPath actualProjectRelativePath = new Path("b/d");
comparer.fMessage.append("Checking delta for ");
comparer.fMessage.append(path);
comparer.fMessage.append("\n");
comparer.recordConflictingChange(expectedStatus, actualStatus, expectedChangeFlags, actualChangeFlags);
comparer.recordConflictingFullPaths(expectedFullPath, actualFullPath);
comparer.recordConflictingMovedFromPaths(expectedMovedFromPath, actualMovedFromPath);
comparer.recordConflictingMovedToPaths(expectedMovedToPath, actualMovedToPath);
comparer.recordConflictingProjectRelativePaths(expectedProjectRelativePath, actualProjectRelativePath);
comparer.recordDuplicateChild(path, formerChildStatus, latterChildStatus, expectedStatus);
comparer.recordIllegalChild(path, expectedStatus, actualStatus);
comparer.recordMissingActualChange(status, changeFlags);
comparer.recordMissingChild(path, status, true);
comparer.recordMissingChild(path, status, false);
comparer.recordMissingExpectedChange(status, changeFlags);
System.out.print(comparer.fMessage.toString());
}
private void recordConflictingChange(int expectedKind, int kind, int expectedChangeFlags, int changeFlags) {
fIsDeltaValid = false;
fMessage.append("\tConflicting change\n");
if (expectedKind != kind) {
fMessage.append("\t\tExpected kind: <");
fMessage.append(convertKind(expectedKind));
fMessage.append("> actual kind: <");
fMessage.append(convertKind(kind));
fMessage.append(">\n");
}
if (expectedChangeFlags != changeFlags) {
fMessage.append("\t\tExpected change flags: <");
fMessage.append(convertChangeFlags(expectedChangeFlags));
fMessage.append("> actual change flags: <");
fMessage.append(convertChangeFlags(changeFlags));
fMessage.append(">\n");
}
}
private void recordConflictingFullPaths(IPath expectedFullPath, IPath actualFullPath) {
fIsDeltaValid = false;
fMessage.append("\tConflicting full paths\n");
fMessage.append("\t\tExpected full path: ");
fMessage.append(expectedFullPath);
fMessage.append("\n");
fMessage.append("\t\tActual full path: ");
fMessage.append(actualFullPath);
fMessage.append("\n");
}
private void recordConflictingMovedFromPaths(IPath expectedMovedFromPath, IPath actualMovedFromPath) {
fIsDeltaValid = false;
fMessage.append("\tConflicting moved from paths\n");
fMessage.append("\t\tExpected moved from path: ");
fMessage.append(expectedMovedFromPath);
fMessage.append("\n");
fMessage.append("\t\tActual moved from path: ");
fMessage.append(actualMovedFromPath);
fMessage.append("\n");
}
private void recordConflictingMovedToPaths(IPath expectedMovedToPath, IPath actualMovedToPath) {
fIsDeltaValid = false;
fMessage.append("\tConflicting moved to paths\n");
fMessage.append("\t\tExpected moved to path: ");
fMessage.append(expectedMovedToPath);
fMessage.append("\n");
fMessage.append("\t\tActual moved to path: ");
fMessage.append(actualMovedToPath);
fMessage.append("\n");
}
private void recordConflictingProjectRelativePaths(IPath expectedProjectRelativePath, IPath actualProjectRelativePath) {
fIsDeltaValid = false;
fMessage.append("\tConflicting project relative paths\n");
fMessage.append("\t\tExpected project relative path: ");
fMessage.append(expectedProjectRelativePath);
fMessage.append("\n");
fMessage.append("\t\tActual project relative path: ");
fMessage.append(actualProjectRelativePath);
fMessage.append("\n");
}
private void recordDuplicateChild(IPath path, int formerChildKind, int latterChildKind, int expectedKind) {
fIsDeltaValid = false;
fMessage.append("\tDuplicate child: ");
fMessage.append(path);
fMessage.append("\n");
fMessage.append("\t\tProduced by IResourceDelta.get");
switch (expectedKind) {
case IResourceDelta.ADDED :
fMessage.append("Added");
break;
case IResourceDelta.CHANGED :
fMessage.append("Changed");
break;
case IResourceDelta.REMOVED :
fMessage.append("Removed");
break;
}
fMessage.append("Children()\n");
fMessage.append("\t\tFormer child's status: ");
fMessage.append(convertKind(formerChildKind));
fMessage.append("\n");
fMessage.append("\t\tLatter child's status: ");
fMessage.append(convertKind(latterChildKind));
fMessage.append("\n");
}
private void recordIllegalChild(IPath path, int expectedKind, int actualKind) {
fIsDeltaValid = false;
fMessage.append("\tIllegal child: ");
fMessage.append(path);
fMessage.append("\n");
fMessage.append("\t\tProduced by IResourceDelta.get");
switch (expectedKind) {
case IResourceDelta.ADDED :
fMessage.append("Added");
break;
case IResourceDelta.CHANGED :
fMessage.append("Changed");
break;
case IResourceDelta.REMOVED :
fMessage.append("Removed");
break;
}
fMessage.append("Children()\n");
fMessage.append("\t\tIlleagal child's status: ");
fMessage.append(convertKind(actualKind));
fMessage.append("\n");
}
private void recordMissingActualChange(int kind, int changeFlags) {
fIsDeltaValid = false;
fMessage.append("\tMissing actual change\n");
fMessage.append("\t\tExpected kind: <");
fMessage.append(convertKind(kind));
fMessage.append(">\n");
fMessage.append("\t\tExpected change flags: <");
fMessage.append(convertChangeFlags(changeFlags));
fMessage.append(">\n");
}
private void recordMissingChild(IPath path, int kind, boolean isMissingFromAffectedChildren) {
fIsDeltaValid = false;
fMessage.append("\tMissing child: ");
fMessage.append(path);
fMessage.append("\n");
fMessage.append("\t\tfrom IResourceDelta.getAffectedChildren(");
if (!isMissingFromAffectedChildren) {
switch (kind) {
case IResourceDelta.ADDED :
fMessage.append("ADDED");
break;
case IResourceDelta.CHANGED :
fMessage.append("CHANGED");
break;
case IResourceDelta.REMOVED :
fMessage.append("REMOVED");
break;
default :
fMessage.append(kind);
}
}
fMessage.append(")\n");
}
private void recordMissingExpectedChange(int kind, int changeFlags) {
fIsDeltaValid = false;
fMessage.append("\tMissing expected change\n");
fMessage.append("\t\tActual kind: <");
fMessage.append(convertKind(kind));
fMessage.append(">\n");
fMessage.append("\t\tActual change flags: <");
fMessage.append(convertChangeFlags(changeFlags));
fMessage.append(">\n");
}
/**
* Resets the listener to its initial state.
*/
public void reset() {
fExpectedChanges.clear();
fIgnoreResources.clear();
fIsDeltaValid = true;
fMessage.setLength(0);
fState = RECEIVING_INPUTS;
}
private void resetIfNecessary() {
if (fState == DELTA_VERIFIED) {
reset();
}
}
/**
* Part of the <code>IResourceChangedListener</code> interface.
* @see IResourceChangedListener
*/
public void resourceChanged(IResourceChangeEvent e) {
fMessage.append("Resource Changed Delta\n");
verifyDelta(e.getDelta());
}
/**
* Compares the given delta with the expected changes. Recursively
* compares child deltas.
*/
public void verifyDelta(IResourceDelta delta) {
internalVerifyDelta(delta);
fState = DELTA_VERIFIED;
}
/**
* Add resources whose changes should be ignored
*/
public void addIgnore(IResource[] resources) {
fIgnoreResources.addAll(Arrays.asList(resources));
}
}

View file

@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2004, 2009 IBM Corporation and others.
* Copyright (c) 2004, 2011 IBM Corporation and others.
* 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
@ -19,6 +19,7 @@ import org.eclipse.cdt.build.core.scannerconfig.tests.CfgScannerConfigProfileMan
import org.eclipse.cdt.build.core.scannerconfig.tests.GCCSpecsConsoleParserTest;
import org.eclipse.cdt.core.CCorePlugin;
import org.eclipse.cdt.core.dom.IPDOMManager;
import org.eclipse.cdt.managedbuilder.core.regressions.RegressionTests;
import org.eclipse.cdt.managedbuilder.core.tests.BuildDescriptionModelTests;
import org.eclipse.cdt.managedbuilder.core.tests.BuildSystem40Tests;
import org.eclipse.cdt.managedbuilder.core.tests.ManagedBuildCoreTests;
@ -85,6 +86,10 @@ public class AllManagedBuildTests {
suite.addTest(CProjectDescriptionSerializationTests.suite());
suite.addTest(OptionStringListValueTests.suite());
suite.addTest(ProjectModelTests.suite());
// regression tests
suite.addTest(RegressionTests.suite());
//$JUnit-END$
return suite;
}

View file

@ -0,0 +1,80 @@
/*******************************************************************************
* Copyright (c) 2011 Broadcom Corporation and others.
* 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:
* Broadcom Corporation - Initial API and implementation
*******************************************************************************/
package org.eclipse.cdt.managedbuilder.core.regressions;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import org.eclipse.cdt.managedbuilder.testplugin.AbstractBuilderTest;
import org.eclipse.cdt.managedbuilder.testplugin.ResourceDeltaVerifier;
import org.eclipse.core.resources.IFile;
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;
/**
* Tests that removing the last source file from a directory
* causes the subdir.mk to be regenerated, and associated dervied
* files should be deleted.
*/
public class Bug_303953 extends AbstractBuilderTest {
public void testBuildAfterSourcefileDelete() throws CoreException {
setWorkspace("regressions");
final IProject app = loadProject("helloworldC");
List<IResource> buildOutputResources = new ArrayList<IResource>();
buildOutputResources.addAll(getProjectBuildExeResources("helloworldC", "Debug", "src/helloworldC"));
// Ensure Debug is the active configuration
setActiveConfigurationByName(app, "Debug");
ResourceDeltaVerifier verifier = new ResourceDeltaVerifier();
verifier.addExpectedChange(buildOutputResources.toArray(new IResource[buildOutputResources.size()]), IResourceDelta.ADDED, IResourceDelta.NO_CHANGE);
verifier.addIgnore(new IResource[]{
getWorkspace().getRoot(), app,
app.getFile(".project")});
verifyBuild(app, IncrementalProjectBuilder.FULL_BUILD, verifier);
// Delete helloworldC
IFile srcFile = app.getFile("src/helloworldC.c");
assertTrue("1.1", srcFile.exists());
srcFile.delete(false, null);
// Build again
// - derived files from helloworldC.c should be removed
// - subdir.mk should be changed
// - ignore other changes in the build tree (not the subject of this bug...)
verifier = new ResourceDeltaVerifier();
// These files should be removed
IResource[] removed = new IResource[] {app.getFile("Debug/src/helloworldC.o"),
app.getFile("Debug/src/helloworldC.d")};
verifier.addExpectedChange(removed, IResourceDelta.REMOVED, IResourceDelta.NO_CHANGE);
// subdir.mk has been updated
IResource[] expected = new IResource[] {app.getFile("Debug/src/subdir.mk")};
verifier.addExpectedChange(expected, IResourceDelta.CHANGED, IResourceDelta.CONTENT);
// Ignore other resources
Collection<IResource> ignored = getProjectBuildExeResources("helloworldC", "Debug", "src/helloworldC");
ignored.removeAll(Arrays.asList(removed));
ignored.removeAll(Arrays.asList(expected));
ignored.add(getWorkspace().getRoot());
ignored.add(app);
verifier.addIgnore(ignored.toArray(new IResource[ignored.size()]));
verifyBuild(app, IncrementalProjectBuilder.INCREMENTAL_BUILD, verifier);
}
}

View file

@ -0,0 +1,40 @@
/*******************************************************************************
* Copyright (c) 2011 Broadcom Corporation and others.
* 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:
* Broadcom Corporation - Initial API and implementation
*******************************************************************************/
package org.eclipse.cdt.managedbuilder.core.regressions;
import org.eclipse.cdt.managedbuilder.core.regressions.Bug_303953;
import junit.framework.Test;
import junit.framework.TestCase;
import junit.framework.TestSuite;
/**
* Regression tests for builder bugs
*/
public class RegressionTests extends TestCase {
public static Test suite() {
TestSuite suite = new TestSuite(RegressionTests.class.getName());
// Test that common builder does the correct amount of work.
suite.addTestSuite(Bug_303953.class);
return suite;
}
public RegressionTests() {
super(null);
}
public RegressionTests(String name) {
super(name);
}
}

View file

@ -10,6 +10,7 @@
* ARM Ltd. - Minor changes to echo commands
* IBM Corporation
* Anna Dushistova (Mentor Graphics) - [307244] extend visibility of fields in GnuMakefileGenerator
* James Blackburn (Broadcom Corp.)
*******************************************************************************/
package org.eclipse.cdt.managedbuilder.makegen.gnu;
@ -20,10 +21,12 @@ import java.io.InputStreamReader;
import java.io.Reader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map.Entry;
import java.util.Set;
@ -308,7 +311,7 @@ public class GnuMakefileGenerator implements IManagedBuilderMakefileGenerator2 {
private static final String BUILD_TARGETS = COMMENT + ".build.toptargets"; //$NON-NLS-1$
private static final String SRC_LISTS = COMMENT + ".source.list"; //$NON-NLS-1$
private static final String EMPTY_STRING = new String();
private static final String EMPTY_STRING = ""; //$NON-NLS-1$
private static final String[] EMPTY_STRING_ARRAY = new String[0];
private static final String OBJS_MACRO = "OBJS"; //$NON-NLS-1$
@ -349,14 +352,16 @@ public class GnuMakefileGenerator implements IManagedBuilderMakefileGenerator2 {
// private IManagedBuildInfo info;
// private IConfiguration cfg
private Vector<IResource> invalidDirList;
private Vector<IResource> modifiedList;
/** Collection of Folders in which sources files have been modified */
private Collection<IContainer> modifiedList;
private IProgressMonitor monitor;
private IProject project;
private IResource[] projectResources;
private Vector<String> ruleList;
private Vector<String> depLineList; // String's of additional dependency lines
private Vector<String> depRuleList; // String's of rules for generating dependency files
private Vector<IResource> subdirList;
/** Collection of Containers which contribute source files to the build */
private Collection<IContainer> subdirList;
private IPath topBuildDir; // Build directory - relative to the workspace
// private Set outputExtensionsSet;
//=== Maps of macro names (String) to values (List)
@ -626,6 +631,10 @@ public class GnuMakefileGenerator implements IManagedBuilderMakefileGenerator2 {
project.accept(resourceVisitor, IResource.NONE);
checkCancel();
// Bug 303953: Ensure that if all resources have been removed from a folder, than the folder still
// appears in the subdir list so it's subdir.mk is correctly regenerated
getSubdirList().addAll(getModifiedList());
// Make sure there is something to build
if (getSubdirList().isEmpty()) {
String info = ManagedMakeMessages.getFormattedString("MakefileGenerator.warning.no.source", project.getName()); //$NON-NLS-1$
@ -4151,10 +4160,9 @@ public class GnuMakefileGenerator implements IManagedBuilderMakefileGenerator2 {
*/
protected void appendBuildSubdirectory(IResource resource) {
IContainer container = resource.getParent();
// Only add the container once
if (!getSubdirList().contains(container)) {
getSubdirList().add(container);
}
// Only add the container once
if (!getSubdirList().contains(container))
getSubdirList().add(container);
}
/**
@ -4475,25 +4483,20 @@ public class GnuMakefileGenerator implements IManagedBuilderMakefileGenerator2 {
}
/**
*
* @return Vector
* @return Collection of Containers which contain modified source files
*/
private Vector<IResource> getModifiedList() {
if (modifiedList == null) {
modifiedList = new Vector<IResource>();
}
private Collection<IContainer> getModifiedList() {
if (modifiedList == null)
modifiedList = new LinkedHashSet<IContainer>();
return modifiedList;
}
/**
* Answers the list of subdirectories (IContainer's) contributing source code to the build
*
* @return List
* @return Collection of subdirectories (IContainers) contributing source code to the build
*/
private Vector<IResource> getSubdirList() {
if (subdirList == null) {
subdirList = new Vector<IResource>();
}
private Collection<IContainer> getSubdirList() {
if (subdirList == null)
subdirList = new LinkedHashSet<IContainer>();
return subdirList;
}