/* * 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<PsiElement> 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<PsiClass> 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<PsiClass>() { @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; } }