Skip to content

Commit ea614c4

Browse files
authored
7810 test coverage regression fix (#7904)
Fix the test coverage regression, this resolves #7810.
1 parent 935c89a commit ea614c4

12 files changed

+647
-0
lines changed

build.gradle.kts

+5
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,11 @@ dependencies {
105105
bundledPlugins(bundledPluginList)
106106
plugins(pluginList)
107107

108+
if (sinceBuildInput == "243" || sinceBuildInput == "251") {
109+
bundledModule("intellij.platform.coverage")
110+
bundledModule("intellij.platform.coverage.agent")
111+
}
112+
108113
// The warning that "instrumentationTools()" is deprecated might be valid, however, this error is produced by Gradle IJ plugin version
109114
// 2.1.0 if this isn't included:
110115
// Caused by: org.gradle.api.GradleException: No Java Compiler dependency found.

flutter-idea/build.gradle.kts

+5
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,11 @@ dependencies {
8585
bundledPlugins(bundledPluginList)
8686
plugins(pluginList)
8787

88+
if (sinceBuildInput == "243" || sinceBuildInput == "251") {
89+
bundledModule("intellij.platform.coverage")
90+
bundledModule("intellij.platform.coverage.agent")
91+
}
92+
8893
// The warning that "instrumentationTools()" is deprecated might be valid, however, this error is produced by Gradle IJ plugin version
8994
// 2.1.0 if this isn't included:
9095
// Caused by: org.gradle.api.GradleException: No Java Compiler dependency found.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
/*
2+
* Copyright 2021 The Chromium Authors. All rights reserved.
3+
* Use of this source code is governed by a BSD-style license that can be
4+
* found in the LICENSE file.
5+
*/
6+
package io.flutter.run.coverage;
7+
8+
import com.intellij.coverage.CoverageDataManager;
9+
import com.intellij.coverage.CoverageSuitesBundle;
10+
import com.intellij.coverage.SimpleCoverageAnnotator;
11+
import com.intellij.openapi.module.Module;
12+
import com.intellij.openapi.project.Project;
13+
import com.intellij.openapi.roots.ModuleRootManager;
14+
import com.intellij.openapi.vfs.VirtualFile;
15+
import io.flutter.utils.FlutterModuleUtils;
16+
import org.jetbrains.annotations.NotNull;
17+
import org.jetbrains.annotations.Nullable;
18+
19+
import java.io.File;
20+
import java.util.ArrayList;
21+
import java.util.Arrays;
22+
import java.util.List;
23+
24+
public class FlutterCoverageAnnotator extends SimpleCoverageAnnotator {
25+
26+
@Nullable
27+
public static FlutterCoverageAnnotator getInstance(Project project) {
28+
return project.getService(FlutterCoverageAnnotator.class);
29+
}
30+
31+
public FlutterCoverageAnnotator(Project project) {
32+
super(project);
33+
}
34+
35+
@Override
36+
protected FileCoverageInfo fillInfoForUncoveredFile(@NotNull File file) {
37+
return new FileCoverageInfo();
38+
}
39+
40+
@Override
41+
protected boolean shouldCollectCoverageInsideLibraryDirs() {
42+
return false;
43+
}
44+
45+
@Override
46+
protected VirtualFile[] getRoots(Project project,
47+
@NotNull CoverageDataManager dataManager,
48+
CoverageSuitesBundle suite) {
49+
return dataManager.doInReadActionIfProjectOpen(() -> {
50+
final List<VirtualFile> roots = new ArrayList<>();
51+
for (Module module : FlutterModuleUtils.findModulesWithFlutterContents(project)) {
52+
final ModuleRootManager rootManager = ModuleRootManager.getInstance(module);
53+
roots.addAll(Arrays.asList(rootManager.getContentRoots()));
54+
}
55+
return roots.toArray(VirtualFile.EMPTY_ARRAY);
56+
});
57+
}
58+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
/*
2+
* Copyright 2021 The Chromium Authors. All rights reserved.
3+
* Use of this source code is governed by a BSD-style license that can be
4+
* found in the LICENSE file.
5+
*/
6+
package io.flutter.run.coverage;
7+
8+
import com.intellij.coverage.CoverageDataManager;
9+
import com.intellij.coverage.CoverageRunner;
10+
import com.intellij.execution.configurations.RunConfigurationBase;
11+
import com.intellij.execution.configurations.coverage.CoverageEnabledConfiguration;
12+
import com.intellij.openapi.application.ModalityState;
13+
import com.intellij.openapi.diagnostic.Logger;
14+
import com.intellij.openapi.vfs.VirtualFile;
15+
import com.intellij.util.ModalityUiUtil;
16+
import io.flutter.pub.PubRoot;
17+
import io.flutter.run.test.TestConfig;
18+
import org.jetbrains.annotations.NotNull;
19+
import org.jetbrains.annotations.Nullable;
20+
21+
public class FlutterCoverageEnabledConfiguration extends CoverageEnabledConfiguration {
22+
private static final Logger LOG = Logger.getInstance(FlutterCoverageEnabledConfiguration.class.getName());
23+
24+
public FlutterCoverageEnabledConfiguration(@NotNull RunConfigurationBase<?> configuration) {
25+
super(configuration);
26+
super.setCoverageRunner(CoverageRunner.getInstance(FlutterCoverageRunner.class));
27+
createCoverageFile();
28+
ModalityUiUtil.invokeLaterIfNeeded(
29+
ModalityState.any(),
30+
() -> setCurrentCoverageSuite(CoverageDataManager.getInstance(configuration.getProject()).addCoverageSuite(this)));
31+
}
32+
33+
@Override
34+
protected String createCoverageFile() {
35+
if (myCoverageFilePath == null) {
36+
if (!(getConfiguration() instanceof TestConfig)) {
37+
return "";
38+
}
39+
VirtualFile file = ((TestConfig)getConfiguration()).getFields().getFileOrDir();
40+
final VirtualFile root = PubRoot.forFile(file).getRoot();
41+
myCoverageFilePath = root.getPath() + "/coverage/lcov.info";
42+
}
43+
return myCoverageFilePath;
44+
}
45+
46+
@Override
47+
public void setCoverageRunner(@Nullable final CoverageRunner coverageRunner) {
48+
// Save and restore myCoverageFilePath because the super method clears it.
49+
final String path = myCoverageFilePath;
50+
super.setCoverageRunner(coverageRunner);
51+
myCoverageFilePath = path;
52+
}
53+
54+
@Override
55+
public void coverageRunnerExtensionRemoved(@NotNull CoverageRunner runner) {
56+
final String path = myCoverageFilePath;
57+
super.coverageRunnerExtensionRemoved(runner);
58+
myCoverageFilePath = path;
59+
}
60+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
/*
2+
* Copyright 2021 The Chromium Authors. All rights reserved.
3+
* Use of this source code is governed by a BSD-style license that can be
4+
* found in the LICENSE file.
5+
*/
6+
package io.flutter.run.coverage;
7+
8+
import com.intellij.coverage.CoverageAnnotator;
9+
import com.intellij.coverage.CoverageEngine;
10+
import com.intellij.coverage.CoverageFileProvider;
11+
import com.intellij.coverage.CoverageRunner;
12+
import com.intellij.coverage.CoverageSuite;
13+
import com.intellij.coverage.CoverageSuitesBundle;
14+
import com.intellij.execution.configurations.RunConfigurationBase;
15+
import com.intellij.execution.configurations.RunProfile;
16+
import com.intellij.execution.configurations.WrappingRunConfiguration;
17+
import com.intellij.execution.configurations.coverage.CoverageEnabledConfiguration;
18+
import com.intellij.execution.testframework.AbstractTestProxy;
19+
import com.intellij.openapi.module.Module;
20+
import com.intellij.openapi.project.Project;
21+
import com.intellij.openapi.vfs.VirtualFile;
22+
import com.intellij.psi.PsiElement;
23+
import com.intellij.psi.PsiFile;
24+
import com.jetbrains.lang.dart.DartFileType;
25+
import com.jetbrains.lang.dart.psi.DartFile;
26+
import io.flutter.FlutterBundle;
27+
import io.flutter.FlutterUtils;
28+
import io.flutter.pub.PubRoot;
29+
import io.flutter.run.test.TestConfig;
30+
31+
import java.io.File;
32+
import java.util.HashSet;
33+
import java.util.List;
34+
import java.util.Set;
35+
36+
import org.jetbrains.annotations.NotNull;
37+
import org.jetbrains.annotations.Nullable;
38+
39+
public class FlutterCoverageEngine extends CoverageEngine {
40+
41+
public static FlutterCoverageEngine getInstance() {
42+
return CoverageEngine.EP_NAME.findExtensionOrFail(FlutterCoverageEngine.class);
43+
}
44+
45+
@Override
46+
public boolean isApplicableTo(@NotNull RunConfigurationBase conf) {
47+
return unwrapRunProfile(conf) instanceof TestConfig;
48+
}
49+
50+
@Override
51+
public boolean canHavePerTestCoverage(@NotNull RunConfigurationBase conf) {
52+
return true;
53+
}
54+
55+
@Override
56+
public @NotNull CoverageEnabledConfiguration createCoverageEnabledConfiguration(@NotNull RunConfigurationBase conf) {
57+
return new FlutterCoverageEnabledConfiguration(conf);
58+
}
59+
60+
@Override
61+
public @Nullable CoverageSuite createCoverageSuite(@NotNull CoverageRunner covRunner,
62+
@NotNull String name,
63+
@NotNull CoverageFileProvider coverageDataFileProvider,
64+
@Nullable String[] filters,
65+
long lastCoverageTimeStamp,
66+
@Nullable String suiteToMerge,
67+
boolean coverageByTestEnabled,
68+
boolean tracingEnabled,
69+
boolean trackTestFolders,
70+
Project project) {
71+
return null;
72+
}
73+
74+
@Override
75+
public @Nullable CoverageSuite createCoverageSuite(@NotNull CoverageRunner covRunner,
76+
@NotNull String name,
77+
@NotNull CoverageFileProvider coverageDataFileProvider,
78+
@NotNull CoverageEnabledConfiguration config) {
79+
if (config instanceof FlutterCoverageEnabledConfiguration) {
80+
return new FlutterCoverageSuite(covRunner, name, coverageDataFileProvider,
81+
config.getConfiguration().getProject(), this);
82+
}
83+
return null;
84+
}
85+
86+
@Override
87+
public @Nullable CoverageSuite createEmptyCoverageSuite(@NotNull CoverageRunner coverageRunner) {
88+
return new FlutterCoverageSuite(this);
89+
}
90+
91+
@Override
92+
public @NotNull CoverageAnnotator getCoverageAnnotator(Project project) {
93+
return FlutterCoverageAnnotator.getInstance(project);
94+
}
95+
96+
@Override
97+
public boolean coverageEditorHighlightingApplicableTo(@NotNull PsiFile psiFile) {
98+
final PubRoot root = PubRoot.forPsiFile(psiFile);
99+
if (root == null) return false;
100+
final VirtualFile file = psiFile.getVirtualFile();
101+
if (file == null) return false;
102+
final String path = root.getRelativePath(file);
103+
if (path == null) return false;
104+
return path.startsWith("lib") && FlutterUtils.isDartFile(file);
105+
}
106+
107+
@Override
108+
public boolean coverageProjectViewStatisticsApplicableTo(VirtualFile fileOrDir) {
109+
return !fileOrDir.isDirectory() && fileOrDir.getFileType() instanceof DartFileType;
110+
}
111+
112+
@Override
113+
public boolean acceptedByFilters(@NotNull PsiFile psiFile, @NotNull CoverageSuitesBundle suite) {
114+
return psiFile instanceof DartFile;
115+
}
116+
117+
@Override
118+
public boolean recompileProjectAndRerunAction(@NotNull Module module,
119+
@NotNull CoverageSuitesBundle suite,
120+
@NotNull Runnable chooseSuiteAction) {
121+
return false;
122+
}
123+
124+
@Override
125+
public String getQualifiedName(@NotNull final File outputFile,
126+
@NotNull final PsiFile sourceFile) {
127+
return getQName(sourceFile);
128+
}
129+
130+
@Override
131+
public @NotNull Set<String> getQualifiedNames(@NotNull PsiFile sourceFile) {
132+
final Set<String> qualifiedNames = new HashSet<>();
133+
qualifiedNames.add(getQName(sourceFile));
134+
return qualifiedNames;
135+
}
136+
137+
@Override
138+
public List<PsiElement> findTestsByNames(@NotNull String[] testNames, @NotNull Project project) {
139+
return null;
140+
}
141+
142+
@Override
143+
public @Nullable String getTestMethodName(@NotNull PsiElement element, @NotNull AbstractTestProxy testProxy) {
144+
return null;
145+
}
146+
147+
@Override
148+
public String getPresentableText() {
149+
return FlutterBundle.message("flutter.coverage.presentable.text");
150+
}
151+
152+
@NotNull
153+
private static String getQName(@NotNull PsiFile sourceFile) {
154+
return sourceFile.getVirtualFile().getPath();
155+
}
156+
157+
static @NotNull RunProfile unwrapRunProfile(@NotNull RunProfile runProfile) {
158+
if (runProfile instanceof WrappingRunConfiguration) {
159+
return ((WrappingRunConfiguration<?>)runProfile).getPeer();
160+
}
161+
return runProfile;
162+
}
163+
}

0 commit comments

Comments
 (0)