Merge remote-tracking branch 'origin/master' phpstorm/163.214
authorValentina Kiryushkina <valentina.kiryushkina@jetbrains.com>
Wed, 15 Jun 2016 13:12:21 +0000 (16:12 +0300)
committerValentina Kiryushkina <valentina.kiryushkina@jetbrains.com>
Wed, 15 Jun 2016 13:12:21 +0000 (16:12 +0300)
65 files changed:
java/execution/impl/execution-impl.iml
java/execution/impl/src/com/intellij/execution/JavaTestFrameworkRunnableState.java
java/execution/impl/src/com/intellij/execution/testDiscovery/ConfigureTestDiscoveryAction.java [new file with mode: 0644]
java/execution/impl/src/com/intellij/execution/testDiscovery/InternalTestDiscoveryListener.java [new file with mode: 0644]
java/execution/impl/src/com/intellij/execution/testDiscovery/JavaAutoRunManager.java [new file with mode: 0644]
java/execution/impl/src/com/intellij/execution/testDiscovery/TestDiscoveryConfigurable.java [deleted file]
java/execution/impl/src/com/intellij/execution/testDiscovery/TestDiscoveryConfiguration.java [deleted file]
java/execution/impl/src/com/intellij/execution/testDiscovery/TestDiscoveryConfigurationProducer.java
java/execution/impl/src/com/intellij/execution/testDiscovery/TestDiscoveryExtension.java
java/execution/impl/src/com/intellij/execution/testDiscovery/TestDiscoveryIndex.java
java/execution/impl/src/com/intellij/execution/testDiscovery/TestDiscoverySearchHelper.java
java/execution/impl/src/com/intellij/execution/testDiscovery/TestInfoHolder.java [new file with mode: 0644]
java/execution/impl/src/com/intellij/testIntegration/RecentTestsData.kt
java/execution/impl/src/com/intellij/testIntegration/RunConfigurationEntry.kt
java/java-impl/src/com/intellij/codeInspection/deadCode/UnusedDeclarationPresentation.java
java/java-tests/testSrc/com/intellij/psi/StubAstSwitchTest.groovy
java/java-tests/testSrc/com/intellij/testIntergration/RecentTestsOrderTest.kt [new file with mode: 0644]
java/java-tests/testSrc/com/intellij/testIntergration/RecentTestsTest.kt
lib/coverage-agent.jar
lib/coverage-instrumenter.jar
lib/coverage-util.jar
lib/src/coverage-src.zip
platform/core-impl/src/com/intellij/openapi/editor/impl/DocumentImpl.java
platform/core-impl/src/com/intellij/psi/impl/source/PsiFileImpl.java
platform/platform-impl/src/com/intellij/idea/SocketLock.java
platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorImpl.java
platform/platform-impl/src/com/intellij/ui/FinderRecursivePanel.java
platform/platform-resources/src/launcher.py
platform/platform-tests/testSrc/com/intellij/openapi/editor/impl/EditorImplTest.java
platform/testFramework/src/com/intellij/TestAll.java
platform/testRunner/src/com/intellij/execution/DelayedDocumentWatcher.java [moved from platform/platform-impl/src/com/intellij/execution/DelayedDocumentWatcher.java with 98% similarity]
platform/testRunner/src/com/intellij/execution/testframework/autotest/AbstractAutoTestManager.java [new file with mode: 0644]
platform/testRunner/src/com/intellij/execution/testframework/autotest/AutoTestManager.java
platform/testRunner/src/com/intellij/execution/testframework/autotest/AutoTestWatcher.java [new file with mode: 0644]
platform/testRunner/src/com/intellij/execution/testframework/autotest/ToggleAutoTestAction.java
plugins/git4idea/src/git4idea/checkin/GitCheckinEnvironment.java
plugins/git4idea/src/git4idea/merge/MergeChangeCollector.java
plugins/junit/src/META-INF/plugin.xml
plugins/junit/src/com/intellij/execution/junit/JUnitConfiguration.java
plugins/junit/src/com/intellij/execution/junit/JUnitDebuggerRunner.java
plugins/junit/src/com/intellij/execution/junit/TestObject.java
plugins/junit/src/com/intellij/execution/junit/testDiscovery/JUnitTestDiscoveryConfiguration.java [deleted file]
plugins/junit/src/com/intellij/execution/junit/testDiscovery/JUnitTestDiscoveryConfigurationProducer.java
plugins/junit/src/com/intellij/execution/junit/testDiscovery/JUnitTestDiscoveryConfigurationType.java [deleted file]
plugins/junit/src/com/intellij/execution/junit/testDiscovery/JUnitTestDiscoveryRunnableState.java [new file with mode: 0644]
plugins/junit/src/com/intellij/execution/junit/testDiscovery/TestBySource.java [new file with mode: 0644]
plugins/junit/src/com/intellij/execution/junit/testDiscovery/TestsByChanges.java [new file with mode: 0644]
plugins/junit/src/com/intellij/execution/junit2/configuration/JUnitConfigurable.form
plugins/junit/src/com/intellij/execution/junit2/configuration/JUnitConfigurable.java
plugins/junit/src/com/intellij/execution/junit2/configuration/JUnitConfigurationModel.java
plugins/testng/src/META-INF/plugin.xml
plugins/testng/src/com/theoryinpractice/testng/configuration/TestNGConfiguration.java
plugins/testng/src/com/theoryinpractice/testng/configuration/TestNGConfigurationEditor.form
plugins/testng/src/com/theoryinpractice/testng/configuration/TestNGConfigurationEditor.java
plugins/testng/src/com/theoryinpractice/testng/configuration/TestNGDebuggerRunner.java
plugins/testng/src/com/theoryinpractice/testng/configuration/testDiscovery/TestNGTestDiscoveryConfiguration.java [deleted file]
plugins/testng/src/com/theoryinpractice/testng/configuration/testDiscovery/TestNGTestDiscoveryConfigurationProducer.java
plugins/testng/src/com/theoryinpractice/testng/configuration/testDiscovery/TestNGTestDiscoveryConfigurationType.java [deleted file]
plugins/testng/src/com/theoryinpractice/testng/configuration/testDiscovery/TestNGTestDiscoveryRunnableState.java [new file with mode: 0644]
plugins/testng/src/com/theoryinpractice/testng/model/TestData.java
plugins/testng/src/com/theoryinpractice/testng/model/TestNGConfigurationModel.java
plugins/testng/src/com/theoryinpractice/testng/model/TestNGSource.java [new file with mode: 0644]
plugins/testng/src/com/theoryinpractice/testng/model/TestNGTestObject.java
plugins/testng/src/com/theoryinpractice/testng/model/TestType.java
resources/src/META-INF/IdeaPlugin.xml

index e77e51c2895d29df5b32a87d360a8fd203cce554..6f09b34438bb8b6863efea807730eabeeee6369f 100644 (file)
@@ -18,6 +18,7 @@
     <orderEntry type="module" module-name="java-indexing-api" />
     <orderEntry type="module" module-name="smRunner" />
     <orderEntry type="library" name="Coverage" level="project" />
+    <orderEntry type="library" name="JUnit3" level="project" />
   </component>
   <component name="copyright">
     <Base>
index ad173f205ebb7c1937ff038bdaa60b1428148657..c33265b8aa2f517099f642b5f8ab6e2271933a25 100644 (file)
@@ -23,8 +23,11 @@ import com.intellij.execution.process.OSProcessHandler;
 import com.intellij.execution.process.ProcessAdapter;
 import com.intellij.execution.process.ProcessEvent;
 import com.intellij.execution.runners.ExecutionEnvironment;
+import com.intellij.execution.testDiscovery.JavaAutoRunManager;
 import com.intellij.execution.testframework.*;
 import com.intellij.execution.testframework.actions.AbstractRerunFailedTestsAction;
+import com.intellij.execution.testframework.autotest.AbstractAutoTestManager;
+import com.intellij.execution.testframework.autotest.ToggleAutoTestAction;
 import com.intellij.execution.testframework.sm.SMTestRunnerConnectionUtil;
 import com.intellij.execution.testframework.sm.runner.SMRunnerConsolePropertiesProvider;
 import com.intellij.execution.testframework.sm.runner.SMTRunnerConsoleProperties;
@@ -167,7 +170,12 @@ public abstract class JavaTestFrameworkRunnableState<T extends
     rerunFailedTestsAction.setModelProvider(() -> viewer);
 
     final DefaultExecutionResult result = new DefaultExecutionResult(consoleView, handler);
-    result.setRestartActions(rerunFailedTestsAction);
+    result.setRestartActions(rerunFailedTestsAction, new ToggleAutoTestAction() {
+      @Override
+      public AbstractAutoTestManager getAutoTestManager(Project project) {
+        return JavaAutoRunManager.getInstance(project);
+      }
+    });
 
     JavaRunConfigurationExtensionManager.getInstance().attachExtensionsToProcess(getConfiguration(), handler, runnerSettings);
     return result;
diff --git a/java/execution/impl/src/com/intellij/execution/testDiscovery/ConfigureTestDiscoveryAction.java b/java/execution/impl/src/com/intellij/execution/testDiscovery/ConfigureTestDiscoveryAction.java
new file mode 100644 (file)
index 0000000..ba6ee42
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2000-2016 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.intellij.execution.testDiscovery;
+
+import com.intellij.openapi.actionSystem.AnAction;
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.fileChooser.FileChooser;
+import com.intellij.openapi.fileChooser.FileChooserDescriptor;
+import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory;
+import com.intellij.openapi.util.registry.Registry;
+import com.intellij.openapi.vfs.VirtualFile;
+
+public class ConfigureTestDiscoveryAction extends AnAction {
+  @Override
+  public void update(AnActionEvent e) {
+    e.getPresentation().setEnabledAndVisible(Registry.is("testDiscovery.enabled") && e.getProject() != null);
+  }
+
+  @Override
+  public void actionPerformed(AnActionEvent e) {
+    final FileChooserDescriptor folderDescriptor = FileChooserDescriptorFactory.createSingleFolderDescriptor();
+    folderDescriptor.setTitle("Choose External Discovery Index Directory");
+    folderDescriptor.setDescription("Local directory with indices retrieved from CI \n" +
+                                    "to be replaced within TeamCity IDEA plugin");
+    final VirtualFile virtualFile = FileChooser.chooseFile(folderDescriptor, e.getProject(), null);
+    if (virtualFile != null) {
+      TestDiscoveryIndex.getInstance(e.getProject()).setRemoteTestRunDataPath(virtualFile.getPath());
+    }
+  }
+}
diff --git a/java/execution/impl/src/com/intellij/execution/testDiscovery/InternalTestDiscoveryListener.java b/java/execution/impl/src/com/intellij/execution/testDiscovery/InternalTestDiscoveryListener.java
new file mode 100644 (file)
index 0000000..5c89e1b
--- /dev/null
@@ -0,0 +1,134 @@
+/*
+ * Copyright 2000-2016 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.intellij.execution.testDiscovery;
+
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.project.ProjectManager;
+import com.intellij.openapi.util.Disposer;
+import com.intellij.util.Alarm;
+import com.intellij.util.ArrayUtil;
+import junit.framework.AssertionFailedError;
+import junit.framework.Test;
+import junit.framework.TestListener;
+
+import java.io.Closeable;
+import java.io.IOException;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Used in TestAll to collect data in command line
+ */
+@SuppressWarnings("unused")
+public class InternalTestDiscoveryListener implements TestListener, Closeable {
+  private List<String> myCompletedMethodNames = new ArrayList<String>();
+  private String myTracesDirectory;
+  private final Alarm myProcessTracesAlarm;
+  private TestDiscoveryIndex myDiscoveryIndex;
+
+  public InternalTestDiscoveryListener() {
+    myProcessTracesAlarm = new Alarm(Alarm.ThreadToUse.POOLED_THREAD, null);
+    myTracesDirectory = System.getProperty("org.jetbrains.instrumentation.trace.dir");
+  }
+
+  private TestDiscoveryIndex getIndex() {
+    if (myDiscoveryIndex == null) {
+      final Project project = ProjectManager.getInstance().getDefaultProject();
+      try {
+        myDiscoveryIndex = (TestDiscoveryIndex)Class.forName(TestDiscoveryIndex.class.getName())
+          .getConstructor(Project.class, String.class)
+          .newInstance(project, myTracesDirectory);
+      }
+      catch (Throwable e) {
+        e.printStackTrace();
+      }
+    }
+    return myDiscoveryIndex;
+  }
+
+  @Override
+  public void addError(Test test, Throwable t) {}
+
+  @Override
+  public void addFailure(Test test, AssertionFailedError t) {}
+
+  @Override
+  public void endTest(Test test) {
+    final String className = getClassName(test);
+    final String methodName = getMethodName(test);
+
+    try {
+      final Object data = getData();
+      Method testEnded = data.getClass().getMethod("testDiscoveryEnded", new Class[] {String.class});
+      testEnded.invoke(data, new Object[] {"j" + className + "-" + methodName});
+    } catch (Throwable t) {
+      t.printStackTrace();
+    }
+
+    myCompletedMethodNames.add("j" + className + "." + methodName);
+
+    if (myCompletedMethodNames.size() > 50) {
+      flushCurrentTraces();
+    }
+  }
+
+  protected void flushCurrentTraces() {
+    final String[] fullTestNames = ArrayUtil.toStringArray(myCompletedMethodNames);
+    myCompletedMethodNames.clear();
+    myProcessTracesAlarm.addRequest(() -> {
+      TestDiscoveryExtension.processAvailableTraces(fullTestNames, myTracesDirectory, null, getIndex());
+    }, 100);
+  }
+
+  private static String getMethodName(Test test) {
+    final String toString = test.toString();
+    final int braceIdx = toString.indexOf("(");
+    return braceIdx > 0 ? toString.substring(0, braceIdx) : toString;
+  }
+
+  private static String getClassName(Test test) {
+    final String toString = test.toString();
+    final int braceIdx = toString.indexOf("(");
+    return braceIdx > 0 && toString.endsWith(")") ? toString.substring(braceIdx + 1, toString.length() - 1) : null;
+  }
+
+  @Override
+  public void startTest(Test test) {
+    try {
+      final Object data = getData();
+      Method testStarted = data.getClass().getMethod("testDiscoveryStarted", new Class[] {String.class});
+      testStarted.invoke(data, new Object[] {getClassName(test) + "-" + getMethodName(test)});
+    } catch (Throwable t) {
+      t.printStackTrace();
+    }
+  }
+
+  protected Object getData() throws Exception {
+    return Class.forName("com.intellij.rt.coverage.data.ProjectData")
+      .getMethod("getProjectData", new Class[0])
+      .invoke(null, new Object[0]);
+  }
+
+  @Override
+  public void close() throws IOException {
+    myProcessTracesAlarm.cancelAllRequests();
+    myProcessTracesAlarm.addRequest(() -> {
+     flushCurrentTraces();
+      Disposer.dispose(myProcessTracesAlarm);
+    }, 0);
+  }
+}
diff --git a/java/execution/impl/src/com/intellij/execution/testDiscovery/JavaAutoRunManager.java b/java/execution/impl/src/com/intellij/execution/testDiscovery/JavaAutoRunManager.java
new file mode 100644 (file)
index 0000000..a3bec01
--- /dev/null
@@ -0,0 +1,93 @@
+/*
+ * Copyright 2000-2016 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.intellij.execution.testDiscovery;
+
+import com.intellij.execution.testframework.autotest.AbstractAutoTestManager;
+import com.intellij.execution.testframework.autotest.AutoTestWatcher;
+import com.intellij.openapi.compiler.CompilationStatusListener;
+import com.intellij.openapi.compiler.CompileContext;
+import com.intellij.openapi.compiler.CompilerManager;
+import com.intellij.openapi.components.ServiceManager;
+import com.intellij.openapi.components.State;
+import com.intellij.openapi.components.Storage;
+import com.intellij.openapi.components.StoragePathMacros;
+import com.intellij.openapi.project.Project;
+import org.jetbrains.annotations.NotNull;
+
+@State(
+  name = "JavaAutoRunManager",
+  storages = {@Storage(StoragePathMacros.WORKSPACE_FILE)}
+)
+public class JavaAutoRunManager extends AbstractAutoTestManager {
+  @NotNull
+  public static JavaAutoRunManager getInstance(Project project) {
+    return ServiceManager.getService(project, JavaAutoRunManager.class);
+  }
+
+  public JavaAutoRunManager(@NotNull Project project) {
+    super(project);
+  }
+
+  @NotNull
+  @Override
+  protected AutoTestWatcher createWatcher(Project project) {
+    return new AutoTestWatcher() {
+      private boolean myHasErrors = false;
+
+      private CompilationStatusListener myStatusListener;
+
+
+      @Override
+      public void activate() {
+        if (myStatusListener == null) {
+          myStatusListener = new CompilationStatusListener() {
+            @Override
+            public void compilationFinished(boolean aborted, int errors, int warnings, CompileContext compileContext) {
+              if (errors == 0) {
+                restartAllAutoTests(0);
+              }
+              myHasErrors = errors == 0;
+            }
+
+            @Override
+            public void automakeCompilationFinished(int errors, int warnings, CompileContext compileContext) {
+              compilationFinished(false, errors, warnings, compileContext);
+            }
+
+            @Override
+            public void fileGenerated(String outputRoot, String relativePath) {
+              int a = 1;
+            }
+          };
+
+          CompilerManager.getInstance(project).addCompilationStatusListener(myStatusListener, project);
+        }
+      }
+
+      @Override
+      public void deactivate() {
+        if (myStatusListener != null) {
+          CompilerManager.getInstance(project).removeCompilationStatusListener(myStatusListener);
+        }
+      }
+
+      @Override
+      public boolean isUpToDate(int modificationStamp) {
+        return !myHasErrors;
+      }
+    };
+  }
+}
diff --git a/java/execution/impl/src/com/intellij/execution/testDiscovery/TestDiscoveryConfigurable.java b/java/execution/impl/src/com/intellij/execution/testDiscovery/TestDiscoveryConfigurable.java
deleted file mode 100644 (file)
index 62f0601..0000000
+++ /dev/null
@@ -1,253 +0,0 @@
-/*
- * Copyright 2000-2016 JetBrains s.r.o.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.intellij.execution.testDiscovery;
-
-import com.intellij.application.options.ModulesComboBox;
-import com.intellij.execution.ExecutionBundle;
-import com.intellij.execution.MethodBrowser;
-import com.intellij.execution.ui.*;
-import com.intellij.ide.util.ClassFilter;
-import com.intellij.openapi.fileTypes.PlainTextLanguage;
-import com.intellij.openapi.options.SettingsEditor;
-import com.intellij.openapi.project.Project;
-import com.intellij.openapi.ui.ComboBox;
-import com.intellij.openapi.ui.LabeledComponent;
-import com.intellij.openapi.util.Condition;
-import com.intellij.openapi.util.Pair;
-import com.intellij.openapi.vcs.changes.ChangeListManager;
-import com.intellij.openapi.vcs.changes.LocalChangeList;
-import com.intellij.psi.JavaCodeFragment;
-import com.intellij.psi.JavaPsiFacade;
-import com.intellij.psi.PsiClass;
-import com.intellij.psi.PsiMethod;
-import com.intellij.psi.search.GlobalSearchScope;
-import com.intellij.ui.EditorTextFieldWithBrowseButton;
-import com.intellij.ui.PanelWithAnchor;
-import com.intellij.util.ui.JBUI;
-import com.intellij.util.ui.UIUtil;
-import org.jetbrains.annotations.NotNull;
-
-import javax.swing.*;
-import java.awt.*;
-import java.awt.event.ActionEvent;
-import java.awt.event.ActionListener;
-import java.util.List;
-
-import static java.awt.GridBagConstraints.*;
-
-
-public class TestDiscoveryConfigurable<T extends TestDiscoveryConfiguration> extends SettingsEditor<T> implements PanelWithAnchor {
-  private final ConfigurationModuleSelector myModuleSelector;
-  // Fields
-  private JPanel myWholePanel = new JPanel(new BorderLayout());
-  private LabeledComponent<ModulesComboBox> myModule = new LabeledComponent<ModulesComboBox>();
-  private CommonJavaParametersPanel myCommonJavaParameters = new CommonJavaParametersPanel();
-  private JrePathEditor myJrePathEditor;
-  private LabeledComponent<EditorTextFieldWithBrowseButton> myClass = new LabeledComponent<EditorTextFieldWithBrowseButton>();
-  private LabeledComponent<EditorTextFieldWithBrowseButton> myMethod = new LabeledComponent<EditorTextFieldWithBrowseButton>();
-
-  private ComboBox myChangeLists = new ComboBox();
-  private JRadioButton myPositionRb = new JRadioButton("Tests for method:");
-  private JRadioButton myChangesRb = new JRadioButton("Tests for change list:");
-  private JComponent anchor;
-
-  public TestDiscoveryConfigurable(final Project project) {
-    myModule.setText(ExecutionBundle.message("application.configuration.use.classpath.and.jdk.of.module.label"));
-    myModule.setLabelLocation(BorderLayout.WEST);
-    myModule.setComponent(new ModulesComboBox());
-    myModuleSelector = new ConfigurationModuleSelector(project, getModulesComponent());
-    myCommonJavaParameters.setModuleContext(myModuleSelector.getModule());
-    myCommonJavaParameters.setHasModuleMacro();
-    myModule.getComponent().addActionListener(new ActionListener() {
-      public void actionPerformed(ActionEvent e) {
-        myCommonJavaParameters.setModuleContext(myModuleSelector.getModule());
-      }
-    });
-    final JPanel panelWithSettings = new JPanel(new GridBagLayout());
-    final GridBagConstraints gc = new GridBagConstraints(0, RELATIVE, 1, 1, 1, 0, NORTHWEST, HORIZONTAL, JBUI.emptyInsets(), 0, 0);
-    panelWithSettings.add(myPositionRb, gc);
-    myClass.setText("Class:");
-    final ClassBrowser classBrowser = new ClassBrowser(project, "Choose Class") {
-      @Override
-      protected ClassFilter.ClassFilterWithScope getFilter() throws NoFilterException {
-        return new ClassFilter.ClassFilterWithScope() {
-          @Override
-          public GlobalSearchScope getScope() {
-            return GlobalSearchScope.allScope(project);
-          }
-
-          @Override
-          public boolean isAccepted(PsiClass aClass) {
-            return true;
-          }
-        };
-      }
-
-      @Override
-      protected PsiClass findClass(String className) {
-        return JavaPsiFacade.getInstance(project).findClass(className, GlobalSearchScope.allScope(project));
-      }
-    };
-    final EditorTextFieldWithBrowseButton classComponent = new EditorTextFieldWithBrowseButton(project, true);
-    myClass.setComponent(classComponent);
-    classBrowser.setField(classComponent);
-    panelWithSettings.add(myClass, gc);
-    myMethod.setText("Method:");
-    final EditorTextFieldWithBrowseButton textFieldWithBrowseButton = new EditorTextFieldWithBrowseButton(project, true,
-                                                                                                          JavaCodeFragment.VisibilityChecker.EVERYTHING_VISIBLE,
-                                                                                                          PlainTextLanguage.INSTANCE.getAssociatedFileType());
-    myMethod.setComponent(textFieldWithBrowseButton);
-    final MethodBrowser methodBrowser = new MethodBrowser(project) {
-      protected Condition<PsiMethod> getFilter(final PsiClass testClass) {
-        return method -> method.getContainingClass() == testClass;
-      }
-
-      @Override
-      protected String getClassName() {
-        return myClass.getComponent().getText().trim();
-      }
-
-      @Override
-      protected ConfigurationModuleSelector getModuleSelector() {
-        return myModuleSelector;
-      }
-    };
-    methodBrowser.setField(textFieldWithBrowseButton);
-    methodBrowser.installCompletion(textFieldWithBrowseButton.getChildComponent());
-    
-    panelWithSettings.add(myMethod, gc);
-    panelWithSettings.add(myChangesRb, gc);
-    panelWithSettings.add(myChangeLists, gc);
-
-    ButtonGroup gr = new ButtonGroup();
-    gr.add(myPositionRb);
-    gr.add(myChangesRb);
-
-    final ActionListener l = new ActionListener() {
-      @Override
-      public void actionPerformed(ActionEvent e) {
-        updateComponents();
-      }
-    };
-    myPositionRb.addActionListener(l);
-    myChangesRb.addActionListener(l);
-
-
-    final List<LocalChangeList> changeLists = ChangeListManager.getInstance(project).getChangeLists();
-    final DefaultComboBoxModel model = new DefaultComboBoxModel();
-    model.addElement("All");
-    for (LocalChangeList changeList : changeLists) {
-      model.addElement(changeList.getName());
-    }
-    myChangeLists.setModel(model);
-    ChangeListManager changeListManager = ChangeListManager.getInstance(project);
-    if (changeListManager.getAffectedFiles().isEmpty()) {
-      myChangesRb.setEnabled(false);
-    }
-
-    myWholePanel.add(panelWithSettings, BorderLayout.NORTH);
-    myWholePanel.add(myCommonJavaParameters, BorderLayout.CENTER);
-    final JPanel classpathPanel = new JPanel(new BorderLayout());
-    myWholePanel.add(classpathPanel, BorderLayout.SOUTH);
-
-    classpathPanel.add(myModule, BorderLayout.NORTH);
-    myJrePathEditor = new JrePathEditor(DefaultJreSelector.fromModuleDependencies(getModulesComponent(), false));
-    classpathPanel.add(myJrePathEditor, BorderLayout.CENTER);
-    UIUtil.setEnabled(myCommonJavaParameters.getProgramParametersComponent(), false, true);
-
-    setAnchor(myModule.getLabel());
-    myJrePathEditor.setAnchor(myModule.getLabel());
-    myCommonJavaParameters.setAnchor(myModule.getLabel());
-  }
-
-  private void updateComponents() {
-    myClass.setEnabled(myPositionRb.isSelected());
-    myMethod.setEnabled(myPositionRb.isSelected());
-    myChangeLists.setEnabled(myChangesRb.isSelected());
-  }
-
-  public void applyEditorTo(final TestDiscoveryConfiguration configuration) {
-    applyHelpersTo(configuration);
-    configuration.setAlternativeJrePath(myJrePathEditor.getJrePathOrName());
-    configuration.setAlternativeJrePathEnabled(myJrePathEditor.isAlternativeJreSelected());
-    configuration.setPosition(myPositionRb.isSelected() ? Pair.create(myClass.getComponent().getText().trim(), 
-                                                                      myMethod.getComponent().getText().trim()) : null);
-    if (myChangesRb.isSelected()) {
-      final Object selectedItem = myChangeLists.getSelectedItem();
-      configuration.setChangeList("All".equals(selectedItem) ? null : (String)selectedItem);
-    }
-    else {
-      configuration.setChangeList(null);
-    }
-    myCommonJavaParameters.applyTo(configuration);
-  }
-
-  public void resetEditorFrom(final TestDiscoveryConfiguration configuration) {
-    myCommonJavaParameters.reset(configuration);
-    getModuleSelector().reset(configuration);
-    myJrePathEditor
-      .setPathOrName(configuration.getAlternativeJrePath(), configuration.isAlternativeJrePathEnabled());
-    final Pair<String, String> position = configuration.getPosition();
-    if (position != null) {
-      myPositionRb.setSelected(true);
-      myClass.getComponent().setText(position.first);
-      myMethod.getComponent().setText(position.second);
-    }
-    else if (myChangesRb.isEnabled()) {
-      myChangesRb.setSelected(true);
-    }
-    else {
-      myPositionRb.setSelected(true);
-    }
-    final String changeList = configuration.getChangeList();
-    if (changeList != null) {
-      myChangeLists.setSelectedItem(changeList);
-    }
-    else if (myChangesRb.isEnabled()) {
-      myChangeLists.setSelectedIndex(0);
-    }
-    updateComponents();
-  }
-
-  public ModulesComboBox getModulesComponent() {
-    return myModule.getComponent();
-  }
-
-  public ConfigurationModuleSelector getModuleSelector() {
-    return myModuleSelector;
-  }
-
-  @Override
-  public JComponent getAnchor() {
-    return anchor;
-  }
-
-  @Override
-  public void setAnchor(JComponent anchor) {
-    this.anchor = anchor;
-  }
-
-
-  @NotNull
-  public JComponent createEditor() {
-    return myWholePanel;
-  }
-
-  private void applyHelpersTo(final TestDiscoveryConfiguration currentState) {
-    myCommonJavaParameters.applyTo(currentState);
-    getModuleSelector().applyTo(currentState);
-  }
-}
diff --git a/java/execution/impl/src/com/intellij/execution/testDiscovery/TestDiscoveryConfiguration.java b/java/execution/impl/src/com/intellij/execution/testDiscovery/TestDiscoveryConfiguration.java
deleted file mode 100644 (file)
index 4b48548..0000000
+++ /dev/null
@@ -1,228 +0,0 @@
-/*
- * Copyright 2000-2015 JetBrains s.r.o.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.intellij.execution.testDiscovery;
-
-import com.intellij.diagnostic.logging.LogConfigurationPanel;
-import com.intellij.execution.ExecutionBundle;
-import com.intellij.execution.Executor;
-import com.intellij.execution.JavaRunConfigurationExtensionManager;
-import com.intellij.execution.JavaTestConfigurationBase;
-import com.intellij.execution.configurations.ConfigurationFactory;
-import com.intellij.execution.configurations.JavaRunConfigurationModule;
-import com.intellij.execution.configurations.RunConfiguration;
-import com.intellij.execution.configurations.RuntimeConfigurationException;
-import com.intellij.execution.testframework.sm.runner.SMTRunnerConsoleProperties;
-import com.intellij.openapi.module.Module;
-import com.intellij.openapi.module.ModuleManager;
-import com.intellij.openapi.options.SettingsEditor;
-import com.intellij.openapi.options.SettingsEditorGroup;
-import com.intellij.openapi.util.InvalidDataException;
-import com.intellij.openapi.util.Pair;
-import com.intellij.openapi.util.WriteExternalException;
-import com.intellij.openapi.util.text.StringUtil;
-import com.intellij.openapi.vcs.changes.ChangeListManager;
-import org.jdom.Element;
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
-
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Map;
-
-public abstract class TestDiscoveryConfiguration extends JavaTestConfigurationBase {
-  private String myChangeList;
-  private Pair<String, String> myPosition;
-
-  protected JavaTestConfigurationBase myDelegate;
-
-  public TestDiscoveryConfiguration(String name,
-                                    @NotNull JavaRunConfigurationModule configurationModule,
-                                    @NotNull ConfigurationFactory factory, 
-                                    JavaTestConfigurationBase delegate) {
-    super(name, configurationModule, factory);
-    myDelegate = delegate;
-  }
-
-  @Override
-  public void setVMParameters(String value) {
-    myDelegate.setVMParameters(value);
-  }
-
-  @Override
-  public String getVMParameters() {
-    return myDelegate.getVMParameters();
-  }
-
-  @Override
-  public boolean isAlternativeJrePathEnabled() {
-    return myDelegate.isAlternativeJrePathEnabled();
-  }
-
-  @Override
-  public void setAlternativeJrePathEnabled(boolean enabled) {
-    myDelegate.setAlternativeJrePathEnabled(enabled);
-  }
-
-  @Override
-  @Nullable
-  public String getAlternativeJrePath() {
-    return myDelegate.getAlternativeJrePath();
-  }
-
-  @Override
-  public void setAlternativeJrePath(String path) {
-    myDelegate.setAlternativeJrePath(path);
-  }
-
-  @Override
-  public void checkConfiguration() throws RuntimeConfigurationException {
-    if (myPosition == null &&
-        myChangeList != null && ChangeListManager.getInstance(getProject()).findChangeList(myChangeList) == null) {
-      throw new RuntimeConfigurationException("Change list " + myChangeList + " doesn't exist");
-    }
-    if (myPosition != null) {
-      if (StringUtil.isEmptyOrSpaces(myPosition.first)) {
-        throw new RuntimeConfigurationException("No class specified");
-      }
-      if (StringUtil.isEmptyOrSpaces(myPosition.second)) {
-        throw new RuntimeConfigurationException("No method specified");
-      }
-    }
-    JavaRunConfigurationExtensionManager.checkConfigurationIsValid(this);
-  }
-
-  @Override
-  public Collection<Module> getValidModules() {
-    return Arrays.asList(ModuleManager.getInstance(getProject()).getModules());
-  }
-
-  @NotNull
-  @Override
-  public SettingsEditor<? extends RunConfiguration> getConfigurationEditor() {
-    SettingsEditorGroup<TestDiscoveryConfiguration> group = new SettingsEditorGroup<TestDiscoveryConfiguration>();
-    group.addEditor(ExecutionBundle.message("run.configuration.configuration.tab.title"),
-                    new TestDiscoveryConfigurable<TestDiscoveryConfiguration>(getProject()));
-    JavaRunConfigurationExtensionManager.getInstance().appendEditors(this, group);
-    group.addEditor(ExecutionBundle.message("logs.tab.title"), new LogConfigurationPanel<TestDiscoveryConfiguration>());
-    return group;
-  }
-
-  @Override
-  public void readExternal(Element element) throws InvalidDataException {
-    myDelegate.readExternal(element);
-    super.readExternal(element);
-    readModule(element);
-
-    final String classQName = element.getAttributeValue("class");
-    final String methodName = element.getAttributeValue("method");
-    myPosition = classQName != null && methodName != null ? Pair.create(classQName, methodName) : null;
-    myChangeList = element.getAttributeValue("changeList");
-    if ("All".equals(myChangeList)) {
-      myChangeList = null;
-    }
-  }
-
-  @Override
-  public void writeExternal(Element element) throws WriteExternalException {
-    myDelegate.writeExternal(element);
-    super.writeExternal(element);
-
-    writeModule(element);
-    
-    if (myPosition != null) {
-      element.setAttribute("class", myPosition.first);
-      element.setAttribute("method", myPosition.second);
-    }
-    element.setAttribute("changeList", myChangeList == null ? "All" : myChangeList);
-  }
-
-
-  @Nullable
-  @Override
-  public String getRunClass() {
-    return null;
-  }
-
-  @Nullable
-  @Override
-  public String getPackage() {
-    return "";
-  }
-
-  @Override
-  public void setProgramParameters(@Nullable String value) {
-    myDelegate.setProgramParameters(value);
-  }
-
-  @Override
-  @Nullable
-  public String getProgramParameters() {
-    return myDelegate.getProgramParameters();
-  }
-
-  @Override
-  public void setWorkingDirectory(@Nullable String value) {
-    myDelegate.setWorkingDirectory(value);
-  }
-
-  @Override
-  @Nullable
-  public String getWorkingDirectory() {
-    return myDelegate.getWorkingDirectory();
-  }
-
-  @Override
-  public void setEnvs(@NotNull Map<String, String> envs) {
-    myDelegate.setEnvs(envs);
-  }
-
-  @Override
-  @NotNull
-  public Map<String, String> getEnvs() {
-    return myDelegate.getEnvs();
-  }
-
-  @Override
-  public void setPassParentEnvs(boolean passParentEnvs) {
-    myDelegate.setPassParentEnvs(passParentEnvs);
-  }
-
-  @Override
-  public boolean isPassParentEnvs() {
-    return myDelegate.isPassParentEnvs();
-  }
-
-  @Override
-  public SMTRunnerConsoleProperties createTestConsoleProperties(Executor executor) {
-    return myDelegate.createTestConsoleProperties(executor);
-  }
-
-  public void setPosition(Pair<String, String> position) {
-    myPosition = position;
-  }
-
-  public void setChangeList(String changeList) {
-    myChangeList = changeList;
-  }
-
-  public Pair<String, String> getPosition() {
-    return myPosition;
-  }
-
-  public String getChangeList() {
-    return myChangeList;
-  }
-}
index 15899ae90efd98d9b247c6600e58eea9b616dc9a..2503982c89c7282f05571f32f5e5cceca88640e7 100644 (file)
 package com.intellij.execution.testDiscovery;
 
 import com.intellij.codeInsight.TestFrameworks;
-import com.intellij.execution.JavaExecutionUtil;
-import com.intellij.execution.Location;
+import com.intellij.execution.*;
 import com.intellij.execution.actions.ConfigurationContext;
 import com.intellij.execution.configurations.ConfigurationType;
+import com.intellij.execution.configurations.ModuleBasedConfiguration;
 import com.intellij.execution.junit.JavaRunConfigurationProducerBase;
-import com.intellij.execution.testframework.TestSearchScope;
 import com.intellij.openapi.module.Module;
-import com.intellij.openapi.util.Condition;
+import com.intellij.openapi.module.ModuleManager;
+import com.intellij.openapi.module.ModuleUtilCore;
+import com.intellij.openapi.project.Project;
 import com.intellij.openapi.util.Pair;
 import com.intellij.openapi.util.Ref;
 import com.intellij.openapi.util.registry.Registry;
@@ -34,17 +35,25 @@ import com.intellij.psi.PsiMethod;
 import com.intellij.psi.util.PsiTreeUtil;
 import com.intellij.testIntegration.TestFramework;
 import com.intellij.util.containers.ContainerUtil;
+import com.intellij.util.containers.HashSet;
 
 import java.io.IOException;
+import java.util.Arrays;
 import java.util.Collection;
+import java.util.List;
+import java.util.Set;
 
-public abstract class TestDiscoveryConfigurationProducer extends JavaRunConfigurationProducerBase<TestDiscoveryConfiguration> {
+public abstract class TestDiscoveryConfigurationProducer extends JavaRunConfigurationProducerBase<JavaTestConfigurationBase> {
   protected TestDiscoveryConfigurationProducer(ConfigurationType type) {
     super(type);
   }
 
+
+  protected abstract void setPosition(JavaTestConfigurationBase configuration, PsiLocation<PsiMethod> position);
+  protected abstract Pair<String, String> getPosition(JavaTestConfigurationBase configuration);
+
   @Override
-  protected boolean setupConfigurationFromContext(final TestDiscoveryConfiguration configuration,
+  protected boolean setupConfigurationFromContext(final JavaTestConfigurationBase configuration,
                                                   ConfigurationContext configurationContext,
                                                   Ref<PsiElement> ref) {
     if (!Registry.is("testDiscovery.enabled")) {
@@ -54,31 +63,62 @@ public abstract class TestDiscoveryConfigurationProducer extends JavaRunConfigur
     assert contextLocation != null;
     final Location location = JavaExecutionUtil.stepIntoSingleClass(contextLocation);
     if (location == null) return false;
-    final Pair<String, String> position = getPosition(location);
-    if (position != null) {
+    final PsiMethod sourceMethod = getSourceMethod(location);
+    final Pair<String, String> position = getPosition(sourceMethod);
+    if (sourceMethod != null && position != null) {
       try {
-        final Collection<String> testsByMethodName = TestDiscoveryIndex
-          .getInstance(configuration.getProject()).getTestsByMethodName(position.first, position.second);
-        if (testsByMethodName == null || ContainerUtil.filter(testsByMethodName, s -> s.startsWith(configuration.getFrameworkPrefix())).isEmpty()) return false;
-        
+        final Project project = configuration.getProject();
+        final TestDiscoveryIndex testDiscoveryIndex = TestDiscoveryIndex.getInstance(project);
+        final Collection<String> testsByMethodName = testDiscoveryIndex.getTestsByMethodName(position.first, position.second);
+        if (testsByMethodName == null ||
+            ContainerUtil.filter(testsByMethodName, s -> s.startsWith(configuration.getFrameworkPrefix())).isEmpty()) {
+          return false;
+        }
+        setPosition(configuration, new PsiLocation<PsiMethod>(sourceMethod));
+        configuration.setName("Tests for " + StringUtil.getShortName(position.first) + "." + position.second);
+
+        final RunnerAndConfigurationSettings template =
+          configurationContext.getRunManager().getConfigurationTemplate(getConfigurationFactory());
+        final Module predefinedModule = ((ModuleBasedConfiguration)template.getConfiguration()).getConfigurationModule().getModule();
+        if (predefinedModule != null) {
+          configuration.setModule(predefinedModule);
+        }
+
+        //potentially this set won't be big, it reflects modules from where user starts his tests
+        final Collection<String> modules = testDiscoveryIndex.getTestModulesByMethodName(position.first,
+                                                                                         position.second,
+                                                                                         configuration.getFrameworkPrefix());
+        if (modules.isEmpty()) return true;
+
+        final ModuleManager moduleManager = ModuleManager.getInstance(project);
+        final Set<Module> allModules = new HashSet<>(Arrays.asList(moduleManager.getModules()));
+        modules.stream()
+          .map(moduleManager::findModuleByName)
+          .filter(module -> module != null)
+          .forEach(module -> {
+            final List<Module> dependentModules = ModuleUtilCore.getAllDependentModules(module);
+            dependentModules.add(module);
+            allModules.retainAll(dependentModules);
+          });
+        if (!allModules.isEmpty()) {
+          configuration.setModule(allModules.iterator().next());
+        }
+
+        return true;
       }
       catch (IOException e) {
         return false;
       }
-      configuration.setPosition(position);
-      configuration.setName("Tests for " + StringUtil.getShortName(position.first) + "." + position.second);
-      setupPackageConfiguration(configurationContext, configuration, TestSearchScope.MODULE_WITH_DEPENDENCIES);
-      return true;
     }
     return false;
   }
 
   @Override
-  protected Module findModule(TestDiscoveryConfiguration configuration, Module contextModule) {
+  protected Module findModule(JavaTestConfigurationBase configuration, Module contextModule) {
     return null;
   }
 
-  private static Pair<String, String> getPosition(Location location) {
+  private static PsiMethod getSourceMethod(Location location) {
     final PsiElement psiElement = location.getPsiElement();
     final PsiMethod psiMethod = PsiTreeUtil.getParentOfType(psiElement, PsiMethod.class);
     if (psiMethod != null) {
@@ -88,18 +128,27 @@ public abstract class TestDiscoveryConfigurationProducer extends JavaRunConfigur
         if (testFramework != null) {
           return null;
         }
-        final String qualifiedName = containingClass.getQualifiedName();
-        if (qualifiedName != null) {
-          return Pair.create(qualifiedName, psiMethod.getName());
-        }
+        return psiMethod;
       }
     }
     return null;
   }
 
+  private static Pair<String, String> getPosition(PsiMethod method) {
+    if (method == null) {
+      return null;
+    }
+    final PsiClass containingClass = method.getContainingClass();
+    final String qualifiedName = containingClass.getQualifiedName();
+    if (qualifiedName != null) {
+      return Pair.create(qualifiedName, method.getName());
+    }
+    return null;
+  }
+
   @Override
-  public boolean isConfigurationFromContext(TestDiscoveryConfiguration configuration, ConfigurationContext configurationContext) {
-    final Pair<String, String> position = getPosition(configurationContext.getLocation());
-    return position != null && position.equals(configuration.getPosition());
+  public boolean isConfigurationFromContext(JavaTestConfigurationBase configuration, ConfigurationContext configurationContext) {
+    final Pair<String, String> position = getPosition(getSourceMethod(configurationContext.getLocation()));
+    return position != null && position.equals(getPosition(configuration));
   }
 }
index eb4dff2eddf25803cc730d15600f34206f42dec5..377583935031e822469c8fed540c256dea52cd58 100644 (file)
@@ -46,7 +46,6 @@ import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
 
 import java.io.File;
-import java.io.FilenameFilter;
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.List;
@@ -89,7 +88,10 @@ public class TestDiscoveryExtension extends RunConfigurationExtension {
               if (myCompletedMethodNames.size() > 50) {
                 final String[] fullTestNames = ArrayUtil.toStringArray(myCompletedMethodNames);
                 myCompletedMethodNames.clear();
-                processTracesAlarm.addRequest(() -> processAvailableTraces(configuration, fullTestNames), 100);
+                processTracesAlarm.addRequest(() -> processAvailableTraces(fullTestNames,
+                                                                           getTracesDirectory(configuration), configuration,
+                                                                           TestDiscoveryIndex.getInstance(configuration.getProject())
+                ), 100);
               }
             }
           }
@@ -160,7 +162,7 @@ public class TestDiscoveryExtension extends RunConfigurationExtension {
       if (testMethodTraces != null) {
         for (File testMethodTrace : testMethodTraces) {
           try {
-            coverageIndex.updateFromTestTrace(testMethodTrace);
+            coverageIndex.updateFromTestTrace(testMethodTrace, (JavaTestConfigurationBase)configuration);
             FileUtil.delete(testMethodTrace);
           }
           catch (IOException e) {
@@ -176,9 +178,10 @@ public class TestDiscoveryExtension extends RunConfigurationExtension {
     }
   }
 
-  private static void processAvailableTraces(RunConfigurationBase configuration, String[] fullTestNames) {
-    final String tracesDirectory = getTracesDirectory(configuration);
-    final TestDiscoveryIndex coverageIndex = TestDiscoveryIndex.getInstance(configuration.getProject());
+  public static void processAvailableTraces(final String[] fullTestNames,
+                                            final String tracesDirectory,
+                                            RunConfigurationBase configuration,
+                                            final TestDiscoveryIndex discoveryIndex) {
     synchronized (ourTracesLock) {
       for (String fullTestName : fullTestNames) {
         final String className = StringUtil.getPackageName(fullTestName);
@@ -187,7 +190,7 @@ public class TestDiscoveryExtension extends RunConfigurationExtension {
           final File testMethodTrace = new File(tracesDirectory, className + "-" + methodName + ".tr");
           if (testMethodTrace.exists()) {
             try {
-              coverageIndex.updateFromTestTrace(testMethodTrace);
+              discoveryIndex.updateFromTestTrace(testMethodTrace, (JavaTestConfigurationBase)configuration);
               FileUtil.delete(testMethodTrace);
             }
             catch (IOException e) {
index 73e19855dfed322822a4b305079f8026215d6761..47da00fb43772b639c66f09493ba1944ba05a732 100644 (file)
  */
 package com.intellij.execution.testDiscovery;
 
+import com.intellij.execution.JavaTestConfigurationBase;
 import com.intellij.openapi.application.ApplicationManager;
 import com.intellij.openapi.components.ProjectComponent;
 import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.module.Module;
 import com.intellij.openapi.project.Project;
 import com.intellij.openapi.startup.StartupManager;
+import com.intellij.openapi.util.Ref;
 import com.intellij.openapi.util.io.FileUtil;
-import com.intellij.openapi.vfs.newvfs.persistent.FlushingDaemon;
-import com.intellij.util.io.*;
+import com.intellij.util.ThrowableConvertor;
+import com.intellij.util.io.DataInputOutputUtil;
+import com.intellij.util.io.IOUtil;
+import gnu.trove.THashSet;
 import gnu.trove.TIntArrayList;
-import gnu.trove.TIntHashSet;
 import gnu.trove.TIntObjectHashMap;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
 
 import java.io.*;
-import java.io.DataOutputStream;
-import java.util.*;
-import java.util.concurrent.ScheduledFuture;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
 
 /**
  * @author Maxim.Mossienko on 7/9/2015.
@@ -40,532 +45,307 @@ import java.util.concurrent.ScheduledFuture;
 public class TestDiscoveryIndex implements ProjectComponent {
   static final Logger LOG = Logger.getInstance(TestDiscoveryIndex.class);
 
-  private static final int REMOVED_MARKER = -1;
-  private final Object ourLock = new Object();
   private final Project myProject;
-  private volatile Holder myHolder;
+
+  //private volatile TestInfoHolder mySystemHolder;
+  private final TestDataController myLocalTestRunDataController;
+  private final TestDataController myRemoteTestRunDataController;
 
   public TestDiscoveryIndex(Project project) {
+    this(project, TestDiscoveryExtension.baseTestDiscoveryPathForProject(project));
+  }
+
+  public TestDiscoveryIndex(final Project project, final String basePath) {
     myProject = project;
 
-    String path = TestDiscoveryExtension.baseTestDiscoveryPathForProject(myProject);
+    myLocalTestRunDataController = new TestDataController(basePath, false);
+    myRemoteTestRunDataController = new TestDataController(null, true);
 
-    if (new File(path).exists()) {
+    if (new File(basePath).exists()) {
       StartupManager.getInstance(project).registerPostStartupActivity(() -> ApplicationManager.getApplication().executeOnPooledThread(() -> {
-        getHolder(); // proactively init with maybe io costly compact
+        myLocalTestRunDataController.getHolder(); // proactively init with maybe io costly compact
       }));
     }
+
+    //{
+    //  setRemoteTestRunDataPath("C:\\ultimate\\system\\testDiscovery\\145.save");
+    //}
   }
 
   public boolean hasTestTrace(@NotNull String testName) throws IOException {
-    synchronized (ourLock) {
-      Holder holder = null;
-      try {
-        holder = getHolder();
-        final int testNameId = holder.myTestNameEnumerator.tryEnumerate(testName);
-        if (testNameId == 0) return false;
-        return holder.myTestNameToUsedClassesAndMethodMap.get(testNameId) != null;
-      } catch (Throwable throwable) {
-        thingsWentWrongLetsReinitialize(holder, throwable);
-        return false;
+    Boolean result = myLocalTestRunDataController.withTestDataHolder(localHolder -> {          // todo: remote run data
+      final int testNameId = localHolder.myTestNameEnumerator.tryEnumerate(testName);
+      if (testNameId == 0) {
+        return myRemoteTestRunDataController.withTestDataHolder(remoteHolder -> {
+          final int testNameId1 = remoteHolder.myTestNameEnumerator.tryEnumerate(testName);
+          return testNameId1 != 0 && remoteHolder.myTestNameToUsedClassesAndMethodMap.get(testNameId1) != null;
+        }) != null;
       }
-    }
+      return localHolder.myTestNameToUsedClassesAndMethodMap.get(testNameId) != null;
+    });
+    return result == Boolean.TRUE;
   }
 
   public void removeTestTrace(@NotNull String testName) throws IOException {
-    synchronized (ourLock) {
-      Holder holder = null;
-      try {
-        holder = getHolder();
-
-        final int testNameId = holder.myTestNameEnumerator.tryEnumerate(testName);
-        if (testNameId == 0) return;
-        doUpdateFromDiff(holder, testNameId, null, holder.myTestNameToUsedClassesAndMethodMap.get(testNameId));
-      } catch (Throwable throwable) {
-        thingsWentWrongLetsReinitialize(holder, throwable);
+    myLocalTestRunDataController.withTestDataHolder(new ThrowableConvertor<TestInfoHolder, Void, IOException>() {
+      @Override
+      public Void convert(TestInfoHolder localHolder) throws IOException {
+        final int testNameId = localHolder.myTestNameEnumerator.tryEnumerate(testName);  // todo remove remote data isn't possible
+        if (testNameId != 0) {
+          localHolder.doUpdateFromDiff(testNameId, null,
+                                  localHolder.myTestNameToUsedClassesAndMethodMap.get(testNameId),
+                                  null);
+        }
+        return null;
       }
+    });
+  }
+
+  public void setRemoteTestRunDataPath(String path) {
+    if(!TestInfoHolder.isValidPath(path)) {
+      path = null;
     }
+    myRemoteTestRunDataController.init(path);
+    // todo: should we remove our local run data ?
   }
 
   public Collection<String> getTestsByMethodName(@NotNull String classFQName, @NotNull String methodName) throws IOException {
-    synchronized (ourLock) {
-      Holder holder = null;
-      try {
-        holder = getHolder();
-        final TIntArrayList list = holder.myMethodQNameToTestNames.get(
-          createKey(
-            holder.myClassEnumerator.enumerate(classFQName),
-            holder.myMethodEnumerator.enumerate(methodName)
+    return myLocalTestRunDataController.withTestDataHolder(new ThrowableConvertor<TestInfoHolder, Collection<String>, IOException>() {
+      @Override
+      public Collection<String> convert(TestInfoHolder localHolder) throws IOException {
+        TIntArrayList remoteList = myRemoteTestRunDataController.withTestDataHolder(
+          remoteHolder -> remoteHolder.myMethodQNameToTestNames.get(
+            TestInfoHolder.createKey(
+              remoteHolder.myClassEnumerator.enumerate(classFQName),
+              remoteHolder.myMethodEnumerator.enumerate(methodName)
+            )
           )
         );
-        if (list == null) return Collections.emptyList();
-        final ArrayList<String> result = new ArrayList<String>(list.size());
-        for (int testNameId : list.toNativeArray()) result.add(holder.myTestNameEnumerator.valueOf(testNameId));
-        return result;
-      } catch (Throwable throwable) {
-        thingsWentWrongLetsReinitialize(holder, throwable);
-        return Collections.emptyList();
-      }
-    }
-  }
-
-  private Holder getHolder() {
-    Holder holder = myHolder;
-
-    if (holder == null) {
-      synchronized (ourLock) {
-        holder = myHolder;
-        if (holder == null) holder = myHolder = new Holder();
-      }
-    }
-    return holder;
-  }
-
-  public static TestDiscoveryIndex getInstance(Project project) {
-    return project.getComponent(TestDiscoveryIndex.class);
-  }
-
-  @Override
-  public void initComponent() {
-  }
-
-  @Override
-  public void disposeComponent() {
-    synchronized (ourLock) {
-      Holder holder = myHolder;
-      if (holder != null) {
-        holder.dispose();
-        myHolder = null;
-      }
-    }
-  }
 
-  @NotNull
-  @Override
-  public String getComponentName() {
-    return getClass().getName();
-  }
-
-  @Override
-  public void projectOpened() {
-  }
-
-  @Override
-  public void projectClosed() {
-  }
+        final TIntArrayList localList = localHolder.myMethodQNameToTestNames.get(
+          TestInfoHolder.createKey(
+            localHolder.myClassEnumerator.enumerate(classFQName),
+            localHolder.myMethodEnumerator.enumerate(methodName)
+          )
+        );
 
-  private static final int VERSION = 2;
-
-  private final class Holder {
-    final PersistentHashMap<Long, TIntArrayList> myMethodQNameToTestNames;
-    final PersistentHashMap<Integer, TIntObjectHashMap<TIntArrayList>> myTestNameToUsedClassesAndMethodMap;
-    final PersistentStringEnumerator myClassEnumerator;
-    final CachingEnumerator<String> myClassEnumeratorCache;
-    final PersistentStringEnumerator myMethodEnumerator;
-    final CachingEnumerator<String> myMethodEnumeratorCache;
-    final PersistentStringEnumerator myTestNameEnumerator;
-    final List<PersistentEnumeratorDelegate> myConstructedDataFiles = new ArrayList<PersistentEnumeratorDelegate>(4);
-
-    private ScheduledFuture<?> myFlushingFuture;
-    private boolean myDisposed;
-
-    Holder() {
-      String path = TestDiscoveryExtension.baseTestDiscoveryPathForProject(myProject);
-      final File versionFile = getVersionFile(path);
-      versionFile.getParentFile().mkdirs();
-      final File methodQNameToTestNameFile = new File(path + File.separator + "methodQNameToTestName.data");
-      final File testNameToUsedClassesAndMethodMapFile = new File(path + File.separator + "testToCalledMethodNames.data");
-      final File classNameEnumeratorFile = new File(path + File.separator + "classNameEnumerator.data");
-      final File methodNameEnumeratorFile = new File(path + File.separator + "methodNameEnumerator.data");
-      final File testNameEnumeratorFile = new File(path + File.separator + "testNameEnumerator.data");
-
-      try {
-        int version = readVersion(versionFile);
-        if (version != VERSION) {
-          LOG.info("TestDiscoveryIndex was rewritten due to version change");
-          deleteAllIndexDataFiles(methodQNameToTestNameFile, testNameToUsedClassesAndMethodMapFile, classNameEnumeratorFile, methodNameEnumeratorFile, testNameEnumeratorFile);
-
-          writeVersion(versionFile);
+        if (remoteList == null) {
+          return testIdsToTestNames(localList, localHolder);
         }
 
-        PersistentHashMap<Long, TIntArrayList> methodQNameToTestNames;
-        PersistentHashMap<Integer, TIntObjectHashMap<TIntArrayList>> testNameToUsedClassesAndMethodMap;
-        PersistentStringEnumerator classNameEnumerator;
-        PersistentStringEnumerator methodNameEnumerator;
-        PersistentStringEnumerator testNameEnumerator;
-
-        int iterations = 0;
-
-        while(true) {
-          ++iterations;
-
-          try {
-            methodQNameToTestNames = new PersistentHashMap<Long, TIntArrayList>(
-              methodQNameToTestNameFile,
-              new MethodQNameSerializer(),
-              new TestNamesExternalizer()
-            );
-            myConstructedDataFiles.add(methodQNameToTestNames);
-
-            testNameToUsedClassesAndMethodMap = new PersistentHashMap<Integer, TIntObjectHashMap<TIntArrayList>>(
-              testNameToUsedClassesAndMethodMapFile,
-              EnumeratorIntegerDescriptor.INSTANCE,
-              new ClassesAndMethodsMapDataExternalizer()
-            );
-            myConstructedDataFiles.add(testNameToUsedClassesAndMethodMap);
-
-            classNameEnumerator = new PersistentStringEnumerator(classNameEnumeratorFile);
-            myConstructedDataFiles.add(classNameEnumerator);
-
-            methodNameEnumerator = new PersistentStringEnumerator(methodNameEnumeratorFile);
-            myConstructedDataFiles.add(methodNameEnumerator);
-
-            testNameEnumerator = new PersistentStringEnumerator(testNameEnumeratorFile);
-            myConstructedDataFiles.add(testNameEnumerator);
-
-            break;
-          } catch (Throwable throwable) {
-            LOG.info("TestDiscoveryIndex problem", throwable);
-            closeAllConstructedFiles(true);
-            myConstructedDataFiles.clear();
-
-            deleteAllIndexDataFiles(methodQNameToTestNameFile, testNameToUsedClassesAndMethodMapFile, classNameEnumeratorFile, methodNameEnumeratorFile,
-                                    testNameEnumeratorFile);
-            // try another time
-          }
+        Collection<String> testsFromRemote =
+          myRemoteTestRunDataController.withTestDataHolder(
+            remoteHolder -> testIdsToTestNames(remoteList, remoteHolder)
+          );
 
-          if (iterations >= 3) {
-            LOG.error("Unexpected circular initialization problem");
-            assert false;
-          }
-        }
+        if (localList == null) return testsFromRemote;
+        THashSet<String> setOfStrings = new THashSet<>(testsFromRemote);
 
-        myMethodQNameToTestNames = methodQNameToTestNames;
-        myTestNameToUsedClassesAndMethodMap = testNameToUsedClassesAndMethodMap;
-        myClassEnumerator = classNameEnumerator;
-        myMethodEnumerator = methodNameEnumerator;
-        myTestNameEnumerator = testNameEnumerator;
-        myMethodEnumeratorCache = new CachingEnumerator<String>(methodNameEnumerator, EnumeratorStringDescriptor.INSTANCE);
-        myClassEnumeratorCache = new CachingEnumerator<String>(classNameEnumerator, EnumeratorStringDescriptor.INSTANCE);
-
-        myFlushingFuture = FlushingDaemon.everyFiveSeconds(() -> {
-          synchronized (ourLock) {
-            if (myDisposed) {
-              myFlushingFuture.cancel(false);
-              return;
-            }
-            for(PersistentEnumeratorDelegate dataFile:myConstructedDataFiles) {
-              if (dataFile.isDirty()) {
-                dataFile.force();
-              }
-            }
-            myClassEnumeratorCache.clear();
-            myMethodEnumeratorCache.clear();
+        for (int testNameId : localList.toNativeArray()) {
+          if (testNameId < 0) {
+            setOfStrings.remove(localHolder.myTestNameEnumerator.valueOf(-testNameId));
+            continue;
           }
-        });
-      }
-      catch (IOException ex) {
-        throw new RuntimeException(ex);
-      }
-    }
-
-    private void closeAllConstructedFiles(boolean ignoreCloseProblem) {
-      for(Closeable closeable:myConstructedDataFiles) {
-        try {
-          closeable.close();
-        } catch (Throwable throwable) {
-          if (!ignoreCloseProblem) throw new RuntimeException(throwable);
+          setOfStrings.add(localHolder.myTestNameEnumerator.valueOf(testNameId));
         }
-      }
-    }
-
-    private void deleteAllIndexDataFiles(File methodQNameToTestNameFile,
-                                         File testNameToUsedClassesAndMethodMapFile,
-                                         File classNameEnumeratorFile, File methodNameEnumeratorFile, File testNameEnumeratorFile) {
-      IOUtil.deleteAllFilesStartingWith(methodQNameToTestNameFile);
-      IOUtil.deleteAllFilesStartingWith(testNameToUsedClassesAndMethodMapFile);
-      IOUtil.deleteAllFilesStartingWith(classNameEnumeratorFile);
-      IOUtil.deleteAllFilesStartingWith(methodNameEnumeratorFile);
-      IOUtil.deleteAllFilesStartingWith(testNameEnumeratorFile);
-    }
-
-    private void writeVersion(File versionFile) throws IOException {
-      final DataOutputStream versionOut = new DataOutputStream(new FileOutputStream(versionFile));
-
-      try {
-        DataInputOutputUtil.writeINT(versionOut, VERSION);
-      } finally {
-        try { versionOut.close(); } catch (IOException ignore) {}
-      }
-    }
-
-    private int readVersion(File versionFile) throws IOException {
-      if (!versionFile.exists()) return 0;
-      final DataInputStream versionInput = new DataInputStream(new FileInputStream(versionFile));
-      int version;
-      try {
-        version = DataInputOutputUtil.readINT(versionInput);
-      } finally {
-        try { versionInput.close(); } catch (IOException ignore) {}
-      }
-      return version;
-    }
 
-    void dispose() {
-      assert Thread.holdsLock(ourLock);
-      try {
-        closeAllConstructedFiles(false);
-      }
-      finally {
-        myDisposed = true;
+        return setOfStrings;
       }
-    }
 
-    private class TestNamesExternalizer implements DataExternalizer<TIntArrayList> {
-      public void save(@NotNull DataOutput dataOutput, TIntArrayList testNameIds) throws IOException {
-        for (int testNameId : testNameIds.toNativeArray()) DataInputOutputUtil.writeINT(dataOutput, testNameId);
-      }
+      private Collection<String> testIdsToTestNames(TIntArrayList localList, TestInfoHolder localHolder) throws IOException {
+        if (localList == null) return Collections.emptyList();
 
-      public TIntArrayList read(@NotNull DataInput dataInput) throws IOException {
-        TIntHashSet result = new TIntHashSet();
-
-        while (((InputStream)dataInput).available() > 0) {
-          int id = DataInputOutputUtil.readINT(dataInput);
-          if (REMOVED_MARKER == id) {
-            id = DataInputOutputUtil.readINT(dataInput);
-            result.remove(id);
-          }
-          else {
-            result.add(id);
+        final ArrayList<String> result = new ArrayList<String>(localList.size());
+        for (int testNameId : localList.toNativeArray()) {
+          if (testNameId < 0) {
+            int a = 1;
+            continue;
           }
+          result.add(localHolder.myTestNameEnumerator.valueOf(testNameId));
         }
-
-        return new TIntArrayList(result.toArray());
+        return result;
       }
-    }
+    });
+  }
 
-    private class ClassesAndMethodsMapDataExternalizer implements DataExternalizer<TIntObjectHashMap<TIntArrayList>> {
-      public void save(@NotNull final DataOutput dataOutput, TIntObjectHashMap<TIntArrayList> classAndMethodsMap)
-        throws IOException {
-        DataInputOutputUtil.writeINT(dataOutput, classAndMethodsMap.size());
-        final int[] classNameIds = classAndMethodsMap.keys();
-        Arrays.sort(classNameIds);
-
-        int prevClassNameId = 0;
-        for(int classNameId:classNameIds) {
-          DataInputOutputUtil.writeINT(dataOutput, classNameId - prevClassNameId);
-          TIntArrayList value = classAndMethodsMap.get(classNameId);
-          DataInputOutputUtil.writeINT(dataOutput, value.size());
-
-          final int[] methodNameIds = value.toNativeArray();
-          Arrays.sort(methodNameIds);
-          int prevMethodNameId = 0;
-          for (int methodNameId : methodNameIds) {
-            DataInputOutputUtil.writeINT(dataOutput, methodNameId - prevMethodNameId);
-            prevMethodNameId = methodNameId;
-          }
-          prevClassNameId = classNameId;
-        }
+
+  public Collection<String> getTestModulesByMethodName(@NotNull String classFQName, @NotNull String methodName, String prefix) throws IOException {
+    return myLocalTestRunDataController.withTestDataHolder(new ThrowableConvertor<TestInfoHolder, Collection<String>, IOException>() {
+      @Override
+      public Collection<String> convert(TestInfoHolder localHolder) throws IOException {
+        List<String> modules = getTestModules(localHolder);
+        List<String> modulesFromRemote = myRemoteTestRunDataController.withTestDataHolder(
+          this::getTestModules);
+        THashSet<String> modulesSet = new THashSet<>(modules);
+        if (modulesFromRemote != null) modulesSet.addAll(modulesFromRemote);
+        return modulesSet;
       }
 
-      public TIntObjectHashMap<TIntArrayList> read(@NotNull DataInput dataInput) throws IOException {
-        int numberOfClasses = DataInputOutputUtil.readINT(dataInput);
-        TIntObjectHashMap<TIntArrayList> result = new TIntObjectHashMap<TIntArrayList>();
-        int prevClassNameId = 0;
-
-        while (numberOfClasses-- > 0) {
-          int classNameId = DataInputOutputUtil.readINT(dataInput) + prevClassNameId;
-          int numberOfMethods = DataInputOutputUtil.readINT(dataInput);
-          TIntArrayList methodNameIds = new TIntArrayList(numberOfMethods);
-
-          int prevMethodNameId = 0;
-          while (numberOfMethods-- > 0) {
-            final int methodNameId = DataInputOutputUtil.readINT(dataInput) + prevMethodNameId;
-            methodNameIds.add(methodNameId);
-            prevMethodNameId = methodNameId;
+      private List<String> getTestModules(TestInfoHolder holder) throws IOException {
+        // todo merging with remote
+        final TIntArrayList list = holder.myTestNameToNearestModule.get(
+          TestInfoHolder.createKey(
+            holder.myClassEnumerator.enumerate(classFQName),
+            holder.myMethodEnumerator.enumerate(methodName)
+          )
+        );
+        if (list == null) return Collections.emptyList();
+        final ArrayList<String> result = new ArrayList<String>(list.size());
+        for (int moduleNameId : list.toNativeArray()) {
+          final String moduleNameWithPrefix = holder.myModuleNameEnumerator.valueOf(moduleNameId);
+          if (moduleNameWithPrefix != null && moduleNameWithPrefix.startsWith(prefix)) {
+            result.add(moduleNameWithPrefix.substring(prefix.length()));
           }
-
-          result.put(classNameId, methodNameIds);
-          prevClassNameId = classNameId;
         }
         return result;
       }
-    }
+    });
   }
 
-  private static class MethodQNameSerializer implements KeyDescriptor<Long> {
-    public static final MethodQNameSerializer INSTANCE = new MethodQNameSerializer();
+  static class TestDataController {
+    private final Object myLock = new Object();
+    private String myBasePath;
+    private final boolean myReadOnly;
+    private volatile TestInfoHolder myHolder;
 
-    @Override
-    public void save(@NotNull DataOutput out, Long value) throws IOException {
-      out.writeLong(value);
+    TestDataController(String basePath, boolean readonly) {
+      myReadOnly = readonly;
+      init(basePath);
     }
 
-    @Override
-    public Long read(@NotNull DataInput in) throws IOException {
-      return in.readLong();
-    }
+    void init(String basePath) {
+      if (myHolder != null) dispose();
 
-    @Override
-    public int getHashCode(Long value) {
-      return value.hashCode();
+      synchronized (myLock) {
+        myBasePath = basePath;
+      }
     }
 
-    @Override
-    public boolean isEqual(Long val1, Long val2) {
-      return val1.equals(val2);
-    }
-  }
-  
-  public void updateFromTestTrace(@NotNull File file) throws IOException {
-    int fileNameDotIndex = file.getName().lastIndexOf('.');
-    final String testName = fileNameDotIndex != -1 ? file.getName().substring(0, fileNameDotIndex) : file.getName();
-    doUpdateFromTestTrace(file, testName);
-  }
+    private TestInfoHolder getHolder() {
+      TestInfoHolder holder = myHolder;
 
-  private void doUpdateFromTestTrace(File file, final String testName) throws IOException {
-    synchronized (ourLock) {
-      Holder holder = getHolder();
-      if (holder.myDisposed) return;
-      try {
-        final int testNameId = holder.myTestNameEnumerator.enumerate(testName);
-        TIntObjectHashMap<TIntArrayList> classData = loadClassAndMethodsMap(file, holder);
-        TIntObjectHashMap<TIntArrayList> previousClassData = holder.myTestNameToUsedClassesAndMethodMap.get(testNameId);
-
-        doUpdateFromDiff(holder, testNameId, classData, previousClassData);
-      } catch (Throwable throwable) {
-        thingsWentWrongLetsReinitialize(holder, throwable);
+      if (holder == null) {
+        synchronized (myLock) {
+          holder = myHolder;
+          if (holder == null && myBasePath != null) holder = myHolder = new TestInfoHolder(myBasePath, myReadOnly, myLock);
+        }
       }
+      return holder;
     }
-  }
 
-  private void doUpdateFromDiff(Holder holder,
-                                final int testNameId,
-                                @Nullable TIntObjectHashMap<TIntArrayList> classData,
-                                @Nullable TIntObjectHashMap<TIntArrayList> previousClassData) throws IOException {
-    ValueDiff valueDiff = new ValueDiff(classData, previousClassData);
-
-    if (valueDiff.hasRemovedDelta()) {
-      for (int classQName : valueDiff.myRemovedClassData.keys()) {
-        for (int methodName : valueDiff.myRemovedClassData.get(classQName).toNativeArray()) {
-          holder.myMethodQNameToTestNames.appendData(createKey(classQName, methodName),
-                                                     new PersistentHashMap.ValueDataAppender() {
-                                                       @Override
-                                                       public void append(DataOutput dataOutput) throws IOException {
-                                                         DataInputOutputUtil.writeINT(dataOutput, REMOVED_MARKER);
-                                                         DataInputOutputUtil.writeINT(dataOutput, testNameId);
-                                                       }
-                                                     }
-          );
+    private void dispose() {
+      synchronized (myLock) {
+        TestInfoHolder holder = myHolder;
+        if (holder != null) {
+          holder.dispose();
+          myHolder = null;
         }
       }
     }
 
-    if (valueDiff.hasAddedDelta()) {
-      for (int classQName : valueDiff.myAddedOrChangedClassData.keys()) {
-        for (int methodName : valueDiff.myAddedOrChangedClassData.get(classQName).toNativeArray()) {
-          holder.myMethodQNameToTestNames.appendData(createKey(classQName, methodName),
-                                                     new PersistentHashMap.ValueDataAppender() {
-                                                       @Override
-                                                       public void append(DataOutput dataOutput) throws IOException {
-                                                         DataInputOutputUtil.writeINT(dataOutput, testNameId);
-                                                       }
-                                                     });
-        }
-      }
+    private void thingsWentWrongLetsReinitialize(@Nullable TestInfoHolder holder, Throwable throwable) throws IOException {
+      LOG.error("Unexpected problem", throwable);
+      if (holder != null) holder.dispose();
+      final File versionFile = TestInfoHolder.getVersionFile(myBasePath);
+      FileUtil.delete(versionFile);
+
+      myHolder = null;
+      if (throwable instanceof IOException) throw (IOException) throwable;
     }
 
-    if ((valueDiff.hasAddedDelta() || valueDiff.hasRemovedDelta())) {
-      if(classData != null) {
-        holder.myTestNameToUsedClassesAndMethodMap.put(testNameId, classData);
-      } else {
-        holder.myTestNameToUsedClassesAndMethodMap.remove(testNameId);
+    public <R> R withTestDataHolder(ThrowableConvertor<TestInfoHolder, R, IOException> action) throws IOException {
+      synchronized (myLock) {
+        TestInfoHolder holder = getHolder();
+        if (holder == null || holder.isDisposed()) return null;
+        try {
+          return action.convert(holder);
+        } catch (Throwable throwable) {
+          if (!myReadOnly) thingsWentWrongLetsReinitialize(holder, throwable);
+          else LOG.error(throwable);
+        }
+        return null;
       }
     }
   }
 
-  @NotNull
-  private static File getVersionFile(String path) {
-    return new File(path + File.separator + "index.version");
+  public static TestDiscoveryIndex getInstance(Project project) {
+    return project.getComponent(TestDiscoveryIndex.class);
   }
 
-  private void thingsWentWrongLetsReinitialize(@Nullable Holder holder, Throwable throwable) throws IOException {
-    LOG.error("Unexpected problem", throwable);
-    if (holder != null) holder.dispose();
-    String path = TestDiscoveryExtension.baseTestDiscoveryPathForProject(myProject);
-    final File versionFile = getVersionFile(path);
-    FileUtil.delete(versionFile);
+  @Override
+  public void initComponent() {
+  }
 
-    myHolder = null;
-    if (throwable instanceof IOException) throw (IOException) throwable;
+  @Override
+  public void disposeComponent() {
+    myLocalTestRunDataController.dispose();
+    myRemoteTestRunDataController.dispose();
   }
 
-  private static long createKey(int classQName, int methodName) {
-    return ((long)classQName << 32) | methodName;
+  @NotNull
+  @Override
+  public String getComponentName() {
+    return getClass().getName();
   }
 
-  static class ValueDiff {
-    final TIntObjectHashMap<TIntArrayList> myAddedOrChangedClassData;
-    final TIntObjectHashMap<TIntArrayList> myRemovedClassData;
-
-    ValueDiff(@Nullable TIntObjectHashMap<TIntArrayList> classData, @Nullable TIntObjectHashMap<TIntArrayList> previousClassData) {
-      TIntObjectHashMap<TIntArrayList> addedOrChangedClassData = classData;
-      TIntObjectHashMap<TIntArrayList> removedClassData = previousClassData;
-
-      if (previousClassData != null && !previousClassData.isEmpty()) {
-        removedClassData = new TIntObjectHashMap<TIntArrayList>();
-        addedOrChangedClassData = new TIntObjectHashMap<TIntArrayList>();
-
-        if (classData != null) {
-          for (int classQName : classData.keys()) {
-            TIntArrayList currentMethods = classData.get(classQName);
-            TIntArrayList previousMethods = previousClassData.get(classQName);
-
-            if (previousMethods == null) {
-              addedOrChangedClassData.put(classQName, currentMethods);
-              continue;
-            }
-
-            final int[] previousMethodIds = previousMethods.toNativeArray();
-            TIntHashSet previousMethodsSet = new TIntHashSet(previousMethodIds);
-            final int[] currentMethodIds = currentMethods.toNativeArray();
-            TIntHashSet currentMethodsSet = new TIntHashSet(currentMethodIds);
-            currentMethodsSet.removeAll(previousMethodIds);
-            previousMethodsSet.removeAll(currentMethodIds);
-
-            if (!currentMethodsSet.isEmpty()) {
-              addedOrChangedClassData.put(classQName, new TIntArrayList(currentMethodsSet.toArray()));
-            }
-            if (!previousMethodsSet.isEmpty()) {
-              removedClassData.put(classQName, new TIntArrayList(previousMethodsSet.toArray()));
-            }
-          }
-        }
-        if (classData != null) {
-          for (int classQName : previousClassData.keys()) {
-            if (classData.containsKey(classQName)) continue;
+  @Override
+  public void projectOpened() {
+  }
 
-            TIntArrayList previousMethods = previousClassData.get(classQName);
-            removedClassData.put(classQName, previousMethods);
-          }
-        }
-      }
+  @Override
+  public void projectClosed() {
+  }
 
-      myAddedOrChangedClassData = addedOrChangedClassData;
-      myRemovedClassData = removedClassData;
-    }
+  public void updateFromTestTrace(@NotNull File file, @Nullable JavaTestConfigurationBase configurationBase) throws IOException {
+    int fileNameDotIndex = file.getName().lastIndexOf('.');
+    final String testName = fileNameDotIndex != -1 ? file.getName().substring(0, fileNameDotIndex) : file.getName();
+    final Module module = configurationBase != null ? configurationBase.getConfigurationModule().getModule() : null;
+    doUpdateFromTestTrace(file, testName, module != null ? configurationBase.getFrameworkPrefix() + module.getName() : null);
+  }
 
-    public boolean hasRemovedDelta() {
-      return myRemovedClassData != null && !myRemovedClassData.isEmpty();
-    }
+  private void doUpdateFromTestTrace(File file, final String testName, @Nullable final String moduleName) throws IOException {
+    myLocalTestRunDataController.withTestDataHolder(new ThrowableConvertor<TestInfoHolder, Void, IOException>() {
+      @Override
+      public Void convert(TestInfoHolder localHolder) throws IOException {
+        final int testNameId = localHolder.myTestNameEnumerator.enumerate(testName);
+        TIntObjectHashMap<TIntArrayList> classData = loadClassAndMethodsMap(file, localHolder);
+        TIntObjectHashMap<TIntArrayList> previousClassData = localHolder.myTestNameToUsedClassesAndMethodMap.get(testNameId);
+        if (previousClassData == null) {
+          previousClassData = myRemoteTestRunDataController.withTestDataHolder(
+            remoteDataHolder -> {
+              TIntObjectHashMap<TIntArrayList> remoteClassData = remoteDataHolder.myTestNameToUsedClassesAndMethodMap.get(testNameId);
+              if (remoteClassData == null) return null;
+              TIntObjectHashMap<TIntArrayList> result = new TIntObjectHashMap<TIntArrayList>(remoteClassData.size());
+              Ref<IOException> exceptionRef = new Ref<IOException>();
+              boolean processingResult = remoteClassData.forEachEntry((remoteClassKey, remoteClassMethodIds) -> {
+                try {
+                  int localClassKey =
+                    localHolder.myClassEnumeratorCache.enumerate(remoteDataHolder.myClassEnumeratorCache.valueOf(remoteClassKey));
+                  TIntArrayList localClassIds = new TIntArrayList(remoteClassMethodIds.size());
+                  for (int methodId : remoteClassMethodIds.toNativeArray()) {
+                    localClassIds
+                      .add(localHolder.myMethodEnumeratorCache.enumerate(remoteDataHolder.myMethodEnumeratorCache.valueOf(methodId)));
+                  }
+                  result.put(localClassKey, localClassIds);
+                  return true;
+                } catch (IOException ex) {
+                  exceptionRef.set(ex);
+                  return false;
+                }
+              });
+              if (!processingResult) throw exceptionRef.get();
+              return result;
+            });
+        }
 
-    public boolean hasAddedDelta() {
-      return myAddedOrChangedClassData != null && !myAddedOrChangedClassData.isEmpty();
-    }
+        localHolder.doUpdateFromDiff(testNameId, classData, previousClassData, moduleName != null ? localHolder.myModuleNameEnumerator.enumerate(moduleName) : null);
+        return null;
+      }
+    });
   }
 
   @NotNull
-  private static TIntObjectHashMap<TIntArrayList> loadClassAndMethodsMap(File file, Holder holder) throws IOException {
+  private static TIntObjectHashMap<TIntArrayList> loadClassAndMethodsMap(File file, TestInfoHolder holder) throws IOException {
     DataInputStream inputStream = new DataInputStream(new BufferedInputStream(new FileInputStream(file), 64 * 1024));
     byte[] buffer = IOUtil.allocReadWriteUTFBuffer();
 
index ba2f124657795d43adfe020129821873b28fff7b..f58e5fbf17b6cec6f8eb2c59a5cc7ced12c81915 100644 (file)
@@ -38,8 +38,8 @@ import java.io.IOException;
 import java.util.*;
 
 public class TestDiscoverySearchHelper {
-  public static Set<String> search(final Project project, 
-                                   final Pair<String, String> position, 
+  public static Set<String> search(final Project project,
+                                   final Pair<String, String> position,
                                    final String changeList,
                                    final String frameworkPrefix) {
     final Set<String> patterns = new LinkedHashSet<String>();
@@ -119,7 +119,7 @@ public class TestDiscoverySearchHelper {
   @NotNull
   private static List<VirtualFile> getAffectedFiles(String changeListName, Project project) {
     final ChangeListManager changeListManager = ChangeListManager.getInstance(project);
-    if (changeListName == null) {
+    if ("All".equals(changeListName)) {
       return changeListManager.getAffectedFiles();
     }
     final LocalChangeList changeList = changeListManager.findChangeList(changeListName);
diff --git a/java/execution/impl/src/com/intellij/execution/testDiscovery/TestInfoHolder.java b/java/execution/impl/src/com/intellij/execution/testDiscovery/TestInfoHolder.java
new file mode 100644 (file)
index 0000000..c167797
--- /dev/null
@@ -0,0 +1,462 @@
+/*
+ * Copyright 2000-2016 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.intellij.execution.testDiscovery;
+
+import com.intellij.openapi.vfs.newvfs.persistent.FlushingDaemon;
+import com.intellij.util.io.*;
+import gnu.trove.TIntArrayList;
+import gnu.trove.TIntHashSet;
+import gnu.trove.TIntObjectHashMap;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.io.*;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.concurrent.ScheduledFuture;
+
+final class TestInfoHolder {
+  final PersistentHashMap<Long, TIntArrayList> myMethodQNameToTestNames;
+  final PersistentHashMap<Integer, TIntObjectHashMap<TIntArrayList>> myTestNameToUsedClassesAndMethodMap;
+  final PersistentHashMap<Long, TIntArrayList> myTestNameToNearestModule;
+  final PersistentStringEnumerator myClassEnumerator;
+  final CachingEnumerator<String> myClassEnumeratorCache;
+  final PersistentStringEnumerator myMethodEnumerator;
+  final CachingEnumerator<String> myMethodEnumeratorCache;
+  final PersistentStringEnumerator myTestNameEnumerator;
+  final PersistentStringEnumerator myModuleNameEnumerator;
+  final List<PersistentEnumeratorDelegate> myConstructedDataFiles = new ArrayList<PersistentEnumeratorDelegate>(6);
+
+  private ScheduledFuture<?> myFlushingFuture;
+  private boolean myDisposed;
+  private final Object myLock;
+
+  private static final int VERSION = 4;
+
+  TestInfoHolder(String basePath, boolean readOnly, Object lock) {
+    myLock = lock;
+    final File versionFile = getVersionFile(basePath);
+    versionFile.getParentFile().mkdirs();
+    final File methodQNameToTestNameFile = new File(basePath + File.separator + "methodQNameToTestName.data");
+    final File testNameToUsedClassesAndMethodMapFile = new File(basePath + File.separator + "testToCalledMethodNames.data");
+    final File classNameEnumeratorFile = new File(basePath + File.separator + "classNameEnumerator.data");
+    final File methodNameEnumeratorFile = new File(basePath + File.separator + "methodNameEnumerator.data");
+    final File testNameEnumeratorFile = new File(basePath + File.separator + "testNameEnumerator.data");
+    final File moduleNameEnumeratorFile = new File(basePath + File.separator + "moduleNameEnumerator.data");
+    final File testNameToNearestModuleFile = new File(basePath + File.separator + "testNameToNearestModule.data");
+
+    try {
+      int version = readVersion(versionFile);
+      if (version != VERSION) {
+        assert !readOnly;
+        TestDiscoveryIndex.LOG.info("TestDiscoveryIndex was rewritten due to version change");
+        deleteAllIndexDataFiles(methodQNameToTestNameFile,
+                                testNameToUsedClassesAndMethodMapFile,
+                                classNameEnumeratorFile,
+                                methodNameEnumeratorFile,
+                                testNameEnumeratorFile, moduleNameEnumeratorFile,
+                                testNameToNearestModuleFile);
+
+        writeVersion(versionFile);
+      }
+
+      PersistentHashMap<Long, TIntArrayList> methodQNameToTestNames;
+      PersistentHashMap<Integer, TIntObjectHashMap<TIntArrayList>> testNameToUsedClassesAndMethodMap;
+      PersistentHashMap<Long, TIntArrayList> testNameToNearestModule;
+      PersistentStringEnumerator classNameEnumerator;
+      PersistentStringEnumerator methodNameEnumerator;
+      PersistentStringEnumerator testNameEnumerator;
+      PersistentStringEnumerator moduleNameEnumerator;
+
+      int iterations = 0;
+
+      while (true) {
+        ++iterations;
+
+        try {
+          methodQNameToTestNames = new PersistentHashMap<Long, TIntArrayList>(
+            methodQNameToTestNameFile,
+            MethodQNameSerializer.INSTANCE,
+            new TestNamesExternalizer()
+          ) {
+            @Override
+            protected boolean isReadOnly() {
+              return readOnly;
+            }
+          };
+          myConstructedDataFiles.add(methodQNameToTestNames);
+
+          testNameToUsedClassesAndMethodMap = new PersistentHashMap<Integer, TIntObjectHashMap<TIntArrayList>>(
+            testNameToUsedClassesAndMethodMapFile,
+            EnumeratorIntegerDescriptor.INSTANCE,
+            new ClassesAndMethodsMapDataExternalizer()
+          ) {
+            @Override
+            protected boolean isReadOnly() {
+              return readOnly;
+            }
+          };
+          myConstructedDataFiles.add(testNameToUsedClassesAndMethodMap);
+
+          testNameToNearestModule = new PersistentHashMap<Long, TIntArrayList>(testNameToNearestModuleFile,
+                                                                               MethodQNameSerializer.INSTANCE,
+                                                                               new TestNamesExternalizer());
+          myConstructedDataFiles.add(testNameToNearestModule);
+
+          classNameEnumerator = new PersistentStringEnumerator(classNameEnumeratorFile);
+          myConstructedDataFiles.add(classNameEnumerator);
+
+          methodNameEnumerator = new PersistentStringEnumerator(methodNameEnumeratorFile);
+          myConstructedDataFiles.add(methodNameEnumerator);
+
+          moduleNameEnumerator = new PersistentStringEnumerator(moduleNameEnumeratorFile);
+          myConstructedDataFiles.add(moduleNameEnumerator);
+
+          testNameEnumerator = new PersistentStringEnumerator(testNameEnumeratorFile);
+          myConstructedDataFiles.add(testNameEnumerator);
+
+          break;
+        }
+        catch (Throwable throwable) {
+          TestDiscoveryIndex.LOG.info("TestDiscoveryIndex problem", throwable);
+          closeAllConstructedFiles(true);
+          myConstructedDataFiles.clear();
+
+          deleteAllIndexDataFiles(methodQNameToTestNameFile, testNameToUsedClassesAndMethodMapFile, classNameEnumeratorFile,
+                                  methodNameEnumeratorFile,
+                                  testNameEnumeratorFile, moduleNameEnumeratorFile, testNameToNearestModuleFile);
+          // try another time
+        }
+
+        if (iterations >= 3) {
+          TestDiscoveryIndex.LOG.error("Unexpected circular initialization problem");
+          assert false;
+        }
+      }
+
+      myMethodQNameToTestNames = methodQNameToTestNames;
+      myTestNameToUsedClassesAndMethodMap = testNameToUsedClassesAndMethodMap;
+      myTestNameToNearestModule = testNameToNearestModule;
+      myClassEnumerator = classNameEnumerator;
+      myMethodEnumerator = methodNameEnumerator;
+      myTestNameEnumerator = testNameEnumerator;
+      myModuleNameEnumerator = moduleNameEnumerator;
+      myMethodEnumeratorCache = new CachingEnumerator<String>(methodNameEnumerator, EnumeratorStringDescriptor.INSTANCE);
+      myClassEnumeratorCache = new CachingEnumerator<String>(classNameEnumerator, EnumeratorStringDescriptor.INSTANCE);
+
+      myFlushingFuture = FlushingDaemon.everyFiveSeconds(() -> {
+        synchronized (myLock) {
+          if (myDisposed) {
+            myFlushingFuture.cancel(false);
+            return;
+          }
+          for (PersistentEnumeratorDelegate dataFile : myConstructedDataFiles) {
+            if (dataFile.isDirty()) {
+              dataFile.force();
+            }
+          }
+          myClassEnumeratorCache.clear();
+          myMethodEnumeratorCache.clear();
+        }
+      });
+    }
+    catch (IOException ex) {
+      throw new RuntimeException(ex);
+    }
+  }
+
+  private void closeAllConstructedFiles(boolean ignoreCloseProblem) {
+    for (Closeable closeable : myConstructedDataFiles) {
+      try {
+        closeable.close();
+      }
+      catch (Throwable throwable) {
+        if (!ignoreCloseProblem) throw new RuntimeException(throwable);
+      }
+    }
+  }
+
+  private static void deleteAllIndexDataFiles(File... files) {
+    for (File file : files) {
+      IOUtil.deleteAllFilesStartingWith(file);
+    }
+  }
+
+  private static void writeVersion(File versionFile) throws IOException {
+    final java.io.DataOutputStream versionOut = new java.io.DataOutputStream(new FileOutputStream(versionFile));
+
+    try {
+      DataInputOutputUtil.writeINT(versionOut, VERSION);
+    }
+    finally {
+      try {
+        versionOut.close();
+      }
+      catch (IOException ignore) {
+      }
+    }
+  }
+
+  private static int readVersion(File versionFile) throws IOException {
+    if (!versionFile.exists()) return 0;
+    final DataInputStream versionInput = new DataInputStream(new FileInputStream(versionFile));
+    int version;
+    try {
+      version = DataInputOutputUtil.readINT(versionInput);
+    }
+    finally {
+      try {
+        versionInput.close();
+      }
+      catch (IOException ignore) {
+      }
+    }
+    return version;
+  }
+
+  void dispose() {
+    assert Thread.holdsLock(myLock);
+    try {
+      closeAllConstructedFiles(false);
+    }
+    finally {
+      myDisposed = true;
+    }
+  }
+
+  private static final int REMOVED_MARKER = -1;
+
+  void doUpdateFromDiff(final int testNameId,
+                        @Nullable TIntObjectHashMap<TIntArrayList> classData,
+                        @Nullable TIntObjectHashMap<TIntArrayList> previousClassData,
+                        @Nullable Integer moduleId) throws IOException {
+    ValueDiff valueDiff = new ValueDiff(classData, previousClassData);
+
+    if (valueDiff.hasRemovedDelta()) {
+      for (int classQName : valueDiff.myRemovedClassData.keys()) {
+        for (int methodName : valueDiff.myRemovedClassData.get(classQName).toNativeArray()) {
+          myMethodQNameToTestNames.appendData(createKey(classQName, methodName),
+                                              dataOutput -> {
+                                                DataInputOutputUtil.writeINT(dataOutput, REMOVED_MARKER);
+                                                DataInputOutputUtil.writeINT(dataOutput, testNameId);
+                                              }
+          );
+        }
+      }
+    }
+
+    if (valueDiff.hasAddedDelta()) {
+      for (int classQName : valueDiff.myAddedOrChangedClassData.keys()) {
+        for (int methodName : valueDiff.myAddedOrChangedClassData.get(classQName).toNativeArray()) {
+          myMethodQNameToTestNames.appendData(createKey(classQName, methodName),
+                                              dataOutput -> DataInputOutputUtil.writeINT(dataOutput, testNameId));
+          if (moduleId != null) {
+            myTestNameToNearestModule.appendData(createKey(classQName, methodName),
+                                                 dataOutput -> DataInputOutputUtil.writeINT(dataOutput, moduleId));
+          }
+        }
+      }
+    }
+
+    if ((valueDiff.hasAddedDelta() || valueDiff.hasRemovedDelta())) {
+      if (classData != null) {
+        myTestNameToUsedClassesAndMethodMap.put(testNameId, classData);
+      }
+      else {
+        myTestNameToUsedClassesAndMethodMap.remove(testNameId);
+      }
+    }
+  }
+
+  public boolean isDisposed() {
+    return myDisposed;
+  }
+
+  public static boolean isValidPath(String path) {
+    try {
+      return readVersion(getVersionFile(path)) == VERSION;
+    } catch (IOException ex) {
+      return false;
+    }
+  }
+
+  private static class TestNamesExternalizer implements DataExternalizer<TIntArrayList> {
+    public void save(@NotNull DataOutput dataOutput, TIntArrayList testNameIds) throws IOException {
+      for (int testNameId : testNameIds.toNativeArray()) DataInputOutputUtil.writeINT(dataOutput, testNameId);
+    }
+
+    public TIntArrayList read(@NotNull DataInput dataInput) throws IOException {
+      TIntHashSet result = new TIntHashSet();
+
+      while (((InputStream)dataInput).available() > 0) {
+        int id = DataInputOutputUtil.readINT(dataInput);
+        if (REMOVED_MARKER == id) {
+          id = DataInputOutputUtil.readINT(dataInput);
+          if(!result.remove(id)) {
+            result.add(-id);
+          }
+        }
+        else {
+          result.add(id);
+        }
+      }
+
+      return new TIntArrayList(result.toArray());
+    }
+  }
+
+  private static class ClassesAndMethodsMapDataExternalizer implements DataExternalizer<TIntObjectHashMap<TIntArrayList>> {
+    public void save(@NotNull final DataOutput dataOutput, TIntObjectHashMap<TIntArrayList> classAndMethodsMap)
+      throws IOException {
+      DataInputOutputUtil.writeINT(dataOutput, classAndMethodsMap.size());
+      final int[] classNameIds = classAndMethodsMap.keys();
+      Arrays.sort(classNameIds);
+
+      int prevClassNameId = 0;
+      for (int classNameId : classNameIds) {
+        DataInputOutputUtil.writeINT(dataOutput, classNameId - prevClassNameId);
+        TIntArrayList value = classAndMethodsMap.get(classNameId);
+        DataInputOutputUtil.writeINT(dataOutput, value.size());
+
+        final int[] methodNameIds = value.toNativeArray();
+        Arrays.sort(methodNameIds);
+        int prevMethodNameId = 0;
+        for (int methodNameId : methodNameIds) {
+          DataInputOutputUtil.writeINT(dataOutput, methodNameId - prevMethodNameId);
+          prevMethodNameId = methodNameId;
+        }
+        prevClassNameId = classNameId;
+      }
+    }
+
+    public TIntObjectHashMap<TIntArrayList> read(@NotNull DataInput dataInput) throws IOException {
+      int numberOfClasses = DataInputOutputUtil.readINT(dataInput);
+      TIntObjectHashMap<TIntArrayList> result = new TIntObjectHashMap<TIntArrayList>();
+      int prevClassNameId = 0;
+
+      while (numberOfClasses-- > 0) {
+        int classNameId = DataInputOutputUtil.readINT(dataInput) + prevClassNameId;
+        int numberOfMethods = DataInputOutputUtil.readINT(dataInput);
+        TIntArrayList methodNameIds = new TIntArrayList(numberOfMethods);
+
+        int prevMethodNameId = 0;
+        while (numberOfMethods-- > 0) {
+          final int methodNameId = DataInputOutputUtil.readINT(dataInput) + prevMethodNameId;
+          methodNameIds.add(methodNameId);
+          prevMethodNameId = methodNameId;
+        }
+
+        result.put(classNameId, methodNameIds);
+        prevClassNameId = classNameId;
+      }
+      return result;
+    }
+  }
+
+  private static class MethodQNameSerializer implements KeyDescriptor<Long> {
+    public static final MethodQNameSerializer INSTANCE = new MethodQNameSerializer();
+
+    @Override
+    public void save(@NotNull DataOutput out, Long value) throws IOException {
+      out.writeLong(value);
+    }
+
+    @Override
+    public Long read(@NotNull DataInput in) throws IOException {
+      return in.readLong();
+    }
+
+    @Override
+    public int getHashCode(Long value) {
+      return value.hashCode();
+    }
+
+    @Override
+    public boolean isEqual(Long val1, Long val2) {
+      return val1.equals(val2);
+    }
+  }
+
+  @NotNull
+  static File getVersionFile(String path) {
+    return new File(path + File.separator + "index.version");
+  }
+
+  static long createKey(int classQName, int methodName) {
+    return ((long)classQName << 32) | methodName;
+  }
+
+  static class ValueDiff {
+    final TIntObjectHashMap<TIntArrayList> myAddedOrChangedClassData;
+    final TIntObjectHashMap<TIntArrayList> myRemovedClassData;
+
+    ValueDiff(@Nullable TIntObjectHashMap<TIntArrayList> classData, @Nullable TIntObjectHashMap<TIntArrayList> previousClassData) {
+      TIntObjectHashMap<TIntArrayList> addedOrChangedClassData = classData;
+      TIntObjectHashMap<TIntArrayList> removedClassData = previousClassData;
+
+      if (previousClassData != null && !previousClassData.isEmpty()) {
+        removedClassData = new TIntObjectHashMap<TIntArrayList>();
+        addedOrChangedClassData = new TIntObjectHashMap<TIntArrayList>();
+
+        if (classData != null) {
+          for (int classQName : classData.keys()) {
+            TIntArrayList currentMethods = classData.get(classQName);
+            TIntArrayList previousMethods = previousClassData.get(classQName);
+
+            if (previousMethods == null) {
+              addedOrChangedClassData.put(classQName, currentMethods);
+              continue;
+            }
+
+            final int[] previousMethodIds = previousMethods.toNativeArray();
+            TIntHashSet previousMethodsSet = new TIntHashSet(previousMethodIds);
+            final int[] currentMethodIds = currentMethods.toNativeArray();
+            TIntHashSet currentMethodsSet = new TIntHashSet(currentMethodIds);
+            currentMethodsSet.removeAll(previousMethodIds);
+            previousMethodsSet.removeAll(currentMethodIds);
+
+            if (!currentMethodsSet.isEmpty()) {
+              addedOrChangedClassData.put(classQName, new TIntArrayList(currentMethodsSet.toArray()));
+            }
+            if (!previousMethodsSet.isEmpty()) {
+              removedClassData.put(classQName, new TIntArrayList(previousMethodsSet.toArray()));
+            }
+          }
+        }
+        if (classData != null) {
+          for (int classQName : previousClassData.keys()) {
+            if (classData.containsKey(classQName)) continue;
+
+            TIntArrayList previousMethods = previousClassData.get(classQName);
+            removedClassData.put(classQName, previousMethods);
+          }
+        }
+      }
+
+      myAddedOrChangedClassData = addedOrChangedClassData;
+      myRemovedClassData = removedClassData;
+    }
+
+    public boolean hasRemovedDelta() {
+      return myRemovedClassData != null && !myRemovedClassData.isEmpty();
+    }
+
+    public boolean hasAddedDelta() {
+      return myAddedOrChangedClassData != null && !myAddedOrChangedClassData.isEmpty();
+    }
+  }
+}
index baf149ac450c988f5fa9111b6e8d82ec8ba7906e..12559a5c7ca07937e485fa0cdc7c94dcbe087199 100644 (file)
@@ -104,32 +104,9 @@ class RecentTestsData {
     val allEntries: List<RecentTestsPopupEntry> = runConfigurationSuites.values + urlSuites
     return allEntries
         .sortedByDescending { it.runDate }
-        .fold(listOf(), { popupList, currentEntry -> 
-          when (currentEntry) {
-            is RunConfigurationEntry -> popupList + currentEntry.entriesToShow()
-            else -> popupList + currentEntry
-          } 
+        .fold(listOf(), { popupList, currentEntry ->
+          popupList + currentEntry.getEntriesToShow()
         })
   }
   
-}
-
-fun RunConfigurationEntry.entriesToShow(): List<RecentTestsPopupEntry> {
-  if (suites.size == 1) {
-    return suites[0].entriesToShow()
-  }
-
-  val failedSuites = suites.filter { it.failedTests.size > 0 }
-  if (failedSuites.size == 0) {
-    return listOf(this)
-  }
-  return failedSuites + this
-}
-
-fun SuiteEntry.entriesToShow(): List<RecentTestsPopupEntry> {
-  val failed = failedTests
-  if (failed.size > 0) {
-    return failed.sortedByDescending { it.runDate } + this
-  }
-  return listOf(this)
 }
\ No newline at end of file
index 7f7b1357e666604ee7b05d1ac9617bac0f887443..26a13c1ba808c5d8bcec817cd91912150b99698a 100644 (file)
@@ -33,6 +33,8 @@ interface RecentTestsPopupEntry {
   fun run(runner: RecentTestRunner)
 
   open fun navigatableElement(locator: TestLocator): PsiElement? = null
+  
+  fun getEntriesToShow(): List<RecentTestsPopupEntry>
 }
 
 open class SingleTestEntry(val url: String, 
@@ -48,6 +50,8 @@ open class SingleTestEntry(val url: String,
   }
 
   override fun navigatableElement(locator: TestLocator) = locator.getLocation(url)?.psiElement
+
+  override fun getEntriesToShow(): List<RecentTestsPopupEntry> = listOf(this)
   
 }
 
@@ -67,6 +71,18 @@ class SuiteEntry(url: String, magnitude: TestStateInfo.Magnitude, runDate: Date)
 
   override val presentation = suiteName
 
+  override fun getEntriesToShow(): List<RecentTestsPopupEntry> {
+    val failed = failedTests
+    if (failed.size > 0) {
+      return failed.sortedByDescending { it.runDate } + this
+    }
+    return listOf(this)
+  }
+  
+  override val magnitude: TestStateInfo.Magnitude by lazy {
+    tests.find { it.magnitude != PASSED_INDEX && it.magnitude != COMPLETE_INDEX }?.magnitude ?: PASSED_INDEX
+  }
+  
 }
 
 
@@ -82,7 +98,9 @@ class RunConfigurationEntry(val runSettings: RunnerAndConfigurationSettings, ini
 
   override val runDate = suites.map { it.runDate }.min()!!
 
-  override val magnitude = COMPLETE_INDEX
+  override val magnitude: TestStateInfo.Magnitude by lazy {
+    suites.find { it.magnitude != PASSED_INDEX && it.magnitude != COMPLETE_INDEX }?.magnitude ?: PASSED_INDEX
+  }
 
   override val presentation = runSettings.name
 
@@ -92,4 +110,18 @@ class RunConfigurationEntry(val runSettings: RunnerAndConfigurationSettings, ini
   override fun run(runner: RecentTestRunner) {
     runner.run(runSettings)
   }
+
+  override fun getEntriesToShow(): List<RecentTestsPopupEntry> {
+    if (suites.size == 1) {
+      return suites[0].getEntriesToShow()
+    }
+    
+    return suites
+        .filter { it.failedTests.size > 0}
+        .sortedByDescending { it.runDate }
+        .fold(listOf<RecentTestsPopupEntry>(), { popupList, currentEntry ->
+          popupList + currentEntry.getEntriesToShow()
+        }) + this
+  }
+  
 }
index 28f3a2e9dfce28652acf97cd135b98e655d656fc..69056e49d799af7e40e73011a579a861b3e1f041 100644 (file)
@@ -23,6 +23,7 @@ import com.intellij.codeInspection.ui.*;
 import com.intellij.codeInspection.util.RefFilter;
 import com.intellij.icons.AllIcons;
 import com.intellij.lang.annotation.HighlightSeverity;
+import com.intellij.openapi.actionSystem.AnActionEvent;
 import com.intellij.openapi.application.ApplicationManager;
 import com.intellij.openapi.editor.Document;
 import com.intellij.openapi.editor.Editor;
@@ -211,6 +212,20 @@ public class UnusedDeclarationPresentation extends DefaultInspectionToolPresenta
       super(InspectionsBundle.message("inspection.dead.code.entry.point.quickfix"), null, KeyStroke.getKeyStroke(KeyEvent.VK_INSERT, 0), toolWrapper);
     }
 
+    @Override
+    public void update(AnActionEvent e) {
+      super.update(e);
+      if (e.getPresentation().isEnabledAndVisible()) {
+        final RefEntity[] elements = getInvoker(e).getTree().getSelectedElements();
+        for (RefEntity element : elements) {
+          if (!((RefElement) element).isEntry()) {
+            return;
+          }
+        }
+        e.getPresentation().setEnabled(false);
+      }
+    }
+
     @Override
     protected boolean applyFix(@NotNull RefEntity[] refElements) {
       final EntryPointsManager entryPointsManager = getEntryPointsManager();
index 88711b188f3fded91a3e178cd9bc4fa83aec6579..a5852ce9ed1e122113c2732e5edf0cba0e150504 100644 (file)
@@ -16,6 +16,7 @@
 package com.intellij.psi
 
 import com.intellij.openapi.application.ApplicationManager
+import com.intellij.openapi.command.WriteCommandAction
 import com.intellij.openapi.fileEditor.FileDocumentManager
 import com.intellij.openapi.vfs.VfsUtil
 import com.intellij.psi.impl.source.PsiFileImpl
@@ -156,4 +157,17 @@ class B {
     assert !file.stub
     assert file.treeElement
   }
+
+  public void "test no AST loading on file rename"() {
+    PsiJavaFile file = (PsiJavaFile) myFixture.addFileToProject('a.java', 'class Foo {}')
+    assert file.classes.length == 1
+    assert ((PsiFileImpl)file).stub
+
+    WriteCommandAction.runWriteCommandAction project, { file.setName('b.java') }
+    assert file.classes.length == 1
+    assert ((PsiFileImpl)file).stub
+
+    assert file.classes[0].nameIdentifier.text == 'Foo'
+    assert ((PsiFileImpl)file).contentsLoaded
+  }
 }
diff --git a/java/java-tests/testSrc/com/intellij/testIntergration/RecentTestsOrderTest.kt b/java/java-tests/testSrc/com/intellij/testIntergration/RecentTestsOrderTest.kt
new file mode 100644 (file)
index 0000000..5488e62
--- /dev/null
@@ -0,0 +1,131 @@
+/*
+ * Copyright 2000-2016 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.intellij.testIntergration
+
+import com.intellij.execution.RunnerAndConfigurationSettings
+import com.intellij.execution.testframework.sm.runner.states.TestStateInfo.Magnitude.FAILED_INDEX
+import com.intellij.execution.testframework.sm.runner.states.TestStateInfo.Magnitude.PASSED_INDEX
+import com.intellij.testFramework.LightIdeaTestCase
+import com.intellij.testIntegration.RecentTestsData
+import org.assertj.core.api.Assertions.assertThat
+import org.mockito.Mockito
+import java.util.*
+
+class RecentTestsOrderTest: LightIdeaTestCase() {
+
+  lateinit var data: RecentTestsData
+  lateinit var allTests: RunnerAndConfigurationSettings
+  lateinit var now: Date
+  
+  
+  override fun setUp() {
+    super.setUp()
+    data = RecentTestsData()
+    allTests = Mockito.mock(RunnerAndConfigurationSettings::class.java)
+    Mockito.`when`(allTests.uniqueID).thenAnswer { "JUnit.all tests" }
+    Mockito.`when`(allTests.name).thenAnswer { "all tests" }
+    now = Date()
+  }
+  
+  fun addPassedSuite(suiteUrl: String, date: Date = Date(), runConfiguration: RunnerAndConfigurationSettings = allTests) {
+    data.addSuite(suiteUrl, PASSED_INDEX, date, runConfiguration)
+  }
+  
+  fun addFailedSuite(suiteUrl: String, date: Date = Date(), runConfiguration: RunnerAndConfigurationSettings = allTests) {
+    data.addSuite(suiteUrl, FAILED_INDEX, date, runConfiguration)
+  }
+  
+  fun addPassedTest(testUrl: String, date: Date = Date(), runConfiguration: RunnerAndConfigurationSettings = allTests) {
+    data.addTest(testUrl, PASSED_INDEX, date, runConfiguration)
+  }
+  
+  fun addFailedTest(testUrl: String, date: Date = Date(), runConfiguration: RunnerAndConfigurationSettings = allTests) {
+    data.addTest(testUrl, FAILED_INDEX, date, runConfiguration)
+  }
+  
+  fun `test run configuration with one suite shows only suite`() {
+    val suite = "MySingleTest".suite()
+    val test1 = "MySingleTest.test1".test()
+    
+    data.addTest(test1, PASSED_INDEX, now, allTests)
+    data.addSuite(suite, PASSED_INDEX, now, allTests)
+    
+    val testsToShow = data.getTestsToShow()
+    assertThat(testsToShow).hasSize(1)
+    assertThat(testsToShow[0].presentation).isEqualTo("MySingleTest")
+  }
+  
+  fun `test run configuration with multiple suites shows run configuration name`() {
+    val suite1 = "MyFirstTest".suite()
+    val test1 = "MyFirstTest.test1".test()
+    
+    val suite2 = "MySecondTest".suite()
+    val test2 = "MySecondTest.test1".test()
+    
+    addPassedSuite(suite1, now)
+    addPassedTest(test1, now)
+    
+    addPassedSuite(suite2)
+    addPassedTest(test2)
+
+    val tests = data.getTestsToShow()
+    assertThat(tests).hasSize(1)
+    assertThat(tests[0].presentation).isEqualTo("all tests")
+  }
+  
+  fun `test show failed suite in test`() {
+    val suite = "SingleTest".suite()
+    val test = "SingleTest.test".test()
+    
+    addPassedSuite(suite)
+    addFailedTest(test)
+    
+    val tests = data.getTestsToShow()
+    assertThat(tests).hasSize(2)
+    
+    assertThat(tests[0].presentation).isEqualTo("SingleTest.test")
+    assertThat(tests[0].magnitude).isEqualTo(FAILED_INDEX)
+    
+    assertThat(tests[1].presentation).isEqualTo("SingleTest")
+    assertThat(tests[1].magnitude).isEqualTo(FAILED_INDEX)
+  }
+  
+  fun `test show failed suite and test in run configuration`() {
+    val suite = "SingleTest".suite()
+    val test = "SingleTest.test".test()
+    val test2 = "SingleTest.test2".test()
+    
+    addPassedSuite(suite)
+    addFailedTest(test)
+    addPassedTest(test2)
+    
+    addPassedSuite("PassedSuite".suite())
+    
+    val tests = data.getTestsToShow()
+    assertThat(tests).hasSize(3)
+    
+    assertThat(tests[0].presentation).isEqualTo("SingleTest.test")
+    assertThat(tests[0].magnitude).isEqualTo(FAILED_INDEX)
+    
+    assertThat(tests[1].presentation).isEqualTo("SingleTest")
+    assertThat(tests[1].magnitude).isEqualTo(FAILED_INDEX)
+    
+    assertThat(tests[2].presentation).isEqualTo("all tests")
+    assertThat(tests[2].magnitude).isEqualTo(FAILED_INDEX)
+  }
+  
+  
+}
\ No newline at end of file
index 6424974bad14d068ce763d5a57f8fb29018fad70..de15e9cbde98f3987d2e209d4a6c7a42f50d39d4 100644 (file)
@@ -78,9 +78,16 @@ class RecentTestsStepTest: LightIdeaTestCase() {
     
     val tests = data.getTestsToShow()
 
-    assertThat(tests).hasSize(2)
-    assertThat(tests[0].presentation).isEqualTo("JFSDTest")
-    assertThat(tests[1].presentation).isEqualTo("all tests")
+    assertThat(tests).hasSize(3)
+    
+    assertThat(tests[0].presentation).isEqualTo("JFSDTest.testItMakesMeSadToFixIt")
+    assertThat(tests[0].magnitude).isEqualTo(FAILED_INDEX)
+    
+    assertThat(tests[1].presentation).isEqualTo("JFSDTest")
+    assertThat(tests[1].magnitude).isEqualTo(FAILED_INDEX)
+    
+    assertThat(tests[2].presentation).isEqualTo("all tests")
+    assertThat(tests[2].magnitude).isEqualTo(FAILED_INDEX)
   }
   
   
index 0c242ec08bc531d56be147022754f1287b331829..fc0e2430501481dec7f4a2b6ad2056a3d90b412a 100755 (executable)
Binary files a/lib/coverage-agent.jar and b/lib/coverage-agent.jar differ
index b6226f70fa01eb295e412cfaa3795083c56a2f7b..17a54cc1b64fd59e87b159628bd48a8bc079eef6 100755 (executable)
Binary files a/lib/coverage-instrumenter.jar and b/lib/coverage-instrumenter.jar differ
index 00135249e4828c0512941a3519f21d40ed72a3a4..c68c6c00336af0ab0b6825b8a2d7006ec7047d57 100755 (executable)
Binary files a/lib/coverage-util.jar and b/lib/coverage-util.jar differ
index 8c6424c7bd590a1fdd1df1ea18d5da6d46713a83..ba9e9f275bc13323763dc416b92135f504614600 100755 (executable)
Binary files a/lib/src/coverage-src.zip and b/lib/src/coverage-src.zip differ
index b7d8c5e3b01c453ab7ff742caa91cb92cda1c17a..8c8fd78fc6bbdef392fa9cf5a1685313c6bbcac8 100644 (file)
@@ -58,6 +58,7 @@ public class DocumentImpl extends UserDataHolderBase implements DocumentEx {
 
   private final Ref<DocumentListener[]> myCachedDocumentListeners = Ref.create(null);
   private final List<DocumentListener> myDocumentListeners = ContainerUtil.createLockFreeCopyOnWriteList();
+  private final List<DocumentBulkUpdateListener> myBulkDocumentInternalListeners = ContainerUtil.createLockFreeCopyOnWriteList();
   private final RangeMarkerTree<RangeMarkerEx> myRangeMarkers = new RangeMarkerTree<RangeMarkerEx>(this);
   private final RangeMarkerTree<RangeMarkerEx> myPersistentRangeMarkers = new RangeMarkerTree<RangeMarkerEx>(this);
   private final List<RangeMarker> myGuardedBlocks = new ArrayList<RangeMarker>();
@@ -895,6 +896,14 @@ public class DocumentImpl extends UserDataHolderBase implements DocumentEx {
     doRemoveDocumentListener(listener, myCachedDocumentListeners, myDocumentListeners);
   }
 
+  public void addInternalBulkModeListener(@NotNull DocumentBulkUpdateListener listener) {
+    myBulkDocumentInternalListeners.add(listener);
+  }
+
+  public void removeInternalBulkModeListener(@NotNull DocumentBulkUpdateListener listener) {
+    myBulkDocumentInternalListeners.remove(listener);
+  }
+
   private static void doRemoveDocumentListener(@NotNull DocumentListener listener,
                                                @NotNull Ref<DocumentListener[]> cachedDocumentListenersRef,
                                                @NotNull List<DocumentListener> documentListeners) {
@@ -1035,10 +1044,12 @@ public class DocumentImpl extends UserDataHolderBase implements DocumentEx {
     try {
       if (value) {
         getPublisher().updateStarted(this);
+        notifyInternalListenersOnBulkModeStarted();
         myDoingBulkUpdate = true;
       }
       else {
         myDoingBulkUpdate = false;
+        notifyInternalListenersOnBulkModeFinished();
         getPublisher().updateFinished(this);
       }
     }
@@ -1047,6 +1058,18 @@ public class DocumentImpl extends UserDataHolderBase implements DocumentEx {
     }
   }
 
+  private void notifyInternalListenersOnBulkModeStarted() {
+    for (DocumentBulkUpdateListener listener : myBulkDocumentInternalListeners) {
+      listener.updateStarted(this);
+    }
+  }
+
+  private void notifyInternalListenersOnBulkModeFinished() {
+    for (DocumentBulkUpdateListener listener : myBulkDocumentInternalListeners) {
+      listener.updateFinished(this);
+    }
+  }
+
   private static class DocumentBulkUpdateListenerHolder {
     private static final DocumentBulkUpdateListener ourBulkChangePublisher =
       ApplicationManager.getApplication().getMessageBus().syncPublisher(DocumentBulkUpdateListener.TOPIC);
index e089e3bf124285cdaedae0eb6fcadb16a336a8ca..b57383d39905d823455a3059c5efda6e4f7d1d50 100644 (file)
@@ -510,7 +510,6 @@ public abstract class PsiFileImpl extends ElementBase implements PsiFileEx, PsiF
   @Override
   public PsiElement setName(@NotNull String name) throws IncorrectOperationException {
     checkSetName(name);
-    beforeAstChange();
     doClearCaches("setName");
     return PsiFileImplUtil.setName(this, name);
   }
@@ -1125,6 +1124,8 @@ public abstract class PsiFileImpl extends ElementBase implements PsiFileEx, PsiF
       FileElement element = getTreeElement();
       if (element != null) {
         AstPath.invalidatePaths(element);
+      } else {
+        LOG.error("No AST; " + derefStub() + "; " + this + " of " + getClass() + "; " + getViewProvider() + " of " + getViewProvider().getClass());
       }
 
       myUseStrongRefs = true;
index 4851e2a63ee97ff91139b4828e0cdd7ccd89d965..8eac730b3f81c9580669dc39ff2cfbf25407ffbc 100644 (file)
@@ -28,11 +28,13 @@ import com.intellij.openapi.util.text.StringUtil;
 import com.intellij.openapi.vfs.CharsetToolkit;
 import com.intellij.util.ArrayUtil;
 import com.intellij.util.Consumer;
+import com.intellij.util.NotNullProducer;
 import com.intellij.util.PlatformUtils;
 import com.intellij.util.containers.ContainerUtil;
 import com.intellij.util.containers.MultiMap;
 import io.netty.buffer.ByteBuf;
 import io.netty.buffer.ByteBufOutputStream;
+import io.netty.channel.ChannelHandler;
 import io.netty.channel.ChannelHandlerContext;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
@@ -123,7 +125,7 @@ public final class SocketLock {
   }
 
   @NotNull
-  public ActivateStatus lock(@NotNull final String[] args) throws Exception {
+  public ActivateStatus lock(@NotNull String[] args) throws Exception {
     log("enter: lock(config=%s system=%s)", myConfigPath, mySystemPath);
 
     return underLocks(() -> {
@@ -137,6 +139,7 @@ public final class SocketLock {
         for (Map.Entry<Integer, Collection<String>> entry : portToPath.entrySet()) {
           ActivateStatus status = tryActivate(entry.getKey(), entry.getValue(), args);
           if (status != ActivateStatus.NO_INSTANCE) {
+            log("exit: lock(): " + status);
             return status;
           }
         }
@@ -147,13 +150,15 @@ public final class SocketLock {
       }
 
       myToken = UUID.randomUUID().toString();
-      final String[] lockedPaths = {myConfigPath, mySystemPath};
+      String[] lockedPaths = {myConfigPath, mySystemPath};
       int workerCount = PlatformUtils.isIdeaCommunity() || PlatformUtils.isDatabaseIDE() || PlatformUtils.isCidr() ? 1 : 2;
-      myServer = BuiltInServer.startNioOrOio(workerCount, 6942, 50, false,
-                                             () -> new MyChannelInboundHandler(lockedPaths, myActivateListener, myToken));
+      NotNullProducer<ChannelHandler> handler = () -> new MyChannelInboundHandler(lockedPaths, myActivateListener, myToken);
+      myServer = BuiltInServer.startNioOrOio(workerCount, 6942, 50, false, handler);
+
       byte[] portBytes = Integer.toString(myServer.getPort()).getBytes(CharsetToolkit.UTF8_CHARSET);
       FileUtil.writeToFile(portMarkerC, portBytes);
       FileUtil.writeToFile(portMarkerS, portBytes);
+
       File tokenFile = new File(mySystemPath, TOKEN_FILE);
       FileUtil.writeToFile(tokenFile, myToken.getBytes(CharsetToolkit.UTF8_CHARSET));
       PosixFileAttributeView view = Files.getFileAttributeView(tokenFile.toPath(), PosixFileAttributeView.class);
@@ -165,6 +170,7 @@ public final class SocketLock {
           log(e);
         }
       }
+
       log("exit: lock(): succeed");
       return ActivateStatus.NO_INSTANCE;
     });
@@ -172,19 +178,11 @@ public final class SocketLock {
 
   private <V> V underLocks(@NotNull Callable<V> action) throws Exception {
     FileUtilRt.createDirectory(new File(myConfigPath));
-    FileOutputStream lock1 = new FileOutputStream(new File(myConfigPath, PORT_LOCK_FILE), true);
-    try {
+    try (@SuppressWarnings("unused") FileOutputStream lock1 = new FileOutputStream(new File(myConfigPath, PORT_LOCK_FILE), true)) {
       FileUtilRt.createDirectory(new File(mySystemPath));
-      FileOutputStream lock2 = new FileOutputStream(new File(mySystemPath, PORT_LOCK_FILE), true);
-      try {
+      try (@SuppressWarnings("unused") FileOutputStream lock2 = new FileOutputStream(new File(mySystemPath, PORT_LOCK_FILE), true)) {
         return action.call();
       }
-      finally {
-        lock2.close();
-      }
-    }
-    finally {
-      lock1.close();
     }
   }
 
index f64e9f766de6a724ad756a99e2b74d7e460e5800..a4f0943fc291d607985d37d953effa8881ef8eec 100644 (file)
@@ -78,7 +78,6 @@ import com.intellij.util.*;
 import com.intellij.util.concurrency.EdtExecutorService;
 import com.intellij.util.containers.ContainerUtil;
 import com.intellij.util.containers.ContainerUtilRt;
-import com.intellij.util.messages.MessageBusConnection;
 import com.intellij.util.text.CharArrayCharSequence;
 import com.intellij.util.text.CharArrayUtil;
 import com.intellij.util.ui.*;
@@ -204,7 +203,7 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
   @NotNull private final SoftWrapModelImpl mySoftWrapModel;
 
   @NotNull private static final RepaintCursorCommand ourCaretBlinkingCommand = new RepaintCursorCommand();
-  private MessageBusConnection myConnection;
+  private DocumentBulkUpdateListener myBulkUpdateListener;
 
   @MouseSelectionState
   private int myMouseSelectionState;
@@ -365,9 +364,9 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
 
     myImmediatePainter = new ImmediatePainter(this);
 
-    if (project != null) {
-      myConnection = project.getMessageBus().connect();
-      myConnection.subscribe(DocumentBulkUpdateListener.TOPIC, new EditorDocumentBulkUpdateAdapter());
+    if (project != null && myDocument instanceof DocumentImpl) {
+      myBulkUpdateListener = new EditorDocumentBulkUpdateAdapter();
+      ((DocumentImpl)myDocument).addInternalBulkModeListener(myBulkUpdateListener);
     }
 
     myMarkupModelListener = new MarkupModelListener() {
@@ -898,8 +897,8 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
     myEditorComponent.removeMouseMotionListener(myMouseMotionListener);
     myGutterComponent.removeMouseMotionListener(myMouseMotionListener);
 
-    if (myConnection != null) {
-      myConnection.disconnect();
+    if (myBulkUpdateListener != null) {
+      ((DocumentImpl)myDocument).removeInternalBulkModeListener(myBulkUpdateListener);
     }
     if (myDocument instanceof DocumentImpl && !myUseNewRendering) {
       ((DocumentImpl)myDocument).giveUpTabTracking();
index d310481611a28f9f308e07c1dd4a40aafbf64835..00111a6dead72751cfa32df40d82e9344adce998 100644 (file)
@@ -32,7 +32,6 @@ import com.intellij.openapi.vfs.VirtualFile;
 import com.intellij.openapi.wm.IdeFocusManager;
 import com.intellij.pom.Navigatable;
 import com.intellij.psi.PsiElement;
-import com.intellij.ui.components.JBLabel;
 import com.intellij.ui.components.JBList;
 import com.intellij.ui.speedSearch.ListWithFilter;
 import com.intellij.util.ArrayUtil;
@@ -182,9 +181,9 @@ public abstract class FinderRecursivePanel<T> extends JBSplitter implements Data
 
   @Nullable
   protected JComponent createDefaultRightComponent() {
-    final JBLabel label = new JBLabel("Nothing selected", SwingConstants.CENTER);
-    label.setFontColor(UIUtil.FontColor.BRIGHTER);
-    return label;
+    JBList list = new JBList();
+    list.setEmptyText("Nothing selected");
+    return list;
   }
 
   protected JComponent createLeftComponent() {
index 020f15cf25d9e4547d91539ae921f525f3adf29a..270cbc04fe8e189df5f55fb5e7b98706ab92811d 100644 (file)
@@ -92,7 +92,7 @@ if os.path.exists(port_path) and os.path.exists(token_path):
         launch_with_port(port, token)
     except:
         type, value, traceback = sys.exc_info()
-        print('No IDE instance has been found. New one will be started.') # todo error
+        print('Cannot activate a running instance: ' + str(value))
 else:
     print('No IDE instance has been found. New one will be started.')
     if sys.platform == "darwin":
index b2401b463938e2edefa0ceb183a524c9bad27e2e..59d5b89fa0bcdcc4300340e4096dfb7739df6015 100644 (file)
@@ -26,11 +26,15 @@ import com.intellij.openapi.editor.event.CaretAdapter;
 import com.intellij.openapi.editor.event.CaretEvent;
 import com.intellij.openapi.editor.event.SelectionEvent;
 import com.intellij.openapi.editor.event.SelectionListener;
+import com.intellij.openapi.editor.ex.DocumentBulkUpdateListener;
 import com.intellij.openapi.editor.ex.DocumentEx;
 import com.intellij.openapi.editor.ex.EditorEx;
 import com.intellij.openapi.editor.ex.util.EditorUtil;
+import com.intellij.openapi.editor.markup.HighlighterTargetArea;
+import com.intellij.openapi.editor.markup.RangeHighlighter;
 import com.intellij.openapi.editor.markup.TextAttributes;
 import com.intellij.testFramework.EditorTestUtil;
+import org.jetbrains.annotations.NotNull;
 
 import javax.swing.*;
 import java.awt.*;
@@ -360,4 +364,30 @@ public class EditorImplTest extends AbstractEditorTest {
     assertEquals("caret:LogicalPosition: (0, 7)selection:(4,7)", output.toString());
     checkResultByText(" abc<selection>def<caret></selection>");
   }
+
+  public void testChangingHighlightersInBulkModeListener() throws Exception {
+    DocumentBulkUpdateListener.Adapter listener = new DocumentBulkUpdateListener.Adapter() {
+      @Override
+      public void updateFinished(@NotNull Document doc) {
+        if (doc == myEditor.getDocument()) {
+          myEditor.getMarkupModel().addRangeHighlighter(7, 8, 0, null, HighlighterTargetArea.EXACT_RANGE);
+        }
+      }
+    };
+    getProject().getMessageBus().connect(myTestRootDisposable).subscribe(DocumentBulkUpdateListener.TOPIC, listener);
+    initText("abcdef");
+    DocumentEx document = (DocumentEx)myEditor.getDocument();
+    new WriteCommandAction.Simple(getProject()) {
+      @Override
+      protected void run() throws Throwable {
+        document.setInBulkUpdate(true);
+        document.insertString(3, "\n\n");
+        document.setInBulkUpdate(false);
+      }
+    }.execute();
+    RangeHighlighter[] highlighters = myEditor.getMarkupModel().getAllHighlighters();
+    assertEquals(1, highlighters.length);
+    assertEquals(7, highlighters[0].getStartOffset());
+    assertEquals(8, highlighters[0].getEndOffset());
+  }
 }
index 8a3aee855dc2804ca1c2745c380b191354b70d29..18a93116e5782f6e0673395c8e7baf94b7b979a5 100644 (file)
@@ -40,6 +40,7 @@ import org.junit.runner.Description;
 import org.junit.runner.manipulation.Filter;
 import org.junit.runner.manipulation.NoTestsRemainException;
 
+import java.io.Closeable;
 import java.io.File;
 import java.io.IOException;
 import java.lang.reflect.Method;
@@ -289,6 +290,11 @@ public class TestAll implements Test {
   public void run(final TestResult testResult) {
     loadTestRecorder();
 
+    final TestListener testListener = loadDiscoveryListener();
+    if (testListener != null) {
+      testResult.addListener(testListener);
+    }
+
     List<Class> classes = myTestCaseLoader.getClasses();
     int totalTests = classes.size();
     for (Class<?> aClass : classes) {
@@ -308,9 +314,31 @@ public class TestAll implements Test {
       if (testResult.shouldStop()) break;
     }
 
+    if (testListener instanceof Closeable) {
+      try {
+        ((Closeable)testListener).close();
+      }
+      catch (IOException e) {
+        e.printStackTrace();
+      }
+    }
+
     tryGc(10);
   }
 
+  private TestListener loadDiscoveryListener() {
+    final String discoveryListener = System.getProperty("test.discovery.listener");
+    if (discoveryListener != null) {
+      try {
+        return (TestListener)Class.forName(discoveryListener).newInstance();
+      }
+      catch (Throwable e) {
+        return null;
+      }
+    }
+    return null;
+  }
+
   private static boolean shouldRecord(@NotNull Class<?> aClass) {
     return aClass.getAnnotation(RecordExecution.class) != null;
   }
similarity index 98%
rename from platform/platform-impl/src/com/intellij/execution/DelayedDocumentWatcher.java
rename to platform/testRunner/src/com/intellij/execution/DelayedDocumentWatcher.java
index 5179249e4b75a0ac732bcdab597b2a9880e2d2c0..6d13a840dbc5fd78a10f865213c4bff4950bfe7d 100644 (file)
@@ -16,6 +16,7 @@
 package com.intellij.execution;
 
 import com.intellij.AppTopics;
+import com.intellij.execution.testframework.autotest.AutoTestWatcher;
 import com.intellij.openapi.Disposable;
 import com.intellij.openapi.application.ApplicationManager;
 import com.intellij.openapi.application.ModalityState;
@@ -42,7 +43,7 @@ import org.jetbrains.annotations.Nullable;
 import java.util.Collection;
 import java.util.Set;
 
-public class DelayedDocumentWatcher {
+public class DelayedDocumentWatcher implements AutoTestWatcher {
 
   // All instance fields are be accessed from EDT
   private final Project myProject;
diff --git a/platform/testRunner/src/com/intellij/execution/testframework/autotest/AbstractAutoTestManager.java b/platform/testRunner/src/com/intellij/execution/testframework/autotest/AbstractAutoTestManager.java
new file mode 100644 (file)
index 0000000..898a864
--- /dev/null
@@ -0,0 +1,260 @@
+/*
+ * Copyright 2000-2016 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.intellij.execution.testframework.autotest;
+
+import com.intellij.execution.ExecutionManager;
+import com.intellij.execution.RunnerAndConfigurationSettings;
+import com.intellij.execution.configurations.RunConfiguration;
+import com.intellij.execution.configurations.RunProfile;
+import com.intellij.execution.impl.RunManagerImpl;
+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.ExecutionEnvironment;
+import com.intellij.execution.runners.ExecutionUtil;
+import com.intellij.execution.ui.RunContentDescriptor;
+import com.intellij.execution.ui.RunContentManager;
+import com.intellij.ide.DataManager;
+import com.intellij.ide.util.PropertiesComponent;
+import com.intellij.openapi.actionSystem.LangDataKeys;
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.application.ModalityState;
+import com.intellij.openapi.components.PersistentStateComponent;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.Key;
+import com.intellij.ui.content.Content;
+import com.intellij.util.ObjectUtils;
+import com.intellij.util.containers.ContainerUtil;
+import com.intellij.util.xmlb.annotations.AbstractCollection;
+import com.intellij.util.xmlb.annotations.Attribute;
+import com.intellij.util.xmlb.annotations.Tag;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+import java.util.List;
+import java.util.Set;
+
+public abstract class AbstractAutoTestManager implements PersistentStateComponent<AbstractAutoTestManager.State> {
+  protected static final String AUTO_TEST_MANAGER_DELAY = "auto.test.manager.delay";
+  protected static final int AUTO_TEST_MANAGER_DELAY_DEFAULT = 3000;
+  private static final Key<ProcessListener> ON_TERMINATION_RESTARTER_KEY = Key.create("auto.test.manager.on.termination.restarter");
+  private final Project myProject;
+  private final Set<RunProfile> myEnabledRunProfiles = ContainerUtil.newHashSet();
+  protected int myDelayMillis;
+  private AutoTestWatcher myWatcher;
+
+  public AbstractAutoTestManager(@NotNull Project project) {
+    myProject = project;
+    myDelayMillis = PropertiesComponent.getInstance(project).getInt(AUTO_TEST_MANAGER_DELAY, AUTO_TEST_MANAGER_DELAY_DEFAULT);
+    myWatcher = createWatcher(project);
+  }
+
+  @Nullable
+  private static ExecutionEnvironment getCurrentEnvironment(@NotNull Content content) {
+    JComponent component = content.getComponent();
+    if (component == null) {
+      return null;
+    }
+    return LangDataKeys.EXECUTION_ENVIRONMENT.getData(DataManager.getInstance().getDataContext(component));
+  }
+
+  private static void clearRestarterListener(@NotNull ProcessHandler processHandler) {
+    ProcessListener restarterListener = ON_TERMINATION_RESTARTER_KEY.get(processHandler, null);
+    if (restarterListener != null) {
+      processHandler.removeProcessListener(restarterListener);
+      ON_TERMINATION_RESTARTER_KEY.set(processHandler, null);
+    }
+  }
+
+  private static void restart(@NotNull RunContentDescriptor descriptor) {
+    descriptor.setActivateToolWindowWhenAdded(false);
+    descriptor.setReuseToolWindowActivation(true);
+    ExecutionUtil.restart(descriptor);
+  }
+
+  public static void saveConfigurationState(State state, RunProfile profile) {
+    RunConfiguration runConfiguration = ObjectUtils.tryCast(profile, RunConfiguration.class);
+    if (runConfiguration != null) {
+      RunConfigurationDescriptor descriptor = new RunConfigurationDescriptor();
+      descriptor.myType = runConfiguration.getType().getId();
+      descriptor.myName = runConfiguration.getName();
+      state.myEnabledRunConfigurations.add(descriptor);
+    }
+  }
+
+  public static List<RunConfiguration> loadConfigurations(State state, Project project) {
+    List<RunConfiguration> configurations = ContainerUtil.newArrayList();
+    RunManagerImpl runManager = RunManagerImpl.getInstanceImpl(project);
+    List<RunConfigurationDescriptor> descriptors = ContainerUtil.notNullize(state.myEnabledRunConfigurations);
+    for (RunConfigurationDescriptor descriptor : descriptors) {
+      if (descriptor.myType != null && descriptor.myName != null) {
+        RunnerAndConfigurationSettings settings = runManager.findConfigurationByTypeAndName(descriptor.myType,
+                                                                                            descriptor.myName);
+        RunConfiguration configuration = settings != null ? settings.getConfiguration() : null;
+        if (configuration != null) {
+          configurations.add(configuration);
+        }
+      }
+    }
+    return configurations;
+  }
+
+  @NotNull
+  protected abstract AutoTestWatcher createWatcher(Project project);
+
+  public void setAutoTestEnabled(@NotNull RunContentDescriptor descriptor, @NotNull ExecutionEnvironment environment, boolean enabled) {
+    Content content = descriptor.getAttachedContent();
+    if (content != null) {
+      if (enabled) {
+        myEnabledRunProfiles.add(environment.getRunProfile());
+        myWatcher.activate();
+      }
+      else {
+        myEnabledRunProfiles.remove(environment.getRunProfile());
+        if (!hasEnabledAutoTests()) {
+          myWatcher.deactivate();
+        }
+        ProcessHandler processHandler = descriptor.getProcessHandler();
+        if (processHandler != null) {
+          clearRestarterListener(processHandler);
+        }
+      }
+    }
+  }
+
+  private boolean hasEnabledAutoTests() {
+    RunContentManager contentManager = ExecutionManager.getInstance(myProject).getContentManager();
+    for (RunContentDescriptor descriptor : contentManager.getAllDescriptors()) {
+      if (isAutoTestEnabledForDescriptor(descriptor)) {
+        return true;
+      }
+    }
+    return false;
+  }
+
+  public boolean isAutoTestEnabled(@NotNull RunContentDescriptor descriptor) {
+    return isAutoTestEnabledForDescriptor(descriptor);
+  }
+
+  private boolean isAutoTestEnabledForDescriptor(@NotNull RunContentDescriptor descriptor) {
+    Content content = descriptor.getAttachedContent();
+    if (content != null) {
+      ExecutionEnvironment environment = getCurrentEnvironment(content);
+      return environment != null && myEnabledRunProfiles.contains(environment.getRunProfile());
+    }
+    return false;
+  }
+
+  protected void restartAllAutoTests(int modificationStamp) {
+    RunContentManager contentManager = ExecutionManager.getInstance(myProject).getContentManager();
+    boolean active = false;
+    for (RunContentDescriptor descriptor : contentManager.getAllDescriptors()) {
+      if (isAutoTestEnabledForDescriptor(descriptor)) {
+        restartAutoTest(descriptor, modificationStamp, myWatcher);
+        active = true;
+      }
+    }
+    if (!active) {
+      myWatcher.deactivate();
+    }
+  }
+
+  private void restartAutoTest(@NotNull RunContentDescriptor descriptor,
+                               int modificationStamp,
+                               @NotNull AutoTestWatcher documentWatcher) {
+    ProcessHandler processHandler = descriptor.getProcessHandler();
+    if (processHandler != null && !processHandler.isProcessTerminated()) {
+      scheduleRestartOnTermination(descriptor, processHandler, modificationStamp, documentWatcher);
+    }
+    else {
+      restart(descriptor);
+    }
+  }
+
+  private void scheduleRestartOnTermination(@NotNull final RunContentDescriptor descriptor,
+                                            @NotNull final ProcessHandler processHandler,
+                                            final int modificationStamp,
+                                            @NotNull final AutoTestWatcher watcher) {
+    ProcessListener restarterListener = ON_TERMINATION_RESTARTER_KEY.get(processHandler);
+    if (restarterListener != null) {
+      clearRestarterListener(processHandler);
+    }
+    restarterListener = new ProcessAdapter() {
+      @Override
+      public void processTerminated(ProcessEvent event) {
+        clearRestarterListener(processHandler);
+        ApplicationManager.getApplication().invokeLater(() -> {
+          if (isAutoTestEnabledForDescriptor(descriptor) && watcher.isUpToDate(modificationStamp)) {
+            restart(descriptor);
+          }
+        }, ModalityState.any());
+      }
+    };
+    ON_TERMINATION_RESTARTER_KEY.set(processHandler, restarterListener);
+    processHandler.addProcessListener(restarterListener);
+  }
+
+  int getDelay() {
+    return myDelayMillis;
+  }
+
+  void setDelay(int delay) {
+    myDelayMillis = delay;
+    myWatcher.deactivate();
+    myWatcher = createWatcher(myProject);
+    if (hasEnabledAutoTests()) {
+      myWatcher.activate();
+    }
+    PropertiesComponent.getInstance(myProject).setValue(AUTO_TEST_MANAGER_DELAY, myDelayMillis, AUTO_TEST_MANAGER_DELAY_DEFAULT);
+  }
+
+  @Nullable
+  @Override
+  public State getState() {
+    State state = new State();
+    for (RunProfile profile : myEnabledRunProfiles) {
+      saveConfigurationState(state, profile);
+    }
+    return state;
+  }
+
+  @Override
+  public void loadState(State state) {
+    List<RunConfiguration> configurations = loadConfigurations(state, myProject);
+    myEnabledRunProfiles.clear();
+    myEnabledRunProfiles.addAll(configurations);
+    if (!configurations.isEmpty()) {
+      myWatcher.activate();
+    }
+  }
+
+  public static class State {
+    @Tag("enabled-run-configurations")
+    @AbstractCollection(surroundWithTag = false)
+    List<AutoTestManager.RunConfigurationDescriptor> myEnabledRunConfigurations = ContainerUtil.newArrayList();
+  }
+
+  @Tag("run-configuration")
+  static class RunConfigurationDescriptor {
+    @Attribute("type")
+    String myType;
+
+    @Attribute("name")
+    String myName;
+  }
+}
index 1ea08a94e8dc01435f5034282964f1d9d5d077f0..c5a3240d1e18d699076604b9326273df5a918c69 100644 (file)
 package com.intellij.execution.testframework.autotest;
 
 import com.intellij.execution.*;
-import com.intellij.execution.configurations.RunConfiguration;
-import com.intellij.execution.configurations.RunProfile;
-import com.intellij.execution.impl.RunManagerImpl;
-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.ExecutionEnvironment;
-import com.intellij.execution.runners.ExecutionUtil;
-import com.intellij.execution.ui.RunContentDescriptor;
-import com.intellij.execution.ui.RunContentManager;
-import com.intellij.ide.DataManager;
 import com.intellij.ide.scratch.ScratchFileService;
-import com.intellij.ide.util.PropertiesComponent;
-import com.intellij.openapi.actionSystem.LangDataKeys;
-import com.intellij.openapi.application.ApplicationManager;
-import com.intellij.openapi.application.ModalityState;
 import com.intellij.openapi.components.*;
 import com.intellij.openapi.fileEditor.FileEditorManager;
 import com.intellij.openapi.project.Project;
-import com.intellij.openapi.util.Condition;
-import com.intellij.openapi.util.Key;
-import com.intellij.openapi.vfs.VirtualFile;
-import com.intellij.ui.content.Content;
-import com.intellij.util.Consumer;
-import com.intellij.util.ObjectUtils;
-import com.intellij.util.containers.ContainerUtil;
-import com.intellij.util.xmlb.annotations.AbstractCollection;
-import com.intellij.util.xmlb.annotations.Attribute;
-import com.intellij.util.xmlb.annotations.Tag;
 import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
-
-import javax.swing.*;
-import java.util.List;
-import java.util.Set;
 
 /**
  * @author yole
@@ -60,16 +29,7 @@ import java.util.Set;
   name = "AutoTestManager",
   storages = {@Storage(StoragePathMacros.WORKSPACE_FILE)}
 )
-public class AutoTestManager implements PersistentStateComponent<AutoTestManager.State> {
-  private static final String AUTO_TEST_MANAGER_DELAY = "auto.test.manager.delay";
-  private static final int AUTO_TEST_MANAGER_DELAY_DEFAULT = 3000;
-
-  private static final Key<ProcessListener> ON_TERMINATION_RESTARTER_KEY = Key.create("auto.test.manager.on.termination.restarter");
-
-  private final Project myProject;
-  private int myDelayMillis;
-  private DelayedDocumentWatcher myDocumentWatcher;
-  private final Set<RunProfile> myEnabledRunProfiles = ContainerUtil.newHashSet();
+public class AutoTestManager extends AbstractAutoTestManager {
 
   @NotNull
   public static AutoTestManager getInstance(Project project) {
@@ -77,198 +37,18 @@ public class AutoTestManager implements PersistentStateComponent<AutoTestManager
   }
 
   public AutoTestManager(@NotNull Project project) {
-    myProject = project;
-    myDelayMillis = PropertiesComponent.getInstance(project).getInt(AUTO_TEST_MANAGER_DELAY, AUTO_TEST_MANAGER_DELAY_DEFAULT);
-    myDocumentWatcher = createWatcher();
+    super(project);
   }
 
+  @Override
   @NotNull
-  private DelayedDocumentWatcher createWatcher() {
-    return new DelayedDocumentWatcher(myProject, myDelayMillis, modificationStamp -> restartAllAutoTests(modificationStamp), file -> {
+  protected AutoTestWatcher createWatcher(Project project) {
+    return new DelayedDocumentWatcher(project, myDelayMillis, this::restartAllAutoTests, file -> {
       if (ScratchFileService.getInstance().getRootType(file) != null) {
         return false;
       }
       // Vladimir.Krivosheev - I don't know, why AutoTestManager checks it, but old behavior is preserved
-      return FileEditorManager.getInstance(myProject).isFileOpen(file);
+      return FileEditorManager.getInstance(project).isFileOpen(file);
     });
   }
-
-  public void setAutoTestEnabled(@NotNull RunContentDescriptor descriptor, @NotNull ExecutionEnvironment environment, boolean enabled) {
-    Content content = descriptor.getAttachedContent();
-    if (content != null) {
-      if (enabled) {
-        myEnabledRunProfiles.add(environment.getRunProfile());
-        myDocumentWatcher.activate();
-      }
-      else {
-        myEnabledRunProfiles.remove(environment.getRunProfile());
-        if (!hasEnabledAutoTests()) {
-          myDocumentWatcher.deactivate();
-        }
-        ProcessHandler processHandler = descriptor.getProcessHandler();
-        if (processHandler != null) {
-          clearRestarterListener(processHandler);
-        }
-      }
-    }
-  }
-
-  private boolean hasEnabledAutoTests() {
-    RunContentManager contentManager = ExecutionManager.getInstance(myProject).getContentManager();
-    for (RunContentDescriptor descriptor : contentManager.getAllDescriptors()) {
-      if (isAutoTestEnabledForDescriptor(descriptor)) {
-        return true;
-      }
-    }
-    return false;
-  }
-
-  public boolean isAutoTestEnabled(@NotNull RunContentDescriptor descriptor) {
-    return isAutoTestEnabledForDescriptor(descriptor);
-  }
-
-  private boolean isAutoTestEnabledForDescriptor(@NotNull RunContentDescriptor descriptor) {
-    Content content = descriptor.getAttachedContent();
-    if (content != null) {
-      ExecutionEnvironment environment = getCurrentEnvironment(content);
-      return environment != null && myEnabledRunProfiles.contains(environment.getRunProfile());
-    }
-    return false;
-  }
-
-  @Nullable
-  private static ExecutionEnvironment getCurrentEnvironment(@NotNull Content content) {
-    JComponent component = content.getComponent();
-    if (component == null) {
-      return null;
-    }
-    return LangDataKeys.EXECUTION_ENVIRONMENT.getData(DataManager.getInstance().getDataContext(component));
-  }
-
-  private static void clearRestarterListener(@NotNull ProcessHandler processHandler) {
-    ProcessListener restarterListener = ON_TERMINATION_RESTARTER_KEY.get(processHandler, null);
-    if (restarterListener != null) {
-      processHandler.removeProcessListener(restarterListener);
-      ON_TERMINATION_RESTARTER_KEY.set(processHandler, null);
-    }
-  }
-
-  private void restartAllAutoTests(int modificationStamp) {
-    RunContentManager contentManager = ExecutionManager.getInstance(myProject).getContentManager();
-    boolean active = false;
-    for (RunContentDescriptor descriptor : contentManager.getAllDescriptors()) {
-      if (isAutoTestEnabledForDescriptor(descriptor)) {
-        restartAutoTest(descriptor, modificationStamp, myDocumentWatcher);
-        active = true;
-      }
-    }
-    if (!active) {
-      myDocumentWatcher.deactivate();
-    }
-  }
-
-  private void restartAutoTest(@NotNull RunContentDescriptor descriptor,
-                               int modificationStamp,
-                               @NotNull DelayedDocumentWatcher documentWatcher) {
-    ProcessHandler processHandler = descriptor.getProcessHandler();
-    if (processHandler != null && !processHandler.isProcessTerminated()) {
-      scheduleRestartOnTermination(descriptor, processHandler, modificationStamp, documentWatcher);
-    }
-    else {
-      restart(descriptor);
-    }
-  }
-
-  private void scheduleRestartOnTermination(@NotNull final RunContentDescriptor descriptor,
-                                            @NotNull final ProcessHandler processHandler,
-                                            final int modificationStamp,
-                                            @NotNull final DelayedDocumentWatcher documentWatcher) {
-    ProcessListener restarterListener = ON_TERMINATION_RESTARTER_KEY.get(processHandler);
-    if (restarterListener != null) {
-      clearRestarterListener(processHandler);
-    }
-    restarterListener = new ProcessAdapter() {
-      @Override
-      public void processTerminated(ProcessEvent event) {
-        clearRestarterListener(processHandler);
-        ApplicationManager.getApplication().invokeLater(() -> {
-          if (isAutoTestEnabledForDescriptor(descriptor) && documentWatcher.isUpToDate(modificationStamp)) {
-            restart(descriptor);
-          }
-        }, ModalityState.any());
-      }
-    };
-    ON_TERMINATION_RESTARTER_KEY.set(processHandler, restarterListener);
-    processHandler.addProcessListener(restarterListener);
-  }
-
-  private static void restart(@NotNull RunContentDescriptor descriptor) {
-    descriptor.setActivateToolWindowWhenAdded(false);
-    descriptor.setReuseToolWindowActivation(true);
-    ExecutionUtil.restart(descriptor);
-  }
-
-  int getDelay() {
-    return myDelayMillis;
-  }
-
-  void setDelay(int delay) {
-    myDelayMillis = delay;
-    myDocumentWatcher.deactivate();
-    myDocumentWatcher = createWatcher();
-    if (hasEnabledAutoTests()) {
-      myDocumentWatcher.activate();
-    }
-    PropertiesComponent.getInstance(myProject).setValue(AUTO_TEST_MANAGER_DELAY, myDelayMillis, AUTO_TEST_MANAGER_DELAY_DEFAULT);
-  }
-
-  @Nullable
-  @Override
-  public State getState() {
-    State state = new State();
-    for (RunProfile profile : myEnabledRunProfiles) {
-      RunConfiguration runConfiguration = ObjectUtils.tryCast(profile, RunConfiguration.class);
-      if (runConfiguration != null) {
-        RunConfigurationDescriptor descriptor = new RunConfigurationDescriptor();
-        descriptor.myType = runConfiguration.getType().getId();
-        descriptor.myName = runConfiguration.getName();
-        state.myEnabledRunConfigurations.add(descriptor);
-      }
-    }
-    return state;
-  }
-
-  @Override
-  public void loadState(State state) {
-    List<RunConfiguration> configurations = ContainerUtil.newArrayList();
-    RunManagerImpl runManager = RunManagerImpl.getInstanceImpl(myProject);
-    List<RunConfigurationDescriptor> descriptors = ContainerUtil.notNullize(state.myEnabledRunConfigurations);
-    for (RunConfigurationDescriptor descriptor : descriptors) {
-      if (descriptor.myType != null && descriptor.myName != null) {
-        RunnerAndConfigurationSettings settings = runManager.findConfigurationByTypeAndName(descriptor.myType,
-                                                                                            descriptor.myName);
-        RunConfiguration configuration = settings != null ? settings.getConfiguration() : null;
-        if (configuration != null) {
-          configurations.add(configuration);
-        }
-      }
-    }
-    myEnabledRunProfiles.clear();
-    myEnabledRunProfiles.addAll(configurations);
-  }
-
-  static class State {
-    @Tag("enabled-run-configurations")
-    @AbstractCollection(surroundWithTag = false)
-    List<RunConfigurationDescriptor> myEnabledRunConfigurations = ContainerUtil.newArrayList();
-  }
-
-  @Tag("run-configuration")
-  static class RunConfigurationDescriptor {
-    @Attribute("type")
-    String myType;
-
-    @Attribute("name")
-    String myName;
-  }
 }
diff --git a/platform/testRunner/src/com/intellij/execution/testframework/autotest/AutoTestWatcher.java b/platform/testRunner/src/com/intellij/execution/testframework/autotest/AutoTestWatcher.java
new file mode 100644 (file)
index 0000000..c52b459
--- /dev/null
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2000-2016 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.intellij.execution.testframework.autotest;
+
+public interface AutoTestWatcher {
+  void activate();
+  void deactivate();
+
+  boolean isUpToDate(int modificationStamp);
+}
index 1031ed63644e69aa2f8a84abfca8c1b1edc4337b..9cf3551954dbc1b91b2cb6a358e76b55f20c1a4a 100644 (file)
@@ -32,7 +32,7 @@ public class ToggleAutoTestAction extends ToggleAction {
   public boolean isSelected(AnActionEvent e) {
     Project project = e.getProject();
     RunContentDescriptor descriptor = e.getData(LangDataKeys.RUN_CONTENT_DESCRIPTOR);
-    return project != null && descriptor != null && AutoTestManager.getInstance(project).isAutoTestEnabled(descriptor);
+    return project != null && descriptor != null && getAutoTestManager(project).isAutoTestEnabled(descriptor);
   }
 
   @Override
@@ -41,7 +41,11 @@ public class ToggleAutoTestAction extends ToggleAction {
     RunContentDescriptor descriptor = e.getData(LangDataKeys.RUN_CONTENT_DESCRIPTOR);
     ExecutionEnvironment environment = e.getData(LangDataKeys.EXECUTION_ENVIRONMENT);
     if (project != null && descriptor != null && environment != null) {
-      AutoTestManager.getInstance(project).setAutoTestEnabled(descriptor, environment, state);
+      getAutoTestManager(project).setAutoTestEnabled(descriptor, environment, state);
     }
   }
+
+  public AbstractAutoTestManager getAutoTestManager(Project project) {
+    return AutoTestManager.getInstance(project);
+  }
 }
index 8b15fbb8a36a7e81b89487f4c8f61be5b2b8708f..7fa62255b26dd60b889e1978cf831f5d9daf9b76 100644 (file)
@@ -376,7 +376,7 @@ public class GitCheckinEnvironment implements CheckinEnvironment {
     GitSimpleHandler diff = new GitSimpleHandler(project, root, GitCommand.DIFF);
     diff.setSilent(true);
     diff.setStdoutSuppressed(true);
-    diff.addParameters("--diff-filter=ADMRUX", "--name-status", "HEAD");
+    diff.addParameters("--diff-filter=ADMRUX", "--name-status", "--no-renames", "HEAD");
     diff.endOptions();
     String output;
     try {
index b74eee23e71d8031a7340cb8968b62f690cff68c..f8edc89bc4aaed9022adf3ee1164b4262f973b39 100644 (file)
@@ -163,7 +163,7 @@ public class MergeChangeCollector {
     GitSimpleHandler h = new GitSimpleHandler(myProject, myRoot, GitCommand.DIFF);
     h.setSilent(true);
     // note that moves are not detected here
-    h.addParameters("--name-status", "--diff-filter=ADMRUX", revisions);
+    h.addParameters("--name-status", "--diff-filter=ADMRUX", "--no-renames", revisions);
     for (StringScanner s = new StringScanner(h.run()); s.hasMoreData();) {
       if (s.isEol()) {
         s.nextLine();
index 861c5df6be3a8ac04bb001eb318bb1ecb3bf414d..949eb08496414889a2e8e63a18698673f988afb8 100644 (file)
@@ -61,7 +61,6 @@
     <programRunner implementation="com.intellij.execution.junit.JUnitDebuggerRunner"/>
     <codeInsight.externalLibraryResolver implementation="com.intellij.execution.junit.codeInsight.JUnitExternalLibraryResolver"/>
     <junitListener implementation="com.intellij.junit4.JUnitTestDiscoveryListener"/>
-    <configurationType implementation="com.intellij.execution.junit.testDiscovery.JUnitTestDiscoveryConfigurationType"/>
     <runConfigurationProducer implementation="com.intellij.execution.junit.testDiscovery.JUnitTestDiscoveryConfigurationProducer"/>
   </extensions>
 
index 801966a3cb056d2b70405f4a041299e6306120e1..4b12dbd100b3ead7c478db46e6323332b1c46996 100644 (file)
@@ -58,6 +58,8 @@ public class JUnitConfiguration extends JavaTestConfigurationBase {
   @NonNls public static final String TEST_DIRECTORY = "directory";
   @NonNls public static final String TEST_CATEGORY = "category";
   @NonNls public static final String TEST_METHOD = "method";
+  @NonNls public static final String BY_SOURCE_POSITION = "source location";
+  @NonNls public static final String BY_SOURCE_CHANGES = "changes";
 
   //fork modes
   @NonNls public static final String FORK_NONE = "none";
@@ -288,6 +290,11 @@ public class JUnitConfiguration extends JavaTestConfigurationBase {
     setGeneratedName();
   }
 
+  public void beFromSourcePosition(PsiLocation<PsiMethod> sourceLocation) {
+    myData.setTestMethod(sourceLocation);
+    myData.TEST_OBJECT = BY_SOURCE_POSITION;
+  }
+
   public void setMainClass(final PsiClass testClass) {
     final boolean shouldUpdateName = isGeneratedName();
     setModule(myData.setMainClass(testClass));
@@ -499,6 +506,7 @@ public class JUnitConfiguration extends JavaTestConfigurationBase {
     private String REPEAT_MODE = RepeatCount.ONCE;
     private LinkedHashSet<String> myPattern = new LinkedHashSet<String>();
     private Map<String, String> myEnvs = new LinkedHashMap<String, String>();
+    private String myChangeList = "All";
 
     public boolean equals(final Object object) {
       if (!(object instanceof Data)) return false;
@@ -682,6 +690,14 @@ public class JUnitConfiguration extends JavaTestConfigurationBase {
     public void setCategoryName(String categoryName) {
       CATEGORY_NAME = categoryName;
     }
+
+    public String getChangeList() {
+      return myChangeList;
+    }
+
+    public void setChangeList(String changeList) {
+      myChangeList = changeList;
+    }
   }
 
 }
index d0a5b221583fb3e9c19ded61bbe49ff0d857e235..2c6e2f613eb6d73b12b9db5ef9e4e7a65f26143f 100644 (file)
@@ -17,7 +17,6 @@ package com.intellij.execution.junit;
 
 import com.intellij.execution.JavaTestFrameworkDebuggerRunner;
 import com.intellij.execution.configurations.RunProfile;
-import com.intellij.execution.junit.testDiscovery.JUnitTestDiscoveryConfiguration;
 import org.jetbrains.annotations.NotNull;
 
 /**
@@ -26,8 +25,7 @@ import org.jetbrains.annotations.NotNull;
 public class JUnitDebuggerRunner extends JavaTestFrameworkDebuggerRunner {
   @Override
   protected boolean validForProfile(@NotNull RunProfile profile) {
-    return profile instanceof JUnitConfiguration || 
-           profile instanceof JUnitTestDiscoveryConfiguration;
+    return profile instanceof JUnitConfiguration;
   }
 
   @NotNull
index 1b4d05abf3a55e261ffa3dfe121005b0e8a683ba..cd7c4abe4ec51d5155e096089411fd230537ca9a 100644 (file)
@@ -21,6 +21,8 @@ import com.intellij.execution.configurations.JavaParameters;
 import com.intellij.execution.configurations.ParametersList;
 import com.intellij.execution.configurations.RunnerSettings;
 import com.intellij.execution.configurations.RuntimeConfigurationException;
+import com.intellij.execution.junit.testDiscovery.TestBySource;
+import com.intellij.execution.junit.testDiscovery.TestsByChanges;
 import com.intellij.execution.junit2.TestProxy;
 import com.intellij.execution.junit2.segments.DeferredActionsQueue;
 import com.intellij.execution.junit2.segments.DeferredActionsQueueImpl;
@@ -47,7 +49,6 @@ import com.intellij.openapi.module.Module;
 import com.intellij.openapi.module.ModuleUtilCore;
 import com.intellij.openapi.project.Project;
 import com.intellij.openapi.util.Disposer;
-import com.intellij.openapi.util.Getter;
 import com.intellij.openapi.util.Key;
 import com.intellij.openapi.util.io.FileUtil;
 import com.intellij.openapi.util.text.StringUtil;
@@ -100,6 +101,12 @@ public abstract class TestObject extends JavaTestFrameworkRunnableState<JUnitCon
     if (JUnitConfiguration.TEST_PATTERN.equals(id)) {
       return new TestsPattern(configuration, environment);
     }
+    if (JUnitConfiguration.BY_SOURCE_POSITION.equals(id)) {
+      return new TestBySource(configuration, environment);
+    }
+    if (JUnitConfiguration.BY_SOURCE_CHANGES.equals(id)) {
+      return new TestsByChanges(configuration, environment);
+    }
     LOG.error(MESSAGE + id);
     return null;
   }
diff --git a/plugins/junit/src/com/intellij/execution/junit/testDiscovery/JUnitTestDiscoveryConfiguration.java b/plugins/junit/src/com/intellij/execution/junit/testDiscovery/JUnitTestDiscoveryConfiguration.java
deleted file mode 100644 (file)
index dddbc7f..0000000
+++ /dev/null
@@ -1,147 +0,0 @@
-/*
- * Copyright 2000-2015 JetBrains s.r.o.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.intellij.execution.junit.testDiscovery;
-
-import com.intellij.execution.ExecutionException;
-import com.intellij.execution.Executor;
-import com.intellij.execution.configurations.*;
-import com.intellij.execution.junit.JUnitConfiguration;
-import com.intellij.execution.junit.JUnitConfigurationType;
-import com.intellij.execution.junit.TestObject;
-import com.intellij.execution.runners.ExecutionEnvironment;
-import com.intellij.execution.testDiscovery.TestDiscoveryConfiguration;
-import com.intellij.execution.testDiscovery.TestDiscoverySearchHelper;
-import com.intellij.execution.testframework.SearchForTestsTask;
-import com.intellij.execution.testframework.SourceScope;
-import com.intellij.execution.testframework.TestSearchScope;
-import com.intellij.openapi.project.Project;
-import com.intellij.openapi.util.text.StringUtil;
-import com.intellij.psi.*;
-import com.intellij.psi.search.GlobalSearchScope;
-import com.intellij.refactoring.listeners.RefactoringElementListener;
-import com.intellij.util.FunctionUtil;
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
-
-import java.util.Set;
-
-public class JUnitTestDiscoveryConfiguration extends TestDiscoveryConfiguration {
-
-  public JUnitTestDiscoveryConfiguration(String name, Project project, ConfigurationFactory factory) {
-    super(name, new JavaRunConfigurationModule(project, false), factory,
-          new JUnitConfiguration("", project, JUnitConfigurationType.getInstance().getConfigurationFactories()[0]));
-  }
-
-  @Nullable
-  @Override
-  public RunProfileState getState(@NotNull Executor executor, @NotNull ExecutionEnvironment environment) throws ExecutionException {
-    return new JUnitTestDiscoveryRunnableState(environment);
-  }
-
-  @Nullable
-  @Override
-  public RefactoringElementListener getRefactoringElementListener(PsiElement element) {
-    return null;
-  }
-
-  @NotNull
-  @Override
-  public String getFrameworkPrefix() {
-    return "j";
-  }
-
-  private class JUnitTestDiscoveryRunnableState extends TestObject {
-    public JUnitTestDiscoveryRunnableState(ExecutionEnvironment environment) {
-      super(((JUnitConfiguration)myDelegate), environment);
-    }
-
-    @Override
-    protected TestSearchScope getScope() {
-      return getConfigurationModule().getModule() != null ? TestSearchScope.MODULE_WITH_DEPENDENCIES : TestSearchScope.WHOLE_PROJECT;
-    }
-
-    @Override
-    protected boolean forkPerModule() {
-      return spansMultipleModules("");
-    }
-
-    @Override
-    protected PsiElement retrievePsiElement(Object pattern) {
-      if (pattern instanceof String) {
-        final String className = StringUtil.getPackageName((String)pattern, ',');
-        if (!pattern.equals(className)) {
-          final JavaPsiFacade facade = JavaPsiFacade.getInstance(getProject());
-          final SourceScope sourceScope = getSourceScope();
-          final GlobalSearchScope globalSearchScope = sourceScope != null ? sourceScope.getGlobalSearchScope()
-                                                                          : GlobalSearchScope.projectScope(getProject());
-          return facade.findClass(className, globalSearchScope);
-        }
-      }
-      return null;
-    }
-
-    @Override
-    public SearchForTestsTask createSearchingForTestsTask() {
-      return new SearchForTestsTask(getProject(), myServerSocket) {
-
-        private Set<String> myPatterns;
-
-        @Override
-        protected void search() throws ExecutionException {
-          myPatterns = TestDiscoverySearchHelper.search(getProject(), getPosition(), getChangeList(), getFrameworkPrefix());
-        }
-
-        @Override
-        protected void onFound() {
-          if (myPatterns != null) {
-            try {
-              addClassesListToJavaParameters(myPatterns, FunctionUtil.<String>id(), "", false, getJavaParameters());
-            }
-            catch (ExecutionException ignored) {}
-          }
-        }
-      };
-    }
-
-    @Override
-    protected JavaParameters createJavaParameters() throws ExecutionException {
-      final JavaParameters javaParameters = super.createJavaParameters();
-      createTempFiles(javaParameters);
-
-      createServerSocket(javaParameters);
-      return javaParameters;
-    }
-
-    @Override
-    public String suggestActionName() {
-      return "";
-    }
-
-    @Override
-    public RefactoringElementListener getListener(PsiElement element, JUnitConfiguration configuration) {
-      return null;
-    }
-
-    @Override
-    public boolean isConfiguredByElement(JUnitConfiguration configuration,
-                                         PsiClass testClass,
-                                         PsiMethod testMethod,
-                                         PsiPackage testPackage,
-                                         PsiDirectory testDir) {
-      return false;
-    }
-  }
-}
index 04c0d760cce5da960a30d13f25d2fa343874c77b..4322448281460865c0328a3e0643870b47728716 100644 (file)
  */
 package com.intellij.execution.junit.testDiscovery;
 
-import com.intellij.execution.configurations.ConfigurationTypeUtil;
+import com.intellij.execution.JavaTestConfigurationBase;
+import com.intellij.execution.PsiLocation;
+import com.intellij.execution.junit.JUnitConfiguration;
+import com.intellij.execution.junit.JUnitConfigurationType;
 import com.intellij.execution.testDiscovery.TestDiscoveryConfigurationProducer;
+import com.intellij.openapi.util.Pair;
+import com.intellij.psi.PsiMethod;
 
 public class JUnitTestDiscoveryConfigurationProducer extends TestDiscoveryConfigurationProducer {
   protected JUnitTestDiscoveryConfigurationProducer() {
-    super(ConfigurationTypeUtil.findConfigurationType(JUnitTestDiscoveryConfigurationType.class));
+    super(JUnitConfigurationType.getInstance());
+  }
+
+  @Override
+  protected void setPosition(JavaTestConfigurationBase configuration, PsiLocation<PsiMethod> position) {
+    ((JUnitConfiguration)configuration).beFromSourcePosition(position);
+  }
+
+  @Override
+  protected Pair<String, String> getPosition(JavaTestConfigurationBase configuration) {
+    final JUnitConfiguration.Data data = ((JUnitConfiguration)configuration).getPersistentData();
+    if (data.TEST_OBJECT.equals(JUnitConfiguration.BY_SOURCE_POSITION)) {
+      return Pair.create(data.getMainClassName(), data.getMethodName());
+    }
+    return null;
   }
 }
diff --git a/plugins/junit/src/com/intellij/execution/junit/testDiscovery/JUnitTestDiscoveryConfigurationType.java b/plugins/junit/src/com/intellij/execution/junit/testDiscovery/JUnitTestDiscoveryConfigurationType.java
deleted file mode 100644 (file)
index 1ce8423..0000000
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * Copyright 2000-2015 JetBrains s.r.o.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.intellij.execution.junit.testDiscovery;
-
-import com.intellij.execution.configuration.ConfigurationFactoryEx;
-import com.intellij.execution.configurations.ConfigurationFactory;
-import com.intellij.execution.configurations.ConfigurationType;
-import com.intellij.execution.configurations.ModuleBasedConfiguration;
-import com.intellij.execution.configurations.RunConfiguration;
-import com.intellij.execution.testDiscovery.TestDiscoveryConfiguration;
-import com.intellij.icons.AllIcons;
-import com.intellij.openapi.project.Project;
-import org.jetbrains.annotations.NotNull;
-
-import javax.swing.*;
-
-public class JUnitTestDiscoveryConfigurationType implements ConfigurationType {
-    private final ConfigurationFactory myFactory;
-
-    public JUnitTestDiscoveryConfigurationType() {
-        myFactory = new ConfigurationFactoryEx(this) {
-            public RunConfiguration createTemplateConfiguration(Project project) {
-                return new JUnitTestDiscoveryConfiguration("", project, this);
-            }
-
-            @Override
-            public void onNewConfigurationCreated(@NotNull RunConfiguration configuration) {
-                ((ModuleBasedConfiguration)configuration).onNewConfigurationCreated();
-            }
-        };
-    }
-
-    @Override
-    public String getDisplayName() {
-        return "JUnit Test Discovery";
-    }
-
-    @Override
-    public String getConfigurationTypeDescription() {
-        return "Runs junit tests which passed changed code";
-    }
-
-    @Override
-    public Icon getIcon() {
-        return AllIcons.RunConfigurations.Junit;
-    }
-
-    @NotNull
-    @Override
-    public String getId() {
-        return "JUnitTestDiscovery";
-    }
-
-    @Override
-    public ConfigurationFactory[] getConfigurationFactories() {
-        return new ConfigurationFactory[] {myFactory};
-    }
-}
diff --git a/plugins/junit/src/com/intellij/execution/junit/testDiscovery/JUnitTestDiscoveryRunnableState.java b/plugins/junit/src/com/intellij/execution/junit/testDiscovery/JUnitTestDiscoveryRunnableState.java
new file mode 100644 (file)
index 0000000..1c01eb9
--- /dev/null
@@ -0,0 +1,123 @@
+/*
+ * Copyright 2000-2016 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.intellij.execution.junit.testDiscovery;
+
+import com.intellij.execution.ExecutionException;
+import com.intellij.execution.configurations.JavaParameters;
+import com.intellij.execution.junit.JUnitConfiguration;
+import com.intellij.execution.junit.TestObject;
+import com.intellij.execution.runners.ExecutionEnvironment;
+import com.intellij.execution.testDiscovery.TestDiscoverySearchHelper;
+import com.intellij.execution.testframework.SearchForTestsTask;
+import com.intellij.execution.testframework.SourceScope;
+import com.intellij.execution.testframework.TestSearchScope;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.Pair;
+import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.psi.*;
+import com.intellij.psi.search.GlobalSearchScope;
+import com.intellij.refactoring.listeners.RefactoringElementListener;
+import com.intellij.util.FunctionUtil;
+
+import java.util.Set;
+
+abstract class JUnitTestDiscoveryRunnableState extends TestObject {
+  public JUnitTestDiscoveryRunnableState(JUnitConfiguration configuration, ExecutionEnvironment environment) {
+    super(configuration, environment);
+  }
+
+  protected abstract String getChangeList();
+  protected abstract Pair<String, String> getPosition();
+
+
+  @Override
+  protected TestSearchScope getScope() {
+    return getConfiguration().getConfigurationModule().getModule() != null ? TestSearchScope.MODULE_WITH_DEPENDENCIES : TestSearchScope.WHOLE_PROJECT;
+  }
+
+  @Override
+  protected boolean forkPerModule() {
+    return getConfiguration().getConfigurationModule().getModule() == null;
+  }
+
+  @Override
+  protected PsiElement retrievePsiElement(Object pattern) {
+    if (pattern instanceof String) {
+      final String className = StringUtil.getPackageName((String)pattern, ',');
+      if (!pattern.equals(className)) {
+        final Project project = getConfiguration().getProject();
+        final JavaPsiFacade facade = JavaPsiFacade.getInstance(project);
+        final SourceScope sourceScope = getSourceScope();
+        final GlobalSearchScope globalSearchScope = sourceScope != null ? sourceScope.getGlobalSearchScope()
+                                                                        : GlobalSearchScope.projectScope(project);
+        return facade.findClass(className, globalSearchScope);
+      }
+    }
+    return null;
+  }
+
+  @Override
+  public SearchForTestsTask createSearchingForTestsTask() {
+    return new SearchForTestsTask(getConfiguration().getProject(), myServerSocket) {
+
+      private Set<String> myPatterns;
+
+      @Override
+      protected void search() throws ExecutionException {
+        myPatterns = TestDiscoverySearchHelper.search(getProject(), getPosition(), getChangeList(), getConfiguration().getFrameworkPrefix());
+      }
+
+      @Override
+      protected void onFound() {
+        if (myPatterns != null) {
+          try {
+            addClassesListToJavaParameters(myPatterns, FunctionUtil.<String>id(), "", false, getJavaParameters());
+          }
+          catch (ExecutionException ignored) {
+          }
+        }
+      }
+    };
+  }
+
+  @Override
+  protected JavaParameters createJavaParameters() throws ExecutionException {
+    final JavaParameters javaParameters = super.createJavaParameters();
+    createTempFiles(javaParameters);
+
+    createServerSocket(javaParameters);
+    return javaParameters;
+  }
+
+  @Override
+  public String suggestActionName() {
+    return "";
+  }
+
+  @Override
+  public RefactoringElementListener getListener(PsiElement element, JUnitConfiguration configuration) {
+    return null;
+  }
+
+  @Override
+  public boolean isConfiguredByElement(JUnitConfiguration configuration,
+                                       PsiClass testClass,
+                                       PsiMethod testMethod,
+                                       PsiPackage testPackage,
+                                       PsiDirectory testDir) {
+    return false;
+  }
+}
diff --git a/plugins/junit/src/com/intellij/execution/junit/testDiscovery/TestBySource.java b/plugins/junit/src/com/intellij/execution/junit/testDiscovery/TestBySource.java
new file mode 100644 (file)
index 0000000..fc254f0
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2000-2016 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.intellij.execution.junit.testDiscovery;
+
+import com.intellij.execution.junit.JUnitConfiguration;
+import com.intellij.execution.runners.ExecutionEnvironment;
+import com.intellij.openapi.util.Pair;
+
+public class TestBySource extends JUnitTestDiscoveryRunnableState {
+  public TestBySource(JUnitConfiguration configuration,
+                      ExecutionEnvironment environment) {
+    super(configuration, environment);
+  }
+
+  @Override
+  protected String getChangeList() {
+    return null;
+  }
+
+  @Override
+  protected Pair<String, String> getPosition() {
+    final JUnitConfiguration.Data data = getConfiguration().getPersistentData();
+    return Pair.create(data.getMainClassName(), data.getMethodName());
+  }
+}
diff --git a/plugins/junit/src/com/intellij/execution/junit/testDiscovery/TestsByChanges.java b/plugins/junit/src/com/intellij/execution/junit/testDiscovery/TestsByChanges.java
new file mode 100644 (file)
index 0000000..6bf529e
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2000-2016 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.intellij.execution.junit.testDiscovery;
+
+import com.intellij.execution.junit.JUnitConfiguration;
+import com.intellij.execution.runners.ExecutionEnvironment;
+import com.intellij.openapi.util.Pair;
+
+public class TestsByChanges extends JUnitTestDiscoveryRunnableState  {
+  public TestsByChanges(JUnitConfiguration configuration, ExecutionEnvironment environment) {
+    super(configuration, environment);
+  }
+
+  @Override
+  protected String getChangeList() {
+    return getConfiguration().getPersistentData().getChangeList();
+  }
+
+  @Override
+  protected Pair<String, String> getPosition() {
+    return null;
+  }
+}
index dd5fe63329407c9142316b8ff64cf7a3b587281f..1d9a7e53c6dcb90c3de4a01d4519236feb4315e4 100644 (file)
@@ -3,7 +3,7 @@
   <grid id="ece1c" binding="myWholePanel" layout-manager="GridLayoutManager" row-count="4" column-count="2" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
     <margin top="0" left="0" bottom="0" right="0"/>
     <constraints>
-      <xy x="28" y="28" width="722" height="566"/>
+      <xy x="28" y="28" width="722" height="591"/>
     </constraints>
     <properties/>
     <border type="none"/>
         </constraints>
         <properties/>
       </component>
-      <grid id="5947e" layout-manager="GridLayoutManager" row-count="7" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
+      <grid id="5947e" layout-manager="GridLayoutManager" row-count="8" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
         <margin top="0" left="0" bottom="0" right="0"/>
         <constraints>
           <grid row="1" column="0" row-span="1" col-span="2" vsize-policy="3" hsize-policy="3" anchor="1" fill="1" indent="0" use-parent-layout="false"/>
         <children>
           <component id="b109" class="com.intellij.openapi.ui.LabeledComponent" binding="myMethod" custom-create="true">
             <constraints>
-              <grid row="5" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
+              <grid row="6" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
             </constraints>
             <properties>
               <enabled value="true"/>
               <text value="Directory"/>
             </properties>
           </component>
+          <component id="9b01f" class="com.intellij.openapi.ui.LabeledComponent" binding="myChangeListLabeledComponent" default-binding="true">
+            <constraints>
+              <grid row="5" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
+            </constraints>
+            <properties>
+              <componentClass value="javax.swing.JComboBox"/>
+              <labelLocation value="West"/>
+              <text value="Change list"/>
+            </properties>
+          </component>
           <grid id="c9a5f" binding="myScopesPanel" layout-manager="GridLayoutManager" row-count="3" column-count="3" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
             <margin top="0" left="0" bottom="0" right="0"/>
             <constraints>
-              <grid row="6" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
+              <grid row="7" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
             </constraints>
             <properties/>
             <border type="none"/>
index ee86a0e129d713e5ff72a8e577e8e0cbf159b679..8e267e1b17c67daad883348021d5d21d666db429 100644 (file)
@@ -42,6 +42,9 @@ import com.intellij.openapi.ui.*;
 import com.intellij.openapi.ui.ex.MessagesEx;
 import com.intellij.openapi.util.Condition;
 import com.intellij.openapi.util.io.FileUtil;
+import com.intellij.openapi.util.registry.Registry;
+import com.intellij.openapi.vcs.changes.ChangeListManager;
+import com.intellij.openapi.vcs.changes.LocalChangeList;
 import com.intellij.openapi.vfs.VirtualFile;
 import com.intellij.psi.*;
 import com.intellij.psi.search.GlobalSearchScope;
@@ -63,6 +66,8 @@ import java.awt.event.ActionEvent;
 import java.awt.event.ActionListener;
 import java.util.Arrays;
 import java.util.List;
+import java.util.Optional;
+import java.util.concurrent.atomic.AtomicReference;
 
 public class JUnitConfigurable<T extends JUnitConfiguration> extends SettingsEditor<T> implements PanelWithAnchor {
   private static final List<TIntArrayList> ourEnabledFields = Arrays.asList(
@@ -71,8 +76,10 @@ public class JUnitConfigurable<T extends JUnitConfiguration> extends SettingsEdi
     new TIntArrayList(new int[]{1, 2}),
     new TIntArrayList(new int[]{3}),
     new TIntArrayList(new int[]{4}),
-    new TIntArrayList(new int[]{5})
-  );
+    new TIntArrayList(new int[]{5}),
+    new TIntArrayList(new int[]{1, 2}),
+    new TIntArrayList(new int[]{6})
+    );
   private static final String[] FORK_MODE_ALL =
     {JUnitConfiguration.FORK_NONE, JUnitConfiguration.FORK_METHOD, JUnitConfiguration.FORK_KLASS};
   private static final String[] FORK_MODE = {JUnitConfiguration.FORK_NONE, JUnitConfiguration.FORK_METHOD};
@@ -103,6 +110,7 @@ public class JUnitConfigurable<T extends JUnitConfiguration> extends SettingsEdi
   private JPanel myScopesPanel;
   private JComboBox myRepeatCb;
   private JTextField myRepeatCountField;
+  private LabeledComponent<JComboBox<String>> myChangeListLabeledComponent;
   private Project myProject;
   private JComponent anchor;
 
@@ -158,6 +166,10 @@ public class JUnitConfigurable<T extends JUnitConfiguration> extends SettingsEdi
     aModel.addElement(JUnitConfigurationModel.CLASS);
     aModel.addElement(JUnitConfigurationModel.METHOD);
     aModel.addElement(JUnitConfigurationModel.CATEGORY);
+    if (Registry.is("testDiscovery.enabled")) {
+      aModel.addElement(JUnitConfigurationModel.BY_SOURCE_POSITION);
+      aModel.addElement(JUnitConfigurationModel.BY_SOURCE_CHANGES);
+    }
     myTypeChooser.setModel(aModel);
     myTypeChooser.setRenderer(new ListCellRendererWrapper<Integer>() {
       @Override
@@ -181,6 +193,12 @@ public class JUnitConfigurable<T extends JUnitConfiguration> extends SettingsEdi
           case JUnitConfigurationModel.CATEGORY:
             setText("Category");
             break;
+          case JUnitConfigurationModel.BY_SOURCE_POSITION:
+            setText("Through source location");
+            break;
+          case JUnitConfigurationModel.BY_SOURCE_CHANGES:
+            setText("Over changes in sources");
+            break;
         }
       }
     });
@@ -256,6 +274,15 @@ public class JUnitConfigurable<T extends JUnitConfiguration> extends SettingsEdi
     setAnchor(mySearchForTestsLabel);
     myJrePathEditor.setAnchor(myModule.getLabel());
     myCommonJavaParameters.setAnchor(myModule.getLabel());
+
+    final DefaultComboBoxModel<String> model = new DefaultComboBoxModel<>();
+    myChangeListLabeledComponent.getComponent().setModel(model);
+    model.addElement("All");
+
+    final List<LocalChangeList> changeLists = ChangeListManager.getInstance(project).getChangeLists();
+    for (LocalChangeList changeList : changeLists) {
+      model.addElement(changeList.getName());
+    }
   }
 
   private static void addRadioButtonsListeners(final JRadioButton[] radioButtons, ChangeListener listener) {
@@ -269,6 +296,7 @@ public class JUnitConfigurable<T extends JUnitConfiguration> extends SettingsEdi
 
   public void applyEditorTo(final JUnitConfiguration configuration) {
     myModel.apply(getModuleSelector().getModule(), configuration);
+    configuration.getPersistentData().setChangeList((String)myChangeListLabeledComponent.getComponent().getSelectedItem());
     applyHelpersTo(configuration);
     final JUnitConfiguration.Data data = configuration.getPersistentData();
     if (myWholeProjectScope.isSelected()) {
@@ -301,6 +329,7 @@ public class JUnitConfigurable<T extends JUnitConfiguration> extends SettingsEdi
     myRepeatCb.setSelectedItem(configuration.getRepeatMode());
 
     myModel.reset(configuration);
+    myChangeListLabeledComponent.getComponent().setSelectedItem(configuration.getPersistentData().getChangeList());
     myCommonJavaParameters.reset(configuration);
     getModuleSelector().reset(configuration);
     final TestSearchScope scope = configuration.getPersistentData().getScope();
@@ -332,6 +361,7 @@ public class JUnitConfigurable<T extends JUnitConfiguration> extends SettingsEdi
       myCategory.setVisible(false);
       myMethod.setVisible(false);
       myDir.setVisible(false);
+      myChangeListLabeledComponent.setVisible(false);
       myForkCb.setEnabled(true);
       myForkCb.setModel(new DefaultComboBoxModel(FORK_MODE_ALL));
       myForkCb.setSelectedItem(selectedItem);
@@ -342,6 +372,7 @@ public class JUnitConfigurable<T extends JUnitConfiguration> extends SettingsEdi
       myPattern.setVisible(false);
       myClass.setVisible(false);
       myCategory.setVisible(false);
+      myChangeListLabeledComponent.setVisible(false);
       myMethod.setVisible(false);
       myForkCb.setEnabled(true);
       myForkCb.setModel(new DefaultComboBoxModel(FORK_MODE_ALL));
@@ -354,12 +385,13 @@ public class JUnitConfigurable<T extends JUnitConfiguration> extends SettingsEdi
       myDir.setVisible(false);
       myClass.setVisible(true);
       myCategory.setVisible(false);
+      myChangeListLabeledComponent.setVisible(false);
       myMethod.setVisible(false);
       myForkCb.setEnabled(true);
       myForkCb.setModel(getForkModelBasedOnRepeat());
       myForkCb.setSelectedItem(selectedItem != JUnitConfiguration.FORK_KLASS ? selectedItem : JUnitConfiguration.FORK_METHOD);
     }
-    else if (selectedType == JUnitConfigurationModel.METHOD){
+    else if (selectedType == JUnitConfigurationModel.METHOD || selectedType == JUnitConfigurationModel.BY_SOURCE_POSITION){
       myPackagePanel.setVisible(false);
       myScopesPanel.setVisible(false);
       myPattern.setVisible(false);
@@ -367,6 +399,7 @@ public class JUnitConfigurable<T extends JUnitConfiguration> extends SettingsEdi
       myClass.setVisible(true);
       myCategory.setVisible(false);
       myMethod.setVisible(true);
+      myChangeListLabeledComponent.setVisible(false);
       myForkCb.setEnabled(false);
       myForkCb.setSelectedItem(JUnitConfiguration.FORK_NONE);
     } else if (selectedType == JUnitConfigurationModel.CATEGORY) {
@@ -377,6 +410,20 @@ public class JUnitConfigurable<T extends JUnitConfiguration> extends SettingsEdi
       myClass.setVisible(false);
       myCategory.setVisible(true);
       myMethod.setVisible(false);
+      myChangeListLabeledComponent.setVisible(false);
+      myForkCb.setEnabled(true);
+      myForkCb.setModel(new DefaultComboBoxModel(FORK_MODE_ALL));
+      myForkCb.setSelectedItem(selectedItem);
+    }
+    else if (selectedType == JUnitConfigurationModel.BY_SOURCE_CHANGES) {
+      myPackagePanel.setVisible(false);
+      myScopesPanel.setVisible(false);
+      myDir.setVisible(false);
+      myPattern.setVisible(false);
+      myClass.setVisible(false);
+      myCategory.setVisible(false);
+      myMethod.setVisible(false);
+      myChangeListLabeledComponent.setVisible(true);
       myForkCb.setEnabled(true);
       myForkCb.setModel(new DefaultComboBoxModel(FORK_MODE_ALL));
       myForkCb.setSelectedItem(selectedItem);
@@ -389,6 +436,7 @@ public class JUnitConfigurable<T extends JUnitConfiguration> extends SettingsEdi
       myClass.setVisible(false);
       myCategory.setVisible(false);
       myMethod.setVisible(true);
+      myChangeListLabeledComponent.setVisible(false);
       myForkCb.setEnabled(true);
       myForkCb.setModel(new DefaultComboBoxModel(FORK_MODE_ALL));
       myForkCb.setSelectedItem(selectedItem);
@@ -496,6 +544,7 @@ public class JUnitConfigurable<T extends JUnitConfiguration> extends SettingsEdi
     myPattern.setAnchor(anchor);
     myPackage.setAnchor(anchor);
     myCategory.setAnchor(anchor);
+    myChangeListLabeledComponent.setAnchor(anchor);
   }
 
   public void onTypeChanged(final int newType) {
index 15325555956dcb96966ad84d558f2887fb7dcf1d..a187b83dc375b12544a20b61b3d448a80f7cdf25 100644 (file)
@@ -43,6 +43,8 @@ public class JUnitConfigurationModel {
   public static final int PATTERN = 3;
   public static final int DIR = 4;
   public static final int CATEGORY = 5;
+  public static final int BY_SOURCE_POSITION = 6;
+  public static final int BY_SOURCE_CHANGES = 7;
 
   private static final List<String> ourTestObjects;
 
@@ -52,7 +54,9 @@ public class JUnitConfigurationModel {
                                    JUnitConfiguration.TEST_METHOD,
                                    JUnitConfiguration.TEST_PATTERN,
                                    JUnitConfiguration.TEST_DIRECTORY,
-                                   JUnitConfiguration.TEST_CATEGORY);
+                                   JUnitConfiguration.TEST_CATEGORY,
+                                   JUnitConfiguration.BY_SOURCE_POSITION,
+                                   JUnitConfiguration.BY_SOURCE_CHANGES);
   }
 
 
@@ -104,7 +108,8 @@ public class JUnitConfigurationModel {
     if (testObject != JUnitConfiguration.TEST_PACKAGE &&
         testObject != JUnitConfiguration.TEST_PATTERN &&
         testObject != JUnitConfiguration.TEST_DIRECTORY &&
-        testObject != JUnitConfiguration.TEST_CATEGORY) {
+        testObject != JUnitConfiguration.TEST_CATEGORY  &&
+        testObject != JUnitConfiguration.BY_SOURCE_CHANGES) {
       try {
         data.METHOD_NAME = getJUnitTextValue(METHOD);
         final PsiClass testClass = !myProject.isDefault() && !StringUtil.isEmptyOrSpaces(className) ? JUnitUtil.findPsiClass(className, module, myProject) : null;
@@ -122,7 +127,7 @@ public class JUnitConfigurationModel {
         data.MAIN_CLASS_NAME = className;
       }
     }
-    else {
+    else if (testObject != JUnitConfiguration.BY_SOURCE_CHANGES) {
       if (testObject == JUnitConfiguration.TEST_PACKAGE) {
         data.PACKAGE_NAME = getJUnitTextValue(ALL_IN_PACKAGE);
       }
index 26d66662e5dc975923180ff955ec7204ef66127b..adcba4c0025d7ca2405b896d76eb32ad7e9e9a47 100644 (file)
@@ -72,7 +72,6 @@
       <programRunner implementation="com.theoryinpractice.testng.configuration.TestNGDebuggerRunner"/>
       <runConfigurationProducer
           implementation="com.theoryinpractice.testng.configuration.testDiscovery.TestNGTestDiscoveryConfigurationProducer"/>
-      <configurationType implementation="com.theoryinpractice.testng.configuration.testDiscovery.TestNGTestDiscoveryConfigurationType"/>
     </extensions>
   <extensions defaultExtensionNs="com.theoryinpractice.testng">
     <listener implementation="org.testng.TestNGTestDiscoveryListener"/>
index a9ee30adcd1b2f992355dda72b40610278f2bf5a..278114640cf04f4e1d348804aae4efacdee8a052 100644 (file)
@@ -47,6 +47,7 @@ import com.intellij.psi.search.GlobalSearchScope;
 import com.intellij.refactoring.listeners.RefactoringElementAdapter;
 import com.intellij.refactoring.listeners.RefactoringElementListener;
 import com.intellij.refactoring.listeners.UndoRefactoringElementListener;
+import com.theoryinpractice.testng.configuration.testDiscovery.TestNGTestDiscoveryRunnableState;
 import com.theoryinpractice.testng.model.TestData;
 import com.theoryinpractice.testng.model.TestNGConsoleProperties;
 import com.theoryinpractice.testng.model.TestNGTestObject;
@@ -128,6 +129,10 @@ public class TestNGConfiguration extends JavaTestConfigurationBase {
   }
 
   public RunProfileState getState(@NotNull final Executor executor, @NotNull final ExecutionEnvironment env) throws ExecutionException {
+    final TestData data = getPersistantData();
+    if (data.TEST_OBJECT.equals(TestType.SOURCE.getType()) || data.getChangeList() != null) {
+      return new TestNGTestDiscoveryRunnableState(env, this);
+    }
     return new TestNGRunnableState(env, this);
   }
 
@@ -439,4 +444,9 @@ public class TestNGConfiguration extends JavaTestConfigurationBase {
       .collect(Collectors.toSet());
     return groups.isEmpty() ? null : groups;
   }
+
+  public void beFromSourcePosition(PsiLocation<PsiMethod> position) {
+    setMethodConfiguration(position);
+    getPersistantData().TEST_OBJECT = TestType.SOURCE.getType();
+  }
 }
index 6dcebbbfb117e33baba708843a29752d95c4dc53..68d67c975184ef2e32561dd58808e888fef972fc 100644 (file)
           <grid row="3" column="0" row-span="1" col-span="1" vsize-policy="6" hsize-policy="1" anchor="0" fill="2" indent="0" use-parent-layout="false"/>
         </constraints>
       </vspacer>
-      <grid id="3054c" layout-manager="GridLayoutManager" row-count="1" column-count="7" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
+      <grid id="3054c" layout-manager="GridLayoutManager" row-count="1" column-count="3" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
         <margin top="0" left="0" bottom="0" right="0"/>
         <constraints>
           <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
         <properties/>
         <border type="none"/>
         <children>
-          <component id="5eaf8" class="javax.swing.JRadioButton" binding="groupTest">
-            <constraints>
-              <grid row="0" column="2" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="9" fill="0" indent="0" use-parent-layout="false"/>
-            </constraints>
-            <properties>
-              <text value="&amp;Group"/>
-            </properties>
-          </component>
-          <component id="61e8b" class="javax.swing.JRadioButton" binding="classTest">
-            <constraints>
-              <grid row="0" column="3" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="9" fill="0" indent="0" use-parent-layout="false"/>
-            </constraints>
-            <properties>
-              <text value="C&amp;lass"/>
-            </properties>
-          </component>
-          <component id="2ab9d" class="javax.swing.JRadioButton" binding="methodTest">
-            <constraints>
-              <grid row="0" column="4" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="9" fill="0" indent="0" use-parent-layout="false"/>
-            </constraints>
-            <properties>
-              <text value="&amp;Method"/>
-            </properties>
-          </component>
-          <component id="dcd9c" class="javax.swing.JRadioButton" binding="packageTest">
+          <hspacer id="99645">
             <constraints>
-              <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="9" fill="0" indent="0" use-parent-layout="false"/>
+              <grid row="0" column="2" row-span="1" col-span="1" vsize-policy="1" hsize-policy="6" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
             </constraints>
-            <properties>
-              <text value="All in &amp;package"/>
-            </properties>
-          </component>
-          <component id="d8c06" class="javax.swing.JRadioButton" binding="suiteTest">
+          </hspacer>
+          <component id="fccce" class="javax.swing.JComboBox" binding="myTestKind">
             <constraints>
-              <grid row="0" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="9" fill="0" indent="0" use-parent-layout="false"/>
+              <grid row="0" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="2" anchor="8" fill="1" indent="0" use-parent-layout="false"/>
             </constraints>
-            <properties>
-              <text value="S&amp;uite"/>
-            </properties>
+            <properties/>
           </component>
-          <component id="99b4d" class="com.intellij.ui.components.JBRadioButton" binding="patternTest">
+          <component id="ec186" class="com.intellij.ui.components.JBLabel" binding="myTestLabel">
             <constraints>
-              <grid row="0" column="5" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="0" fill="0" indent="0" use-parent-layout="false"/>
+              <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="0" fill="0" indent="0" use-parent-layout="false"/>
             </constraints>
             <properties>
-              <text value="Pattern"/>
+              <text value="Test kind:"/>
             </properties>
           </component>
-          <hspacer id="99645">
-            <constraints>
-              <grid row="0" column="6" row-span="1" col-span="1" vsize-policy="1" hsize-policy="6" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
-            </constraints>
-          </hspacer>
         </children>
       </grid>
     </children>
index 2d954563d7cbd9bd75eab6a1f5dac2af50a94220..2466d11f3689473d336b3d9a6c3e77634481a721 100644 (file)
@@ -45,10 +45,12 @@ import com.intellij.openapi.options.SettingsEditor;
 import com.intellij.openapi.project.Project;
 import com.intellij.openapi.ui.*;
 import com.intellij.openapi.util.Condition;
+import com.intellij.openapi.util.registry.Registry;
 import com.intellij.openapi.vfs.VirtualFile;
 import com.intellij.psi.*;
 import com.intellij.psi.search.GlobalSearchScope;
 import com.intellij.ui.*;
+import com.intellij.ui.components.JBLabel;
 import com.intellij.ui.components.JBList;
 import com.intellij.ui.table.TableView;
 import com.intellij.util.IconUtil;
@@ -71,6 +73,7 @@ import java.awt.*;
 import java.awt.event.ActionEvent;
 import java.awt.event.ActionListener;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Map;
 
 public class TestNGConfigurationEditor<T extends TestNGConfiguration> extends SettingsEditor<T> implements PanelWithAnchor {
@@ -83,12 +86,8 @@ public class TestNGConfigurationEditor<T extends TestNGConfiguration> extends Se
   private LabeledComponent<ModulesComboBox> moduleClasspath;
   private JrePathEditor alternateJDK;
   private final ConfigurationModuleSelector moduleSelector;
-  private JRadioButton suiteTest;
-  private JRadioButton packageTest;
-  private JRadioButton classTest;
-  private JRadioButton methodTest;
-  private JRadioButton groupTest;
-  private JRadioButton patternTest;
+  private JComboBox<TestType> myTestKind;
+  private JBLabel myTestLabel;
   private final TestNGConfigurationModel model;
   private LabeledComponent<EditorTextFieldWithBrowseButton> methodField;
   private LabeledComponent<EditorTextFieldWithBrowseButton> packageField;
@@ -160,31 +159,25 @@ public class TestNGConfigurationEditor<T extends TestNGConfiguration> extends Se
     });
     panel.add(editBtn, BorderLayout.EAST);
 
-    registerListener(new JRadioButton[]{packageTest, classTest, methodTest, groupTest, suiteTest, patternTest}, new ChangeListener() {
-      public void stateChanged(ChangeEvent e) {
-        ButtonModel buttonModel = (ButtonModel)e.getSource();
-        if (buttonModel.isSelected()) {
-          if (buttonModel == packageTest.getModel()) {
-            model.setType(TestType.PACKAGE);
-          }
-          else if (buttonModel == classTest.getModel()) {
-            model.setType(TestType.CLASS);
-          }
-          else if (buttonModel == methodTest.getModel()) {
-            model.setType(TestType.METHOD);
-          }
-          else if (buttonModel == groupTest.getModel()) {
-            model.setType(TestType.GROUP);
-          }
-          else if (buttonModel == suiteTest.getModel()) {
-            model.setType(TestType.SUITE);
-          }
-          else if (buttonModel == patternTest.getModel()) {
-            model.setType(TestType.PATTERN);
-          }
-        }
+    final CollectionComboBoxModel<TestType> testKindModel = new CollectionComboBoxModel<>(Arrays.asList(TestType.values()));
+    if (!Registry.is("testDiscovery.enabled")) {
+      testKindModel.remove(TestType.SOURCE);
+    }
+    myTestKind.setModel(testKindModel);
+    myTestKind.addActionListener(new ActionListener() {
+      @Override
+      public void actionPerformed(ActionEvent e) {
+        TestNGConfigurationEditor.this.model.setType((TestType)myTestKind.getSelectedItem());
       }
     });
+    myTestKind.setRenderer(new ListCellRendererWrapper<TestType>() {
+                             @Override
+                             public void customize(JList list, TestType value, int index, boolean selected, boolean hasFocus) {
+                               if (value != null) {
+                                 setText(value.getPresentableName());
+                               }
+                             }
+                           });
     registerListener(new JRadioButton[]{packagesInProject, packagesInModule, packagesAcrossModules}, null);
     packagesInProject.addChangeListener(new ChangeListener() {
       public void stateChanged(ChangeEvent e) {
@@ -236,7 +229,8 @@ public class TestNGConfigurationEditor<T extends TestNGConfiguration> extends Se
   }
 
   private void redisplay() {
-    if (packageTest.isSelected()) {
+    final TestType testKind = (TestType)myTestKind.getSelectedItem();
+    if (testKind == TestType.PACKAGE) {
       packagePanel.setVisible(true);
       packageField.setVisible(true);
       classField.setVisible(false);
@@ -245,7 +239,7 @@ public class TestNGConfigurationEditor<T extends TestNGConfiguration> extends Se
       suiteField.setVisible(false);
       myPattern.setVisible(false);
     }
-    else if (classTest.isSelected()) {
+    else if (testKind == TestType.CLASS) {
       packagePanel.setVisible(false);
       classField.setVisible(true);
       methodField.setVisible(false);
@@ -253,7 +247,7 @@ public class TestNGConfigurationEditor<T extends TestNGConfiguration> extends Se
       suiteField.setVisible(false);
       myPattern.setVisible(false);
     }
-    else if (methodTest.isSelected()) {
+    else if (testKind == TestType.METHOD || testKind == TestType.SOURCE) {
       packagePanel.setVisible(false);
       classField.setVisible(true);
       methodField.setVisible(true);
@@ -261,7 +255,7 @@ public class TestNGConfigurationEditor<T extends TestNGConfiguration> extends Se
       suiteField.setVisible(false);
       myPattern.setVisible(false);
     }
-    else if (groupTest.isSelected()) {
+    else if (testKind == TestType.GROUP) {
       packagePanel.setVisible(true);
       classField.setVisible(false);
       methodField.setVisible(false);
@@ -269,7 +263,7 @@ public class TestNGConfigurationEditor<T extends TestNGConfiguration> extends Se
       suiteField.setVisible(false);
       myPattern.setVisible(false);
     }
-    else if (suiteTest.isSelected()) {
+    else if (testKind == TestType.SUITE) {
       packagePanel.setVisible(true);
       classField.setVisible(false);
       methodField.setVisible(false);
@@ -277,7 +271,7 @@ public class TestNGConfigurationEditor<T extends TestNGConfiguration> extends Se
       suiteField.setVisible(true);
       myPattern.setVisible(false);
     }
-    else if (patternTest.isSelected()) {
+    else if (testKind == TestType.PATTERN) {
       packagePanel.setVisible(true);
       classField.setVisible(false);
       methodField.setVisible(false);
@@ -327,7 +321,8 @@ public class TestNGConfigurationEditor<T extends TestNGConfiguration> extends Se
     model.apply(getModuleSelector().getModule(), config);
     getModuleSelector().applyTo(config);
     TestData data = config.getPersistantData();
-    if (!classTest.isSelected() && !methodTest.isSelected()) {
+    final TestType testKind = (TestType)myTestKind.getSelectedItem();
+    if (testKind != TestType.CLASS && testKind != TestType.METHOD && testKind != TestType.SOURCE) {
       if (packagesInProject.isSelected()) {
         data.setScope(TestSearchScope.WHOLE_PROJECT);
       }
@@ -379,6 +374,7 @@ public class TestNGConfigurationEditor<T extends TestNGConfiguration> extends Se
     outputDirectory.setAnchor(anchor);
     classField.setAnchor(anchor);
     myPattern.setAnchor(anchor);
+    myTestLabel.setAnchor(anchor);
   }
 
   private static void registerListener(JRadioButton[] buttons, ChangeListener changelistener) {
@@ -393,17 +389,6 @@ public class TestNGConfigurationEditor<T extends TestNGConfiguration> extends Se
 
   private void createView() {
     commonParametersPanel.add(commonJavaParameters, BorderLayout.CENTER);
-
-    packageTest.setSelected(false);
-    suiteTest.setSelected(false);
-    suiteTest.setEnabled(true);
-    groupTest.setSelected(false);
-    groupTest.setEnabled(true);
-    classTest.setSelected(false);
-    classTest.setEnabled(true);
-    patternTest.setSelected(false);
-    patternTest.setEnabled(true);
-
     classField.setComponent(new EditorTextFieldWithBrowseButton(project, true, new JavaCodeFragment.VisibilityChecker() {
       @Override
       public Visibility isDeclarationVisible(PsiElement declaration, PsiElement place) {
@@ -503,8 +488,8 @@ public class TestNGConfigurationEditor<T extends TestNGConfiguration> extends Se
 
   public void onTypeChanged(TestType type) {
     //LOGGER.info("onTypeChanged with " + type);
+    myTestKind.setSelectedItem(type);
     if (type == TestType.PACKAGE) {
-      packageTest.setSelected(true);
       packageField.setEnabled(true);
       classField.setEnabled(false);
       methodField.setEnabled(false);
@@ -513,7 +498,6 @@ public class TestNGConfigurationEditor<T extends TestNGConfiguration> extends Se
       myPattern.setEnabled(false);
     }
     else if (type == TestType.CLASS) {
-      classTest.setSelected(true);
       packageField.setEnabled(false);
       classField.setEnabled(true);
       methodField.setEnabled(false);
@@ -521,8 +505,7 @@ public class TestNGConfigurationEditor<T extends TestNGConfiguration> extends Se
       suiteField.setEnabled(false);
       myPattern.setEnabled(false);
     }
-    else if (type == TestType.METHOD) {
-      methodTest.setSelected(true);
+    else if (type == TestType.METHOD || type == TestType.SOURCE) {
       packageField.setEnabled(false);
       classField.setEnabled(true);
       methodField.setEnabled(true);
@@ -531,7 +514,6 @@ public class TestNGConfigurationEditor<T extends TestNGConfiguration> extends Se
       myPattern.setEnabled(false);
     }
     else if (type == TestType.GROUP) {
-      groupTest.setSelected(true);
       groupField.setEnabled(true);
       packageField.setVisible(false);
       classField.setEnabled(false);
@@ -540,7 +522,6 @@ public class TestNGConfigurationEditor<T extends TestNGConfiguration> extends Se
       myPattern.setEnabled(false);
     }
     else if (type == TestType.SUITE) {
-      suiteTest.setSelected(true);
       suiteField.setEnabled(true);
       packageField.setVisible(false);
       classField.setEnabled(false);
@@ -549,7 +530,6 @@ public class TestNGConfigurationEditor<T extends TestNGConfiguration> extends Se
       myPattern.setEnabled(false);
     }
     else if (type == TestType.PATTERN) {
-      patternTest.setSelected(true);
       myPattern.setEnabled(true);
       suiteField.setEnabled(false);
       packageField.setVisible(false);
index bdd14ce60176836ba5bdfaa6db9211a7cd6953cd..80c682cfc494619f86a9f26a1b5a26f8fe57053d 100644 (file)
@@ -17,15 +17,13 @@ package com.theoryinpractice.testng.configuration;
 
 import com.intellij.execution.JavaTestFrameworkDebuggerRunner;
 import com.intellij.execution.configurations.RunProfile;
-import com.theoryinpractice.testng.configuration.testDiscovery.TestNGTestDiscoveryConfiguration;
 import org.jetbrains.annotations.NotNull;
 
 
 public class TestNGDebuggerRunner extends JavaTestFrameworkDebuggerRunner {
   @Override
   protected boolean validForProfile(@NotNull RunProfile profile) {
-    return profile instanceof TestNGConfiguration || 
-           profile instanceof TestNGTestDiscoveryConfiguration;
+    return profile instanceof TestNGConfiguration;
   }
 
   @NotNull
diff --git a/plugins/testng/src/com/theoryinpractice/testng/configuration/testDiscovery/TestNGTestDiscoveryConfiguration.java b/plugins/testng/src/com/theoryinpractice/testng/configuration/testDiscovery/TestNGTestDiscoveryConfiguration.java
deleted file mode 100644 (file)
index 9ecb0d5..0000000
+++ /dev/null
@@ -1,103 +0,0 @@
-/*
- * Copyright 2000-2015 JetBrains s.r.o.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.theoryinpractice.testng.configuration.testDiscovery;
-
-import com.intellij.execution.CantRunException;
-import com.intellij.execution.ExecutionException;
-import com.intellij.execution.Executor;
-import com.intellij.execution.configurations.*;
-import com.intellij.execution.runners.ExecutionEnvironment;
-import com.intellij.execution.testDiscovery.TestDiscoveryConfiguration;
-import com.intellij.execution.testDiscovery.TestDiscoverySearchHelper;
-import com.intellij.execution.testframework.TestSearchScope;
-import com.intellij.openapi.module.Module;
-import com.intellij.openapi.project.Project;
-import com.intellij.psi.PsiElement;
-import com.intellij.psi.search.GlobalSearchScope;
-import com.intellij.refactoring.listeners.RefactoringElementListener;
-import com.theoryinpractice.testng.configuration.SearchingForTestsTask;
-import com.theoryinpractice.testng.configuration.TestNGConfiguration;
-import com.theoryinpractice.testng.configuration.TestNGConfigurationType;
-import com.theoryinpractice.testng.configuration.TestNGRunnableState;
-import com.theoryinpractice.testng.model.TestNGTestPattern;
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
-
-import java.util.Set;
-
-public class TestNGTestDiscoveryConfiguration extends TestDiscoveryConfiguration {
-
-  public TestNGTestDiscoveryConfiguration(String name, Project project, ConfigurationFactory factory) {
-    super(name, new JavaRunConfigurationModule(project, false), factory,
-          new TestNGConfiguration("", project, TestNGConfigurationType.getInstance().getConfigurationFactories()[0]));
-  }
-
-  @Nullable
-  @Override
-  public RunProfileState getState(@NotNull Executor executor, @NotNull ExecutionEnvironment environment) throws ExecutionException {
-    return new TestNGTestDiscoveryRunnableState(environment);
-  }
-
-  @Nullable
-  @Override
-  public RefactoringElementListener getRefactoringElementListener(PsiElement element) {
-    return null;
-  }
-
-  @NotNull
-  @Override
-  public String getFrameworkPrefix() {
-    return "g";
-  }
-
-  private class TestNGTestDiscoveryRunnableState extends TestNGRunnableState {
-    public TestNGTestDiscoveryRunnableState(ExecutionEnvironment environment) {
-      super(environment, ((TestNGConfiguration)myDelegate));
-    }
-
-    @Override
-    protected TestSearchScope getScope() {
-      return TestSearchScope.MODULE_WITH_DEPENDENCIES;
-    }
-
-    @Override
-    protected boolean forkPerModule() {
-      return spansMultipleModules("");
-    }
-
-    @Override
-    public SearchingForTestsTask createSearchingForTestsTask() {
-      return new SearchingForTestsTask(myServerSocket, getConfiguration(), myTempFile, client) {
-        @Override
-        protected void search() throws CantRunException {
-          myClasses.clear();
-          final Set<String> patterns = TestDiscoverySearchHelper.search(getProject(), getPosition(), getChangeList(), getFrameworkPrefix());
-          final Module module = getConfigurationModule().getModule();
-          final GlobalSearchScope searchScope =
-            module != null ? GlobalSearchScope.moduleWithDependenciesScope(module) : GlobalSearchScope.projectScope(getProject());
-          TestNGTestPattern.fillTestObjects(myClasses, patterns, TestSearchScope.MODULE_WITH_DEPENDENCIES, 
-                                            TestNGTestDiscoveryConfiguration.this, searchScope);
-        }
-
-        @Override
-        protected void onFound() {
-          super.onFound();
-          writeClassesPerModule(myClasses);
-        }
-      };
-    }
-  }
-}
index 8d1e95bf00d60f99f0016e9bfb9ff49118c78753..01833657345e9c673af5acf1a6cd807a38d54b6f 100644 (file)
  */
 package com.theoryinpractice.testng.configuration.testDiscovery;
 
+import com.intellij.execution.JavaTestConfigurationBase;
+import com.intellij.execution.PsiLocation;
 import com.intellij.execution.configurations.ConfigurationTypeUtil;
 import com.intellij.execution.testDiscovery.TestDiscoveryConfigurationProducer;
+import com.intellij.openapi.util.Pair;
+import com.intellij.psi.PsiMethod;
+import com.theoryinpractice.testng.configuration.TestNGConfiguration;
+import com.theoryinpractice.testng.configuration.TestNGConfigurationType;
+import com.theoryinpractice.testng.model.TestData;
+import com.theoryinpractice.testng.model.TestType;
 
 public class TestNGTestDiscoveryConfigurationProducer extends TestDiscoveryConfigurationProducer {
   protected TestNGTestDiscoveryConfigurationProducer() {
-    super(ConfigurationTypeUtil.findConfigurationType(TestNGTestDiscoveryConfigurationType.class));
+    super(TestNGConfigurationType.getInstance());
+  }
+
+  @Override
+  protected void setPosition(JavaTestConfigurationBase configuration, PsiLocation<PsiMethod> position) {
+    ((TestNGConfiguration)configuration).beFromSourcePosition(position);
+  }
+
+  @Override
+  protected Pair<String, String> getPosition(JavaTestConfigurationBase configuration) {
+    final TestData data = ((TestNGConfiguration)configuration).getPersistantData();
+    if (data.TEST_OBJECT.equals(TestType.SOURCE.getType())) {
+      return Pair.create(data.getMainClassName(), data.getMethodName());
+    }
+    return null;
   }
 }
diff --git a/plugins/testng/src/com/theoryinpractice/testng/configuration/testDiscovery/TestNGTestDiscoveryConfigurationType.java b/plugins/testng/src/com/theoryinpractice/testng/configuration/testDiscovery/TestNGTestDiscoveryConfigurationType.java
deleted file mode 100644 (file)
index 1a80550..0000000
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * Copyright 2000-2015 JetBrains s.r.o.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.theoryinpractice.testng.configuration.testDiscovery;
-
-import com.intellij.execution.configuration.ConfigurationFactoryEx;
-import com.intellij.execution.configurations.ConfigurationFactory;
-import com.intellij.execution.configurations.ConfigurationType;
-import com.intellij.execution.configurations.ModuleBasedConfiguration;
-import com.intellij.execution.configurations.RunConfiguration;
-import com.intellij.icons.AllIcons;
-import com.intellij.openapi.project.Project;
-import icons.TestngIcons;
-import org.jetbrains.annotations.NotNull;
-
-import javax.swing.*;
-
-public class TestNGTestDiscoveryConfigurationType implements ConfigurationType {
-    private final ConfigurationFactory myFactory;
-
-    public TestNGTestDiscoveryConfigurationType() {
-        myFactory = new ConfigurationFactoryEx(this) {
-            public RunConfiguration createTemplateConfiguration(Project project) {
-                return new TestNGTestDiscoveryConfiguration("", project, this);
-            }
-
-            @Override
-            public void onNewConfigurationCreated(@NotNull RunConfiguration configuration) {
-                ((ModuleBasedConfiguration)configuration).onNewConfigurationCreated();
-            }
-        };
-    }
-
-    @Override
-    public String getDisplayName() {
-        return "TestNG Test Discovery";
-    }
-
-    @Override
-    public String getConfigurationTypeDescription() {
-        return "Runs TestNG tests which passed changed code";
-    }
-
-    @Override
-    public Icon getIcon() {
-        return TestngIcons.TestNG;
-    }
-
-    @NotNull
-    @Override
-    public String getId() {
-        return "TestNGTestDiscovery";
-    }
-
-    @Override
-    public ConfigurationFactory[] getConfigurationFactories() {
-        return new ConfigurationFactory[] {myFactory};
-    }
-}
diff --git a/plugins/testng/src/com/theoryinpractice/testng/configuration/testDiscovery/TestNGTestDiscoveryRunnableState.java b/plugins/testng/src/com/theoryinpractice/testng/configuration/testDiscovery/TestNGTestDiscoveryRunnableState.java
new file mode 100644 (file)
index 0000000..fc4f12e
--- /dev/null
@@ -0,0 +1,76 @@
+/*
+ * Copyright 2000-2016 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.theoryinpractice.testng.configuration.testDiscovery;
+
+import com.intellij.execution.CantRunException;
+import com.intellij.execution.runners.ExecutionEnvironment;
+import com.intellij.execution.testDiscovery.TestDiscoverySearchHelper;
+import com.intellij.execution.testframework.TestSearchScope;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.util.Pair;
+import com.intellij.psi.search.GlobalSearchScope;
+import com.theoryinpractice.testng.configuration.SearchingForTestsTask;
+import com.theoryinpractice.testng.configuration.TestNGConfiguration;
+import com.theoryinpractice.testng.configuration.TestNGRunnableState;
+import com.theoryinpractice.testng.model.TestData;
+import com.theoryinpractice.testng.model.TestNGTestPattern;
+import com.theoryinpractice.testng.model.TestType;
+
+import java.util.Set;
+
+public class TestNGTestDiscoveryRunnableState extends TestNGRunnableState {
+
+  public TestNGTestDiscoveryRunnableState(ExecutionEnvironment environment,
+                                          TestNGConfiguration configuration) {
+    super(environment, configuration);
+  }
+
+  @Override
+  protected TestSearchScope getScope() {
+    return TestSearchScope.MODULE_WITH_DEPENDENCIES;
+  }
+
+  @Override
+  protected boolean forkPerModule() {
+    return getConfiguration().getConfigurationModule().getModule() == null;
+  }
+
+  @Override
+  public SearchingForTestsTask createSearchingForTestsTask() {
+    return new SearchingForTestsTask(myServerSocket, getConfiguration(), myTempFile, client) {
+      @Override
+      protected void search() throws CantRunException {
+        myClasses.clear();
+        final TestData data = getConfiguration().getPersistantData();
+        final Pair<String, String> position = data.TEST_OBJECT.equals(TestType.SOURCE.getType())
+                                              ? Pair.create(data.getMainClassName(), data.getMethodName()) : null;
+        final Set<String> patterns = TestDiscoverySearchHelper
+          .search(getProject(), position, data.getChangeList(), getConfiguration().getFrameworkPrefix());
+        final Module module = getConfiguration().getConfigurationModule().getModule();
+        final GlobalSearchScope searchScope =
+          module != null ? GlobalSearchScope.moduleWithDependenciesScope(module) : GlobalSearchScope.projectScope(getProject());
+        TestNGTestPattern.fillTestObjects(myClasses, patterns, TestSearchScope.MODULE_WITH_DEPENDENCIES,
+                                          getConfiguration(), searchScope);
+      }
+
+      @Override
+      protected void onFound() {
+        super.onFound();
+        writeClassesPerModule(myClasses);
+      }
+    };
+  }
+}
index a83e58840093a8c863cc171a05e641985456c7b6..5b86ccab78d732e6e21756539cb82f4a68be606f 100644 (file)
@@ -24,6 +24,7 @@ import com.intellij.execution.testframework.TestSearchScope;
 import com.intellij.openapi.module.Module;
 import com.intellij.openapi.project.Project;
 import com.intellij.openapi.util.Comparing;
+import com.intellij.openapi.util.Pair;
 import com.intellij.psi.PsiClass;
 import com.intellij.psi.PsiMethod;
 import com.intellij.psi.PsiPackage;
@@ -57,6 +58,7 @@ public class TestData implements Cloneable
   public boolean USE_DEFAULT_REPORTERS = false;
   public String PROPERTIES_FILE;
   private LinkedHashSet<String> myPatterns = new LinkedHashSet<String>();
+  private String myChangeList;
 
   public TestData() {
     TEST_OBJECT = TestType.CLASS.getType();
@@ -220,4 +222,12 @@ public class TestData implements Cloneable
   public void setPatterns(LinkedHashSet<String> set) {
     myPatterns = set;
   }
+
+  public String getChangeList() {
+    return myChangeList;
+  }
+
+  public void setChangeList(String changeList) {
+    myChangeList = changeList;
+  }
 }
index 2be2d936b2eb8d9c19773e0807f9792ebbea217c..db6076395e019d240eb92d283112d4eec1ec7019 100644 (file)
@@ -47,7 +47,7 @@ public class TestNGConfigurationModel
     private final Project project;
 
     public TestNGConfigurationModel(Project project) {
-        type = TestType.INVALID;
+        type = TestType.CLASS;
         for (int i = 3; i < typeDocuments.length; i++)
             typeDocuments[i] = new PlainDocument();
 
diff --git a/plugins/testng/src/com/theoryinpractice/testng/model/TestNGSource.java b/plugins/testng/src/com/theoryinpractice/testng/model/TestNGSource.java
new file mode 100644 (file)
index 0000000..6037d99
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2000-2016 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.theoryinpractice.testng.model;
+
+import com.intellij.execution.CantRunException;
+import com.intellij.execution.configurations.RuntimeConfigurationException;
+import com.intellij.openapi.util.Pair;
+import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.psi.PsiClass;
+import com.intellij.psi.PsiMethod;
+import com.theoryinpractice.testng.configuration.TestNGConfiguration;
+
+import java.util.List;
+import java.util.Map;
+
+public class TestNGSource extends TestNGTestMethod {
+  public TestNGSource(TestNGConfiguration config) {
+    super(config);
+  }
+
+  @Override
+  public void fillTestObjects(Map<PsiClass, Map<PsiMethod, List<String>>> classes) throws CantRunException {}
+
+  @Override
+  public String getGeneratedName() {
+    final TestData data = myConfig.getPersistantData();
+    return "Tests for " + StringUtil.getQualifiedName(data.getMainClassName(), data.getMethodName());
+  }
+
+  @Override
+  public String getActionName() {
+    return getGeneratedName();
+  }
+
+  @Override
+  public void checkConfiguration() throws RuntimeConfigurationException {
+    super.checkConfiguration();
+  }
+}
index 64e7f1c14a1ef5c8e2b8b807f292dfa35a0635bf..d63a6199c1f9048790e5f161d1fbaeca2eba2c1c 100644 (file)
@@ -72,6 +72,10 @@ public abstract class TestNGTestObject {
     if (testObject.equals(TestType.SUITE.getType())){
       return new TestNGTestSuite(config);
     }
+
+    if (testObject.equals(TestType.SOURCE.getType())) {
+      return new TestNGSource(config);
+    }
     assert false : testObject;
     return null;
   }
index 788c6cd58641e7d79046e7532f52217bc336e1b5..c61adb7c0e22fe4685e81f56e2170e62124fc1bc 100644 (file)
  */
 package com.theoryinpractice.testng.model;
 
-public class TestType
+public enum TestType
 {
-    public static final TestType INVALID = new TestType("INVALID", -1);
-    public static final TestType PACKAGE = new TestType("PACKAGE", 0);
-    public static final TestType CLASS = new TestType("CLASS", 1);
-    public static final TestType METHOD = new TestType("METHOD", 2);
-    public static final TestType GROUP = new TestType("GROUP", 3);
-    public static final TestType SUITE = new TestType("SUITE", 4);
-    public static final TestType PATTERN = new TestType("PATTERN", 5);
+
+    PACKAGE("PACKAGE", "All in package", 0),
+    CLASS  ("CLASS", "Class", 1),
+    METHOD ("METHOD", "Method", 2),
+    GROUP  ("GROUP", "Group", 3),
+    SUITE  ("SUITE", "Suite", 4),
+    PATTERN("PATTERN", "Pattern", 5),
+    SOURCE ("SOURCE", "Source location", 6);
     
     public final String type;
+    private final String presentableName;
     public final int value;
 
-    private TestType(String type, int value) {
+    TestType(String type, String presentableName, int value) {
         this.type = type;
+        this.presentableName = presentableName;
         this.value = value;
     }
 
@@ -47,36 +50,8 @@ public class TestType
     public int getValue() {
         return value;
     }
-    
-    public static TestType valueOf(String type)
-    {
-        if(INVALID.type.equals(type))
-        {
-            return INVALID;
-        }
-        if(PACKAGE.type.equals(type))
-        {
-            return PACKAGE;
-        }
-        if(CLASS.type.equals(type))
-        {
-            return CLASS;
-        }
-        if(METHOD.type.equals(type))
-        {
-            return METHOD;
-        }
-        if(GROUP.type.equals(type))
-        {
-            return GROUP;
-        }
-        if(SUITE.type.equals(type))
-        {
-            return SUITE;
-        }
-        if (PATTERN.type.equals(type)) {
-            return PATTERN;
-        }
-        throw new IllegalArgumentException("Invalid type requested " + type);
+
+    public String getPresentableName() {
+        return presentableName;
     }
 }
\ No newline at end of file
index 0ff569a863210d1671d632a3e78540f5df9c9b74..45058ed3ffa0abc1ffdd88471b5f3622e3c1e832 100644 (file)
     <applicationService serviceInterface="com.intellij.codeInsight.TestFrameworks"
                         serviceImplementation="com.intellij.codeInsight.TestFrameworksImpl"/>
 
+    <projectService serviceInterface="com.intellij.execution.testDiscovery.JavaAutoRunManager"
+                    serviceImplementation="com.intellij.execution.testDiscovery.JavaAutoRunManager"/>
+
     <projectService serviceInterface="com.intellij.ide.util.TreeClassChooserFactory"
                     serviceImplementation="com.intellij.ide.util.TreeClassChooserFactoryImpl"/>
     <projectService serviceInterface="com.intellij.psi.JavaPsiFacade"
     <action id="ShowRecentTests" class="com.intellij.testIntegration.ShowRecentTests">
       <keyboard-shortcut first-keystroke="control shift SEMICOLON" keymap="$default"/>
     </action>
+    <action class="com.intellij.execution.testDiscovery.ConfigureTestDiscoveryAction" id="TestDiscoveryIndexChooser" text="Choose external test discovery indices">
+      <add-to-group group-id="Internal" anchor="last"/>
+    </action>
   </actions>
 
 </idea-plugin>