Skip to main content

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [List Home]
Re: [wtp-dev] JUnit tip: How to delete projects

Last week we fixed a bug in 3.0.1 where Dali was not closing InputStreams (bug 244268).  After fixing this bug I was able to remove our unit test workarounds for deleting projects (using ProjectUtility and creating new projects with unique names). We no longer have any difficulty with deleting projects in our unit tests.  I mention this in case other teams have the same problem, a review of InputStream usage might be in order.

thanks,
Karen

Jason A Sholl wrote:

Take a look at org.eclipse.wst.common.tests.ProjectUtility in the org.eclipse.wst.common.tests plugin.  There is a method in there for deleting projects as well.

Thank you,

Jason A. Sholl
jsholl@xxxxxxxxxx
919-543-0011 (t/l 441-0011)




"Mitov, Kiril" <k.mitov@xxxxxxx>
Sent by: wtp-dev-bounces@xxxxxxxxxxx

02/01/2008 07:47 AM

Please respond to
"General discussion of project-wide or architectural issues."        <wtp-dev@xxxxxxxxxxx>

To
"General discussion of project-wide or architectural issues." <wtp-dev@xxxxxxxxxxx>
cc

Subject
RE: [wtp-dev] JUnit tip: How to delete projects







When it comes to deleting files I have once implemented this method
 
private void deleteFileAndUpdate(IFile file) throws Exception {
            ChangeListenerWithSemaphore listener = new ChangeListenerWithSemaphore(1);
            ResourcesPlugin.getWorkspace().addResourceChangeListener(listener, IResourceChangeEvent.POST_CHANGE);
            int retries = 3;
            while (retries > 0) {
                  try {
                        deleteFile(file);
                        if (listener.waitForEvents()) {
                              retries = 0;
                              break;
                        }
                  } catch (CoreException e) {
                      ... more core here... see in the description.
                  } finally {
                        retries--;
                  }
            }
            assertTrue(retries == -1);
            ResourcesPlugin.getWorkspace().removeResourceChangeListener(listener);
      }
 
The basic idea here is that you have not deleted a file/folder/project until you receive a ResourceChangeEvent for the deletion.
Since the event might be in the same thread or in another thread it is important to stop the current thread until an event occurs. This is what I am doing here. This has also worked for folders and projects. The listener.waitForEvents() is blocking the current thread until a notification for the deletion is received. In general the thread is blocked until the specified number of events (the number in the constructor of ChangeListenerWithSemaphore) is reached. But this can easily be changed to an another logic.
 
Here the "retires" are the number of failiers that I am willing to accept. In the catch(CoreException e) there is a special logic. If the message of the exception is "The requested operation cannot be performed on a file with a user-mapped section open" the code of the catch block is:
 
                        /*
                         * An exception was occurring - "The requested operation cannot
                         * be performed on a file with a user-mapped section open".
                         * Running the finalization as suggested at
                         * http://webteam.archive.org/jira/browse/HER-661?decorator=printable
                         */
                        System.runFinalization();
                        System.gc();

But I was unable to investigate the error more deeply.

The deleteFile method is:
protected static boolean deleteFile(final IFile file) throws Exception {
            ResourcesPlugin.getWorkspace().run(new IWorkspaceRunnable() {
                  public void run(IProgressMonitor monitor) throws CoreException {
                        file.delete(false, monitor);
                  }
            }, monitor);
            return true;
}

 

The code of ChangeListenerWithSemaphore is
/**
 * This change listener will release a semaphore after the resource changed
 * method is called. For every resourceChanged call one release will be called.
 *
 * @author Kiril Mitov k.mitov@xxxxxxx
 *
 */
public final class ChangeListenerWithSemaphore implements IResourceChangeListener {
      private final Semaphore s;
      private final List<Object> deltas;
      private final int expectedEvents;
      private final List<IResourceChangeEvent> receivedEvents;
 
      public ChangeListenerWithSemaphore(int expectedEvents) throws InterruptedException {
            this.expectedEvents = expectedEvents;
            this.s = new Semaphore(1);
            this.s.acquire();
            this.deltas = Collections.synchronizedList(new LinkedList<Object>());
            this.receivedEvents = Collections.synchronizedList(new LinkedList<IResourceChangeEvent>());
      }
 
      public synchronized void resourceChanged(IResourceChangeEvent event) {
            receivedEvents.add(event);
            if (receivedEvents.size() > expectedEvents)
                  throw new IllegalStateException("The expected events were already reached");
            try {
                  deltas.add(event.getDelta());
            } catch (Exception e) {
                  Activator.getDefault().getLog().log(new Status(IStatus.ERROR, Activator.PLUGIN_ID, e.getMessage(), e));
                  return;
            } finally {
                  if (expectedEvents == receivedEvents.size())
                        s.release();
            }
      }
 
      public boolean waitForEvents() throws InterruptedException {
            return s.tryAcquire(5, TimeUnit.SECONDS);
      }
 
      public synchronized List<Object> getDeltas() {
            return deltas;
      }
 
      public synchronized int getEvents() {
            return receivedEvents.size();
      }
 
      public List<IResourceChangeEvent> getReceivedEvents() {
            return receivedEvents;
      }
}
This is how I am creating, deleting and updating files.
 
Best Regards,
Kiril


From: wtp-dev-bounces@xxxxxxxxxxx [mailto:wtp-dev-bounces@xxxxxxxxxxx] On Behalf Of David M Williams
Sent:
Friday, 1. February 2008 10:16
To:
wtp-dev@xxxxxxxxxxx
Subject:
[wtp-dev] JUnit tip: How to delete projects



Some of our JUnits suites require that projects or other resources be deleted during the test, for various reasons, and I think some of the "random errors" we see are because it's hard to delete a project, or file, in a multi-threaded Eclipse. If there is a file from the project open in another thread, for example, perhaps in a thread that's running validation, hence reading files, the delete will fail ("randomly") and then that might cause a JUnit test to fail, directly or indirectly (by having an unexpected state).

I had to solve this, just this evening, and took me a while, reading platform test examples, and googling around, to find a solution that I think is fairly good ... for JUnit tests, at least.

I've pasted the code below ... just in case it helps anyone. Hopefully the constants are obvious, but if anyone wants to see the whole class, it is
org.eclipse.jst.jsp.core.tests.taglibindex.TestIndex

And ... as always the case in open development ... if anyone has other tips/tricks, or better ways, feel free to let us know.

Thanks,




       
/**
       
* It's not easy to delete projects. If any of it's files are open by another thread,
       
* the operation will fail. So, this method will make several attempts before giving up.
       
* @param project
       
* @throws CoreException
       
* @throws InterruptedException
       
*/
       
private void deleteProject(IProject project) throws CoreException, InterruptedException {
               
int nTrys = 0;
               
while (project != null && project.exists() && nTrys < MAX_RETRYS) {
                       
try {
                               nTrys++;

                               project.delete(
true, true, null);
                       }

                       
catch (ResourceException e) {
                               
if (DEBUG) {
                                       System.
out.println();
                                       System.
out.println("Could not delete project on attempt number: "+ nTrys);
                                       IStatus eStatus = e.getStatus();

                                       
// should always be MultiStatus, but we'll check
                                       
if (eStatus instanceof MultiStatus) {
                                               MultiStatus mStatus = (MultiStatus) eStatus;

                                               IStatus[] iStatus = mStatus.getChildren();

                                               
for (int j = 0; j < iStatus.length; j++) {
                                                       System.
out.println("Status: " + j + " " + iStatus[j]);
                                               }

                                       }

                                       
else {
                                               System.
out.println("Status: " + eStatus);
                                       }

                               }

                               
/*
                                * If we could not delete the first time, wait a bit and

                                * re-try. If we could not delete, it is likely because

                                * another thread has a file open, or similar (such as the

                                * validation thread).

                                */

                               Thread.sleep(
PAUSE_TIME);
                       }

               }

               
               
if (project != null && project.exists()) {
                       fail(
"Error in test infrastructure. Could not delete project " + project + " after " + MAX_RETRYS + "attempts.");
               }

       }
_______________________________________________
wtp-dev mailing list
wtp-dev@xxxxxxxxxxx
https://dev.eclipse.org/mailman/listinfo/wtp-dev


_______________________________________________ wtp-dev mailing list wtp-dev@xxxxxxxxxxx https://dev.eclipse.org/mailman/listinfo/wtp-dev

Back to the top