diff options
author | Michael Valenta | 2003-10-16 19:41:04 +0000 |
---|---|---|
committer | Michael Valenta | 2003-10-16 19:41:04 +0000 |
commit | 0df4a708a4c47a452f18f8860043ea6689a1b348 (patch) | |
tree | fdef089ec3ff0c8bc852a8b9d495ade47d969bff | |
parent | bff61b7227693c8ada76d0877de33fff7a81ea03 (diff) | |
download | eclipse.platform.team-0df4a708a4c47a452f18f8860043ea6689a1b348.tar.gz eclipse.platform.team-0df4a708a4c47a452f18f8860043ea6689a1b348.tar.xz eclipse.platform.team-0df4a708a4c47a452f18f8860043ea6689a1b348.zip |
44614: [CVS] Unable to commit, NPE, broken UI
3 files changed, 367 insertions, 99 deletions
diff --git a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/resources/EclipseSynchronizer.java b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/resources/EclipseSynchronizer.java index 36840732d..32373d967 100644 --- a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/resources/EclipseSynchronizer.java +++ b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/resources/EclipseSynchronizer.java @@ -596,13 +596,6 @@ public class EclipseSynchronizer implements IFlushOperation { monitor.done(); } } - - private void purgeCache(IResource resource, boolean deep) throws CVSException { - sessionPropertyCache.purgeResourceSyncCache(resource); - if (resource.getType() != IResource.FILE) { - sessionPropertyCache.purgeCache((IContainer)resource, deep); - } - } /** * Called to notify the synchronizer that meta files have changed on disk, outside @@ -654,12 +647,21 @@ public class EclipseSynchronizer implements IFlushOperation { (IResource[]) changed.toArray(new IResource[changed.size()])); } - /** - * The folder is about to be deleted (including its CVS subfolder). - * Take any appropriate action to remember the CVS information. + /* + * The resource is about to be deleted by the move delete hook. + * In all cases (except when the resource doesn't exist), this method + * will indicate that the dirty state of the parent needs to be recomputed. + * For managed resources, it will move the cached sync info from the session + * property cache into the sycnrhonizer cache, purging the session cache. + * @param resource the resource about to be deleted. + * <p> + * Note taht this method is not recursive. Hence, for managed resources + * + * @returns whether children need to be prepared + * @throws CVSException */ - public void prepareForDeletion(IResource resource) throws CVSException { - if (!resource.exists()) return; + /* private */ boolean prepareForDeletion(IResource resource) throws CVSException { + if (!resource.exists()) return false; ISchedulingRule rule = null; try { rule = beginBatching(resource, null); @@ -678,21 +680,27 @@ public class EclipseSynchronizer implements IFlushOperation { syncBytes = convertToDeletion(syncBytes); synchronizerCache.setCachedSyncBytes(resource, syncBytes, true); } + sessionPropertyCache.purgeResourceSyncCache(resource); resourceChanged(resource); } + return false; } else { IContainer container = (IContainer)resource; if (container.getType() == IResource.PROJECT) { synchronizerCache.flush((IProject)container); + return false; } else { // Move the folder sync info into phantom space FolderSyncInfo info = getFolderSync(container); - if (info == null) return; + if (info == null) return false; synchronizerCache.setCachedFolderSync(container, info, true); folderChanged(container); // move the resource sync as well byte[] syncBytes = getSyncBytes(resource); synchronizerCache.setCachedSyncBytes(resource, syncBytes, true); + sessionPropertyCache.purgeResourceSyncCache(container); + sessionPropertyCache.purgeCache(container, false); + return true; } } } finally { @@ -722,37 +730,41 @@ public class EclipseSynchronizer implements IFlushOperation { } /** - * Prepare for a move or delete within the move/delete hook by moving the - * sync info into phantom space and flushing the session properties cache. - * This will allow sync info for deletions to be maintained in the source - * location and sync info at the destination to be preserved as well. + * Prepare for the deletion of the target resource from within + * the move/delete hook. The method is invoked by both the + * deleteFile/Folder methods and for the source resource + * of moveFile/Folder. This method will move the cached sync info + * into the phantom (ISynchronizer) cache so that outgoing deletions + * and known remote folders are preserved. * * @param resource * @param monitor * @throws CVSException */ - public void prepareForMoveDelete(IResource resource, IProgressMonitor monitor) throws CVSException { + public void prepareForDeletion(IResource resource, IProgressMonitor monitor) throws CVSException { // Move sync info to phantom space for the resource and all it's children + monitor = Policy.monitorFor(monitor); try { + beginOperation(); monitor.beginTask(null, 100); - resource.accept(new IResourceVisitor() { - public boolean visit(IResource innerResource) throws CoreException { - try { - prepareForDeletion(innerResource); - } catch (CVSException e) { - CVSProviderPlugin.log(e); - throw new CoreException(e.getStatus()); + try { + resource.accept(new IResourceVisitor() { + public boolean visit(IResource innerResource) throws CoreException { + try { + return prepareForDeletion(innerResource); + } catch (CVSException e) { + CVSProviderPlugin.log(e); + throw new CoreException(e.getStatus()); + } } - return true; - } - }); - } catch (CoreException e) { - throw CVSException.wrapException(e); + }); + } catch (CoreException e) { + throw CVSException.wrapException(e); + } } finally { + endOperation(); monitor.done(); } - // purge the sync info to clear the session properties - purgeCache(resource, true); } /** @@ -1542,14 +1554,69 @@ public class EclipseSynchronizer implements IFlushOperation { } /** - * React to a file that was just moved by the move/delete hook. - * @param file the file that was moved (at its new location) + * React to a resource that was just moved by the move/delete hook. + * @param resource the resource that was moved (at its new location) */ - public void fileMoved(IFile file) throws CVSException { - if (!isWithinActiveOperationScope(file)) { - sessionPropertyCache.purgeCache(file.getParent(), false /*don't flush children*/); + public void postMove(IResource resource) throws CVSException { + try { + beginOperation(); + if (resource.getType() == IResource.FILE) { + // Purge any copied sync info so true sync info will + // be obtained from the synchronizer cache + sessionPropertyCache.purgeResourceSyncCache(resource); + } else { + IContainer container = (IContainer)resource; + // Purge any copied sync info + sessionPropertyCache.purgeCache(container, true /* deep */); + // Dirty all resources so old sync info will be rewritten to disk + try { + container.accept(new IResourceVisitor() { + public boolean visit(IResource resource) throws CoreException { + if (getSyncBytes(resource) != null) { + resourceChanged(resource); + } + if (resource.getType() != IResource.FILE) { + if (getFolderSync((IContainer)resource) != null) { + folderChanged((IContainer)resource); + return true; + } + } + return false; + } + }); + } catch (CoreException e) { + throw CVSException.wrapException(e); + } + // Flush the sync info to disk + flush(container, true /* deep */, null); + } + } finally { + endOperation(); } - } + /** + * This method is to be invoked only from the move/delete hook. It's purpose + * is to obtain the sync look in order to prevent other threads from accessing + * sync info while the move/delete is taking place. + * @param runnable + * @param monitor + * @throws CVSException + */ + public void performMoveDelete(ICVSRunnable runnable, IProgressMonitor monitor) throws CVSException { + ISchedulingRule rule = null; + try { + monitor.beginTask(null, 100); + rule = beginBatching(null, null); + try { + beginOperation(); + runnable.run(Policy.subMonitorFor(monitor, 95)); + } finally { + endOperation(); + } + } finally { + if (rule != null) endBatching(rule, Policy.subMonitorFor(monitor, 5)); + monitor.done(); + } + } } diff --git a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/util/MoveDeleteHook.java b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/util/MoveDeleteHook.java index 5265ea932..8dad4e254 100644 --- a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/util/MoveDeleteHook.java +++ b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/util/MoveDeleteHook.java @@ -33,7 +33,6 @@ import org.eclipse.team.internal.ccvs.core.CVSTeamProvider; import org.eclipse.team.internal.ccvs.core.ICVSFile; import org.eclipse.team.internal.ccvs.core.ICVSFileModificationValidator; import org.eclipse.team.internal.ccvs.core.ICVSFolder; -import org.eclipse.team.internal.ccvs.core.ICVSResource; import org.eclipse.team.internal.ccvs.core.ICVSRunnable; import org.eclipse.team.internal.ccvs.core.Policy; import org.eclipse.team.internal.ccvs.core.resources.CVSWorkspaceRoot; @@ -55,28 +54,39 @@ public class MoveDeleteHook implements IMoveDeleteHook { IProgressMonitor monitor) { try { + monitor.beginTask(null, 100); + // No special handling required for team-private members if (file.isTeamPrivateMember()) return false; + // If the file is ignored by CVS then we can just delete it. ICVSFile cvsFile = CVSWorkspaceRoot.getCVSFileFor(file); if (cvsFile.isIgnored()) return false; + + // If we can't check out the files, return. + if (!checkOutFiles(tree, new IFile[] {file}, Policy.subMonitorFor(monitor, 30))) { + // Return that the delete was handled because the checkout + // will have reported the error to the IResourceTree + return true; + } + // Otherwise, we need to prepare properly for the delete - EclipseSynchronizer.getInstance().run(new ICVSRunnable() { + EclipseSynchronizer.getInstance().performMoveDelete(new ICVSRunnable() { public void run(IProgressMonitor monitor) throws CVSException { try { - monitor.beginTask(null, 100); - if (checkOutFiles(tree, new IFile[] {file}, Policy.subMonitorFor(monitor, 35))) { - EclipseSynchronizer.getInstance().prepareForMoveDelete(file, Policy.subMonitorFor(monitor, 30)); - tree.standardDeleteFile(file, updateFlags, Policy.subMonitorFor(monitor, 35)); - } + monitor.beginTask(null, 100); + EclipseSynchronizer.getInstance().prepareForDeletion(file, Policy.subMonitorFor(monitor, 40)); + tree.standardDeleteFile(file, updateFlags, Policy.subMonitorFor(monitor, 60)); } finally { monitor.done(); } } - }, monitor); + }, Policy.subMonitorFor(monitor, 70)); } catch (CVSException e) { tree.failed(e.getStatus()); - } + } finally { + monitor.done(); + } return true; } @@ -95,11 +105,11 @@ public class MoveDeleteHook implements IMoveDeleteHook { try { final ICVSFolder cvsFolder = CVSWorkspaceRoot.getCVSFolderFor(folder); if (cvsFolder.isCVSFolder() && ensureCheckedOut(new IFolder[] {folder}, tree, Policy.subMonitorFor(monitor, 30))) { - EclipseSynchronizer.getInstance().run(new ICVSRunnable() { + EclipseSynchronizer.getInstance().performMoveDelete(new ICVSRunnable() { public void run(IProgressMonitor monitor) throws CVSException { try { monitor.beginTask(null, 100); - EclipseSynchronizer.getInstance().prepareForMoveDelete(folder, Policy.subMonitorFor(monitor, 50)); + EclipseSynchronizer.getInstance().prepareForDeletion(folder, Policy.subMonitorFor(monitor, 20)); tree.standardDeleteFolder(folder, updateFlags, Policy.subMonitorFor(monitor, 50)); } finally { monitor.done(); @@ -108,7 +118,7 @@ public class MoveDeleteHook implements IMoveDeleteHook { }, Policy.subMonitorFor(monitor, 70)); return true; } else if (!cvsFolder.isIgnored()) { - prepareToDelete(cvsFolder); + EclipseSynchronizer.getInstance().prepareForDeletion(cvsFolder.getIResource(), Policy.subMonitorFor(monitor, 70)); } } catch (CVSException e) { tree.failed(e.getStatus()); @@ -131,7 +141,7 @@ public class MoveDeleteHook implements IMoveDeleteHook { // All other sync info is stored in session and persistant properties, which // are deleted when the associated resources are deleted try { - EclipseSynchronizer.getInstance().prepareForDeletion(project); + EclipseSynchronizer.getInstance().prepareForDeletion(project, monitor); } catch (CVSException e) { CVSProviderPlugin.log(e); } @@ -150,32 +160,41 @@ public class MoveDeleteHook implements IMoveDeleteHook { IProgressMonitor monitor) { try { - EclipseSynchronizer.getInstance().run(new ICVSRunnable() { + monitor.beginTask(null, 100); + + // ensure we can write to both the source and the destination + IFile[] filesToCheckOut; + if (destination.exists()) { + filesToCheckOut = new IFile[] {source, destination}; + } else { + filesToCheckOut = new IFile[] {source}; + } + if (!checkOutFiles(tree, filesToCheckOut, Policy.subMonitorFor(monitor, 30))) { + // Return that the move was handled because the checkout + // will have reported the error to the IResourceTree + return true; + } + + // Perform the move + EclipseSynchronizer.getInstance().performMoveDelete(new ICVSRunnable() { public void run(IProgressMonitor monitor) throws CVSException { try { monitor.beginTask(null, 100); - // ensure we can write to both the source and the destination - IFile[] filesToCheckOut; + EclipseSynchronizer.getInstance().prepareForDeletion(source, Policy.subMonitorFor(monitor, 40)); if (destination.exists()) { - filesToCheckOut = new IFile[] {source, destination}; - } else { - filesToCheckOut = new IFile[] {source}; - } - if (checkOutFiles(tree, filesToCheckOut, Policy.subMonitorFor(monitor, 30))) { - EclipseSynchronizer.getInstance().prepareForMoveDelete(source, Policy.subMonitorFor(monitor, 20)); - if (destination.exists()) { - EclipseSynchronizer.getInstance().prepareForMoveDelete(destination, Policy.subMonitorFor(monitor, 20)); - } - tree.standardMoveFile(source, destination, updateFlags, Policy.subMonitorFor(monitor, 30)); - EclipseSynchronizer.getInstance().fileMoved(destination); + EclipseSynchronizer.getInstance().prepareForDeletion(destination, Policy.subMonitorFor(monitor, 20)); } + tree.standardMoveFile(source, destination, updateFlags, Policy.subMonitorFor(monitor, 40)); + EclipseSynchronizer.getInstance().postMove(destination); } finally { monitor.done(); } } - }, monitor); + }, Policy.subMonitorFor(monitor, 70)); } catch (CVSException e) { tree.failed(e.getStatus()); + } finally { + monitor.done(); } return true; } @@ -195,14 +214,15 @@ public class MoveDeleteHook implements IMoveDeleteHook { final ICVSFolder cvsFolder = CVSWorkspaceRoot.getCVSFolderFor(source); if (cvsFolder.isManaged()) { if (!ensureCheckedOut(new IFolder[] {source, destination}, tree, Policy.subMonitorFor(monitor, 20))) return true; - EclipseSynchronizer.getInstance().run(new ICVSRunnable() { + EclipseSynchronizer.getInstance().performMoveDelete(new ICVSRunnable() { public void run(IProgressMonitor monitor) throws CVSException { - EclipseSynchronizer.getInstance().prepareForMoveDelete(source, Policy.subMonitorFor(monitor, 20)); + EclipseSynchronizer.getInstance().prepareForDeletion(source, Policy.subMonitorFor(monitor, 20)); if (destination.exists()) { - EclipseSynchronizer.getInstance().prepareForMoveDelete(destination, Policy.subMonitorFor(monitor, 20)); + EclipseSynchronizer.getInstance().prepareForDeletion(destination, Policy.subMonitorFor(monitor, 20)); } tree.standardMoveFolder(source, destination, updateFlags, Policy.subMonitorFor(monitor, 30)); purgeCVSFolders(destination, Policy.subMonitorFor(monitor, 20)); + EclipseSynchronizer.getInstance().postMove(destination); } private void purgeCVSFolders(IFolder destination, final IProgressMonitor monitor) throws CVSException { // Delete any CVS folders @@ -223,8 +243,7 @@ public class MoveDeleteHook implements IMoveDeleteHook { }, Policy.subMonitorFor(monitor, 60)); return true; } else if (!cvsFolder.isIgnored()) { - // XXX Should be inside cvs runnable - prepareToDelete(cvsFolder); + EclipseSynchronizer.getInstance().prepareForDeletion(cvsFolder.getIResource(), Policy.subMonitorFor(monitor, 60)); } } catch (CVSException e) { tree.failed(e.getStatus()); @@ -251,7 +270,7 @@ public class MoveDeleteHook implements IMoveDeleteHook { // project will mean that the file deletions will be lost. It also means that phantom // folders are lost. try { - EclipseSynchronizer.getInstance().prepareForDeletion(source); + EclipseSynchronizer.getInstance().prepareForDeletion(source, monitor); } catch (CVSException e) { CVSProviderPlugin.log(e); } @@ -318,12 +337,4 @@ public class MoveDeleteHook implements IMoveDeleteHook { CVSTeamProvider provider = (CVSTeamProvider)RepositoryProvider.getProvider(files[0].getProject(), CVSProviderPlugin.getTypeId()); return provider; } - - /* - * Signal that the unmanaged resource is about to be deleted so that the - * dirty count of it's parent can be reduced if appropriate. - */ - private void prepareToDelete(ICVSResource resource) throws CVSException { - EclipseSynchronizer.getInstance().prepareForDeletion(resource.getIResource()); - } } diff --git a/tests/org.eclipse.team.tests.cvs.core/src/org/eclipse/team/tests/ccvs/core/cvsresources/EclipseSynchronizerTest.java b/tests/org.eclipse.team.tests.cvs.core/src/org/eclipse/team/tests/ccvs/core/cvsresources/EclipseSynchronizerTest.java index 30dc75971..f69af7787 100644 --- a/tests/org.eclipse.team.tests.cvs.core/src/org/eclipse/team/tests/ccvs/core/cvsresources/EclipseSynchronizerTest.java +++ b/tests/org.eclipse.team.tests.cvs.core/src/org/eclipse/team/tests/ccvs/core/cvsresources/EclipseSynchronizerTest.java @@ -26,15 +26,18 @@ import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IFolder; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IResource; +import org.eclipse.core.resources.IResourceVisitor; import org.eclipse.core.resources.IWorkspaceRoot; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.Path; import org.eclipse.team.core.RepositoryProvider; import org.eclipse.team.core.TeamException; import org.eclipse.team.internal.ccvs.core.CVSException; import org.eclipse.team.internal.ccvs.core.CVSProviderPlugin; import org.eclipse.team.internal.ccvs.core.CVSTag; +import org.eclipse.team.internal.ccvs.core.ICVSRunnable; import org.eclipse.team.internal.ccvs.core.resources.EclipseSynchronizer; import org.eclipse.team.internal.ccvs.core.syncinfo.FolderSyncInfo; import org.eclipse.team.internal.ccvs.core.syncinfo.MutableResourceSyncInfo; @@ -522,38 +525,225 @@ public class EclipseSynchronizerTest extends EclipseTest { } } - /* - * See bug 44446 + /** + * Create a test project whose name is derived from the currently running test case. + * The resources are built using the names supplied in the given String array. + * Paths ending in / will be folders while others will be files. Intermediate folders + * are created as needed. Dummy sync info is applied to all created resources and + * the project is mapped to the CVS repository provider. + * @param resourcePaths paths of resources to be generated + * @return the create project */ - public void testRecreation() throws CoreException { - - // Set up a project with dummy sync info + protected IProject createProject(String[] resourcePaths) throws CoreException { + // Create the project and build the resources IProject project = getUniqueTestProject(getName()); - sync.setFolderSync(project, dummyFolderSync(project)); - - IFolder folder = project.getFolder("folder1"); - folder.create(false, true, null); - sync.setFolderSync(folder, dummyFolderSync(folder)); - sync.setResourceSync(folder, dummyResourceSync(folder)); - - IFile file = folder.getFile("file1"); - file.create(getRandomContents(), false /*force*/, null); - sync.setResourceSync(file, dummyResourceSync(file)); + buildResources(project, resourcePaths, true); + + // Associate dummy sync info with al create resources + project.accept(new IResourceVisitor() { + public boolean visit(IResource resource) throws CoreException { + if (resource.getType() != IResource.PROJECT) { + sync.setResourceSync(resource, dummyResourceSync(resource)); + } + if (resource.getType() != IResource.FILE) { + sync.setFolderSync((IContainer)resource, dummyFolderSync((IContainer)resource)); + } + return true; + } + }); // Map the project to CVS so the Move/Delete hook works RepositoryProvider.map(project, CVSProviderPlugin.getTypeId()); + return project; + } + + /** + * Assert that the resources at the given resource paths have sync info. + * Also assert that the ancestors of the resources also have sync info + * @param project the project containing the resources + * @param resourcePaths the project relative resource paths + * @throws CVSException + */ + protected void assertHasSyncInfo(IProject project, String[] resourcePaths) throws CVSException { + for (int i = 0; i < resourcePaths.length; i++) { + String path = resourcePaths[i]; + IResource resource = findResource(project, path); + assertHasSyncInfo(resource); + } + } + + private IResource findResource(IProject project, String path) { + IResource resource = project.findMember(path); + if (resource == null) { + if (path.charAt(path.length()-1) == Path.SEPARATOR) + resource = (IResource) project.getFolder(path); + else + resource = (IResource) project.getFile(path); + } + return resource; + } + + /** + * Assert that the resource and its ancestors have sync info + * @param resource the resource being queried + * @throws CVSException + */ + protected void assertHasSyncInfo(IResource resource) throws CVSException { + if (resource.getType() == IResource.ROOT) return; + if (resource.getType() != IResource.FILE) { + assertNotNull("Folder should have folder sync info but does not: " + resource.getProjectRelativePath(), sync.getFolderSync((IContainer)resource)); + } + if (resource.getType() != IResource.PROJECT) { + assertNotNull("Resource should have sync bytes but does not: " + resource.getProjectRelativePath(), sync.getSyncBytes(resource)); + assertHasSyncInfo(resource.getParent()); + } + } + + /** + * Assert that the resources at the given resource paths do not have sync info. + * Also assert that the descendants of the resources also do not have sync info + * @param project + * @param resourcePaths + * @throws CVSException + */ + private void assertHasNoSyncInfo(IProject project, String[] resourcePaths) throws CoreException { + for (int i = 0; i < resourcePaths.length; i++) { + String path = resourcePaths[i]; + IResource resource = findResource(project, path); + assertHasNoSyncInfo(resource); + } + } + + protected void assertHasNoSyncInfo(IResource resource) throws CoreException { + if (resource.getType() == IResource.ROOT) return; + if (resource.getType() != IResource.FILE) { + assertNull("Folder should not have folder sync but does: " + resource.getProjectRelativePath(), sync.getFolderSync((IContainer)resource)); + IResource[] members = ((IContainer)resource).members(); + for (int i = 0; i < members.length; i++) { + IResource child = members[i]; + assertHasNoSyncInfo(child); + } + } + if (resource.getType() != IResource.PROJECT) { + assertNull("Resource should not have sync bytes but does: " + resource.getProjectRelativePath(), sync.getSyncBytes(resource)); + } + } + + public void testDeleteFile() throws CoreException { + // Create a project with dummy sync info + IProject project = createProject(new String[] {"folder1/folder2/file1", "folder1/folder2/file2"}); + + // Delete the file and assert old sync info is still in place and new has no sync info + IFile file = project.getFile("folder1/folder2/file1"); + file.delete(false, false, null); + assertHasSyncInfo(project, new String[] {"folder1/folder2/file1"}); + } + + public void testDeleteFolder() throws CoreException { + // Create a project with dummy sync info + IProject project = createProject(new String[] {"folder1/folder2/file1", "folder1/folder2/file2"}); + + // Delete the folder and assert old sync info is still in place and new has no sync info + IFolder folder = project.getFolder("folder1/folder2/"); + folder.delete(false, false, null); + assertHasSyncInfo(project, new String[] {"folder1/folder2/file1", "folder1/folder2/file2"}); + } + + public void testMoveFile() throws CoreException { + // Create a project with dummy sync info + IProject project = createProject(new String[] {"folder1/folder2/file1", "folder1/folder2/file2"}); + + // Move the file and assert old sync info is still in place and new has no sync info + IFile file = project.getFile("folder1/folder2/file1"); + project.getFolder("folder1/folder3/").create(false, true, null); + file.move(project.getFolder("folder1/folder3/file1").getFullPath(), false, null); + assertHasSyncInfo(project, new String[] {"folder1/folder2/file1"}); + assertHasNoSyncInfo(project, new String[] {"folder1/folder3"}); + } + + public void testMoveFolder() throws CoreException { + // Create a project with dummy sync info + IProject project = createProject(new String[] {"folder1/folder2/file1"}); + + // Move the folder and assert old sync info is still in place and new has no sync info + IFolder folder = project.getFolder("folder1/folder2/"); + folder.move(project.getFolder("folder1/folder3").getFullPath(), false, null); + assertHasSyncInfo(project, new String[] {"folder1/folder2/file1"}); + assertHasNoSyncInfo(project, new String[] {"folder1/folder3/"}); + } + + /* + * See bug 44446 + */ + public void testFileRecreation() throws CoreException { + // Create a project with dummy sync info + IProject project = createProject(new String[] {"folder1/file1"}); // Remove the file and assert that it still has sync info + IFile file = project.getFile("folder1/file1"); file.delete(false, false, null); - assertNotNull("Sync bytes should not be null for " + file.getFullPath(), sync.getSyncBytes(file)); + assertHasSyncInfo(file); // Recreate the file and assert that it still has sync info file.create(getRandomContents(), false /*force*/, null); - assertNotNull("Sync bytes should not be null for " + file.getFullPath(), sync.getSyncBytes(file)); + assertHasSyncInfo(file); // unmanage the file and assert that sync info is gone sync.deleteResourceSync(file); - assertNull("Sync bytes should be null for " + file.getFullPath(), sync.getSyncBytes(file)); - + assertHasNoSyncInfo(file); + } + + /* + * This testcase simulates an update that has an incoming deletion and a merge + * (which may do a move). + */ + public void testFileMoveAndDelete() throws CoreException { + // Create a project with dummy sync info + final IProject project = createProject(new String[] {"folder1/file1", "folder1/file2"}); + + sync.run(project, new ICVSRunnable() { + public void run(IProgressMonitor monitor) throws CVSException { + try { + IFile file1 = project.getFile("folder1/file1"); + IFile file2 = project.getFile("folder1/file2"); + // Delete file 1 + file1.delete(false, false, null); + assertHasSyncInfo(file1); + assertHasSyncInfo(file2); + sync.deleteResourceSync(file1); + assertHasNoSyncInfo(file1); + assertHasSyncInfo(file2); + // Move file 2 + file2.move(new Path("file3"), false, false, null); + assertHasNoSyncInfo(file1); + assertHasSyncInfo(file2); + } catch (CoreException e) { + throw CVSException.wrapException(e); + } + } + }, null); + } + + public void testMoveFolderOverFolder() throws CoreException { + // Create a project with dummy sync info + final IProject project = createProject(new String[] {"folder1/file1", "folder2/file1"}); + + // Change the sync info of folder1/file1 to be revision 1.9 + String revision = "1.9"; + IFile file11 = project.getFile("folder1/file1"); + ResourceSyncInfo info = sync.getResourceSync(file11); + MutableResourceSyncInfo muttable = info.cloneMutable(); + muttable.setRevision(revision); + sync.setResourceSync(file11, muttable); + + // Move the folder and verify that the sync info stays + project.getFolder("folder2").delete(false, false, null); + project.getFolder("folder1").move(new Path("folder2"), false, false, null); + assertHasSyncInfo(file11); + IFile file21 = project.getFile("folder2/file1"); + assertHasSyncInfo(file21); + assertTrue(sync.getResourceSync(file11).getRevision().equals(revision)); + assertTrue(!sync.getResourceSync(file21).getRevision().equals(revision)); + } } |