Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

7810 test coverage regression fix #7904

Merged
merged 1 commit into from
Feb 10, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,11 @@ dependencies {
bundledPlugins(bundledPluginList)
plugins(pluginList)

if (sinceBuildInput == "243" || sinceBuildInput == "251") {
bundledModule("intellij.platform.coverage")
bundledModule("intellij.platform.coverage.agent")
}

// The warning that "instrumentationTools()" is deprecated might be valid, however, this error is produced by Gradle IJ plugin version
// 2.1.0 if this isn't included:
// Caused by: org.gradle.api.GradleException: No Java Compiler dependency found.
Expand Down
5 changes: 5 additions & 0 deletions flutter-idea/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,11 @@ dependencies {
bundledPlugins(bundledPluginList)
plugins(pluginList)

if (sinceBuildInput == "243" || sinceBuildInput == "251") {
bundledModule("intellij.platform.coverage")
bundledModule("intellij.platform.coverage.agent")
}

// The warning that "instrumentationTools()" is deprecated might be valid, however, this error is produced by Gradle IJ plugin version
// 2.1.0 if this isn't included:
// Caused by: org.gradle.api.GradleException: No Java Compiler dependency found.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
/*
* Copyright 2021 The Chromium Authors. All rights reserved.
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
package io.flutter.run.coverage;

import com.intellij.coverage.CoverageDataManager;
import com.intellij.coverage.CoverageSuitesBundle;
import com.intellij.coverage.SimpleCoverageAnnotator;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.roots.ModuleRootManager;
import com.intellij.openapi.vfs.VirtualFile;
import io.flutter.utils.FlutterModuleUtils;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class FlutterCoverageAnnotator extends SimpleCoverageAnnotator {

@Nullable
public static FlutterCoverageAnnotator getInstance(Project project) {
return project.getService(FlutterCoverageAnnotator.class);
}

public FlutterCoverageAnnotator(Project project) {
super(project);
}

@Override
protected FileCoverageInfo fillInfoForUncoveredFile(@NotNull File file) {
return new FileCoverageInfo();
}

@Override
protected boolean shouldCollectCoverageInsideLibraryDirs() {
return false;
}

@Override
protected VirtualFile[] getRoots(Project project,
@NotNull CoverageDataManager dataManager,
CoverageSuitesBundle suite) {
return dataManager.doInReadActionIfProjectOpen(() -> {
final List<VirtualFile> roots = new ArrayList<>();
for (Module module : FlutterModuleUtils.findModulesWithFlutterContents(project)) {
final ModuleRootManager rootManager = ModuleRootManager.getInstance(module);
roots.addAll(Arrays.asList(rootManager.getContentRoots()));
}
return roots.toArray(VirtualFile.EMPTY_ARRAY);
});
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
/*
* Copyright 2021 The Chromium Authors. All rights reserved.
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
package io.flutter.run.coverage;

import com.intellij.coverage.CoverageDataManager;
import com.intellij.coverage.CoverageRunner;
import com.intellij.execution.configurations.RunConfigurationBase;
import com.intellij.execution.configurations.coverage.CoverageEnabledConfiguration;
import com.intellij.openapi.application.ModalityState;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.util.ModalityUiUtil;
import io.flutter.pub.PubRoot;
import io.flutter.run.test.TestConfig;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

public class FlutterCoverageEnabledConfiguration extends CoverageEnabledConfiguration {
private static final Logger LOG = Logger.getInstance(FlutterCoverageEnabledConfiguration.class.getName());

public FlutterCoverageEnabledConfiguration(@NotNull RunConfigurationBase<?> configuration) {
super(configuration);
super.setCoverageRunner(CoverageRunner.getInstance(FlutterCoverageRunner.class));
createCoverageFile();
ModalityUiUtil.invokeLaterIfNeeded(
ModalityState.any(),
() -> setCurrentCoverageSuite(CoverageDataManager.getInstance(configuration.getProject()).addCoverageSuite(this)));
}

@Override
protected String createCoverageFile() {
if (myCoverageFilePath == null) {
if (!(getConfiguration() instanceof TestConfig)) {
return "";
}
VirtualFile file = ((TestConfig)getConfiguration()).getFields().getFileOrDir();
final VirtualFile root = PubRoot.forFile(file).getRoot();
myCoverageFilePath = root.getPath() + "/coverage/lcov.info";
}
return myCoverageFilePath;
}

@Override
public void setCoverageRunner(@Nullable final CoverageRunner coverageRunner) {
// Save and restore myCoverageFilePath because the super method clears it.
final String path = myCoverageFilePath;
super.setCoverageRunner(coverageRunner);
myCoverageFilePath = path;
}

@Override
public void coverageRunnerExtensionRemoved(@NotNull CoverageRunner runner) {
final String path = myCoverageFilePath;
super.coverageRunnerExtensionRemoved(runner);
myCoverageFilePath = path;
}
}
163 changes: 163 additions & 0 deletions flutter-idea/src/io/flutter/run/coverage/FlutterCoverageEngine.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
/*
* Copyright 2021 The Chromium Authors. All rights reserved.
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
package io.flutter.run.coverage;

import com.intellij.coverage.CoverageAnnotator;
import com.intellij.coverage.CoverageEngine;
import com.intellij.coverage.CoverageFileProvider;
import com.intellij.coverage.CoverageRunner;
import com.intellij.coverage.CoverageSuite;
import com.intellij.coverage.CoverageSuitesBundle;
import com.intellij.execution.configurations.RunConfigurationBase;
import com.intellij.execution.configurations.RunProfile;
import com.intellij.execution.configurations.WrappingRunConfiguration;
import com.intellij.execution.configurations.coverage.CoverageEnabledConfiguration;
import com.intellij.execution.testframework.AbstractTestProxy;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.jetbrains.lang.dart.DartFileType;
import com.jetbrains.lang.dart.psi.DartFile;
import io.flutter.FlutterBundle;
import io.flutter.FlutterUtils;
import io.flutter.pub.PubRoot;
import io.flutter.run.test.TestConfig;

import java.io.File;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

public class FlutterCoverageEngine extends CoverageEngine {

public static FlutterCoverageEngine getInstance() {
return CoverageEngine.EP_NAME.findExtensionOrFail(FlutterCoverageEngine.class);
}

@Override
public boolean isApplicableTo(@NotNull RunConfigurationBase conf) {
return unwrapRunProfile(conf) instanceof TestConfig;
}

@Override
public boolean canHavePerTestCoverage(@NotNull RunConfigurationBase conf) {
return true;
}

@Override
public @NotNull CoverageEnabledConfiguration createCoverageEnabledConfiguration(@NotNull RunConfigurationBase conf) {
return new FlutterCoverageEnabledConfiguration(conf);
}

@Override
public @Nullable CoverageSuite createCoverageSuite(@NotNull CoverageRunner covRunner,
@NotNull String name,
@NotNull CoverageFileProvider coverageDataFileProvider,
@Nullable String[] filters,
long lastCoverageTimeStamp,
@Nullable String suiteToMerge,
boolean coverageByTestEnabled,
boolean tracingEnabled,
boolean trackTestFolders,
Project project) {
return null;
}

@Override
public @Nullable CoverageSuite createCoverageSuite(@NotNull CoverageRunner covRunner,
@NotNull String name,
@NotNull CoverageFileProvider coverageDataFileProvider,
@NotNull CoverageEnabledConfiguration config) {
if (config instanceof FlutterCoverageEnabledConfiguration) {
return new FlutterCoverageSuite(covRunner, name, coverageDataFileProvider,
config.getConfiguration().getProject(), this);
}
return null;
}

@Override
public @Nullable CoverageSuite createEmptyCoverageSuite(@NotNull CoverageRunner coverageRunner) {
return new FlutterCoverageSuite(this);
}

@Override
public @NotNull CoverageAnnotator getCoverageAnnotator(Project project) {
return FlutterCoverageAnnotator.getInstance(project);
}

@Override
public boolean coverageEditorHighlightingApplicableTo(@NotNull PsiFile psiFile) {
final PubRoot root = PubRoot.forPsiFile(psiFile);
if (root == null) return false;
final VirtualFile file = psiFile.getVirtualFile();
if (file == null) return false;
final String path = root.getRelativePath(file);
if (path == null) return false;
return path.startsWith("lib") && FlutterUtils.isDartFile(file);
}

@Override
public boolean coverageProjectViewStatisticsApplicableTo(VirtualFile fileOrDir) {
return !fileOrDir.isDirectory() && fileOrDir.getFileType() instanceof DartFileType;
}

@Override
public boolean acceptedByFilters(@NotNull PsiFile psiFile, @NotNull CoverageSuitesBundle suite) {
return psiFile instanceof DartFile;
}

@Override
public boolean recompileProjectAndRerunAction(@NotNull Module module,
@NotNull CoverageSuitesBundle suite,
@NotNull Runnable chooseSuiteAction) {
return false;
}

@Override
public String getQualifiedName(@NotNull final File outputFile,
@NotNull final PsiFile sourceFile) {
return getQName(sourceFile);
}

@Override
public @NotNull Set<String> getQualifiedNames(@NotNull PsiFile sourceFile) {
final Set<String> qualifiedNames = new HashSet<>();
qualifiedNames.add(getQName(sourceFile));
return qualifiedNames;
}

@Override
public List<PsiElement> findTestsByNames(@NotNull String[] testNames, @NotNull Project project) {
return null;
}

@Override
public @Nullable String getTestMethodName(@NotNull PsiElement element, @NotNull AbstractTestProxy testProxy) {
return null;
}

@Override
public String getPresentableText() {
return FlutterBundle.message("flutter.coverage.presentable.text");
}

@NotNull
private static String getQName(@NotNull PsiFile sourceFile) {
return sourceFile.getVirtualFile().getPath();
}

static @NotNull RunProfile unwrapRunProfile(@NotNull RunProfile runProfile) {
if (runProfile instanceof WrappingRunConfiguration) {
return ((WrappingRunConfiguration<?>)runProfile).getPeer();
}
return runProfile;
}
}
Loading