package org.jnario.ui.handler; import static org.jnario.util.Strings.PENDING_FLAG; import java.util.Iterator; import java.util.LinkedList; import org.apache.log4j.Logger; import org.eclipse.core.runtime.IStatus; import org.eclipse.debug.core.ILaunch; import org.eclipse.jdt.core.IAnnotation; import org.eclipse.jdt.core.IJavaElement; import org.eclipse.jdt.core.IJavaProject; import org.eclipse.jdt.core.IMemberValuePair; import org.eclipse.jdt.core.IMethod; import org.eclipse.jdt.core.ISourceRange; import org.eclipse.jdt.core.IType; import org.eclipse.jdt.core.ITypeHierarchy; import org.eclipse.jdt.core.JavaModelException; import org.eclipse.jdt.internal.corext.util.JavaConventionsUtil; import org.eclipse.jdt.internal.junit.BasicElementLabels; import org.eclipse.jdt.internal.junit.Messages; import org.eclipse.jdt.internal.junit.model.TestCaseElement; import org.eclipse.jdt.internal.junit.model.TestElement; import org.eclipse.jdt.internal.junit.model.TestRoot; import org.eclipse.jdt.internal.junit.model.TestRunSession; import org.eclipse.jdt.internal.junit.model.TestSuiteElement; import org.eclipse.jdt.internal.junit.ui.JUnitMessages; import org.eclipse.jdt.internal.junit.ui.OpenEditorAction; import org.eclipse.jdt.internal.junit.ui.OpenTestAction; import org.eclipse.jdt.internal.junit.ui.TestRunnerViewPart; import org.eclipse.jdt.junit.model.ITestRunSession; import org.eclipse.jdt.junit.runners.ReflectionUtil; import org.eclipse.jface.dialogs.MessageDialog; import org.eclipse.ui.texteditor.ITextEditor; import org.jnario.util.Strings; @SuppressWarnings("restriction") public abstract class AbstractJnarioOpenTestAction extends OpenTestAction { private final static Logger logger = Logger.getLogger(AbstractJnarioOpenTestAction.class); private IType fSuiteType; public AbstractJnarioOpenTestAction(TestRunnerViewPart testRunnerPart, TestCaseElement testCase) { super(testRunnerPart, testCase); String factname = extractTestName(testCase); String methodName = Strings.toMethodName(factname.replace(PENDING_FLAG, "")); ReflectionUtil.writeField(OpenTestAction.class, this, "fMethodName", methodName); } static String extractTestName(TestElement testCase) { String testname = testCase.getTestName(); String classname = testCase.getClassName(); String factname = testname.replace("(" + classname + ")", ""); return factname; } public AbstractJnarioOpenTestAction(TestRunnerViewPart testRunnerPart, TestSuiteElement testSuite) { super(testRunnerPart, testSuite.getClassName()); setText("Go to Test"); String suiteType = findSuiteType(testSuite); ReflectionUtil.writeField(OpenEditorAction.class, this, "fClassName", suiteType); } private String getSuiteClassName(TestSuiteElement testSuite) { String mainType= null; try { ITestRunSession runSession = testSuite.getRoot().getTestRunSession(); if (runSession instanceof TestRunSession) { ILaunch launch = ((TestRunSession)runSession).getLaunch(); if (launch != null && launch.getLaunchConfiguration() != null) { mainType = launch.getLaunchConfiguration().getAttribute("org.eclipse.jdt.launching.MAIN_TYPE", (String)null); } } } catch (Exception e) { logger.debug("Unable to find launch classname for suite", e); } if (mainType == null) { mainType = testSuite.getRoot().getClassName(); } return mainType; } private String findSuiteType(TestSuiteElement suite) { String mainType = getSuiteClassName(suite); Iterable<String> path = getSuitePath(suite); IType type = findType(suite.getRoot().getTestRunSession().getLaunchedProject(), mainType); if (type == null) { return null; } logger.debug("Main type of suite execution is " + type.getFullyQualifiedName()); logger.debug("Path from root to suite " + suite.getTestName() + " is " + path); Iterator<String> pathSteps = path.iterator(); while(type != null && pathSteps.hasNext()) { String suiteName = pathSteps.next(); type = findContainedType(type, suiteName); } if (pathSteps.hasNext() || type == null) { return suite.getSuiteTypeName(); } logger.debug("Suite path resolves to type " + type.getFullyQualifiedName()); return type.getFullyQualifiedName(); } private IType findContainedType(IType type, String suiteName) { try { Object containsValue = getAnnotationMemberValue(type, "Contains", "value"); if (containsValue == null) { return null; } Object[] containedTypes = (containsValue instanceof Object[]) ? (Object[]) containsValue : new Object[] {containsValue}; for (int j = 0; j < containedTypes.length; j++) { Object containedType = containedTypes[j]; IType subType = findType(getLaunchedProject(), String.valueOf(containedType)); if (subType != null) { Object name = getAnnotationMemberValue(subType, "Named", "value"); if (name != null && suiteName.equals(name.toString())) { return subType; } } } } catch (JavaModelException e) { logger.debug("Model error finding Jnario contained types", e); } return null; } private static Object getAnnotationMemberValue(IType type, String annotationName, String memberName) throws JavaModelException { IAnnotation[] annotations = type.getAnnotations(); for (int i = 0; i < annotations.length; i++) { IAnnotation annotation = annotations[i]; if (annotation.getElementName().equals(annotationName)) { IMemberValuePair[] values = annotation.getMemberValuePairs(); for (int j = 0; j < values.length; j++) { IMemberValuePair member = values[j]; if (member.getMemberName().equals(memberName)) { return member.getValue(); } } } } return null; } private static Iterable<String> getSuitePath(TestSuiteElement testSuite) { LinkedList<String> path = new LinkedList<String>(); TestElement current = testSuite; while(current != null && !(current.getParent() instanceof TestRoot)) { path.addFirst(extractTestName(current)); current = current.getParent(); } return path; } @Override protected IJavaElement findElement(IJavaProject project, String className) throws JavaModelException { IType type = findType(project, className); if (type == null) return null; String fMethodName = ReflectionUtil.readField(OpenTestAction.class, this, "fMethodName", String.class); if (fMethodName == null) { // Suite level selection. Flag so reveal() can handle. fSuiteType = type; return type; } fSuiteType = null; IMethod method = findMethod(type); if (method == null) { ITypeHierarchy typeHierarchy = type.newSupertypeHierarchy(null); IType[] supertypes = typeHierarchy.getAllSuperclasses(type); for (IType supertype : supertypes) { method = findMethod(supertype); if (method != null) break; } } if (method == null) { String title = errorTitleMessage(); String message = Messages.format( JUnitMessages.OpenTestAction_error_methodNoFound, BasicElementLabels.getJavaElementName(fMethodName)); MessageDialog.openInformation(getShell(), title, message); return type; } ReflectionUtil .writeField(OpenTestAction.class, this, "fMethod", method); return method; } private static String errorTitleMessage() { try { // Luna + Mars return (String) JUnitMessages.class.getField("OpenTestAction_dialog_title").get(null); } catch(Throwable t) { } try { // Kepler return (String) JUnitMessages.class.getField("OpenTestAction_error_title").get(null); } catch(Throwable t) { } return "Go to Test"; } private IMethod findMethod(IType type) { String fMethodName = ReflectionUtil.readField(OpenTestAction.class, this, "fMethodName", String.class); IStatus status = JavaConventionsUtil.validateMethodName(fMethodName, type); // FIX: also find methods with parameters if (!status.isOK() || fMethodName == null) return null; try { for (IMethod m : type.getMethods()) { if (fMethodName.equals(m.getElementName()) && m.exists()) return m; } } catch (JavaModelException e) { return null; } return null; } @Override protected void reveal(ITextEditor textEditor) { // Handle suite only selection if (fSuiteType != null) { try { ISourceRange range= fSuiteType.getNameRange(); if (range != null && range.getOffset() >= 0) textEditor.selectAndReveal(range.getOffset(), range.getLength()); } catch (JavaModelException e) { // not a problem } return; } super.reveal(textEditor); } }