auto test manager: rerun on process termination if changes were made during test run
authorSergey Simonchik <sergey.simonchik@jetbrains.com>
Tue, 27 Jan 2015 14:24:37 +0000 (17:24 +0300)
committerSergey Simonchik <sergey.simonchik@jetbrains.com>
Tue, 27 Jan 2015 14:24:37 +0000 (17:24 +0300)
platform/platform-impl/src/com/intellij/execution/DelayedDocumentWatcher.java
platform/testRunner/src/com/intellij/execution/testframework/autotest/AutoTestManager.java

index b8e17ea3eded0441429f0ab39d800eefc55b96ff..822a69e3b22738988119356c118fe29cb985cc48 100644 (file)
@@ -15,7 +15,6 @@
  */
 package com.intellij.execution;
 
-import com.google.common.collect.ImmutableSet;
 import com.intellij.AppTopics;
 import com.intellij.openapi.application.ApplicationManager;
 import com.intellij.openapi.application.ModalityState;
@@ -55,7 +54,7 @@ public class DelayedDocumentWatcher {
   private final Project myProject;
   private final Alarm myAlarm;
   private final int myDelayMillis;
-  private final Consumer<Set<VirtualFile>> myConsumer;
+  private final Consumer<Integer> myModificationStampConsumer;
   private final Condition<VirtualFile> myChangedFileFilter;
   private final MyDocumentAdapter myListener;
   private final Runnable myAlarmRunnable;
@@ -63,16 +62,16 @@ public class DelayedDocumentWatcher {
   private final Set<VirtualFile> myChangedFiles = new THashSet<VirtualFile>();
   private boolean myDocumentSavingInProgress = false;
   private MessageBusConnection myConnection;
-  private int myEventCount = 0;
+  private int myModificationStamp = 0;
 
   public DelayedDocumentWatcher(@NotNull Project project,
                                 int delayMillis,
-                                @NotNull Consumer<Set<VirtualFile>> consumer,
+                                @NotNull Consumer<Integer> modificationStampConsumer,
                                 @Nullable Condition<VirtualFile> changedFileFilter) {
     myProject = project;
     myAlarm = new Alarm(Alarm.ThreadToUse.SWING_THREAD, myProject);
     myDelayMillis = delayMillis;
-    myConsumer = consumer;
+    myModificationStampConsumer = modificationStampConsumer;
     myChangedFileFilter = changedFileFilter;
     myListener = new MyDocumentAdapter();
     myAlarmRunnable = new MyRunnable();
@@ -110,6 +109,10 @@ public class DelayedDocumentWatcher {
     }
   }
 
+  public boolean isUpToDate(int modificationStamp) {
+    return myModificationStamp == modificationStamp;
+  }
+
   private class MyDocumentAdapter extends DocumentAdapter {
     @Override
     public void documentChanged(DocumentEvent event) {
@@ -136,19 +139,18 @@ public class DelayedDocumentWatcher {
 
       myAlarm.cancelRequest(myAlarmRunnable);
       myAlarm.addRequest(myAlarmRunnable, myDelayMillis);
-      myEventCount++;
+      myModificationStamp++;
     }
   }
 
   private class MyRunnable implements Runnable {
     @Override
     public void run() {
-      final int oldEventCount = myEventCount;
-      final Set<VirtualFile> copy = ImmutableSet.copyOf(myChangedFiles);
-      asyncCheckErrors(copy, new Consumer<Boolean>() {
+      final int oldModificationStamp = myModificationStamp;
+      asyncCheckErrors(myChangedFiles, new Consumer<Boolean>() {
         @Override
         public void consume(Boolean errorsFound) {
-          if (myEventCount != oldEventCount) {
+          if (myModificationStamp != oldModificationStamp) {
             // 'documentChanged' event was raised during async checking files for errors
             // Do nothing in that case, this method will be invoked subsequently.
             return;
@@ -159,7 +161,7 @@ public class DelayedDocumentWatcher {
             return;
           }
           myChangedFiles.clear();
-          myConsumer.consume(copy);
+          myModificationStampConsumer.consume(myModificationStamp);
         }
       });
     }
@@ -218,5 +220,4 @@ public class DelayedDocumentWatcher {
     }
     return WolfTheProblemSolver.getInstance(myProject).hasSyntaxErrors(file);
   }
-
 }
index 14e0cf32ef62def2fbbffc4a17cf5400f1a7b6b6..c0993f9449a26a1eeb4895421510e80016a798fc 100644 (file)
 package com.intellij.execution.testframework.autotest;
 
 import com.intellij.execution.DelayedDocumentWatcher;
+import com.intellij.execution.process.ProcessAdapter;
+import com.intellij.execution.process.ProcessEvent;
 import com.intellij.execution.process.ProcessHandler;
+import com.intellij.execution.process.ProcessListener;
 import com.intellij.execution.runners.ExecutionUtil;
 import com.intellij.execution.ui.RunContentDescriptor;
 import com.intellij.ide.DataManager;
 import com.intellij.ide.util.PropertiesComponent;
 import com.intellij.openapi.actionSystem.DataContext;
 import com.intellij.openapi.actionSystem.LangDataKeys;
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.application.ModalityState;
 import com.intellij.openapi.components.ServiceManager;
 import com.intellij.openapi.fileEditor.FileEditorManager;
 import com.intellij.openapi.project.Project;
@@ -32,6 +37,8 @@ import com.intellij.openapi.vfs.VirtualFile;
 import com.intellij.ui.content.Content;
 import com.intellij.util.Consumer;
 import com.intellij.util.containers.WeakHashMap;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
 
 import javax.swing.*;
 import java.util.Collections;
@@ -43,6 +50,7 @@ import java.util.Set;
 public class AutoTestManager {
   static final Key<Boolean> AUTOTESTABLE = Key.create("auto.test.manager.supported");
   public static final String AUTO_TEST_MANAGER_DELAY = "auto.test.manager.delay";
+  private static final Key<ProcessListener> ON_TERMINATION_RESTARTER_KEY = Key.create("auto.test.manager.on.termination.restarter");
 
   private final Project myProject;
 
@@ -63,11 +71,11 @@ public class AutoTestManager {
   }
 
   private DelayedDocumentWatcher createWatcher() {
-    return new DelayedDocumentWatcher(myProject, myDelay, new Consumer<Set<VirtualFile>>() {
+    return new DelayedDocumentWatcher(myProject, myDelay, new Consumer<Integer>() {
       @Override
-      public void consume(Set<VirtualFile> files) {
+      public void consume(Integer modificationStamp) {
         for (Content content : myEnabledDescriptors) {
-          runAutoTest(content);
+          runAutoTest(content, modificationStamp, myDocumentWatcher);
         }
       }
     }, new Condition<VirtualFile>() {
@@ -79,13 +87,14 @@ public class AutoTestManager {
     });
   }
 
-  public void setAutoTestEnabled(RunContentDescriptor descriptor, boolean enabled) {
+  public void setAutoTestEnabled(@NotNull RunContentDescriptor descriptor, boolean enabled) {
     Content content = descriptor.getAttachedContent();
     if (enabled) {
       myEnabledDescriptors.add(content);
       myDocumentWatcher.activate();
     }
     else {
+      clearRestarterListener(descriptor.getProcessHandler());
       myEnabledDescriptors.remove(content);
       if (myEnabledDescriptors.isEmpty()) {
         myDocumentWatcher.deactivate();
@@ -93,11 +102,21 @@ public class AutoTestManager {
     }
   }
 
+  private static void clearRestarterListener(@Nullable ProcessHandler processHandler) {
+    if (processHandler != null) {
+      ProcessListener restarterListener = ON_TERMINATION_RESTARTER_KEY.get(processHandler, null);
+      if (restarterListener != null) {
+        processHandler.removeProcessListener(restarterListener);
+        ON_TERMINATION_RESTARTER_KEY.set(processHandler, null);
+      }
+    }
+  }
+
   public boolean isAutoTestEnabled(RunContentDescriptor descriptor) {
     return myEnabledDescriptors.contains(descriptor.getAttachedContent());
   }
 
-  private static void runAutoTest(Content content) {
+  private static void runAutoTest(@NotNull Content content, int modificationStamp, @NotNull DelayedDocumentWatcher documentWatcher) {
     JComponent component = content.getComponent();
     if (component != null) {
       DataContext dataContext = DataManager.getInstance().getDataContext(component);
@@ -105,16 +124,48 @@ public class AutoTestManager {
       if (descriptor != null) {
         ProcessHandler processHandler = descriptor.getProcessHandler();
         if (processHandler != null && !processHandler.isProcessTerminated()) {
-          return;
+          scheduleRestartOnTermination(descriptor, content, processHandler, modificationStamp, documentWatcher);
+        }
+        else {
+          restart(descriptor, content);
         }
-
-        descriptor.setActivateToolWindowWhenAdded(false);
-        descriptor.setReuseToolWindowActivation(true);
-        ExecutionUtil.restart(content);
       }
     }
   }
 
+  private static void scheduleRestartOnTermination(@NotNull final RunContentDescriptor descriptor,
+                                                   @NotNull final Content content,
+                                                   @NotNull final ProcessHandler processHandler,
+                                                   final int modificationStamp,
+                                                   @NotNull final DelayedDocumentWatcher documentWatcher) {
+    ProcessListener restarterListener = ON_TERMINATION_RESTARTER_KEY.get(processHandler);
+    if (restarterListener != null) {
+      return;
+    }
+    restarterListener = new ProcessAdapter() {
+      @Override
+      public void processTerminated(ProcessEvent event) {
+        clearRestarterListener(processHandler);
+        ApplicationManager.getApplication().invokeLater(new Runnable() {
+          @Override
+          public void run() {
+            if (documentWatcher.isUpToDate(modificationStamp)) {
+              restart(descriptor, content);
+            }
+          }
+        }, ModalityState.any());
+      }
+    };
+    ON_TERMINATION_RESTARTER_KEY.set(processHandler, restarterListener);
+    processHandler.addProcessListener(restarterListener);
+  }
+
+  private static void restart(@NotNull RunContentDescriptor descriptor, @NotNull Content content) {
+    descriptor.setActivateToolWindowWhenAdded(false);
+    descriptor.setReuseToolWindowActivation(true);
+    ExecutionUtil.restart(content);
+  }
+
   int getDelay() {
     return myDelay;
   }