test discovery: set class/method separately to avoid ambiguities when working with groovy names in position part;

test method browser extracted, one for all configurations
This commit is contained in:
Anna Kozlova
2015-07-16 12:53:13 +03:00
parent 73f043718e
commit 6d5d272e92
11 changed files with 549 additions and 495 deletions
@@ -0,0 +1,61 @@
/*
* 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;
import com.intellij.execution.configuration.BrowseModuleValueActionListener;
import com.intellij.execution.junit.JUnitUtil;
import com.intellij.execution.ui.ConfigurationModuleSelector;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.util.Condition;
import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiMethod;
public abstract class MethodBrowser extends BrowseModuleValueActionListener {
public MethodBrowser(final Project project) {
super(project);
}
protected abstract String getClassName();
protected abstract ConfigurationModuleSelector getModuleSelector();
protected abstract Condition<PsiMethod> getFilter(PsiClass testClass);
protected String showDialog() {
final String className = getClassName();
if (className.trim().length() == 0) {
Messages.showMessageDialog(getField(), ExecutionBundle.message("set.class.name.message"),
ExecutionBundle.message("cannot.browse.method.dialog.title"), Messages.getInformationIcon());
return null;
}
final PsiClass testClass = getModuleSelector().findClass(className);
if (testClass == null) {
Messages.showMessageDialog(getField(), ExecutionBundle.message("class.does.not.exists.error.message", className),
ExecutionBundle.message("cannot.browse.method.dialog.title"),
Messages.getInformationIcon());
return null;
}
final MethodListDlg dlg = new MethodListDlg(testClass, getFilter(testClass), getField());
if (dlg.showAndGet()) {
final PsiMethod method = dlg.getSelected();
if (method != null) {
return method.getName();
}
}
return null;
}
}
@@ -14,9 +14,8 @@
* limitations under the License.
*/
package com.intellij.execution.junit2.configuration;
package com.intellij.execution;
import com.intellij.execution.ExecutionBundle;
import com.intellij.ide.structureView.impl.StructureNodeRenderer;
import com.intellij.openapi.ui.DialogWrapper;
import com.intellij.openapi.util.Condition;
@@ -15,16 +15,29 @@
*/
package com.intellij.execution.testDiscovery;
import com.intellij.codeInsight.completion.CompletionResultSet;
import com.intellij.codeInsight.lookup.LookupElementBuilder;
import com.intellij.execution.MethodBrowser;
import com.intellij.execution.junit.JUnitUtil;
import com.intellij.execution.ui.AlternativeJREPanel;
import com.intellij.execution.ui.ClassBrowser;
import com.intellij.execution.ui.CommonJavaParametersPanel;
import com.intellij.execution.ui.ConfigurationModuleSelector;
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.*;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.ui.EditorTextFieldWithBrowseButton;
import com.intellij.ui.PanelWithAnchor;
import com.intellij.util.TextFieldCompletionProvider;
import com.intellij.util.ui.UIUtil;
import org.jetbrains.annotations.NotNull;
@@ -32,6 +45,7 @@ import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.List;
public class TestDiscoveryConfigurable<T extends TestDiscoveryConfiguration> extends SettingsEditor<T> implements PanelWithAnchor {
@@ -41,7 +55,9 @@ public class TestDiscoveryConfigurable<T extends TestDiscoveryConfiguration> ext
private LabeledComponent<JComboBox> myModule = new LabeledComponent<JComboBox>();
private CommonJavaParametersPanel myCommonJavaParameters = new CommonJavaParametersPanel();
private AlternativeJREPanel myAlternativeJREPanel = new AlternativeJREPanel();
private JTextField myPosition = new JTextField();
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:");
@@ -64,7 +80,77 @@ public class TestDiscoveryConfigurable<T extends TestDiscoveryConfiguration> ext
GridBagConstraints.NORTHWEST, GridBagConstraints.HORIZONTAL,
new Insets(0, 0, 0, 0), 0, 0);
panelWithSettings.add(myPositionRb, gc);
panelWithSettings.add(myPosition, 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());
new TextFieldCompletionProvider() {
@Override
protected void addCompletionVariants(@NotNull String text, int offset, @NotNull String prefix, @NotNull CompletionResultSet result) {
final String className = myClass.getComponent().getText();
if (className.trim().length() == 0) {
return;
}
final PsiClass testClass = getModuleSelector().findClass(className);
if (testClass == null) return;
final JUnitUtil.TestMethodFilter filter = new JUnitUtil.TestMethodFilter(testClass);
for (PsiMethod psiMethod : testClass.getAllMethods()) {
if (filter.value(psiMethod)) {
result.addElement(LookupElementBuilder.create(psiMethod.getName()));
}
}
}
}.apply(textFieldWithBrowseButton.getChildComponent());
myMethod.setComponent(textFieldWithBrowseButton);
final MethodBrowser methodBrowser = new MethodBrowser(project) {
protected Condition<PsiMethod> getFilter(final PsiClass testClass) {
return new Condition<PsiMethod>() {
@Override
public boolean value(PsiMethod method) {
return method.getContainingClass() == testClass;
}
};
}
@Override
protected String getClassName() {
return myClass.getComponent().getText().trim();
}
@Override
protected ConfigurationModuleSelector getModuleSelector() {
return myModuleSelector;
}
};
methodBrowser.setField(textFieldWithBrowseButton);
panelWithSettings.add(myMethod, gc);
panelWithSettings.add(myChangesRb, gc);
panelWithSettings.add(myChangeLists, gc);
@@ -82,7 +168,7 @@ public class TestDiscoveryConfigurable<T extends TestDiscoveryConfiguration> ext
myChangesRb.addActionListener(l);
final java.util.List<LocalChangeList> changeLists = ChangeListManager.getInstance(project).getChangeLists();
final List<LocalChangeList> changeLists = ChangeListManager.getInstance(project).getChangeLists();
final DefaultComboBoxModel model = new DefaultComboBoxModel();
model.addElement("All");
for (LocalChangeList changeList : changeLists) {
@@ -109,7 +195,8 @@ public class TestDiscoveryConfigurable<T extends TestDiscoveryConfiguration> ext
}
private void updateComponents() {
myPosition.setEnabled(myPositionRb.isSelected());
myClass.setEnabled(myPositionRb.isSelected());
myMethod.setEnabled(myPositionRb.isSelected());
myChangeLists.setEnabled(myChangesRb.isSelected());
}
@@ -117,7 +204,8 @@ public class TestDiscoveryConfigurable<T extends TestDiscoveryConfiguration> ext
applyHelpersTo(configuration);
configuration.setAlternativeJrePath(myAlternativeJREPanel.getPath());
configuration.setAlternativeJrePathEnabled(myAlternativeJREPanel.isPathEnabled());
configuration.setPosition(myPositionRb.isSelected() ? myPosition.getText() : null);
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);
@@ -132,10 +220,11 @@ public class TestDiscoveryConfigurable<T extends TestDiscoveryConfiguration> ext
myCommonJavaParameters.reset(configuration);
getModuleSelector().reset(configuration);
myAlternativeJREPanel.init(configuration.getAlternativeJrePath(), configuration.isAlternativeJrePathEnabled());
final String position = configuration.getPosition();
final Pair<String, String> position = configuration.getPosition();
if (position != null) {
myPositionRb.setSelected(true);
myPosition.setText(position);
myClass.getComponent().setText(position.first);
myMethod.getComponent().setText(position.second);
}
else if (myChangesRb.isEnabled()) {
myChangesRb.setSelected(true);
@@ -24,7 +24,9 @@ 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;
@@ -36,7 +38,7 @@ import java.util.Map;
public abstract class TestDiscoveryConfiguration extends JavaTestConfigurationBase {
private String myChangeList;
private String myPosition;
private Pair<String, String> myPosition;
protected JavaTestConfigurationBase myDelegate;
@@ -85,8 +87,13 @@ public abstract class TestDiscoveryConfiguration extends JavaTestConfigurationBa
myChangeList != null && ChangeListManager.getInstance(getProject()).findChangeList(myChangeList) == null) {
throw new RuntimeConfigurationException("Change list " + myChangeList + " doesn't exist");
}
if (myPosition != null && !myPosition.contains(",")) {
throw new RuntimeConfigurationException("Wrong position format: className,methodName expected");
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);
}
@@ -113,7 +120,9 @@ public abstract class TestDiscoveryConfiguration extends JavaTestConfigurationBa
super.readExternal(element);
readModule(element);
myPosition = element.getAttributeValue("position");
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;
@@ -128,7 +137,8 @@ public abstract class TestDiscoveryConfiguration extends JavaTestConfigurationBa
writeModule(element);
if (myPosition != null) {
element.setAttribute("position", myPosition);
element.setAttribute("class", myPosition.first);
element.setAttribute("method", myPosition.second);
}
element.setAttribute("changeList", myChangeList == null ? "All" : myChangeList);
}
@@ -194,7 +204,7 @@ public abstract class TestDiscoveryConfiguration extends JavaTestConfigurationBa
return myDelegate.createTestConsoleProperties(executor);
}
public void setPosition(String position) {
public void setPosition(Pair<String, String> position) {
myPosition = position;
}
@@ -202,7 +212,7 @@ public abstract class TestDiscoveryConfiguration extends JavaTestConfigurationBa
myChangeList = changeList;
}
public String getPosition() {
public Pair<String, String> getPosition() {
return myPosition;
}
@@ -22,6 +22,7 @@ import com.intellij.execution.actions.ConfigurationContext;
import com.intellij.execution.configurations.ConfigurationType;
import com.intellij.execution.junit.JavaRunConfigurationProducerBase;
import com.intellij.execution.testframework.TestSearchScope;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.Ref;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.PsiClass;
@@ -46,25 +47,25 @@ public abstract class TestDiscoveryConfigurationProducer extends JavaRunConfigur
assert contextLocation != null;
final Location location = JavaExecutionUtil.stepIntoSingleClass(contextLocation);
if (location == null) return false;
final String position = getPosition(location);
final Pair<String, String> position = getPosition(location);
if (position != null) {
try {
final Collection<String> testsByMethodName = TestDiscoveryIndex
.getInstance(configuration.getProject()).getTestsByMethodName(position.replace(',', '.'));
.getInstance(configuration.getProject()).getTestsByMethodName(position.first, position.second);
if (testsByMethodName == null || testsByMethodName.isEmpty()) return false;
}
catch (IOException e) {
return false;
}
configuration.setPosition(position);
configuration.setName("Tests for " + StringUtil.getShortName(position));
configuration.setName("Tests for " + StringUtil.getShortName(position.first) + "," + position.second);
setupPackageConfiguration(configurationContext, configuration, TestSearchScope.MODULE_WITH_DEPENDENCIES);
return true;
}
return false;
}
private static String getPosition(Location location) {
private static Pair<String, String> getPosition(Location location) {
final PsiElement psiElement = location.getPsiElement();
final PsiMethod psiMethod = PsiTreeUtil.getParentOfType(psiElement, PsiMethod.class);
if (psiMethod != null) {
@@ -76,7 +77,7 @@ public abstract class TestDiscoveryConfigurationProducer extends JavaRunConfigur
}
final String qualifiedName = containingClass.getQualifiedName();
if (qualifiedName != null) {
return qualifiedName + "," + psiMethod.getName();
return Pair.create(qualifiedName, psiMethod.getName());
}
}
}
@@ -85,7 +86,7 @@ public abstract class TestDiscoveryConfigurationProducer extends JavaRunConfigur
@Override
public boolean isConfigurationFromContext(TestDiscoveryConfiguration configuration, ConfigurationContext configurationContext) {
final String position = getPosition(configurationContext.getLocation());
final Pair<String, String> position = getPosition(configurationContext.getLocation());
return position != null && position.equals(configuration.getPosition());
}
}
@@ -17,6 +17,7 @@ package com.intellij.execution.testDiscovery;
import com.intellij.openapi.components.ProjectComponent;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.ThrowableComputable;
import com.intellij.openapi.util.io.BufferExposingByteArrayOutputStream;
import com.intellij.openapi.vfs.newvfs.persistent.FlushingDaemon;
@@ -37,309 +38,348 @@ import java.util.zip.InflaterInputStream;
* Created by Maxim.Mossienko on 7/9/2015.
*/
public class TestDiscoveryIndex implements ProjectComponent {
private static final String REMOVED_MARKER = "-removed-";
private final Object ourLock = new Object();
private final Project myProject;
private volatile Holder myHolder;
private static final String REMOVED_MARKER = "-removed-";
private final Object ourLock = new Object();
private final Project myProject;
private volatile Holder myHolder;
public TestDiscoveryIndex(Project project) {
myProject = project;
public TestDiscoveryIndex(Project project) {
myProject = project;
}
public Collection<String> getTestsByMethodName(String classFQName, String methodName) throws IOException {
synchronized (ourLock) {
return getHolder().myMethodQNameToTestNames.get(Pair.create(classFQName, methodName));
}
}
public Collection<String> getTestsByMethodName(String fqmethodName) throws IOException {
synchronized (ourLock) {
return getHolder().myMethodQNameToTestNames.get(fqmethodName);
}
private Holder getHolder() {
Holder holder = myHolder;
if (holder == null) {
synchronized (ourLock) {
holder = myHolder;
if (holder == null) holder = myHolder = new Holder();
}
}
return holder;
}
private Holder getHolder() {
Holder holder = myHolder;
public static TestDiscoveryIndex getInstance(Project project) {
return project.getComponent(TestDiscoveryIndex.class);
}
if (holder == null) {
@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() {
}
private final class Holder {
final PersistentHashMap<Pair<String, String>, Collection<String>> myMethodQNameToTestNames;
final PersistentHashMap<String, Map<String, List<String>>> myTestNameToUsedClassesAndMethodMap;
private ScheduledFuture<?> myFlushingFuture;
private boolean myDisposed;
Holder() {
String path = myProject != null ?
TestDiscoveryExtension.baseTestDiscoveryPathForProject(myProject) :
"out";
final File methodQNameToTestNameFile = new File(path + File.separator + "methodQNameToTestName.data");
final File testNameToUsedClassesAndMethodMapFile = new File(path + File.separator + "testToCalledMethodNames");
try {
// todo better io recovery
myMethodQNameToTestNames = IOUtil.openCleanOrResetBroken(
new ThrowableComputable<PersistentHashMap<Pair<String, String>, Collection<String>>, IOException>() {
@Override
public PersistentHashMap<Pair<String, String>, Collection<String>> compute() throws IOException {
return new PersistentHashMap<Pair<String, String>, Collection<String>>(
methodQNameToTestNameFile,
new StringPairKeyDescriptor(),
new DataExternalizer<Collection<String>>() {
public void save(@NotNull DataOutput dataOutput, Collection<String> strings) throws IOException {
for (String string : strings) IOUtil.writeUTF(dataOutput, string);
}
public Collection<String> read(@NotNull DataInput dataInput) throws IOException {
Set<String> result = new THashSet<String>();
while (((InputStream)dataInput).available() > 0) {
String string = IOUtil.readUTF(dataInput);
if (REMOVED_MARKER.equals(string)) {
string = IOUtil.readUTF(dataInput);
result.remove(string);
}
else {
result.add(string);
}
}
return result;
}
}
);
}
}, methodQNameToTestNameFile);
myTestNameToUsedClassesAndMethodMap = IOUtil.openCleanOrResetBroken(
new ThrowableComputable<PersistentHashMap<String, Map<String, List<String>>>, IOException>() {
@Override
public PersistentHashMap<String, Map<String, List<String>>> compute() throws IOException {
return new PersistentHashMap<String, Map<String, List<String>>>(
testNameToUsedClassesAndMethodMapFile,
EnumeratorStringDescriptor.INSTANCE,
new DataExternalizer<Map<String, List<String>>>() {
public void save(@NotNull DataOutput dataOutput, Map<String, List<String>> classAndMethodsMap)
throws IOException {
BufferExposingByteArrayOutputStream out = new BufferExposingByteArrayOutputStream();
Deflater deflater = new Deflater(1);
DataOutputStream dataOutputStream =
new DataOutputStream(new BufferedOutputStream(new DeflaterOutputStream(out, deflater)));
DataInputOutputUtil.writeINT(dataOutputStream, classAndMethodsMap.size());
for (Map.Entry<String, List<String>> e : classAndMethodsMap.entrySet()) {
IOUtil.writeUTF(dataOutputStream, e.getKey());
DataInputOutputUtil.writeINT(dataOutputStream, e.getValue().size());
for (String methodName : e.getValue()) IOUtil.writeUTF(dataOutputStream, methodName);
}
dataOutputStream.close();
deflater.end();
dataOutput.write(out.getInternalBuffer(), 0, out.size());
}
public Map<String, List<String>> read(@NotNull DataInput dataInput) throws IOException {
byte[] buf;
dataInput.readFully(buf = new byte[((InputStream)dataInput).available()]);
DataInputStream dataInputStream =
new DataInputStream(new InflaterInputStream(new ByteArrayInputStream(buf)));
int numberOfClasses = DataInputOutputUtil.readINT(dataInputStream);
THashMap<String, List<String>> result = new THashMap<String, List<String>>(numberOfClasses);
while (numberOfClasses-- > 0) {
String className = IOUtil.readUTF(dataInputStream);
int numberOfMethods = DataInputOutputUtil.readINT(dataInputStream);
ArrayList<String> methods = new ArrayList<String>(numberOfMethods);
while (numberOfMethods-- > 0) methods.add(IOUtil.readUTF(dataInputStream));
result.put(className, methods);
}
dataInputStream.close();
return result;
}
}
);
}
}, testNameToUsedClassesAndMethodMapFile);
myFlushingFuture = FlushingDaemon.everyFiveSeconds(new Runnable() {
@Override
public void run() {
synchronized (ourLock) {
holder = myHolder;
if (holder == null) holder = myHolder = new Holder();
if (myDisposed) {
myFlushingFuture.cancel(false);
return;
}
if (myMethodQNameToTestNames.isDirty()) {
myMethodQNameToTestNames.force();
}
if (myTestNameToUsedClassesAndMethodMap.isDirty()) {
myTestNameToUsedClassesAndMethodMap.force();
}
}
}
return holder;
}
});
}
catch (IOException ex) {
throw new RuntimeException(ex);
}
}
public static TestDiscoveryIndex getInstance(Project project) {
return project.getComponent(TestDiscoveryIndex.class);
void dispose() {
assert Thread.holdsLock(ourLock);
try {
myMethodQNameToTestNames.close();
myTestNameToUsedClassesAndMethodMap.close();
}
catch (IOException e) {
throw new RuntimeException(e);
}
finally {
myDisposed = true;
}
}
@Override
public void initComponent() {}
}
private static class StringPairKeyDescriptor implements KeyDescriptor<Pair<String, String>> {
public static final StringPairKeyDescriptor INSTANCE = new StringPairKeyDescriptor();
@Override
public void disposeComponent() {
synchronized (ourLock) {
Holder holder = myHolder;
if (holder != null) {
holder.dispose();
myHolder = null;
}
}
}
@NotNull
@Override
public String getComponentName() {
return getClass().getName();
public void save(@NotNull DataOutput out, Pair<String, String> value) throws IOException {
IOUtil.writeUTF(out, value.first);
IOUtil.writeUTF(out, value.second);
}
@Override
public void projectOpened() {}
public Pair<String, String> read(@NotNull DataInput in) throws IOException {
return Pair.create(IOUtil.readUTF(in), IOUtil.readUTF(in));
}
@Override
public void projectClosed() {}
private final class Holder {
final PersistentHashMap<String, Collection<String>> myMethodQNameToTestNames;
final PersistentHashMap<String, Map<String, List<String>>> myTestNameToUsedClassesAndMethodMap;
private ScheduledFuture<?> myFlushingFuture;
private boolean myDisposed;
Holder() {
String path = myProject != null ?
TestDiscoveryExtension.baseTestDiscoveryPathForProject(myProject) :
"out";
final File methodQNameToTestNameFile = new File(path + File.separator + "methodQNameToTestName.data");
final File testNameToUsedClassesAndMethodMapFile = new File(path + File.separator + "testToCalledMethodNames");
try {
// todo better io recovery
myMethodQNameToTestNames = IOUtil.openCleanOrResetBroken(
new ThrowableComputable<PersistentHashMap<String, Collection<String>>, IOException>() {
@Override
public PersistentHashMap<String, Collection<String>> compute() throws IOException {
return new PersistentHashMap<String, Collection<String>>(
methodQNameToTestNameFile,
EnumeratorStringDescriptor.INSTANCE,
new DataExternalizer<Collection<String>>() {
public void save(@NotNull DataOutput dataOutput, Collection<String> strings) throws IOException {
for (String string : strings) IOUtil.writeUTF(dataOutput, string);
}
public Collection<String> read(@NotNull DataInput dataInput) throws IOException {
Set<String> result = new THashSet<String>();
while (((InputStream)dataInput).available() > 0) {
String string = IOUtil.readUTF(dataInput);
if (REMOVED_MARKER.equals(string)) {
string = IOUtil.readUTF(dataInput);
result.remove(string);
}
else {
result.add(string);
}
}
return result;
}
}
);
}
}, methodQNameToTestNameFile);
myTestNameToUsedClassesAndMethodMap = IOUtil.openCleanOrResetBroken(
new ThrowableComputable<PersistentHashMap<String, Map<String, List<String>>>, IOException>() {
@Override
public PersistentHashMap<String, Map<String, List<String>>> compute() throws IOException {
return new PersistentHashMap<String, Map<String, List<String>>>(
testNameToUsedClassesAndMethodMapFile,
EnumeratorStringDescriptor.INSTANCE,
new DataExternalizer<Map<String, List<String>>>() {
public void save(@NotNull DataOutput dataOutput, Map<String, List<String>> classAndMethodsMap)
throws IOException {
BufferExposingByteArrayOutputStream out = new BufferExposingByteArrayOutputStream();
Deflater deflater = new Deflater(1);
DataOutputStream dataOutputStream =
new DataOutputStream(new BufferedOutputStream(new DeflaterOutputStream(out, deflater)));
DataInputOutputUtil.writeINT(dataOutputStream, classAndMethodsMap.size());
for (Map.Entry<String, List<String>> e : classAndMethodsMap.entrySet()) {
IOUtil.writeUTF(dataOutputStream, e.getKey());
DataInputOutputUtil.writeINT(dataOutputStream, e.getValue().size());
for (String methodName : e.getValue()) IOUtil.writeUTF(dataOutputStream, methodName);
}
dataOutputStream.close();
deflater.end();
dataOutput.write(out.getInternalBuffer(), 0, out.size());
}
public Map<String, List<String>> read(@NotNull DataInput dataInput) throws IOException {
byte[] buf;
dataInput.readFully(buf = new byte[((InputStream)dataInput).available()]);
DataInputStream dataInputStream =
new DataInputStream(new InflaterInputStream(new ByteArrayInputStream(buf)));
int numberOfClasses = DataInputOutputUtil.readINT(dataInputStream);
THashMap<String, List<String>> result = new THashMap<String, List<String>>(numberOfClasses);
while (numberOfClasses-- > 0) {
String className = IOUtil.readUTF(dataInputStream);
int numberOfMethods = DataInputOutputUtil.readINT(dataInputStream);
ArrayList<String> methods = new ArrayList<String>(numberOfMethods);
while (numberOfMethods-- > 0) methods.add(IOUtil.readUTF(dataInputStream));
result.put(className, methods);
}
dataInputStream.close();
return result;
}
}
);
}
}, testNameToUsedClassesAndMethodMapFile);
myFlushingFuture = FlushingDaemon.everyFiveSeconds(new Runnable() {
@Override
public void run() {
synchronized (ourLock) {
if (myDisposed) {
myFlushingFuture.cancel(false);
return;
}
if (myMethodQNameToTestNames.isDirty()) {
myMethodQNameToTestNames.force();
}
if (myTestNameToUsedClassesAndMethodMap.isDirty()) {
myTestNameToUsedClassesAndMethodMap.force();
}
}
}
});
} catch (IOException ex) {
throw new RuntimeException(ex);
}
}
void dispose() {
assert Thread.holdsLock(ourLock);
try {
myMethodQNameToTestNames.close();
myTestNameToUsedClassesAndMethodMap.close();
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
myDisposed = true;
}
}
public int getHashCode(Pair<String, String> value) {
return value.hashCode();
}
public void updateFromTestTrace(File file) throws IOException {
synchronized (ourLock) {
int fileNameDotIndex = file.getName().lastIndexOf('.');
final String testName = fileNameDotIndex != -1 ? file.getName().substring(0, fileNameDotIndex) : file.getName();
Holder holder = getHolder();
if (holder.myDisposed) return;
Map<String, List<String>> classData = loadClassAndMethodsMap(file);
Map<String, List<String>> previousClassData = holder.myTestNameToUsedClassesAndMethodMap.get(testName);
ValueDiff valueDiff = new ValueDiff(classData, previousClassData);
if (valueDiff.myRemovedClassData != null && !valueDiff.myRemovedClassData.isEmpty()) {
for(String classQName: valueDiff.myRemovedClassData.keySet()) {
for(String methodName: valueDiff.myRemovedClassData.get(classQName)) {
holder.myMethodQNameToTestNames.appendData(classQName + "." + methodName,
new PersistentHashMap.ValueDataAppender() {
@Override
public void append(DataOutput dataOutput) throws IOException {
IOUtil.writeUTF(dataOutput, REMOVED_MARKER);
IOUtil.writeUTF(dataOutput, testName);
}
}
);
}
}
}
if (valueDiff.myAddedOrChangedClassData != null && !valueDiff.myAddedOrChangedClassData.isEmpty()) {
for(String classQName: valueDiff.myAddedOrChangedClassData.keySet()) {
for(String methodName: valueDiff.myAddedOrChangedClassData.get(classQName)) {
holder.myMethodQNameToTestNames.appendData(classQName + "." + methodName,
new PersistentHashMap.ValueDataAppender() {
@Override
public void append(DataOutput dataOutput) throws IOException {
IOUtil.writeUTF(dataOutput, testName);
}
});
}
}
}
if (valueDiff.myAddedOrChangedClassData != null && !valueDiff.myAddedOrChangedClassData.isEmpty() ||
valueDiff.myRemovedClassData != null && !valueDiff.myRemovedClassData.isEmpty()
) {
holder.myTestNameToUsedClassesAndMethodMap.put(testName, classData);
}
}
@Override
public boolean isEqual(Pair<String, String> val1, Pair<String, String> val2) {
return val1.equals(val2);
}
}
public void updateFromTestTrace(File file) throws IOException {
synchronized (ourLock) {
int fileNameDotIndex = file.getName().lastIndexOf('.');
final String testName = fileNameDotIndex != -1 ? file.getName().substring(0, fileNameDotIndex) : file.getName();
static class ValueDiff {
final Map<String, List<String>> myAddedOrChangedClassData;
final Map<String, List<String>> myRemovedClassData;
Holder holder = getHolder();
if (holder.myDisposed) return;
Map<String, List<String>> classData = loadClassAndMethodsMap(file);
Map<String, List<String>> previousClassData = holder.myTestNameToUsedClassesAndMethodMap.get(testName);
ValueDiff(Map<String, List<String>> classData, Map<String, List<String>> previousClassData) {
Map<String, List<String>> addedOrChangedClassData = classData;
Map<String, List<String>> removedClassData = previousClassData;
ValueDiff valueDiff = new ValueDiff(classData, previousClassData);
if (previousClassData != null && !previousClassData.isEmpty()) {
removedClassData = new THashMap<String, List<String>>();
addedOrChangedClassData = new THashMap<String, List<String>>();
for(String classQName: classData.keySet()) {
List<String> currentMethods = classData.get(classQName);
List<String> previousMethods = previousClassData.get(classQName);
if (previousMethods == null) {
addedOrChangedClassData.put(classQName, currentMethods);
continue;
}
THashSet<String> previousMethodsSet = new THashSet<String>(previousMethods);
THashSet<String> currentMethodsSet = new THashSet<String>(currentMethods);
currentMethodsSet.removeAll(previousMethods);
previousMethodsSet.removeAll(currentMethods);
if (!currentMethodsSet.isEmpty()) {
addedOrChangedClassData.put(classQName, new ArrayList<String>(currentMethodsSet));
}
if (!previousMethodsSet.isEmpty()) {
removedClassData.put(classQName, new ArrayList<String>(previousMethodsSet));
}
}
for(String classQName: previousClassData.keySet()) {
if (classData.containsKey(classQName)) continue;
List<String> previousMethods = previousClassData.get(classQName);
removedClassData.put(classQName, previousMethods);
}
}
myAddedOrChangedClassData = addedOrChangedClassData;
myRemovedClassData = removedClassData;
if (valueDiff.myRemovedClassData != null && !valueDiff.myRemovedClassData.isEmpty()) {
for (String classQName : valueDiff.myRemovedClassData.keySet()) {
for (String methodName : valueDiff.myRemovedClassData.get(classQName)) {
holder.myMethodQNameToTestNames.appendData(createKey(classQName, methodName),
new PersistentHashMap.ValueDataAppender() {
@Override
public void append(DataOutput dataOutput) throws IOException {
IOUtil.writeUTF(dataOutput, REMOVED_MARKER);
IOUtil.writeUTF(dataOutput, testName);
}
}
);
}
}
}
@NotNull
private static Map<String, List<String>> loadClassAndMethodsMap(File file) throws IOException {
DataInputStream inputStream = new DataInputStream(new InflaterInputStream(new BufferedInputStream(new FileInputStream(file))));
}
try {
int numberOfClasses = inputStream.readInt();
Map<String, List<String>> classData = new THashMap<String, List<String>>();
while (numberOfClasses-- > 0) {
String classQName = inputStream.readUTF();
int numberOfMethods = inputStream.readInt();
List<String> methodsList;
classData.put(classQName, methodsList = new ArrayList<String>(numberOfMethods));
//System.out.println(classQName + "," + numberOfMethods);
while (numberOfMethods-- > 0) {
String methodName = inputStream.readUTF();
methodsList.add(methodName);
//System.out.println(methodName);
}
}
return classData;
} finally {
inputStream.close();
if (valueDiff.myAddedOrChangedClassData != null && !valueDiff.myAddedOrChangedClassData.isEmpty()) {
for (String classQName : valueDiff.myAddedOrChangedClassData.keySet()) {
for (String methodName : valueDiff.myAddedOrChangedClassData.get(classQName)) {
holder.myMethodQNameToTestNames.appendData(createKey(classQName, methodName),
new PersistentHashMap.ValueDataAppender() {
@Override
public void append(DataOutput dataOutput) throws IOException {
IOUtil.writeUTF(dataOutput, testName);
}
});
}
}
}
if (valueDiff.myAddedOrChangedClassData != null && !valueDiff.myAddedOrChangedClassData.isEmpty() ||
valueDiff.myRemovedClassData != null && !valueDiff.myRemovedClassData.isEmpty()
) {
holder.myTestNameToUsedClassesAndMethodMap.put(testName, classData);
}
}
}
private static Pair<String, String> createKey(String classQName, String methodName) {
return Pair.create(classQName, methodName);
}
static class ValueDiff {
final Map<String, List<String>> myAddedOrChangedClassData;
final Map<String, List<String>> myRemovedClassData;
ValueDiff(Map<String, List<String>> classData, Map<String, List<String>> previousClassData) {
Map<String, List<String>> addedOrChangedClassData = classData;
Map<String, List<String>> removedClassData = previousClassData;
if (previousClassData != null && !previousClassData.isEmpty()) {
removedClassData = new THashMap<String, List<String>>();
addedOrChangedClassData = new THashMap<String, List<String>>();
for (String classQName : classData.keySet()) {
List<String> currentMethods = classData.get(classQName);
List<String> previousMethods = previousClassData.get(classQName);
if (previousMethods == null) {
addedOrChangedClassData.put(classQName, currentMethods);
continue;
}
THashSet<String> previousMethodsSet = new THashSet<String>(previousMethods);
THashSet<String> currentMethodsSet = new THashSet<String>(currentMethods);
currentMethodsSet.removeAll(previousMethods);
previousMethodsSet.removeAll(currentMethods);
if (!currentMethodsSet.isEmpty()) {
addedOrChangedClassData.put(classQName, new ArrayList<String>(currentMethodsSet));
}
if (!previousMethodsSet.isEmpty()) {
removedClassData.put(classQName, new ArrayList<String>(previousMethodsSet));
}
}
for (String classQName : previousClassData.keySet()) {
if (classData.containsKey(classQName)) continue;
List<String> previousMethods = previousClassData.get(classQName);
removedClassData.put(classQName, previousMethods);
}
}
myAddedOrChangedClassData = addedOrChangedClassData;
myRemovedClassData = removedClassData;
}
}
@NotNull
private static Map<String, List<String>> loadClassAndMethodsMap(File file) throws IOException {
DataInputStream inputStream = new DataInputStream(new InflaterInputStream(new BufferedInputStream(new FileInputStream(file))));
try {
int numberOfClasses = inputStream.readInt();
Map<String, List<String>> classData = new THashMap<String, List<String>>();
while (numberOfClasses-- > 0) {
String classQName = inputStream.readUTF();
int numberOfMethods = inputStream.readInt();
List<String> methodsList;
classData.put(classQName, methodsList = new ArrayList<String>(numberOfMethods));
//System.out.println(classQName + "," + numberOfMethods);
while (numberOfMethods-- > 0) {
String methodName = inputStream.readUTF();
methodsList.add(methodName);
//System.out.println(methodName);
}
}
return classData;
}
finally {
inputStream.close();
}
}
}
@@ -20,8 +20,8 @@ import com.intellij.execution.ExecutionException;
import com.intellij.execution.testframework.SearchForTestsTask;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.TextRange;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vcs.changes.Change;
import com.intellij.openapi.vcs.changes.ChangeListManager;
import com.intellij.openapi.vcs.changes.ContentRevision;
@@ -38,10 +38,10 @@ import java.net.ServerSocket;
import java.util.*;
public abstract class TestDiscoverySearchTask extends SearchForTestsTask {
private final String myPosition;
private final Pair<String, String> myPosition;
private final String myChangeList;
public TestDiscoverySearchTask(Project project, ServerSocket socket, String position, String changeList) {
public TestDiscoverySearchTask(Project project, ServerSocket socket, Pair<String, String> position, String changeList) {
super(project, socket);
myPosition = position;
myChangeList = changeList;
@@ -56,7 +56,7 @@ public abstract class TestDiscoverySearchTask extends SearchForTestsTask {
if (myPosition != null) {
try {
final Collection<String> testsByMethodName = TestDiscoveryIndex
.getInstance(project).getTestsByMethodName(myPosition.replace(',', '.'));
.getInstance(project).getTestsByMethodName(myPosition.first, myPosition.second);
if (testsByMethodName != null) {
for (String pattern : testsByMethodName) {
patterns.add(pattern.replace('-', ','));
@@ -141,10 +141,9 @@ public abstract class TestDiscoverySearchTask extends SearchForTestsTask {
if (containingClass != null) {
final String qualifiedName = containingClass.getQualifiedName();
if (qualifiedName != null) {
final String methodFQN = StringUtil.getQualifiedName(qualifiedName, psiMethod.getName());
try {
final Collection<String> testsByMethodName
= TestDiscoveryIndex.getInstance(containingClass.getProject()).getTestsByMethodName(methodFQN);
= TestDiscoveryIndex.getInstance(containingClass.getProject()).getTestsByMethodName(qualifiedName, psiMethod.getName());
if (testsByMethodName != null) {
for (String pattern : testsByMethodName) {
patterns.add(pattern.replace('-', ','));
@@ -19,6 +19,7 @@ package com.intellij.execution.junit2.configuration;
import com.intellij.codeInsight.completion.CompletionResultSet;
import com.intellij.codeInsight.lookup.LookupElementBuilder;
import com.intellij.execution.ExecutionBundle;
import com.intellij.execution.MethodBrowser;
import com.intellij.execution.configuration.BrowseModuleValueActionListener;
import com.intellij.execution.junit.JUnitConfiguration;
import com.intellij.execution.junit.JUnitConfigurationType;
@@ -43,6 +44,7 @@ import com.intellij.openapi.options.SettingsEditor;
import com.intellij.openapi.project.Project;
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.vfs.VirtualFile;
import com.intellij.psi.*;
@@ -123,7 +125,21 @@ public class JUnitConfigurable<T extends JUnitConfiguration> extends SettingsEdi
myBrowsers = new BrowseModuleValueActionListener[]{
new PackageChooserActionListener(project),
new TestClassBrowser(project),
new MethodBrowser(project),
new MethodBrowser(project) {
protected Condition<PsiMethod> getFilter(PsiClass testClass) {
return new JUnitUtil.TestMethodFilter(testClass);
}
@Override
protected String getClassName() {
return JUnitConfigurable.this.getClassName();
}
@Override
protected ConfigurationModuleSelector getModuleSelector() {
return myModuleSelector;
}
},
new TestsChooserActionListener(project),
new BrowseModuleValueActionListener(project) {
@Override
@@ -610,36 +626,6 @@ public class JUnitConfigurable<T extends JUnitConfiguration> extends SettingsEdi
}
}
private class MethodBrowser extends BrowseModuleValueActionListener {
public MethodBrowser(final Project project) {
super(project);
}
protected String showDialog() {
final String className = getClassName();
if (className.trim().length() == 0) {
Messages.showMessageDialog(getField(), ExecutionBundle.message("set.class.name.message"),
ExecutionBundle.message("cannot.browse.method.dialog.title"), Messages.getInformationIcon());
return null;
}
final PsiClass testClass = getModuleSelector().findClass(className);
if (testClass == null) {
Messages.showMessageDialog(getField(), ExecutionBundle.message("class.does.not.exists.error.message", className),
ExecutionBundle.message("cannot.browse.method.dialog.title"),
Messages.getInformationIcon());
return null;
}
final MethodListDlg dlg = new MethodListDlg(testClass, new JUnitUtil.TestMethodFilter(testClass), getField());
if (dlg.showAndGet()) {
final PsiMethod method = dlg.getSelected();
if (method != null) {
return method.getName();
}
}
return null;
}
}
private class CategoryBrowser extends ClassBrowser {
public CategoryBrowser(Project project) {
super(project, "Category Interface");
@@ -26,7 +26,9 @@ import com.intellij.codeInsight.completion.CompletionResultSet;
import com.intellij.codeInsight.lookup.LookupElementBuilder;
import com.intellij.execution.ExecutionBundle;
import com.intellij.execution.JavaExecutionUtil;
import com.intellij.execution.MethodBrowser;
import com.intellij.execution.configuration.BrowseModuleValueActionListener;
import com.intellij.execution.junit.JUnitUtil;
import com.intellij.execution.testframework.TestSearchScope;
import com.intellij.execution.ui.AlternativeJREPanel;
import com.intellij.execution.ui.CommonJavaParametersPanel;
@@ -43,6 +45,7 @@ import com.intellij.openapi.module.Module;
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.vfs.VirtualFile;
import com.intellij.psi.*;
import com.intellij.psi.search.GlobalSearchScope;
@@ -115,7 +118,7 @@ public class TestNGConfigurationEditor extends SettingsEditor<TestNGConfiguratio
public TestNGConfigurationEditor(Project project) {
this.project = project;
BrowseModuleValueActionListener[] browseListeners = new BrowseModuleValueActionListener[]{new PackageBrowser(project),
new TestClassBrowser(project, this), new MethodBrowser(project, this), new GroupBrowser(project, this), new SuiteBrowser(project),
new TestClassBrowser(project, this), new TestNGMethodBrowser(project), new GroupBrowser(project, this), new SuiteBrowser(project),
new TestClassBrowser(project, this) {
@Override
protected void onClassChoosen(PsiClass psiClass) {
@@ -612,4 +615,29 @@ public class TestNGConfigurationEditor extends SettingsEditor<TestNGConfiguratio
}
}
}
private class TestNGMethodBrowser extends MethodBrowser {
public TestNGMethodBrowser(Project project) {
super(project);
}
protected Condition<PsiMethod> getFilter(PsiClass testClass) {
return new Condition<PsiMethod>() {
@Override
public boolean value(PsiMethod method) {
return TestNGUtil.hasTest(method);
}
};
}
@Override
protected String getClassName() {
return TestNGConfigurationEditor.this.getClassName();
}
@Override
protected ConfigurationModuleSelector getModuleSelector() {
return moduleSelector;
}
}
}
@@ -1,56 +0,0 @@
/*
* Copyright 2000-2009 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.browser;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.project.Project;
import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiMethod;
import com.intellij.execution.configuration.BrowseModuleValueActionListener;
import com.theoryinpractice.testng.configuration.TestNGConfigurationEditor;
/**
* @author Hani Suleiman
* Date: Jul 21, 2005
* Time: 4:29:00 PM
*/
public class MethodBrowser extends BrowseModuleValueActionListener
{
private final TestNGConfigurationEditor editor;
public MethodBrowser(Project project, TestNGConfigurationEditor editor) {
super(project);
this.editor = editor;
}
@Override
protected String showDialog() {
String className = editor.getClassName();
if(className.trim().length() == 0) {
Messages.showMessageDialog(getField(), "Set class name first", "Cannot Browse Methods", Messages.getInformationIcon());
return null;
}
PsiClass psiclass = editor.getModuleSelector().findClass(className);
if(psiclass == null) {
Messages.showMessageDialog(getField(), "Class " + className + " does not exist", "Cannot Browse Methods", Messages.getInformationIcon());
return null;
} else {
PsiMethod psimethod = MethodList.showDialog(psiclass, getField());
return psimethod == null ? null : psimethod.getName();
}
}
}
@@ -1,103 +0,0 @@
/*
* Copyright 2000-2013 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.browser;
import com.intellij.ide.structureView.impl.StructureNodeRenderer;
import com.intellij.openapi.ui.DialogBuilder;
import com.intellij.openapi.util.Condition;
import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiMethod;
import com.intellij.psi.PsiSubstitutor;
import com.intellij.psi.util.PsiFormatUtil;
import com.intellij.psi.util.PsiFormatUtilBase;
import com.intellij.ui.*;
import com.intellij.ui.components.JBList;
import com.theoryinpractice.testng.util.TestNGUtil;
import javax.swing.*;
import java.awt.*;
import java.util.Comparator;
public class MethodList extends JPanel
{
private final SortedListModel<PsiMethod> model;
private final JList list;
private final PsiClass psiClass;
private static final Comparator<PsiMethod> comparator = new Comparator<PsiMethod>() {
public int compare(PsiMethod method1, PsiMethod method2)
{
return method1.getName().compareToIgnoreCase(method2.getName());
}
};
public static class TestMethodFilter implements Condition<PsiMethod>
{
public boolean value(PsiMethod method)
{
return TestNGUtil.hasTest(method);
}
}
public MethodList(PsiClass psiClass)
{
super(new BorderLayout());
model = new SortedListModel<PsiMethod>(comparator);
list = new JBList(model);
this.psiClass = psiClass;
evaluate(psiClass.getAllMethods(), new TestMethodFilter());
add(ScrollPaneFactory.createScrollPane(list));
list.setCellRenderer(new ColoredListCellRenderer() {
@Override
protected void customizeCellRenderer(JList jlist, Object obj, int i, boolean flag, boolean flag1)
{
PsiMethod psimethod = (PsiMethod)obj;
append(PsiFormatUtil.formatMethod(psimethod, PsiSubstitutor.EMPTY, PsiFormatUtilBase.SHOW_NAME, 0), StructureNodeRenderer.applyDeprecation(psimethod, SimpleTextAttributes.REGULAR_ATTRIBUTES));
PsiClass psiclass1 = psimethod.getContainingClass();
if(!MethodList.this.psiClass.equals(psiclass1)) {
append(" (" + psiclass1.getQualifiedName() + ')', StructureNodeRenderer.applyDeprecation(psiclass1, SimpleTextAttributes.GRAY_ATTRIBUTES));
}
}
});
list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
ListScrollingUtil.ensureSelectionExists(list);
}
private void evaluate(PsiMethod methods[], Condition<PsiMethod> condition)
{
for(PsiMethod psimethod : methods) {
if(condition.value(psimethod)) model.add(psimethod);
}
}
public PsiMethod getSelected()
{
return (PsiMethod)list.getSelectedValue();
}
public static PsiMethod showDialog(PsiClass psiClass, JComponent component)
{
MethodList list = new MethodList(psiClass);
DialogBuilder builder = new DialogBuilder(component);
builder.setCenterPanel(list);
builder.setPreferredFocusComponent(list.list);
builder.setTitle("Choose Test Method");
return builder.show() != 0 ? null : list.getSelected();
}
}