From 898105a5fafd836055f4a7c01787e400233cb2b6 Mon Sep 17 00:00:00 2001 From: Alexey Sedunov Date: Fri, 20 Apr 2018 12:53:30 +0300 Subject: [PATCH] [REVERT/172] Test Support: Implement proper detection of existing TestNG configurations --- ...KotlinTestNgConfigurationProducer.java.172 | 233 ++++++++++++++++++ 1 file changed, 233 insertions(+) create mode 100644 idea/idea-jvm/src/org/jetbrains/kotlin/idea/run/KotlinTestNgConfigurationProducer.java.172 diff --git a/idea/idea-jvm/src/org/jetbrains/kotlin/idea/run/KotlinTestNgConfigurationProducer.java.172 b/idea/idea-jvm/src/org/jetbrains/kotlin/idea/run/KotlinTestNgConfigurationProducer.java.172 new file mode 100644 index 00000000000..e1dda75ada2 --- /dev/null +++ b/idea/idea-jvm/src/org/jetbrains/kotlin/idea/run/KotlinTestNgConfigurationProducer.java.172 @@ -0,0 +1,233 @@ +/* + * Copyright 2010-2015 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 org.jetbrains.kotlin.idea.run; + +import com.intellij.execution.JavaRunConfigurationExtensionManager; +import com.intellij.execution.Location; +import com.intellij.execution.PsiLocation; +import com.intellij.execution.actions.ConfigurationContext; +import com.intellij.execution.actions.ConfigurationFromContext; +import com.intellij.execution.junit.InheritorChooser; +import com.intellij.execution.junit2.info.MethodLocation; +import com.intellij.openapi.module.Module; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Condition; +import com.intellij.openapi.util.Ref; +import com.intellij.psi.*; +import com.intellij.psi.util.PsiClassUtil; +import com.intellij.psi.util.PsiTreeUtil; +import com.theoryinpractice.testng.configuration.TestNGConfiguration; +import com.theoryinpractice.testng.configuration.TestNGConfigurationProducer; +import com.theoryinpractice.testng.util.TestNGUtil; +import kotlin.collections.CollectionsKt; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.kotlin.asJava.LightClassUtilsKt; +import org.jetbrains.kotlin.idea.project.TargetPlatformDetector; +import org.jetbrains.kotlin.idea.util.ProjectRootsUtil; +import org.jetbrains.kotlin.psi.*; +import org.jetbrains.kotlin.resolve.jvm.platform.JvmPlatform; + +import java.util.List; + +import static org.jetbrains.kotlin.asJava.LightClassUtilsKt.toLightClass; + +public class KotlinTestNgConfigurationProducer extends TestNGConfigurationProducer { + @Override + public boolean shouldReplace(ConfigurationFromContext self, ConfigurationFromContext other) { + return other.isProducedBy(TestNGConfigurationProducer.class); + } + + @Override + protected boolean setupConfigurationFromContext( + TestNGConfiguration configuration, ConfigurationContext context, Ref sourceElement + ) { + // TODO: check TestNG Pattern running first, before method/class (see TestNGInClassConfigurationProducer for logic) + // TODO: and PsiClassOwner not handled, which is in TestNGInClassConfigurationProducer + + Location location = context.getLocation(); + if (location == null) { + return false; + } + + Project project = context.getProject(); + PsiElement leaf = location.getPsiElement(); + + if (!ProjectRootsUtil.isInProjectOrLibSource(leaf)) { + return false; + } + + if (!(leaf.getContainingFile() instanceof KtFile)) { + return false; + } + + KtFile ktFile = (KtFile) leaf.getContainingFile(); + + if (TargetPlatformDetector.getPlatform(ktFile) != JvmPlatform.INSTANCE) { + return false; + } + + KtNamedDeclaration declarationToRun = getDeclarationToRun(leaf); + + if (declarationToRun instanceof KtNamedFunction) { + KtNamedFunction function = (KtNamedFunction) declarationToRun; + + @SuppressWarnings("unchecked") + KtElement owner = PsiTreeUtil.getParentOfType(function, KtFunction.class, KtClass.class); + + if (owner instanceof KtClass) { + PsiClass delegate = toLightClass((KtClass) owner); + if (delegate != null) { + for (PsiMethod method : delegate.getMethods()) { + if (method.getNavigationElement() == function) { + if (TestNGUtil.hasTest(method)) { + return configure(configuration, location, context, project, delegate, method); + } + break; + } + } + } + } + } + + if (declarationToRun instanceof KtClass) { + PsiClass delegate = toLightClass((KtClassOrObject) declarationToRun); + if (!isTestNGClass(delegate)) { + return false; + } + + return configure(configuration, location, context, project, delegate, null); + } + + return false; + } + + @Override + public void onFirstRun(ConfigurationFromContext configuration, ConfigurationContext context, Runnable startRunnable) { + KtNamedDeclaration declarationToRun = getDeclarationToRun(configuration.getSourceElement()); + final PsiNamedElement lightElement = CollectionsKt.firstOrNull(LightClassUtilsKt.toLightElements(declarationToRun)); + + // Copied from TestNGInClassConfigurationProducer.onFirstRun() + if (lightElement instanceof PsiMethod || lightElement instanceof PsiClass) { + PsiMethod psiMethod; + PsiClass containingClass; + + if (lightElement instanceof PsiMethod) { + psiMethod = (PsiMethod)lightElement; + containingClass = psiMethod.getContainingClass(); + } else { + psiMethod = null; + containingClass = (PsiClass)lightElement; + } + + InheritorChooser inheritorChooser = new InheritorChooser() { + @Override + protected void runForClasses(List classes, PsiMethod method, ConfigurationContext context, Runnable performRunnable) { + ((TestNGConfiguration)context.getConfiguration().getConfiguration()).bePatternConfiguration(classes, method); + super.runForClasses(classes, method, context, performRunnable); + } + + @Override + protected void runForClass(PsiClass aClass, + PsiMethod psiMethod, + ConfigurationContext context, + Runnable performRunnable) { + if (lightElement instanceof PsiMethod) { + Project project = psiMethod.getProject(); + MethodLocation methodLocation = new MethodLocation(project, psiMethod, PsiLocation.fromPsiElement(aClass)); + ((TestNGConfiguration)context.getConfiguration().getConfiguration()).setMethodConfiguration(methodLocation); + } else { + ((TestNGConfiguration)context.getConfiguration().getConfiguration()).setClassConfiguration(aClass); + } + super.runForClass(aClass, psiMethod, context, performRunnable); + } + }; + if (inheritorChooser.runMethodInAbstractClass(context, + startRunnable, + psiMethod, + containingClass, + new Condition() { + @Override + public boolean value(PsiClass aClass) { + return aClass.hasModifierProperty(PsiModifier.ABSTRACT) && + TestNGUtil.hasTest(aClass); + } + })) return; + } + + super.onFirstRun(configuration, context, startRunnable); + } + + @Nullable + private static KtNamedDeclaration getDeclarationToRun(@NotNull PsiElement leaf) { + if (!(leaf.getContainingFile() instanceof KtFile)) return null; + KtFile jetFile = (KtFile) leaf.getContainingFile(); + + KtNamedFunction function = PsiTreeUtil.getParentOfType(leaf, KtNamedFunction.class, false); + if (function != null) return function; + + KtClass ktClass = PsiTreeUtil.getParentOfType(leaf, KtClass.class, false); + if (ktClass != null) return ktClass; + + return getClassDeclarationInFile(jetFile); + } + + private boolean configure( + TestNGConfiguration configuration, Location location, ConfigurationContext context, Project project, + @Nullable PsiClass delegate, @Nullable PsiMethod method + ) { + if (delegate == null) { + return false; + } + + setupConfigurationModule(context, configuration); + Module originalModule = configuration.getConfigurationModule().getModule(); + configuration.setClassConfiguration(delegate); + if (method != null) { + configuration.setMethodConfiguration(PsiLocation.fromPsiElement(project, method)); + } + configuration.restoreOriginalModule(originalModule); + configuration.setName(configuration.getName()); + JavaRunConfigurationExtensionManager.getInstance().extendCreatedConfiguration(configuration, location); + return true; + } + + private static boolean isTestNGClass(PsiClass psiClass) { + return psiClass != null && PsiClassUtil.isRunnableClass(psiClass, true, false) && TestNGUtil.hasTest(psiClass); + } + + @Nullable + static KtClass getClassDeclarationInFile(KtFile jetFile) { + KtClass tempSingleDeclaration = null; + + for (KtDeclaration ktDeclaration : jetFile.getDeclarations()) { + if (ktDeclaration instanceof KtClass) { + KtClass declaration = (KtClass) ktDeclaration; + + if (tempSingleDeclaration == null) { + tempSingleDeclaration = declaration; + } + else { + // There are several class declarations in file + return null; + } + } + } + + return tempSingleDeclaration; + } +}