fileOrDir is a file or directory from the test content source
*
+ * @see TestSourcesFilter#isTestSources(VirtualFile, Project)
+ *
* @param fileOrDir the file or directory to check.
* @return true if the file or directory belongs to a test source root, false otherwise.
*/
diff --git a/platform/projectModel-api/src/com/intellij/openapi/roots/TestSourcesFilter.java b/platform/projectModel-api/src/com/intellij/openapi/roots/TestSourcesFilter.java
new file mode 100644
index 000000000000..66b9b617348a
--- /dev/null
+++ b/platform/projectModel-api/src/com/intellij/openapi/roots/TestSourcesFilter.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2000-2016 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.openapi.roots;
+
+import com.intellij.openapi.extensions.ExtensionPointName;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.vfs.VirtualFile;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * Implementations of this extension point can tell IDE whether some particular file is a test file despite project roots configuration.
+ *
+ * By default, IntelliJ Platform considers files as tests only if they are located under test
+ * sources root {@link FileIndex#isInTestSourceContent(VirtualFile)}.
+ *
+ * However there plenty frameworks and languages which keep test files just nearby production files.
+ * E.g. *_test.go files are test files in Go language and some js/dart files are test files depending
+ * on their content. The extensions allow IDE to highlight such files with a green background,
+ * properly check if they are included in built-in search scopes, etc.
+ *
+ * @see FileIndex#isInTestSourceContent(VirtualFile)
+ * @since 2016.3
+ * @author zolotov
+ */
+
+public abstract class TestSourcesFilter {
+ public static final ExtensionPointName EP_NAME = ExtensionPointName.create("com.intellij.testSourcesFilter");
+
+ public static boolean isTestSources(@NotNull VirtualFile file, @NotNull Project project) {
+ for (TestSourcesFilter filter : EP_NAME.getExtensions()) {
+ if (filter.isTestSource(file, project)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public abstract boolean isTestSource(@NotNull VirtualFile file, @NotNull Project project);
+}
diff --git a/platform/projectModel-impl/src/com/intellij/openapi/roots/ProjectRootTestSourcesFilter.java b/platform/projectModel-impl/src/com/intellij/openapi/roots/ProjectRootTestSourcesFilter.java
new file mode 100644
index 000000000000..345f557e4f29
--- /dev/null
+++ b/platform/projectModel-impl/src/com/intellij/openapi/roots/ProjectRootTestSourcesFilter.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2000-2016 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.openapi.roots;
+
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.vfs.VirtualFile;
+import org.jetbrains.annotations.NotNull;
+
+public class ProjectRootTestSourcesFilter extends TestSourcesFilter {
+ @Override
+ public boolean isTestSource(@NotNull VirtualFile file, @NotNull Project project) {
+ return ProjectFileIndex.SERVICE.getInstance(project).isInTestSourceContent(file);
+ }
+}
diff --git a/platform/usageView/src/com/intellij/usages/impl/rules/UsageScopeGroupingRule.java b/platform/usageView/src/com/intellij/usages/impl/rules/UsageScopeGroupingRule.java
index b62d93533d12..80b216211e84 100644
--- a/platform/usageView/src/com/intellij/usages/impl/rules/UsageScopeGroupingRule.java
+++ b/platform/usageView/src/com/intellij/usages/impl/rules/UsageScopeGroupingRule.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2000-2013 JetBrains s.r.o.
+ * Copyright 2000-2016 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.
@@ -17,8 +17,10 @@ package com.intellij.usages.impl.rules;
import com.intellij.icons.AllIcons;
import com.intellij.openapi.project.DumbAware;
+import com.intellij.openapi.project.Project;
import com.intellij.openapi.roots.ProjectFileIndex;
import com.intellij.openapi.roots.ProjectRootManager;
+import com.intellij.openapi.roots.TestSourcesFilter;
import com.intellij.openapi.vcs.FileStatus;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiElement;
@@ -50,11 +52,11 @@ public class UsageScopeGroupingRule implements UsageGroupingRule, DumbAware {
if (virtualFile == null) {
return null;
}
- ProjectFileIndex fileIndex = ProjectRootManager.getInstance(element.getProject()).getFileIndex();
+ Project project = element.getProject();
+ ProjectFileIndex fileIndex = ProjectRootManager.getInstance(project).getFileIndex();
boolean isInLib = fileIndex.isInLibraryClasses(virtualFile) || fileIndex.isInLibrarySource(virtualFile);
if (isInLib) return LIBRARY;
- boolean isInTest = fileIndex.isInTestSourceContent(virtualFile);
- return isInTest ? TEST : PRODUCTION;
+ return TestSourcesFilter.isTestSources(virtualFile, project) ? TEST : PRODUCTION;
}
private static final UsageScopeGroup TEST = new UsageScopeGroup(0) {
diff --git a/plugins/coverage-common/src/com/intellij/coverage/SimpleCoverageAnnotator.java b/plugins/coverage-common/src/com/intellij/coverage/SimpleCoverageAnnotator.java
index 8afe8628db3d..10c24d5596cb 100644
--- a/plugins/coverage-common/src/com/intellij/coverage/SimpleCoverageAnnotator.java
+++ b/plugins/coverage-common/src/com/intellij/coverage/SimpleCoverageAnnotator.java
@@ -1,9 +1,24 @@
+/*
+ * Copyright 2000-2016 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.project.Project;
import com.intellij.openapi.roots.ProjectFileIndex;
import com.intellij.openapi.roots.ProjectRootManager;
-import com.intellij.openapi.util.Computable;
+import com.intellij.openapi.roots.TestSourcesFilter;
import com.intellij.openapi.util.SystemInfo;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.vfs.VfsUtilCore;
@@ -51,10 +66,7 @@ public abstract class SimpleCoverageAnnotator extends BaseCoverageAnnotator {
@NotNull final CoverageSuitesBundle currentSuite) {
final VirtualFile dir = directory.getVirtualFile();
- final ProjectFileIndex projectFileIndex = ProjectRootManager.getInstance(directory.getProject()).getFileIndex();
- //final Module module = projectFileIndex.getModuleForFile(dir);
-
- final boolean isInTestContent = projectFileIndex.isInTestSourceContent(dir);
+ final boolean isInTestContent = TestSourcesFilter.isTestSources(dir, directory.getProject());
if (!currentSuite.isTrackTestFolders() && isInTestContent) {
return null;
}
@@ -171,8 +183,7 @@ public abstract class SimpleCoverageAnnotator extends BaseCoverageAnnotator {
@NotNull final ProjectFileIndex index,
@NotNull final CoverageEngine coverageEngine,
Set visitedDirs,
- @NotNull final Map normalizedFiles2Files)
- {
+ @NotNull final Map normalizedFiles2Files) {
if (!index.isInContent(dir)) {
return null;
}
@@ -188,15 +199,14 @@ public abstract class SimpleCoverageAnnotator extends BaseCoverageAnnotator {
}
visitedDirs.add(dir);
- final boolean isInTestSrcContent = index.isInTestSourceContent(dir);
+ final boolean isInTestSrcContent = TestSourcesFilter.isTestSources(dir, getProject());
// Don't count coverage for tests folders if track test folders is switched off
if (!trackTestFolders && isInTestSrcContent) {
return null;
}
- final VirtualFile[] children = dataManager.doInReadActionIfProjectOpen(() -> dir.getChildren());
-
+ final VirtualFile[] children = dataManager.doInReadActionIfProjectOpen(dir::getChildren);
if (children == null) {
return null;
}
@@ -281,7 +291,7 @@ public abstract class SimpleCoverageAnnotator extends BaseCoverageAnnotator {
suite.isTrackTestFolders(),
index,
suite.getCoverageEngine(),
- ContainerUtil.newHashSet(),
+ ContainerUtil.newHashSet(),
Collections.unmodifiableMap(normalizedFiles2Files));
}