blob: 3ee186402b83ff321dc9a4802ead410c8f01ecae [file] [log] [blame]
/*
* Copyright 2000-2010 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 com.intellij.testAssistant;
import com.intellij.openapi.util.Computable;
import com.intellij.openapi.util.NullableComputable;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.*;
import com.intellij.testFramework.UsefulTestCase;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.File;
import java.util.*;
/**
* @author yole
*/
public class TestDataReferenceCollector {
private static final String TEST_DATA_FILE_ANNOTATION_QUALIFIED_NAME = "com.intellij.testFramework.TestDataFile";
private final String myTestDataPath;
private final String myTestName;
private final List<String> myLogMessages = new ArrayList<String>();
private PsiClass myContainingClass;
private boolean myFoundTestDataParameters = false;
public TestDataReferenceCollector(@Nullable String testDataPath, String testName) {
if (StringUtil.isNotEmpty(testDataPath) && StringUtil.endsWithChar(testDataPath, File.separatorChar)) {
testDataPath += File.separatorChar;
}
myTestDataPath = testDataPath;
myTestName = testName;
}
@Nullable
List<String> collectTestDataReferences(@NotNull final PsiMethod method) {
myContainingClass = method.getContainingClass();
List<String> result = collectTestDataReferences(method, new HashMap<String, Computable<String>>());
if (!myFoundTestDataParameters) {
myLogMessages.add("Found no parameters annotated with @TestDataFile");
}
if (result == null || result.isEmpty()) {
result = TestDataGuessByExistingFilesUtil.collectTestDataByExistingFiles(method);
}
return result;
}
private List<String> collectTestDataReferences(final PsiMethod method, final Map<String, Computable<String>> argumentMap) {
final List<String> result = new ArrayList<String>();
if (myTestDataPath == null) {
return result;
}
method.accept(new JavaRecursiveElementVisitor() {
@Override
public void visitMethodCallExpression(PsiMethodCallExpression expression) {
String callText = expression.getMethodExpression().getReferenceName();
if (callText == null) return;
PsiMethod callee = expression.resolveMethod();
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);
if (implementation != null) {
callee = implementation;
}
}
}
if (callee != null) {
boolean haveAnnotatedParameters = false;
final PsiParameter[] psiParameters = callee.getParameterList().getParameters();
for (int i = 0, psiParametersLength = psiParameters.length; i < psiParametersLength; i++) {
PsiParameter psiParameter = psiParameters[i];
final PsiModifierList modifierList = psiParameter.getModifierList();
if (modifierList != null && modifierList.findAnnotation(TEST_DATA_FILE_ANNOTATION_QUALIFIED_NAME) != null) {
myFoundTestDataParameters = true;
processCallArgument(expression, argumentMap, result, i);
haveAnnotatedParameters = true;
}
}
if (expression.getMethodExpression().getQualifierExpression() == null && !haveAnnotatedParameters) {
result.addAll(collectTestDataReferences(callee, buildArgumentMap(expression, callee)));
}
}
}
});
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 Map<String, Computable<String>> buildArgumentMap(PsiMethodCallExpression expression, PsiMethod method) {
Map<String, Computable<String>> result = new HashMap<String, Computable<String>>();
final PsiParameter[] parameters = method.getParameterList().getParameters();
final PsiExpression[] arguments = expression.getArgumentList().getExpressions();
for (int i = 0; i < arguments.length && i < parameters.length; i++) {
final int finalI = i;
result.put(parameters [i].getName(), new NullableComputable<String>() {
public String compute() {
return evaluate(arguments [finalI], Collections.<String, Computable<String>>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 UsefulTestCase.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");
}
}