mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
UAST-based implementation of testdata navigation
This commit is contained in:
@@ -39,5 +39,6 @@
|
||||
<orderEntry type="module" module-name="dom-impl" />
|
||||
<orderEntry type="module" module-name="devkit-jps-plugin" scope="RUNTIME" />
|
||||
<orderEntry type="module" module-name="images" />
|
||||
<orderEntry type="module" module-name="uast-common" />
|
||||
</component>
|
||||
</module>
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2000-2016 JetBrains s.r.o.
|
||||
* Copyright 2000-2017 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.
|
||||
@@ -37,6 +37,7 @@ import com.intellij.testFramework.Parameterized;
|
||||
import com.intellij.ui.awt.RelativePoint;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.uast.*;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
@@ -86,15 +87,17 @@ public class NavigateToTestDataAction extends AnAction implements TestTreeViewAc
|
||||
if (location instanceof PsiMemberParameterizedLocation) {
|
||||
PsiClass containingClass = ((PsiMemberParameterizedLocation)location).getContainingClass();
|
||||
if (containingClass == null) {
|
||||
containingClass = PsiTreeUtil.getParentOfType(location.getPsiElement(), PsiClass.class, false);
|
||||
containingClass = UastContextKt.getUastParentOfType(location.getPsiElement(), UClass.class, false);
|
||||
}
|
||||
if (containingClass != null) {
|
||||
final PsiAnnotation annotation = AnnotationUtil.findAnnotationInHierarchy(containingClass, Collections.singleton(JUnitUtil.RUN_WITH));
|
||||
final UAnnotation annotation =
|
||||
UastContextKt.toUElement(AnnotationUtil.findAnnotationInHierarchy(containingClass, Collections.singleton(JUnitUtil.RUN_WITH)), UAnnotation.class);
|
||||
if (annotation != null) {
|
||||
final PsiAnnotationMemberValue memberValue = annotation.findAttributeValue("value");
|
||||
if (memberValue instanceof PsiClassObjectAccessExpression) {
|
||||
final PsiTypeElement operand = ((PsiClassObjectAccessExpression)memberValue).getOperand();
|
||||
if (operand.getType().equalsToText(Parameterized.class.getName())) {
|
||||
UExpression value = annotation.findAttributeValue("value");
|
||||
if (value instanceof UClassLiteralExpression) {
|
||||
UClassLiteralExpression classLiteralExpression = (UClassLiteralExpression)value;
|
||||
PsiType type = classLiteralExpression.getType();
|
||||
if (type != null && type.equalsToText(Parameterized.class.getName())) {
|
||||
final String testDataPath = TestDataLineMarkerProvider.getTestDataBasePath(containingClass);
|
||||
final String paramSetName = ((PsiMemberParameterizedLocation)location).getParamSetName();
|
||||
final String baseFileName = StringUtil.trimEnd(StringUtil.trimStart(paramSetName, "["), "]");
|
||||
@@ -126,8 +129,7 @@ public class NavigateToTestDataAction extends AnAction implements TestTreeViewAc
|
||||
final Editor editor = CommonDataKeys.EDITOR.getData(context);
|
||||
final PsiFile file = CommonDataKeys.PSI_FILE.getData(context);
|
||||
if (file != null && editor != null) {
|
||||
PsiElement element = file.findElementAt(editor.getCaretModel().getOffset());
|
||||
return PsiTreeUtil.getParentOfType(element, PsiMethod.class);
|
||||
return UastContextKt.findUElementAt(file, editor.getCaretModel().getOffset(), UMethod.class);
|
||||
}
|
||||
|
||||
return null;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2000-2016 JetBrains s.r.o.
|
||||
* Copyright 2000-2017 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.
|
||||
@@ -24,6 +24,8 @@ import com.intellij.openapi.project.Project;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.PsiMethod;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.uast.UMethod;
|
||||
import org.jetbrains.uast.UastContextKt;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
@@ -41,20 +43,14 @@ public class TestDataAsRelatedFileProvider extends GotoRelatedProvider {
|
||||
final Project project = context.getData(CommonDataKeys.PROJECT);
|
||||
final PsiElement element = context.getData(CommonDataKeys.PSI_ELEMENT);
|
||||
if (editor == null || element == null || project == null) {
|
||||
return Collections.emptyList();
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
PsiMethod method = null;
|
||||
for (PsiElement e = element; e != null; e = e.getParent()) {
|
||||
if (e instanceof PsiMethod) {
|
||||
method = (PsiMethod)e;
|
||||
break;
|
||||
}
|
||||
}
|
||||
PsiMethod method = UastContextKt.getUastParentOfType(element, UMethod.class);
|
||||
if (method == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
final List<String> testDataFiles = NavigateToTestDataAction.findTestDataFiles(context);
|
||||
if (testDataFiles == null || testDataFiles.isEmpty()) {
|
||||
return Collections.emptyList();
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2000-2014 JetBrains s.r.o.
|
||||
* Copyright 2000-2017 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.
|
||||
@@ -29,6 +29,9 @@ import com.intellij.psi.util.PsiUtilCore;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.idea.devkit.util.PsiUtil;
|
||||
import org.jetbrains.uast.UAnnotation;
|
||||
import org.jetbrains.uast.UExpression;
|
||||
import org.jetbrains.uast.UastContextKt;
|
||||
|
||||
import java.util.Collections;
|
||||
|
||||
@@ -73,14 +76,13 @@ public class TestDataLineMarkerProvider extends RunLineMarkerContributor {
|
||||
public static String getTestDataBasePath(@Nullable PsiClass psiClass) {
|
||||
if (psiClass == null) return null;
|
||||
|
||||
final PsiAnnotation annotation =
|
||||
AnnotationUtil.findAnnotationInHierarchy(psiClass, Collections.singleton(TEST_DATA_PATH_ANNOTATION_QUALIFIED_NAME));
|
||||
final UAnnotation annotation =
|
||||
UastContextKt.toUElement(AnnotationUtil.findAnnotationInHierarchy(psiClass, Collections.singleton(TEST_DATA_PATH_ANNOTATION_QUALIFIED_NAME)), UAnnotation.class);
|
||||
if (annotation != null) {
|
||||
final PsiAnnotationMemberValue value = annotation.findAttributeValue(PsiAnnotation.DEFAULT_REFERENCED_METHOD_NAME);
|
||||
if (value instanceof PsiExpression) {
|
||||
final Project project = value.getProject();
|
||||
final PsiConstantEvaluationHelper evaluationHelper = JavaPsiFacade.getInstance(project).getConstantEvaluationHelper();
|
||||
final Object constantValue = evaluationHelper.computeConstantExpression(value, false);
|
||||
UExpression value = annotation.findAttributeValue(PsiAnnotation.DEFAULT_REFERENCED_METHOD_NAME);
|
||||
if (value != null) {
|
||||
final Project project = psiClass.getProject();
|
||||
final Object constantValue = value.evaluate();
|
||||
if (constantValue instanceof String) {
|
||||
String path = (String) constantValue;
|
||||
if (path.contains(CONTENT_ROOT_VARIABLE)) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2000-2015 JetBrains s.r.o.
|
||||
* Copyright 2000-2017 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.
|
||||
@@ -22,6 +22,14 @@ import com.intellij.psi.*;
|
||||
import com.intellij.testFramework.PlatformTestUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.uast.*;
|
||||
import org.jetbrains.uast.evaluation.SimpleEvaluatorExtension;
|
||||
import org.jetbrains.uast.evaluation.UEvaluationContextKt;
|
||||
import org.jetbrains.uast.values.UBooleanConstant;
|
||||
import org.jetbrains.uast.values.UConstant;
|
||||
import org.jetbrains.uast.values.UStringConstant;
|
||||
import org.jetbrains.uast.values.UValue;
|
||||
import org.jetbrains.uast.visitor.AbstractUastVisitor;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.*;
|
||||
@@ -61,22 +69,26 @@ public class TestDataReferenceCollector {
|
||||
|
||||
@NotNull
|
||||
private List<String> collectTestDataReferences(final PsiMethod method,
|
||||
final Map<String, Computable<String>> argumentMap,
|
||||
final Map<String, Computable<UValue>> argumentMap,
|
||||
final HashSet<PsiMethod> proceed) {
|
||||
final List<String> result = new ArrayList<>();
|
||||
if (myTestDataPath == null) {
|
||||
return result;
|
||||
}
|
||||
method.accept(new JavaRecursiveElementVisitor() {
|
||||
UMethod uMethod = (UMethod)UastContextKt.toUElement(method);
|
||||
if (uMethod == null) {
|
||||
return result;
|
||||
}
|
||||
uMethod.accept(new AbstractUastVisitor() {
|
||||
@Override
|
||||
public void visitMethodCallExpression(PsiMethodCallExpression expression) {
|
||||
String callText = expression.getMethodExpression().getReferenceName();
|
||||
if (callText == null) return;
|
||||
PsiMethod callee = expression.resolveMethod();
|
||||
public boolean visitCallExpression(UCallExpression expression) {
|
||||
String callText = expression.getMethodName();
|
||||
if (callText == null) return true;
|
||||
UMethod callee = UastContextKt.toUElement(expression.resolve(), UMethod.class);
|
||||
if (callee != null && callee.hasModifierProperty(PsiModifier.ABSTRACT)) {
|
||||
final PsiClass calleeContainingClass = callee.getContainingClass();
|
||||
if (calleeContainingClass != null && myContainingClass.isInheritor(calleeContainingClass, true)) {
|
||||
final PsiMethod implementation = myContainingClass.findMethodBySignature(callee, true);
|
||||
final UMethod implementation = UastContextKt.toUElement(myContainingClass.findMethodBySignature(callee, true), UMethod.class);
|
||||
if (implementation != null) {
|
||||
callee = implementation;
|
||||
}
|
||||
@@ -94,91 +106,78 @@ public class TestDataReferenceCollector {
|
||||
haveAnnotatedParameters = true;
|
||||
}
|
||||
}
|
||||
if (expression.getMethodExpression().getQualifierExpression() == null && !haveAnnotatedParameters) {
|
||||
if (expression.getReceiver() == null && !haveAnnotatedParameters) {
|
||||
result.addAll(collectTestDataReferences(callee, buildArgumentMap(expression, callee), proceed));
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
private void processCallArgument(PsiMethodCallExpression expression, Map<String, Computable<String>> argumentMap, List<String> result, final int index) {
|
||||
final PsiExpression[] arguments = expression.getArgumentList().getExpressions();
|
||||
if (arguments.length > index) {
|
||||
String testDataFile = evaluate(arguments [index], argumentMap);
|
||||
if (testDataFile != null) {
|
||||
result.add(myTestDataPath + testDataFile);
|
||||
private void processCallArgument(UCallExpression expression,
|
||||
Map<String, Computable<UValue>> argumentMap,
|
||||
List<String> result,
|
||||
final int index) {
|
||||
final List<UExpression> arguments = expression.getValueArguments();
|
||||
if (arguments.size() > index) {
|
||||
UValue testDataFileValue = UEvaluationContextKt.uValueOf(arguments.get(index), new TestDataEvaluatorExtension(argumentMap));
|
||||
if (testDataFileValue instanceof UStringConstant) {
|
||||
result.add(myTestDataPath + ((UStringConstant) testDataFileValue).getValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Map<String, Computable<String>> buildArgumentMap(PsiMethodCallExpression expression, PsiMethod method) {
|
||||
Map<String, Computable<String>> result = new HashMap<>();
|
||||
private Map<String, Computable<UValue>> buildArgumentMap(UCallExpression expression, PsiMethod method) {
|
||||
Map<String, Computable<UValue>> result = new HashMap<>();
|
||||
final PsiParameter[] parameters = method.getParameterList().getParameters();
|
||||
final PsiExpression[] arguments = expression.getArgumentList().getExpressions();
|
||||
for (int i = 0; i < arguments.length && i < parameters.length; i++) {
|
||||
final List<UExpression> arguments = expression.getValueArguments();
|
||||
for (int i = 0; i < arguments.size() && i < parameters.length; i++) {
|
||||
final int finalI = i;
|
||||
result.put(parameters [i].getName(),
|
||||
(NullableComputable<String>)() -> evaluate(arguments [finalI], Collections.<String, Computable<String>>emptyMap()));
|
||||
(NullableComputable<UValue>)() -> UEvaluationContextKt.uValueOf(arguments.get(finalI),
|
||||
new TestDataEvaluatorExtension(Collections.emptyMap())));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private String evaluate(PsiExpression expression, Map<String, Computable<String>> arguments) {
|
||||
if (expression instanceof PsiPolyadicExpression) {
|
||||
PsiPolyadicExpression binaryExpression = (PsiPolyadicExpression)expression;
|
||||
if (binaryExpression.getOperationTokenType() == JavaTokenType.PLUS) {
|
||||
String r = "";
|
||||
for (PsiExpression op : binaryExpression.getOperands()) {
|
||||
String lhs = evaluate(op, arguments);
|
||||
if (lhs == null) return null;
|
||||
r += lhs;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
}
|
||||
else if (expression instanceof PsiLiteralExpression) {
|
||||
final Object value = ((PsiLiteralExpression)expression).getValue();
|
||||
if (value instanceof String) {
|
||||
return (String) value;
|
||||
}
|
||||
}
|
||||
else if (expression instanceof PsiReferenceExpression) {
|
||||
final PsiElement result = ((PsiReferenceExpression)expression).resolve();
|
||||
if (result instanceof PsiParameter) {
|
||||
final String name = ((PsiParameter)result).getName();
|
||||
final Computable<String> arg = arguments.get(name);
|
||||
return arg == null ? null : arg.compute();
|
||||
}
|
||||
if (result instanceof PsiVariable) {
|
||||
final PsiExpression initializer = ((PsiVariable)result).getInitializer();
|
||||
if (initializer != null) {
|
||||
return evaluate(initializer, arguments);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (expression instanceof PsiMethodCallExpression) {
|
||||
final PsiMethodCallExpression methodCall = (PsiMethodCallExpression)expression;
|
||||
final String callText = methodCall.getMethodExpression().getText();
|
||||
if (callText.equals("getTestName")) {
|
||||
final PsiExpression[] psiExpressions = methodCall.getArgumentList().getExpressions();
|
||||
if (psiExpressions.length == 1) {
|
||||
if ("true".equals(psiExpressions[0].getText()) && !StringUtil.isEmpty(myTestName)) {
|
||||
return PlatformTestUtil.lowercaseFirstLetter(myTestName, true);
|
||||
}
|
||||
return myTestName;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (expression != null) {
|
||||
myLogMessages.add("Failed to evaluate " + expression.getText());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public String getLog() {
|
||||
return StringUtil.join(myLogMessages, "\n");
|
||||
}
|
||||
|
||||
private class TestDataEvaluatorExtension extends SimpleEvaluatorExtension {
|
||||
private final Map<String, Computable<UValue>> myArguments;
|
||||
|
||||
private TestDataEvaluatorExtension(Map<String, Computable<UValue>> arguments) {
|
||||
myArguments = arguments;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object evaluateMethodCall(PsiMethod target, List<? extends UValue> argumentValues) {
|
||||
if (target.getName().equals("getTestName") && argumentValues.size() == 1) {
|
||||
UValue lowercaseArg = argumentValues.get(0);
|
||||
boolean lowercaseArgValue = lowercaseArg instanceof UBooleanConstant && ((UBooleanConstant) lowercaseArg).getValue();
|
||||
if (lowercaseArgValue && !StringUtil.isEmpty(myTestName)) {
|
||||
return PlatformTestUtil.lowercaseFirstLetter(myTestName, true);
|
||||
}
|
||||
return myTestName;
|
||||
|
||||
}
|
||||
return super.evaluateMethodCall(target, argumentValues);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object evaluateVariable(UVariable variable) {
|
||||
if (variable instanceof UParameter) {
|
||||
Computable<UValue> value = myArguments.get(variable.getName());
|
||||
if (value != null) {
|
||||
UValue computedValue = value.compute();
|
||||
UConstant constant = computedValue.toConstant();
|
||||
return constant != null ? constant.getValue() : super.evaluateVariable(variable);
|
||||
}
|
||||
}
|
||||
return super.evaluateVariable(variable);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,4 +17,8 @@ class ATest extends AbstractClass {
|
||||
|
||||
private void configureByFile(@com.intellij.testFramework.TestDataFile String file) {
|
||||
}
|
||||
|
||||
private String getTestName(boolean toUpperCase) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -14,4 +14,8 @@ public class ATest extends LightCodeInsightFixtureTestCase {
|
||||
|
||||
private void configureByFile(@com.intellij.testFramework.TestDataFile String file) {
|
||||
}
|
||||
|
||||
private String getTestName(boolean toUpperCase) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,4 +10,8 @@ public class ATest extends LightCodeInsightFixtureTestCase {
|
||||
|
||||
private void configureByFile(@com.intellij.testFramework.TestDataFile String file) {
|
||||
}
|
||||
|
||||
private String getTestName(boolean toUpperCase) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user