/* * User: anna * Date: 29-May-2008 */ package com.intellij.coverage.actions; import com.intellij.codeInsight.hint.HintManager; import com.intellij.codeInsight.hint.ImplementationViewComponent; import com.intellij.coverage.CoverageDataManager; import com.intellij.coverage.CoverageSuite; import com.intellij.coverage.CoverageSuitesBundle; import com.intellij.icons.AllIcons; import com.intellij.openapi.actionSystem.*; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.progress.ProgressManager; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.PanelWithText; import com.intellij.openapi.ui.popup.ComponentPopupBuilder; import com.intellij.openapi.ui.popup.JBPopup; import com.intellij.openapi.ui.popup.JBPopupFactory; import com.intellij.openapi.util.Comparing; import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.util.text.StringUtil; import com.intellij.psi.PsiElement; import com.intellij.psi.util.PsiUtilCore; import com.intellij.rt.coverage.data.LineCoverage; import com.intellij.rt.coverage.data.LineData; import com.intellij.ui.popup.NotLookupOrSearchCondition; import com.intellij.util.ArrayUtil; import com.intellij.util.Processor; import com.intellij.util.containers.HashSet; import org.jetbrains.annotations.Nullable; import javax.swing.*; import java.io.DataInputStream; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.util.*; public class ShowCoveringTestsAction extends AnAction { private static final Logger LOG = Logger.getInstance("#" + ShowCoveringTestsAction.class.getName()); private final String myClassFQName; private final LineData myLineData; public ShowCoveringTestsAction(final String classFQName, LineData lineData) { super("Show tests covering line", "Show tests covering line", AllIcons.Modules.TestRoot); myClassFQName = classFQName; myLineData = lineData; } public void actionPerformed(final AnActionEvent e) { final DataContext context = e.getDataContext(); final Project project = CommonDataKeys.PROJECT.getData(context); LOG.assertTrue(project != null); final Editor editor = CommonDataKeys.EDITOR.getData(context); LOG.assertTrue(editor != null); final CoverageSuitesBundle currentSuite = CoverageDataManager.getInstance(project).getCurrentSuitesBundle(); LOG.assertTrue(currentSuite != null); final File[] traceFiles = getTraceFiles(project); final Set<String> tests = new HashSet<String>(); Runnable runnable = new Runnable() { public void run() { for (File traceFile : traceFiles) { DataInputStream in = null; try { in = new DataInputStream(new FileInputStream(traceFile)); extractTests(traceFile, in, tests); } catch (Exception ex) { LOG.error(traceFile.getName(), ex); } finally { try { in.close(); } catch (IOException ex) { LOG.error(ex); } } } } }; if (ProgressManager.getInstance().runProcessWithProgressSynchronously(runnable, "Extract information about tests", false, project)) { //todo cache them? show nothing found message final String[] testNames = ArrayUtil.toStringArray(tests); Arrays.sort(testNames); if (testNames.length == 0) { HintManager.getInstance().showErrorHint(editor, "Failed to load covered tests"); return; } final List<PsiElement> elements = currentSuite.getCoverageEngine().findTestsByNames(testNames, project); final ImplementationViewComponent component; final String title = "Tests covering line " + myClassFQName + ":" + myLineData.getLineNumber(); final ComponentPopupBuilder popupBuilder; if (!elements.isEmpty()) { component = new ImplementationViewComponent(PsiUtilCore.toPsiElementArray(elements), 0); popupBuilder = JBPopupFactory.getInstance().createComponentPopupBuilder(component, component.getPreferredFocusableComponent()) .setDimensionServiceKey(project, "ShowTestsPopup", false) .setCouldPin(new Processor<JBPopup>() { @Override public boolean process(JBPopup popup) { component.showInUsageView(); popup.cancel(); return false; } }); } else { component = null; final JPanel panel = new PanelWithText("Following test" + (testNames.length > 1 ? "s" : "") + " could not be found: " + StringUtil.join(testNames, ",").replace("_", ".")); popupBuilder = JBPopupFactory.getInstance().createComponentPopupBuilder(panel, null); } final JBPopup popup = popupBuilder.setRequestFocusCondition(project, NotLookupOrSearchCondition.INSTANCE) .setProject(project) .setResizable(true) .setMovable(true) .setTitle(title) .createPopup(); popup.showInBestPositionFor(editor); if (component != null) { component.setHint(popup, title); } } } private void extractTests(final File traceFile, final DataInputStream in, final Set<String> tests) throws IOException { long traceSize = in.readInt(); for (int i = 0; i < traceSize; i++) { final String className = in.readUTF(); final int linesSize = in.readInt(); for(int l = 0; l < linesSize; l++) { final int line = in.readInt(); if (Comparing.strEqual(className, myClassFQName)) { if (myLineData.getLineNumber() == line) { tests.add(FileUtil.getNameWithoutExtension(traceFile)); return; } } } } } @Override public void update(final AnActionEvent e) { final Presentation presentation = e.getPresentation(); presentation.setEnabled(false); if (myLineData != null && myLineData.getStatus() != LineCoverage.NONE) { final Project project = CommonDataKeys.PROJECT.getData(e.getDataContext()); if (project != null) { final File[] files = getTraceFiles(project); if (files != null && files.length > 0) { presentation.setEnabled(CoverageDataManager.getInstance(project).getCurrentSuitesBundle().isCoverageByTestEnabled()); } } } } @Nullable private static File[] getTraceFiles(Project project) { final CoverageSuitesBundle currentSuite = CoverageDataManager.getInstance(project).getCurrentSuitesBundle(); if (currentSuite == null) return null; final List<File> files = new ArrayList<File>(); for (CoverageSuite coverageSuite : currentSuite.getSuites()) { final String filePath = coverageSuite.getCoverageDataFileName(); final String dirName = FileUtil.getNameWithoutExtension(new File(filePath).getName()); final File parentDir = new File(filePath).getParentFile(); final File tracesDir = new File(parentDir, dirName); final File[] suiteFiles = tracesDir.listFiles(); if (suiteFiles != null) { Collections.addAll(files, suiteFiles); } } return files.isEmpty() ? null : files.toArray(new File[files.size()]); } }