[REVERT/172] Test Support: Implement proper detection of existing TestNG configurations
This commit is contained in:
+233
@@ -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<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;
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user