From 224bf4f33bfd52e931ecd1f12dc9708ea52b3205 Mon Sep 17 00:00:00 2001 From: Anna Kozlova Date: Wed, 13 Sep 2017 16:46:02 +0300 Subject: [PATCH] coverage: setting to ignore trivial constructors --- .../coverage/CoverageDataManagerImpl.java | 1 - plugins/coverage/src/META-INF/plugin.xml | 2 + .../intellij/coverage/JavaCoverageEngine.java | 7 +- .../coverage/JavaCoverageOptions.java | 84 +++++++++++++++++++ .../coverage/JavaCoverageOptionsProvider.java | 60 +++++++++++++ .../intellij/coverage/PackageAnnotator.java | 16 ++-- .../coverage/SourceLineCounterUtil.java | 32 ++++--- 7 files changed, 180 insertions(+), 22 deletions(-) create mode 100644 plugins/coverage/src/com/intellij/coverage/JavaCoverageOptions.java create mode 100644 plugins/coverage/src/com/intellij/coverage/JavaCoverageOptionsProvider.java diff --git a/plugins/coverage-common/src/com/intellij/coverage/CoverageDataManagerImpl.java b/plugins/coverage-common/src/com/intellij/coverage/CoverageDataManagerImpl.java index 7c96cd7de1e8..a84bfe67f691 100644 --- a/plugins/coverage-common/src/com/intellij/coverage/CoverageDataManagerImpl.java +++ b/plugins/coverage-common/src/com/intellij/coverage/CoverageDataManagerImpl.java @@ -137,7 +137,6 @@ public class CoverageDataManagerImpl extends CoverageDataManager { @Override public void readExternal(Element element) throws InvalidDataException { - //noinspection unchecked for (Element suiteElement : element.getChildren(SUITE)) { final CoverageRunner coverageRunner = BaseCoverageSuite.readRunnerAttribute(suiteElement); // skip unknown runners diff --git a/plugins/coverage/src/META-INF/plugin.xml b/plugins/coverage/src/META-INF/plugin.xml index 6b9f5961d53e..97c83b556bf5 100644 --- a/plugins/coverage/src/META-INF/plugin.xml +++ b/plugins/coverage/src/META-INF/plugin.xml @@ -26,6 +26,8 @@ + + diff --git a/plugins/coverage/src/com/intellij/coverage/JavaCoverageEngine.java b/plugins/coverage/src/com/intellij/coverage/JavaCoverageEngine.java index 49be86c13f47..b36f7847dc83 100644 --- a/plugins/coverage/src/com/intellij/coverage/JavaCoverageEngine.java +++ b/plugins/coverage/src/com/intellij/coverage/JavaCoverageEngine.java @@ -654,8 +654,11 @@ public class JavaCoverageEngine extends CoverageEngine { @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()); + if (JavaCoverageOptionsProvider.getInstance(project).ignoreEmptyPrivateConstructors()) { + PsiClass psiClass = ReadAction.compute(() -> ClassUtil.findPsiClassByJVMName(PsiManager.getInstance(project), qualifiedName)); + return PackageAnnotator.isGeneratedDefaultConstructor(psiClass, ((LineData)lineData).getMethodSignature()); + } + return super.isGeneratedCode(project, qualifiedName, lineData); } @Override diff --git a/plugins/coverage/src/com/intellij/coverage/JavaCoverageOptions.java b/plugins/coverage/src/com/intellij/coverage/JavaCoverageOptions.java new file mode 100644 index 000000000000..2af4314e3c82 --- /dev/null +++ b/plugins/coverage/src/com/intellij/coverage/JavaCoverageOptions.java @@ -0,0 +1,84 @@ +/* + * Copyright 2000-2017 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.coverage; + +import com.intellij.ui.IdeBorderFactory; + +import javax.swing.*; +import java.awt.*; + +public class JavaCoverageOptions extends CoverageOptions { + + private final JavaCoverageOptionsProvider myCoverageOptionsProvider; + private JavaCoverageOptionsEditor myEditor; + + public JavaCoverageOptions(JavaCoverageOptionsProvider coverageOptionsProvider) { + myCoverageOptionsProvider = coverageOptionsProvider; + } + + @Override + public JComponent getComponent() { + myEditor = new JavaCoverageOptionsEditor(); + return myEditor.getComponent(); + } + + @Override + public boolean isModified() { + return myEditor.isModified(myCoverageOptionsProvider); + } + + @Override + public void apply() { + myEditor.apply(myCoverageOptionsProvider); + } + + @Override + public void reset() { + myEditor.reset(myCoverageOptionsProvider); + } + + @Override + public void disposeUIResources() { + myEditor = null; + } + + private static class JavaCoverageOptionsEditor { + + private JPanel myPanel = new JPanel(new BorderLayout(0, 10)); + private JCheckBox myCheckBox = new JCheckBox("Ignore empty private and implicit constructors", true); + + public JavaCoverageOptionsEditor() { + myPanel.setBorder(IdeBorderFactory.createTitledBorder("Java coverage")); + myPanel.add(myCheckBox, BorderLayout.NORTH); + } + + public JPanel getComponent() { + return myPanel; + } + + public boolean isModified(JavaCoverageOptionsProvider provider) { + return myCheckBox.isSelected() != provider.ignoreEmptyPrivateConstructors(); + } + + public void apply(JavaCoverageOptionsProvider provider) { + provider.setIgnoreEmptyPrivateConstructors(myCheckBox.isSelected()); + } + + public void reset(JavaCoverageOptionsProvider provider) { + myCheckBox.setSelected(provider.ignoreEmptyPrivateConstructors()); + } + } +} diff --git a/plugins/coverage/src/com/intellij/coverage/JavaCoverageOptionsProvider.java b/plugins/coverage/src/com/intellij/coverage/JavaCoverageOptionsProvider.java new file mode 100644 index 000000000000..22fb29900059 --- /dev/null +++ b/plugins/coverage/src/com/intellij/coverage/JavaCoverageOptionsProvider.java @@ -0,0 +1,60 @@ +/* + * Copyright 2000-2017 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.coverage; + +import com.intellij.openapi.components.*; +import com.intellij.openapi.project.Project; +import org.jetbrains.annotations.Nullable; + +@State( + name = "JavaCoverageOptionsProvider", + storages = { + @Storage(StoragePathMacros.WORKSPACE_FILE) + } +) +public class JavaCoverageOptionsProvider implements PersistentStateComponent { + private State myState = new State(); + + public static JavaCoverageOptionsProvider getInstance(Project project) { + return ServiceManager.getService(project, JavaCoverageOptionsProvider.class); + } + + + public void setIgnoreEmptyPrivateConstructors(boolean state) { + myState.myIgnoreEmptyPrivateConstructors = state; + } + + public boolean ignoreEmptyPrivateConstructors() { + return myState.myIgnoreEmptyPrivateConstructors; + } + + @Nullable + @Override + public JavaCoverageOptionsProvider.State getState() { + return myState; + } + + @Override + public void loadState(JavaCoverageOptionsProvider.State state) { + myState.myIgnoreEmptyPrivateConstructors = state.myIgnoreEmptyPrivateConstructors; + } + + + public static class State { + public boolean myIgnoreEmptyPrivateConstructors = true; + } + +} diff --git a/plugins/coverage/src/com/intellij/coverage/PackageAnnotator.java b/plugins/coverage/src/com/intellij/coverage/PackageAnnotator.java index 2f90f21a4db0..4f450db59e0d 100644 --- a/plugins/coverage/src/com/intellij/coverage/PackageAnnotator.java +++ b/plugins/coverage/src/com/intellij/coverage/PackageAnnotator.java @@ -23,6 +23,7 @@ import com.intellij.openapi.module.ModuleUtilCore; import com.intellij.openapi.project.DumbService; import com.intellij.openapi.project.Project; import com.intellij.openapi.roots.*; +import com.intellij.openapi.util.Condition; import com.intellij.openapi.util.Ref; import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.util.text.StringUtil; @@ -37,6 +38,7 @@ import com.intellij.rt.coverage.data.LineData; import com.intellij.rt.coverage.data.ProjectData; import com.intellij.util.containers.HashMap; import com.intellij.util.containers.SmartHashSet; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.jps.model.java.JavaModuleSourceRootTypes; import org.jetbrains.jps.model.java.JavaSourceRootType; @@ -57,12 +59,14 @@ public class PackageAnnotator { private final Project myProject; private final PsiManager myManager; private final CoverageDataManager myCoverageManager; + private final boolean myIgnoreEmptyPrivateConstructors; public PackageAnnotator(final PsiPackage aPackage) { myPackage = aPackage; myProject = myPackage.getProject(); myManager = PsiManager.getInstance(myProject); myCoverageManager = CoverageDataManager.getInstance(myProject); + myIgnoreEmptyPrivateConstructors = JavaCoverageOptionsProvider.getInstance(myProject).ignoreEmptyPrivateConstructors(); } public interface Annotator { @@ -455,7 +459,7 @@ public class PackageAnnotator { touchedClass = true; } - if (isGeneratedDefaultConstructor(psiClass, (String)nameAndSig)) { + if (myIgnoreEmptyPrivateConstructors && isGeneratedDefaultConstructor(psiClass, (String)nameAndSig)) { continue; } @@ -505,16 +509,16 @@ public class PackageAnnotator { * in the bytecode, so we need to look at the PSI to see if the class defines such a constructor. */ public static boolean isGeneratedDefaultConstructor(@Nullable final PsiClass aClass, String nameAndSig) { + if (aClass == null) { + return false; + } if (DEFAULT_CONSTRUCTOR_NAME_SIGNATURE.equals(nameAndSig)) { return hasGeneratedOrEmptyPrivateConstructor(aClass); } return false; } - private static boolean hasGeneratedOrEmptyPrivateConstructor(@Nullable final PsiClass aClass) { - if (aClass == null) { - return false; - } + private static boolean hasGeneratedOrEmptyPrivateConstructor(@NotNull final PsiClass aClass) { return ReadAction.compute(() -> { PsiMethod[] constructors = aClass.getConstructors(); if (constructors.length == 1 && constructors[0].hasModifierProperty(PsiModifier.PRIVATE)) { @@ -563,6 +567,6 @@ public class PackageAnnotator { if (coverageSuite == null) return false; return SourceLineCounterUtil .collectNonCoveredClassInfo(classCoverageInfo, packageCoverageInfo, content, coverageSuite.isTracingEnabled(), - psiClass); + myIgnoreEmptyPrivateConstructors ? description -> !isGeneratedDefaultConstructor(psiClass, description) : Condition.TRUE); } } diff --git a/plugins/coverage/src/com/intellij/coverage/SourceLineCounterUtil.java b/plugins/coverage/src/com/intellij/coverage/SourceLineCounterUtil.java index e5e8851fbae7..b3be240bcdaf 100644 --- a/plugins/coverage/src/com/intellij/coverage/SourceLineCounterUtil.java +++ b/plugins/coverage/src/com/intellij/coverage/SourceLineCounterUtil.java @@ -17,6 +17,7 @@ package com.intellij.coverage; import com.intellij.openapi.application.ReadAction; import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Condition; import com.intellij.psi.PsiClass; import com.intellij.psi.PsiManager; import com.intellij.psi.util.ClassUtil; @@ -29,11 +30,10 @@ import java.util.List; import java.util.Set; public class SourceLineCounterUtil { - public static boolean collectNonCoveredClassInfo(final PackageAnnotator.ClassCoverageInfo classCoverageInfo, - final PackageAnnotator.PackageCoverageInfo packageCoverageInfo, - byte[] content, + public static boolean collectNonCoveredClassInfo(final PackageAnnotator.ClassCoverageInfo classCoverageInfo, + final PackageAnnotator.PackageCoverageInfo packageCoverageInfo, byte[] content, final boolean excludeLines, - final PsiClass psiClass) { + final Condition includeDescriptionCondition) { if (content == null) return false; ClassReader reader = new ClassReader(content, 0, content.length); @@ -42,13 +42,13 @@ public class SourceLineCounterUtil { Set 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; - }); + if (includeDescriptionCondition.value((String)description)) { + classCoverageInfo.totalLineCount++; + packageCoverageInfo.totalLineCount++; + descriptions.add(description); + } + return true; + }); classCoverageInfo.totalMethodCount += descriptions.size(); packageCoverageInfo.totalMethodCount += descriptions.size(); @@ -68,10 +68,16 @@ public class SourceLineCounterUtil { reader.accept(collector, 0); String qualifiedName = reader.getClassName(); - PsiClass psiClass = ReadAction.compute(() -> ClassUtil.findPsiClassByJVMName(PsiManager.getInstance(project), qualifiedName)); + boolean ignoreEmptyPrivateConstructors = JavaCoverageOptionsProvider.getInstance(project).ignoreEmptyPrivateConstructors(); + PsiClass psiClass = ignoreEmptyPrivateConstructors + ? ReadAction.compute(() -> ClassUtil.findPsiClassByJVMName(PsiManager.getInstance(project), qualifiedName)) + : null; + Condition includeDescriptionCondition = ignoreEmptyPrivateConstructors + ? description -> !PackageAnnotator.isGeneratedDefaultConstructor(psiClass, description) + : Condition.TRUE; TIntObjectHashMap lines = collector.getSourceLines(); lines.forEachEntry((line, description) -> { - if (!PackageAnnotator.isGeneratedDefaultConstructor(psiClass, (String)description)) { + if (includeDescriptionCondition.value((String)description)) { line--; uncoveredLines.add(line); }