diff --git a/resources/bundles/org.eclipse.core.resources/src/org/eclipse/core/internal/properties/PropertyManager2.java b/resources/bundles/org.eclipse.core.resources/src/org/eclipse/core/internal/properties/PropertyManager2.java index cb2e38021f4..a97a6f9dce9 100644 --- a/resources/bundles/org.eclipse.core.resources/src/org/eclipse/core/internal/properties/PropertyManager2.java +++ b/resources/bundles/org.eclipse.core.resources/src/org/eclipse/core/internal/properties/PropertyManager2.java @@ -12,6 +12,7 @@ * IBM Corporation - initial API and implementation * Lars Vogel - Bug 473427 * Mickael Istria (Red Hat Inc.) - Bug 488937 + * Latha Patil (ETAS GmbH) - GitHub Issue 468 *******************************************************************************/ package org.eclipse.core.internal.properties; @@ -23,6 +24,7 @@ import org.eclipse.core.internal.properties.PropertyBucket.PropertyEntry; import org.eclipse.core.internal.resources.*; import org.eclipse.core.internal.utils.Messages; +import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.IResourceStatus; import org.eclipse.core.runtime.*; @@ -119,7 +121,8 @@ public int visit(Entry entry) { @Override public void deleteResource(IResource target) throws CoreException { - deleteProperties(target, IResource.DEPTH_INFINITE); + deleteProperties(target, ((target instanceof IFile) && (null != target.getProject())) ? IResource.DEPTH_ZERO + : IResource.DEPTH_INFINITE); } @Override diff --git a/resources/tests/org.eclipse.core.tests.resources/src/org/eclipse/core/tests/internal/properties/AllPropertiesTests.java b/resources/tests/org.eclipse.core.tests.resources/src/org/eclipse/core/tests/internal/properties/AllPropertiesTests.java index 53d774b162a..7007891699f 100644 --- a/resources/tests/org.eclipse.core.tests.resources/src/org/eclipse/core/tests/internal/properties/AllPropertiesTests.java +++ b/resources/tests/org.eclipse.core.tests.resources/src/org/eclipse/core/tests/internal/properties/AllPropertiesTests.java @@ -10,6 +10,7 @@ * * Contributors: * IBM Corporation - initial API and implementation + * Latha Patil (ETAS GmbH) - GitHub Issue 468 *******************************************************************************/ package org.eclipse.core.tests.internal.properties; @@ -17,6 +18,6 @@ import org.junit.runners.Suite; @RunWith(Suite.class) -@Suite.SuiteClasses({ PropertyManagerTest.class }) +@Suite.SuiteClasses({ PropertyManagerTest.class, Bug468PerformanceTest.class }) public class AllPropertiesTests { } diff --git a/resources/tests/org.eclipse.core.tests.resources/src/org/eclipse/core/tests/internal/properties/Bug468PerformanceTest.java b/resources/tests/org.eclipse.core.tests.resources/src/org/eclipse/core/tests/internal/properties/Bug468PerformanceTest.java new file mode 100644 index 00000000000..3a9f1f2a91f --- /dev/null +++ b/resources/tests/org.eclipse.core.tests.resources/src/org/eclipse/core/tests/internal/properties/Bug468PerformanceTest.java @@ -0,0 +1,166 @@ +/******************************************************************************* + * Copyright (c) ETAS GmbH 2023, all rights reserved. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * ETAS GmbH - initial API and implementation + *******************************************************************************/ +package org.eclipse.core.tests.internal.properties; + +import java.io.ByteArrayInputStream; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.TimeUnit; +import junit.framework.TestCase; +import org.eclipse.core.resources.IFile; +import org.eclipse.core.resources.IFolder; +import org.eclipse.core.resources.IProject; +import org.eclipse.core.resources.IWorkspace; +import org.eclipse.core.resources.ResourcesPlugin; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.ICoreRunnable; +import org.eclipse.core.runtime.NullProgressMonitor; +import org.eclipse.core.runtime.QualifiedName; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +//Test case for GitHub Issue 468 +public class Bug468PerformanceTest extends TestCase { + + private static final String TO_BE_DELETED_FILE_NAME_PREFIX = "file_"; + + private static final String TEMP_FOLDER_NAME = "temp"; + + private IProject project; + + /** + * Creates project with below folder and file structure. + *
    + *
  1. Project contains 1 single temp folder.
  2. + *
  3. 'temp' folder contains 10 folders and 6000 files as direct children.
  4. + *
  5. each folder under 'temp' contains 10 folders (called as GrandChildren) + * and a single file.
  6. + *
  7. each GrandChildren, inturn contains another 10 folders (3rd level folder) + * and a single file.
  8. + *
  9. each 3rd level folder contains a single file. + *
  10. + *
+ * A dummy property is created for all the files to ensure that the folder + * structure are created in the workspace .metadata area. + * + * @throws Exception + * If anything goes wrong during the set up. + */ + @Override + @Before + protected void setUp() throws Exception { + + super.setUp(); + ResourcesPlugin.getWorkspace().run((ICoreRunnable) monitor -> { + createTestProject(); + List childFolders = new ArrayList<>(); + List grandChildFolders = new ArrayList<>(); + IFolder tempFolder = Bug468PerformanceTest.this.project.getFolder(TEMP_FOLDER_NAME); + tempFolder.create(true, true, new NullProgressMonitor()); + // 'temp' folder contains 10 folder and 6000 files as direct children. + for (int fileIndex = 0; fileIndex < 6000; fileIndex++) { + createFile(tempFolder, TO_BE_DELETED_FILE_NAME_PREFIX + fileIndex); + } + for (int childIndex = 0; childIndex < 10; childIndex++) { + IFolder childFolder = createFolder(tempFolder, "temp_child_" + childIndex); + // each child contains 10 folders and a single file. + createTempFile(childFolder); + childFolders.add(childFolder); + } + for (int grandChildIndex = 0; grandChildIndex < 10; grandChildIndex++) { + IFolder grandChildFolder = createFolder(childFolders.get(grandChildIndex), + "temp_grandChild_" + grandChildIndex); + // each GrandChildren, intern contains another 10 folders and a single file. + createTempFile(grandChildFolder); + grandChildFolders.add(grandChildFolder); + + } + for (int thirdLevelIndex = 0; thirdLevelIndex < 10; thirdLevelIndex++) { + IFolder thirdLvLChildFolder = createFolder(grandChildFolders.get(thirdLevelIndex), + "temp_3rdLvlChild_" + thirdLevelIndex); + // each 3rd level folder contains a single file. + createTempFile(thirdLvLChildFolder); + } + }, this.project, IWorkspace.AVOID_UPDATE, new NullProgressMonitor()); + } + + private void createTestProject() throws CoreException { + Bug468PerformanceTest.this.project = ResourcesPlugin.getWorkspace().getRoot() + .getProject(getName() + "_TestProject"); + Bug468PerformanceTest.this.project.create(new NullProgressMonitor()); + Bug468PerformanceTest.this.project.open(new NullProgressMonitor()); + } + + private IFolder createFolder(final IFolder parent, final String folderName) throws CoreException { + IFolder childFolder = parent.getFolder(folderName); + childFolder.create(true, true, new NullProgressMonitor()); + return childFolder; + } + + private IFile createFile(final IFolder parent, final String fileName) throws CoreException { + IFile file = parent.getFile(fileName); + InputStream source = new ByteArrayInputStream(file.getName().getBytes()); + file.create(source, true, new NullProgressMonitor()); + file.setPersistentProperty(new QualifiedName(this.getClass().getName(), file.getName()), file.getName()); + return file; + } + + private IFile createTempFile(final IFolder parent) throws CoreException { + return createFile(parent, "tempFile"); + } + + /** + * Deletes the project + * + * @throws Exception + * if any exception happens during the deleting of the project. + */ + @Override + @After + protected void tearDown() throws Exception { + this.project.delete(true, new NullProgressMonitor()); + super.tearDown(); + } + + /* + * Test the timings for file deletion + */ + // For 3 tries, the time was around 18000 ms to 25000 ms in windows 10 machine, + // so, set a limit of 1 minute. + @Test + public void test() throws CoreException { + IFolder tempFolder = this.project.getFolder(TEMP_FOLDER_NAME); + long[] timeTakenForDeletingFiles = new long[1]; + + ResourcesPlugin.getWorkspace().run((ICoreRunnable) monitor -> { + long startTime = System.currentTimeMillis(); + + for (int fileIndex = 0; fileIndex < 6000; fileIndex++) { + IFile fileToBeDeleted = tempFolder.getFile(TO_BE_DELETED_FILE_NAME_PREFIX + fileIndex); + fileToBeDeleted.delete(true, new NullProgressMonitor()); + } + + long endTime = System.currentTimeMillis(); + timeTakenForDeletingFiles[0] = endTime - startTime; + }, this.project, IWorkspace.AVOID_UPDATE, new NullProgressMonitor()); + + long maxTime = TimeUnit.MILLISECONDS.convert(1, TimeUnit.MINUTES); + assertTrue("The expected min time(ms): " + 0 + ", actual time(ms): " + timeTakenForDeletingFiles[0], + 0 <= timeTakenForDeletingFiles[0]); + assertTrue("The expected max time(ms): " + maxTime + ", actual time(ms): " + timeTakenForDeletingFiles[0], + timeTakenForDeletingFiles[0] <= maxTime); + } +}