coverage: ignore empty private constructors for all engines (IDEA-26988)

This commit is contained in:
Anna Kozlova
2017-09-12 21:05:35 +03:00
parent 786a908b21
commit 93b1084c31
7 changed files with 65 additions and 23 deletions
@@ -353,6 +353,14 @@ public abstract class CoverageEngine {
return false;
}
/**
*
* @return true if highlighting should skip the line as it represents no actual source code
*/
public boolean isGeneratedCode(Project project, String qualifiedName, Object lineData) {
return false;
}
public static String getEditorTitle() {
return "Code Coverage";
}
@@ -60,6 +60,9 @@ public class CoverageSuitesBundle {
return true;
}
public Project getProject() {
return mySuites[0].getProject();
}
public long getLastCoverageTimeStamp() {
long max = 0;
@@ -355,7 +355,7 @@ public class SrcFileAnnotator implements Disposable {
if (fileData != null) {
final Object[] lines = fileData.getLines();
if (lines != null) {
final Object[] postProcessedLines = suite.getCoverageEngine().postProcessExecutableLines(lines, editor);
final Object[] postProcessedLines = engine.postProcessExecutableLines(lines, editor);
for (Object lineData : postProcessedLines) {
if (lineData instanceof LineData) {
final int line = ((LineData)lineData).getLineNumber() - 1;
@@ -371,6 +371,7 @@ public class SrcFileAnnotator implements Disposable {
// use id mapping
lineNumberInCurrent = line;
}
if (engine.isGeneratedCode(myProject, qualifiedName, lineData)) continue;
executableLines.put(line, (LineData)lineData);
classLines.put(line, postProcessedLines);
@@ -255,7 +255,7 @@ public class JavaCoverageEngine extends CoverageEngine {
}
try {
SourceLineCounterUtil.collectSrcLinesForUntouchedFiles(uncoveredLines, content, suite.isTracingEnabled());
SourceLineCounterUtil.collectSrcLinesForUntouchedFiles(uncoveredLines, content, suite.isTracingEnabled(), suite.getProject());
}
catch (Exception e) {
LOG.error("Fail to process class from: " + classFile.getPath(), e);
@@ -274,6 +274,7 @@ public class JavaCoverageEngine extends CoverageEngine {
}
@NotNull
public String getQualifiedName(@NotNull final File outputFile, @NotNull final PsiFile sourceFile) {
final String packageFQName = getPackageName(sourceFile);
return StringUtil.getQualifiedName(packageFQName, FileUtil.getNameWithoutExtension(outputFile));
@@ -651,6 +652,12 @@ public class JavaCoverageEngine extends CoverageEngine {
return "Java Coverage";
}
@Override
public boolean isGeneratedCode(Project project, String qualifiedName, Object lineData) {
PsiClass psiClass = ReadAction.compute(() -> ClassUtil.findPsiClassByJVMName(PsiManager.getInstance(project), qualifiedName));
return PackageAnnotator.isGeneratedDefaultConstructor(psiClass, ((LineData)lineData).getMethodSignature());
}
@Override
public CoverageViewExtension createCoverageViewExtension(Project project,
CoverageSuitesBundle suiteBundle,
@@ -28,10 +28,7 @@ import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.VfsUtilCore;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.JavaPsiFacade;
import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiManager;
import com.intellij.psi.PsiPackage;
import com.intellij.psi.*;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.util.PsiUtilCore;
import com.intellij.rt.coverage.data.ClassData;
@@ -509,16 +506,23 @@ public class PackageAnnotator {
*/
public static boolean isGeneratedDefaultConstructor(@Nullable final PsiClass aClass, String nameAndSig) {
if (DEFAULT_CONSTRUCTOR_NAME_SIGNATURE.equals(nameAndSig)) {
return hasGeneratedConstructor(aClass);
return hasGeneratedOrEmptyPrivateConstructor(aClass);
}
return false;
}
private static boolean hasGeneratedConstructor(@Nullable final PsiClass aClass) {
private static boolean hasGeneratedOrEmptyPrivateConstructor(@Nullable final PsiClass aClass) {
if (aClass == null) {
return false;
}
return ReadAction.compute(() -> aClass.getConstructors().length == 0);
return ReadAction.compute(() -> {
PsiMethod[] constructors = aClass.getConstructors();
if (constructors.length == 1 && constructors[0].hasModifierProperty(PsiModifier.PRIVATE)) {
PsiCodeBlock body = constructors[0].getBody();
return body != null && body.getStatements().length == 0;
}
return constructors.length == 0;
});
}
private static ClassCoverageInfo getOrCreateClassCoverageInfo(final Map<String, ClassCoverageInfo> toplevelClassCoverage,
@@ -15,12 +15,18 @@
*/
package com.intellij.coverage;
import com.intellij.openapi.application.ReadAction;
import com.intellij.openapi.project.Project;
import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiManager;
import com.intellij.psi.util.ClassUtil;
import com.intellij.rt.coverage.instrumentation.SourceLineCounter;
import com.intellij.util.containers.HashSet;
import gnu.trove.TIntObjectHashMap;
import org.jetbrains.org.objectweb.asm.ClassReader;
import java.util.List;
import java.util.Set;
public class SourceLineCounterUtil {
public static boolean collectNonCoveredClassInfo(final PackageAnnotator.ClassCoverageInfo classCoverageInfo,
@@ -33,14 +39,20 @@ public class SourceLineCounterUtil {
SourceLineCounter counter = new SourceLineCounter(null, excludeLines, null);
reader.accept(counter, 0);
classCoverageInfo.totalLineCount += counter.getNSourceLines();
packageCoverageInfo.totalLineCount += counter.getNSourceLines();
for (Object nameAndSig : counter.getMethodsWithSourceCode()) {
if (!PackageAnnotator.isGeneratedDefaultConstructor(psiClass, (String) nameAndSig)) {
classCoverageInfo.totalMethodCount++;
packageCoverageInfo.totalMethodCount++;
}
}
Set<Object> descriptions = new HashSet<>();
TIntObjectHashMap<?> lines = counter.getSourceLines();
lines.forEachEntry((line, description) -> {
if (!PackageAnnotator.isGeneratedDefaultConstructor(psiClass, (String)description)) {
classCoverageInfo.totalLineCount ++;
packageCoverageInfo.totalLineCount ++;
descriptions.add(description);
}
return true;
});
classCoverageInfo.totalMethodCount += descriptions.size();
packageCoverageInfo.totalMethodCount += descriptions.size();
if (!counter.isInterface()) {
packageCoverageInfo.totalClassCount++;
}
@@ -48,14 +60,21 @@ public class SourceLineCounterUtil {
}
public static void collectSrcLinesForUntouchedFiles(final List<Integer> uncoveredLines,
byte[] content, final boolean excludeLines) {
byte[] content,
final boolean excludeLines,
final Project project) {
final ClassReader reader = new ClassReader(content);
final SourceLineCounter collector = new SourceLineCounter(null, excludeLines, null);
reader.accept(collector, 0);
final TIntObjectHashMap lines = collector.getSourceLines();
lines.forEachKey(line -> {
line--;
uncoveredLines.add(line);
String qualifiedName = reader.getClassName();
PsiClass psiClass = ReadAction.compute(() -> ClassUtil.findPsiClassByJVMName(PsiManager.getInstance(project), qualifiedName));
TIntObjectHashMap<?> lines = collector.getSourceLines();
lines.forEachEntry((line, description) -> {
if (!PackageAnnotator.isGeneratedDefaultConstructor(psiClass, (String)description)) {
line--;
uncoveredLines.add(line);
}
return true;
});
}
@@ -56,7 +56,7 @@ public class CoverageIntegrationTest extends ModuleTestCase {
assertEquals(1, barClassCoverage.coveredMethodCount);
PackageAnnotator.PackageCoverageInfo barPackageCoverage = consumer.myPackageCoverage.get("foo.bar");
assertEquals(2, barPackageCoverage.coveredLineCount);
assertEquals(9, barPackageCoverage.totalLineCount);
assertEquals(8, barPackageCoverage.totalLineCount);
assertEquals(1, barPackageCoverage.coveredMethodCount);
assertEquals(7, barPackageCoverage.totalMethodCount);
PackageAnnotator.ClassCoverageInfo uncoveredClassInfo = consumer.myClassCoverageInfo.get("foo.bar.UncoveredClass");