blob: 6c9be40eb59003f30e7df4276474bf3808f65650 [file] [log] [blame]
package com.intellij.coverage;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.components.ServiceManager;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.roots.ProjectFileIndex;
import com.intellij.openapi.roots.ProjectRootManager;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.*;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.util.containers.HashMap;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
/**
* @author Roman.Chernyatchik
*/
public class JavaCoverageAnnotator extends BaseCoverageAnnotator {
private final Map<String, PackageAnnotator.PackageCoverageInfo> myPackageCoverageInfos = new HashMap<String, PackageAnnotator.PackageCoverageInfo>();
private final Map<String, PackageAnnotator.PackageCoverageInfo> myFlattenPackageCoverageInfos = new HashMap<String, PackageAnnotator.PackageCoverageInfo>();
private final Map<VirtualFile, PackageAnnotator.PackageCoverageInfo> myDirCoverageInfos =
new HashMap<VirtualFile, PackageAnnotator.PackageCoverageInfo>();
private final Map<VirtualFile, PackageAnnotator.PackageCoverageInfo> myTestDirCoverageInfos =
new HashMap<VirtualFile, PackageAnnotator.PackageCoverageInfo>();
private final Map<String, PackageAnnotator.ClassCoverageInfo> myClassCoverageInfos = new HashMap<String, PackageAnnotator.ClassCoverageInfo>();
public JavaCoverageAnnotator(final Project project) {
super(project);
}
public static JavaCoverageAnnotator getInstance(final Project project) {
return ServiceManager.getService(project, JavaCoverageAnnotator.class);
}
@Nullable
public String getDirCoverageInformationString(@NotNull final PsiDirectory directory,
@NotNull final CoverageSuitesBundle currentSuite,
@NotNull final CoverageDataManager coverageDataManager) {
final PsiPackage psiPackage = JavaDirectoryService.getInstance().getPackage(directory);
if (psiPackage == null) return null;
final ProjectFileIndex projectFileIndex = ProjectRootManager.getInstance(directory.getProject()).getFileIndex();
final VirtualFile virtualFile = directory.getVirtualFile();
final boolean isInTestContent = projectFileIndex.isInTestSourceContent(virtualFile);
if (!currentSuite.isTrackTestFolders() && isInTestContent) {
return null;
}
return isInTestContent ? getCoverageInformationString(myTestDirCoverageInfos.get(virtualFile), coverageDataManager.isSubCoverageActive())
: getCoverageInformationString(myDirCoverageInfos.get(virtualFile), coverageDataManager.isSubCoverageActive());
}
@Nullable
public String getFileCoverageInformationString(@NotNull PsiFile file, @NotNull CoverageSuitesBundle currentSuite, @NotNull CoverageDataManager manager) {
// N/A here we work with java classes
return null;
}
public void onSuiteChosen(CoverageSuitesBundle newSuite) {
super.onSuiteChosen(newSuite);
myPackageCoverageInfos.clear();
myFlattenPackageCoverageInfos.clear();
myDirCoverageInfos.clear();
myTestDirCoverageInfos.clear();
myClassCoverageInfos.clear();
}
protected Runnable createRenewRequest(@NotNull final CoverageSuitesBundle suite, @NotNull final CoverageDataManager dataManager) {
final Project project = getProject();
final List<PsiPackage> packages = new ArrayList<PsiPackage>();
final List<PsiClass> classes = new ArrayList<PsiClass>();
for (CoverageSuite coverageSuite : suite.getSuites()) {
final JavaCoverageSuite javaSuite = (JavaCoverageSuite)coverageSuite;
classes.addAll(javaSuite.getCurrentSuiteClasses(project));
packages.addAll(javaSuite.getCurrentSuitePackages(project));
}
if (packages.isEmpty() && classes.isEmpty()) {
return null;
}
return new Runnable() {
public void run() {
final PackageAnnotator.Annotator annotator = new PackageAnnotator.Annotator() {
public void annotatePackage(String packageQualifiedName, PackageAnnotator.PackageCoverageInfo packageCoverageInfo) {
myPackageCoverageInfos.put(packageQualifiedName, packageCoverageInfo);
}
public void annotatePackage(String packageQualifiedName,
PackageAnnotator.PackageCoverageInfo packageCoverageInfo,
boolean flatten) {
if (flatten) {
myFlattenPackageCoverageInfos.put(packageQualifiedName, packageCoverageInfo);
}
else {
annotatePackage(packageQualifiedName, packageCoverageInfo);
}
}
public void annotateSourceDirectory(VirtualFile dir,
PackageAnnotator.PackageCoverageInfo dirCoverageInfo,
Module module) {
myDirCoverageInfos.put(dir, dirCoverageInfo);
}
public void annotateTestDirectory(VirtualFile virtualFile,
PackageAnnotator.PackageCoverageInfo packageCoverageInfo,
Module module) {
myTestDirCoverageInfos.put(virtualFile, packageCoverageInfo);
}
public void annotateClass(String classQualifiedName, PackageAnnotator.ClassCoverageInfo classCoverageInfo) {
myClassCoverageInfos.put(classQualifiedName, classCoverageInfo);
}
};
for (PsiPackage aPackage : packages) {
new PackageAnnotator(aPackage).annotate(suite, annotator);
}
for (final PsiClass aClass : classes) {
Runnable runnable = new Runnable() {
public void run() {
final String packageName = ((PsiClassOwner)aClass.getContainingFile()).getPackageName();
final PsiPackage psiPackage = JavaPsiFacade.getInstance(project).findPackage(packageName);
if (psiPackage == null) return;
new PackageAnnotator(psiPackage).annotateFilteredClass(aClass, suite, annotator);
}
};
ApplicationManager.getApplication().runReadAction(runnable);
}
dataManager.triggerPresentationUpdate();
}
};
}
@Nullable
public static String getCoverageInformationString(PackageAnnotator.PackageCoverageInfo info, boolean subCoverageActive) {
if (info == null) return null;
if (info.totalClassCount == 0 || info.totalLineCount == 0) return null;
if (subCoverageActive) {
return info.coveredClassCount + info.coveredLineCount > 0 ? "covered" : null;
}
return (int)((double)info.coveredClassCount / info.totalClassCount * 100) + "% classes, " +
(int)((double)info.coveredLineCount / info.totalLineCount * 100) + "% lines covered";
}
/**
*
* @param psiPackage qualified name of a package to obtain coverage information for
* @param module optional parameter to restrict coverage to source directories of a certain module
* @param coverageDataManager
* @return human-readable coverage information
*/
@Nullable
public String getPackageCoverageInformationString(final PsiPackage psiPackage,
@Nullable final Module module,
@NotNull final CoverageDataManager coverageDataManager) {
return getPackageCoverageInformationString(psiPackage, module, coverageDataManager, false);
}
/**
*
*
* @param psiPackage qualified name of a package to obtain coverage information for
* @param module optional parameter to restrict coverage to source directories of a certain module
* @param coverageDataManager
* @param flatten
* @return human-readable coverage information
*/
@Nullable
public String getPackageCoverageInformationString(final PsiPackage psiPackage,
@Nullable final Module module,
@NotNull final CoverageDataManager coverageDataManager,
boolean flatten) {
if (psiPackage == null) return null;
final boolean subCoverageActive = coverageDataManager.isSubCoverageActive();
PackageAnnotator.PackageCoverageInfo info;
if (module != null) {
final PsiDirectory[] directories = psiPackage.getDirectories(GlobalSearchScope.moduleScope(module));
PackageAnnotator.PackageCoverageInfo result = null;
for (PsiDirectory directory : directories) {
final VirtualFile virtualFile = directory.getVirtualFile();
result = merge(result, myDirCoverageInfos.get(virtualFile));
result = merge(result, myTestDirCoverageInfos.get(virtualFile));
}
return getCoverageInformationString(result, subCoverageActive);
}
else {
info = getPackageCoverageInfo(psiPackage, flatten);
}
return getCoverageInformationString(info, subCoverageActive);
}
public PackageAnnotator.PackageCoverageInfo getPackageCoverageInfo(@NotNull PsiPackage psiPackage, boolean flattenPackages) {
final String qualifiedName = psiPackage.getQualifiedName();
return flattenPackages ? myFlattenPackageCoverageInfos.get(qualifiedName) : myPackageCoverageInfos.get(qualifiedName);
}
public String getPackageClassPercentage(@NotNull final PsiPackage psiPackage, boolean flatten) {
final PackageAnnotator.PackageCoverageInfo packageCoverageInfo = getPackageCoverageInfo(psiPackage, flatten);
if (packageCoverageInfo == null) return null;
return getPercentage(packageCoverageInfo.coveredClassCount, packageCoverageInfo.totalClassCount);
}
public String getPackageMethodPercentage(PsiPackage psiPackage, boolean flatten) {
final PackageAnnotator.PackageCoverageInfo packageCoverageInfo = getPackageCoverageInfo(psiPackage, flatten);
if (packageCoverageInfo == null) return null;
return getPercentage(packageCoverageInfo.coveredMethodCount, packageCoverageInfo.totalMethodCount);
}
public String getPackageLinePercentage(final PsiPackage psiPackage, boolean flatten) {
final PackageAnnotator.PackageCoverageInfo packageCoverageInfo = getPackageCoverageInfo(psiPackage, flatten);
if (packageCoverageInfo == null) return null;
return getPercentage(packageCoverageInfo.coveredLineCount, packageCoverageInfo.totalLineCount);
}
public String getClassLinePercentage(String classFQName) {
final PackageAnnotator.ClassCoverageInfo info = myClassCoverageInfos.get(classFQName);
if (info == null) return null;
final int coveredLines = info.fullyCoveredLineCount + info.partiallyCoveredLineCount;
return getPercentage(coveredLines, info.totalLineCount);
}
public String getClassMethodPercentage(String classFQName) {
final PackageAnnotator.ClassCoverageInfo info = myClassCoverageInfos.get(classFQName);
if (info == null) return null;
return getPercentage(info.coveredMethodCount, info.totalMethodCount);
}
public String getClassCoveredPercentage(String classFQName) {
final PackageAnnotator.ClassCoverageInfo info = myClassCoverageInfos.get(classFQName);
if (info == null) return null;
return getPercentage(info.coveredClassCount, info.totalClassCount);
}
private static String getPercentage(int covered, int total) {
return (int)((double)covered /total * 100) +"% (" + covered + "/" + total + ")";
}
public static PackageAnnotator.PackageCoverageInfo merge(final PackageAnnotator.PackageCoverageInfo info,
final PackageAnnotator.PackageCoverageInfo testInfo) {
if (info == null) return testInfo;
if (testInfo == null) return info;
final PackageAnnotator.PackageCoverageInfo coverageInfo = new PackageAnnotator.PackageCoverageInfo();
coverageInfo.totalClassCount = info.totalClassCount + testInfo.totalClassCount;
coverageInfo.coveredClassCount = info.coveredClassCount + testInfo.coveredClassCount;
coverageInfo.totalLineCount = info.totalLineCount + testInfo.totalLineCount;
coverageInfo.coveredLineCount = info.coveredLineCount + testInfo.coveredLineCount;
return coverageInfo;
}
/**
* @param classFQName to obtain coverage information for
* @return human-readable coverage information
*/
@Nullable
public String getClassCoverageInformationString(String classFQName, CoverageDataManager coverageDataManager) {
final PackageAnnotator.ClassCoverageInfo info = myClassCoverageInfos.get(classFQName);
if (info == null) return null;
if (info.totalMethodCount == 0 || info.totalLineCount == 0) return null;
if (coverageDataManager.isSubCoverageActive()){
return info.coveredMethodCount + info.fullyCoveredLineCount + info.partiallyCoveredLineCount > 0 ? "covered" : null;
}
return (int)((double)info.coveredMethodCount / info.totalMethodCount * 100) + "% methods, " +
(int)((double)(info.fullyCoveredLineCount + info.partiallyCoveredLineCount) / info.totalLineCount * 100) + "% lines covered";
}
@Nullable
public PackageAnnotator.ClassCoverageInfo getClassCoverageInfo(String classFQName) {
return myClassCoverageInfos.get(classFQName);
}
}