diff --git a/src/main/kotlin/com/github/xepozz/testo/mixin.kt b/src/main/kotlin/com/github/xepozz/testo/mixin.kt index 6fad0d0..e653495 100644 --- a/src/main/kotlin/com/github/xepozz/testo/mixin.kt +++ b/src/main/kotlin/com/github/xepozz/testo/mixin.kt @@ -1,6 +1,9 @@ package com.github.xepozz.testo import com.github.xepozz.testo.tests.TestoTestDescriptor +import com.intellij.openapi.diagnostic.Logger +import com.intellij.openapi.progress.ProcessCanceledException +import com.intellij.openapi.roots.ProjectFileIndex import com.intellij.psi.PsiElement import com.intellij.psi.PsiFile import com.intellij.psi.util.PsiTreeUtil @@ -12,6 +15,8 @@ import com.jetbrains.php.lang.psi.elements.ClassReference import com.jetbrains.php.lang.psi.elements.NewExpression import com.jetbrains.php.lang.psi.elements.PhpClass +private val LOG = Logger.getInstance("#com.github.xepozz.testo.mixin") + fun PsiElement.isTestoExecutable() = isTestoFunction() || isTestoMethod() || isTestoBench() fun PsiElement.isTestoBench() = when(this) { @@ -54,9 +59,28 @@ fun PsiElement.isTestoClass() = when (this) { else -> false } -fun PsiFile.isTestoFile() = when (this) { - is PhpFile -> TestoTestDescriptor.isTestClassName(name.substringBeforeLast(".")) || isTestoClassFile() || isTestoFunctionFile() || isTestBenchFile() || isTestoConfigFile() - else -> false +fun PsiFile.isTestoFile(): Boolean { + if (this !is PhpFile) return false + val vFile = virtualFile ?: return false + if (!vFile.isValid) return false + + val fileIndex = ProjectFileIndex.getInstance(project) + if (!fileIndex.isInContent(vFile)) return false + if (fileIndex.isExcluded(vFile)) return false + if (fileIndex.isUnderIgnored(vFile)) return false + + return try { + TestoTestDescriptor.isTestClassName(name.substringBeforeLast(".")) + || isTestoClassFile() + || isTestoFunctionFile() + || isTestBenchFile() + || isTestoConfigFile() + } catch (e: ProcessCanceledException) { + throw e + } catch (e: Throwable) { + LOG.warn("Failed to determine whether ${vFile.path} is a Testo file", e) + false + } } fun PhpFile.isTestoConfigFile() = PsiTreeUtil.findChildrenOfType(this, ClassReference::class.java) diff --git a/src/test/kotlin/com/github/xepozz/testo/MixinPsiTest.kt b/src/test/kotlin/com/github/xepozz/testo/MixinPsiTest.kt index 1282975..2ba5682 100644 --- a/src/test/kotlin/com/github/xepozz/testo/MixinPsiTest.kt +++ b/src/test/kotlin/com/github/xepozz/testo/MixinPsiTest.kt @@ -1,5 +1,6 @@ package com.github.xepozz.testo +import com.intellij.openapi.application.WriteAction import com.intellij.psi.util.PsiTreeUtil import com.intellij.testFramework.TestDataPath import com.intellij.testFramework.fixtures.BasePlatformTestCase @@ -155,6 +156,24 @@ class MixinPsiTest : BasePlatformTestCase() { assertTrue("File containing test class should be a Testo file", psiFile.isTestoFile()) } + fun testIsTestoFile_invalidVirtualFile_returnsFalse() { + val psiFile = myFixture.configureByText( + "DeletedTest.php", + """ { vFile.delete(this) } + + assertFalse("VirtualFile should be invalid after deletion", vFile.isValid) + assertFalse( + "isTestoFile must return false when the underlying VirtualFile is invalid", + psiFile.isTestoFile() + ) + } + // ---- isTestoExecutable ---- fun testIsTestoExecutable_testMethod() {