| /******************************************************************************* |
| * Copyright (c) 2008, Walter Harley and others. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/epl-v10.html |
| * |
| * Contributors: |
| * eclipse@cafewalter.com - initial API and implementation |
| * |
| *******************************************************************************/ |
| |
| package org.eclipse.jdt.apt.pluggable.tests.processors.modeltester; |
| |
| import java.lang.reflect.InvocationTargetException; |
| import java.lang.reflect.Method; |
| import java.util.HashMap; |
| import java.util.Map; |
| import java.util.Set; |
| |
| import javax.annotation.processing.AbstractProcessor; |
| import javax.annotation.processing.ProcessingEnvironment; |
| import javax.annotation.processing.RoundEnvironment; |
| import javax.annotation.processing.SupportedAnnotationTypes; |
| import javax.annotation.processing.SupportedOptions; |
| import javax.annotation.processing.SupportedSourceVersion; |
| import javax.lang.model.SourceVersion; |
| import javax.lang.model.element.Element; |
| import javax.lang.model.element.ElementKind; |
| import javax.lang.model.element.ExecutableElement; |
| import javax.lang.model.element.TypeElement; |
| import javax.lang.model.element.VariableElement; |
| import javax.lang.model.type.TypeKind; |
| import javax.lang.model.type.TypeMirror; |
| import javax.lang.model.util.ElementFilter; |
| |
| import org.eclipse.jdt.apt.pluggable.tests.ProcessorTestStatus; |
| import org.eclipse.jdt.apt.pluggable.tests.annotations.LookAt; |
| import org.eclipse.jdt.apt.pluggable.tests.annotations.ModelTestTrigger; |
| |
| /** |
| * Testing annotation processors through JUnit in the IDE is complex, because each test requires |
| * something different of the processor and all processors must coexist in the plugin registry, and |
| * because the processor has very limited communication with the rest of the IDE. So, we make one |
| * processor run many tests. The JUnit tests specify which test to run by passing its name in to the |
| * ModelTestTrigger annotation. Test failures are reported via the Messager interface. |
| * |
| * @since 3.5 |
| */ |
| @SupportedAnnotationTypes( { "org.eclipse.jdt.apt.pluggable.tests.annotations.ModelTestTrigger" }) |
| @SupportedSourceVersion(SourceVersion.RELEASE_6) |
| @SupportedOptions( {}) |
| public class ModelTesterProc extends AbstractProcessor { |
| |
| public static final String TEST_FIELD_TYPE_PKG = "p"; |
| public static final String TEST_FIELD_TYPE_CLASS = "Foo"; |
| public static final String TEST_FIELD_TYPE_SOURCE = |
| "package p;\n" + |
| "import org.eclipse.jdt.apt.pluggable.tests.annotations.ModelTestTrigger;\n" + |
| "import org.eclipse.jdt.apt.pluggable.tests.annotations.LookAt;\n" + |
| "@ModelTestTrigger(test = \"testFieldType\")" + |
| "@SuppressWarnings(\"unused\")\n" + |
| "public class Foo {\n" + |
| " @LookAt\n" + |
| " private int _fInt = 0;\n" + |
| " @LookAt\n" + |
| " private String _fString = \"\";\n" + |
| " @LookAt\n" + |
| " private Foo _fFoo = null;\n" + |
| "}"; |
| |
| public static final String TEST_METHOD_TYPE_PKG = "p"; |
| public static final String TEST_METHOD_TYPE_CLASS = "Foo"; |
| public static final String TEST_METHOD_TYPE_SOURCE = |
| "package p;\n" + |
| "import org.eclipse.jdt.apt.pluggable.tests.annotations.ModelTestTrigger;\n" + |
| "import org.eclipse.jdt.apt.pluggable.tests.annotations.LookAt;\n" + |
| "@ModelTestTrigger(test = \"testMethodType\")" + |
| "@SuppressWarnings(\"unused\")\n" + |
| "public class Foo {\n" + |
| " @LookAt\n" + |
| " private Foo self() { return this;}\n" + |
| "}"; |
| |
| @SuppressWarnings("unused") |
| private ProcessingEnvironment _processingEnv; |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see javax.annotation.processing.AbstractProcessor#init(javax.annotation.processing.ProcessingEnvironment) |
| */ |
| @Override |
| public synchronized void init(ProcessingEnvironment processingEnv) { |
| super.init(processingEnv); |
| _processingEnv = processingEnv; |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see javax.annotation.processing.AbstractProcessor#process(java.util.Set, |
| * javax.annotation.processing.RoundEnvironment) |
| */ |
| @Override |
| public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) { |
| ProcessorTestStatus.setProcessorRan(); |
| if (!roundEnv.processingOver() && !annotations.isEmpty()) { |
| round(annotations, roundEnv); |
| } |
| return true; |
| } |
| |
| /** |
| * Perform a round of processing: for a given annotation instance, determine what test method it |
| * specifies, and invoke that method, passing in the annotated element. |
| */ |
| private void round(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) { |
| TypeElement modelTesterAnno = annotations.iterator().next(); |
| Set<? extends Element> annotatedEls = roundEnv.getElementsAnnotatedWith(modelTesterAnno); |
| for (Element annotatedEl : annotatedEls) { |
| ModelTestTrigger modelTesterMirror = annotatedEl.getAnnotation(ModelTestTrigger.class); |
| String testMethodName = modelTesterMirror.test(); |
| String arg0 = modelTesterMirror.arg0(); |
| String arg1 = modelTesterMirror.arg1(); |
| if (null != testMethodName && testMethodName.length() > 0) { |
| try { |
| Method testMethod = ModelTesterProc.class.getMethod(testMethodName, |
| RoundEnvironment.class, Element.class, String.class, String.class); |
| testMethod.invoke(this, roundEnv, annotatedEl, arg0, arg1); |
| } catch (Exception e) { |
| Throwable t; |
| t = (e instanceof InvocationTargetException) ? t = e.getCause() : e; |
| t.printStackTrace(); |
| // IllegalStateException probably means test method called ProcessorTestStatus.fail() |
| String msg = (t instanceof IllegalStateException) ? |
| t.getMessage() : |
| t.getClass().getSimpleName() + " invoking test method " + |
| testMethodName + " - see console for details"; |
| ProcessorTestStatus.fail(msg); |
| } |
| } |
| } |
| } |
| |
| /** |
| * Check the types of some fields (see https://bugs.eclipse.org/bugs/show_bug.cgi?id=248163) |
| * @see #TEST_FIELD_TYPE_SOURCE |
| */ |
| public void testFieldType(RoundEnvironment roundEnv, Element e, String arg0, String arg1) |
| throws Exception |
| { |
| Map<String, VariableElement> fields = new HashMap<String, VariableElement>(); |
| Iterable<? extends Element> elements; |
| // With this line uncommented, test would pass: |
| // elements = e.getEnclosedElements(); |
| // With this line uncommented, test would fail: |
| elements = roundEnv.getElementsAnnotatedWith(LookAt.class); |
| for (VariableElement field : ElementFilter.fieldsIn(elements)) { |
| fields.put(field.getSimpleName().toString(), field); |
| } |
| |
| VariableElement fInt = fields.get("_fInt"); |
| if (fInt == null) { |
| ProcessorTestStatus.fail("Field _fInt was not found"); |
| } |
| if (fInt.getKind() != ElementKind.FIELD) { |
| ProcessorTestStatus.fail("ElementKind of field _fInt was " + fInt.getKind() + |
| ", expected FIELD"); |
| } |
| TypeMirror fIntType = fInt.asType(); |
| if (fIntType.getKind() != TypeKind.INT) { |
| ProcessorTestStatus.fail("Field _fInt asType returned type kind of " + fIntType.getKind() + |
| ", expected INT"); |
| } |
| |
| VariableElement fString = fields.get("_fString"); |
| if (fString == null) { |
| ProcessorTestStatus.fail("Field _fString was not found"); |
| } |
| if (fString.getKind() != ElementKind.FIELD) { |
| ProcessorTestStatus.fail("ElementKind of field _fString was " + fString.getKind() + |
| ", expected FIELD"); |
| } |
| TypeMirror fStringType = fString.asType(); |
| if (fStringType.getKind() != TypeKind.DECLARED) { |
| ProcessorTestStatus.fail("Field _fString asType returned type kind of " + fStringType.getKind() + |
| ", expected DECLARED"); |
| } |
| VariableElement fFoo = fields.get("_fFoo"); |
| if (fFoo == null) { |
| ProcessorTestStatus.fail("Field _fFoo was not found"); |
| } |
| if (fFoo.getKind() != ElementKind.FIELD) { |
| ProcessorTestStatus.fail("ElementKind of field _fFoo was " + fFoo.getKind() + |
| ", expected FIELD"); |
| } |
| TypeMirror fFooType = fFoo.asType(); |
| if (fFooType.getKind() != TypeKind.DECLARED) { |
| ProcessorTestStatus.fail("Field _fFoo asType returned type kind of " + fFooType.getKind() + |
| ", expected DECLARED"); |
| } |
| } |
| |
| /** |
| * Check the types of some fields (see https://bugs.eclipse.org/bugs/show_bug.cgi?id=248163) |
| * @see #TEST_METHOD_TYPE_SOURCE |
| */ |
| public void testMethodType(RoundEnvironment roundEnv, Element e, String arg0, String arg1) |
| throws Exception |
| { |
| Map<String, ExecutableElement> methods = new HashMap<String, ExecutableElement>(); |
| Iterable<? extends Element> elements; |
| // With this line uncommented, test would pass: |
| // elements = e.getEnclosedElements(); |
| // With this line uncommented, test would fail: |
| elements = roundEnv.getElementsAnnotatedWith(LookAt.class); |
| for (ExecutableElement method : ElementFilter.methodsIn(elements)) { |
| methods.put(method.getSimpleName().toString(), method); |
| } |
| |
| ExecutableElement mSelf = methods.get("self"); |
| if (mSelf == null) { |
| ProcessorTestStatus.fail("Method self() was not found"); |
| } |
| if (mSelf.getKind() != ElementKind.METHOD) { |
| ProcessorTestStatus.fail("ElementKind of method self() was " + mSelf.getKind() + |
| ", expected METHOD"); |
| } |
| TypeMirror mSelfType = mSelf.getReturnType(); |
| if (mSelfType.getKind() != TypeKind.DECLARED) { |
| ProcessorTestStatus.fail("Method self() asType returned type kind of " + mSelfType.getKind() + |
| ", expected DECLARED"); |
| } |
| } |
| } |