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:
parent
834cc7b829
commit
094a51f44a
8 changed files with 1276 additions and 21 deletions
|
@ -0,0 +1,4 @@
|
|||
|
||||
helloworldC.zip:
|
||||
Basic standard Linux GCC C HelloWorld ManagedBuild Project template.
|
||||
Contains Debug + Release configurations.
|
Binary file not shown.
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
|
@ -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));
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue