| /******************************************************************************* |
| * Copyright (c) 2007 - 2009 BEA Systems, Inc. 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: |
| * wharley@bea.com - initial API and implementation |
| * |
| *******************************************************************************/ |
| |
| package org.eclipse.jdt.apt.pluggable.tests.processors.filertester; |
| |
| import java.io.InputStream; |
| import java.io.InputStreamReader; |
| import java.io.PrintWriter; |
| import java.io.Reader; |
| import java.lang.reflect.InvocationTargetException; |
| import java.lang.reflect.Method; |
| import java.net.URI; |
| import java.util.Arrays; |
| import java.util.Set; |
| |
| import javax.annotation.processing.AbstractProcessor; |
| import javax.annotation.processing.Filer; |
| 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.TypeElement; |
| import javax.tools.FileObject; |
| import javax.tools.JavaFileObject; |
| import javax.tools.StandardLocation; |
| |
| import org.eclipse.jdt.apt.pluggable.tests.ProcessorTestStatus; |
| import org.eclipse.jdt.apt.pluggable.tests.annotations.FilerTestTrigger; |
| |
| /** |
| * 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 |
| * FilerTest annotation. Test failures are reported via the Messager interface. |
| * |
| * @since 3.4 |
| */ |
| @SupportedAnnotationTypes( { "org.eclipse.jdt.apt.pluggable.tests.annotations.FilerTestTrigger" }) |
| @SupportedSourceVersion(SourceVersion.RELEASE_6) |
| @SupportedOptions( {}) |
| public class FilerTesterProc extends AbstractProcessor { |
| |
| private ProcessingEnvironment _processingEnv; |
| private Filer _filer; |
| |
| public static final String resource01FileContents = |
| "package g;\n" + |
| "public class Test {}\n"; |
| public static final String resource01Name = |
| ".apt_generated/g/Test.java"; |
| |
| public static final String resource02FileContents = |
| "This is some test text\n"; |
| public static final String resource02Name = |
| "bin/t/Test.txt"; |
| |
| public static final String helloStr = "Hello world"; |
| public static final String javaStr = "package g;\nclass G {}\n"; |
| |
| /** |
| * @return a string representing a large Java class. |
| */ |
| public static String largeJavaClass() { |
| StringBuffer sb = new StringBuffer(); |
| sb.append("package g;\n"); |
| sb.append("public class Test {\n"); |
| sb.append(" public static final String bigString = \n"); |
| for (int i = 0; i < 500; ++i) { |
| sb.append(" \"the quick brown dog jumped over the lazy fox, in a peculiar reversal\\n\" +\n"); |
| } |
| sb.append(" \"\";\n"); |
| sb.append("\n"); |
| sb.append(" /** This file is at least this big */\n"); |
| sb.append(" public static final int SIZE = "); |
| sb.append(sb.length()); |
| sb.append(";\n"); |
| sb.append("}\n"); |
| return sb.toString(); |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see javax.annotation.processing.AbstractProcessor#init(javax.annotation.processing.ProcessingEnvironment) |
| */ |
| @Override |
| public synchronized void init(ProcessingEnvironment processingEnv) { |
| super.init(processingEnv); |
| _processingEnv = processingEnv; |
| _filer = _processingEnv.getFiler(); |
| } |
| |
| /* |
| * (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 filerTesterAnno = annotations.iterator().next(); |
| Set<? extends Element> annotatedEls = roundEnv.getElementsAnnotatedWith(filerTesterAnno); |
| for (Element annotatedEl : annotatedEls) { |
| FilerTestTrigger filerTesterMirror = annotatedEl.getAnnotation(FilerTestTrigger.class); |
| String testMethodName = filerTesterMirror.test(); |
| String arg0 = filerTesterMirror.arg0(); |
| String arg1 = filerTesterMirror.arg1(); |
| if (null != testMethodName && testMethodName.length() > 0) { |
| try { |
| Method testMethod = FilerTesterProc.class.getMethod(testMethodName, |
| Element.class, String.class, String.class); |
| testMethod.invoke(this, 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); |
| } |
| } |
| } |
| } |
| |
| /** |
| * Attempt to get an existing resource from the SOURCE_OUTPUT. |
| */ |
| public void testGetResource01(Element e, String arg0, String arg1) throws Exception { |
| FileObject resource = _filer.getResource(StandardLocation.SOURCE_OUTPUT, arg0, arg1); |
| checkResourceContents01(resource, resource01Name, resource01FileContents); |
| } |
| |
| /** |
| * Attempt to get an existing resource from the CLASS_OUTPUT. |
| */ |
| public void testGetResource02(Element e, String arg0, String arg1) throws Exception { |
| FileObject resource = _filer.getResource(StandardLocation.CLASS_OUTPUT, arg0, arg1); |
| checkResourceContents01(resource, resource02Name, resource02FileContents); |
| } |
| |
| /** |
| * Attempt to create a new resource in SOURCE_OUTPUT. |
| */ |
| public void testCreateNonSourceFile(Element e, String pkg, String relName) throws Exception { |
| FileObject fo = _filer.createResource(StandardLocation.SOURCE_OUTPUT, |
| pkg, relName, e); |
| PrintWriter pw = null; |
| try { |
| pw = new PrintWriter(fo.openWriter()); |
| pw.println("Hello world"); |
| } finally { |
| if (pw != null) |
| pw.close(); |
| } |
| String name = fo.getName().toString(); |
| // JSR269 spec does not make strict requirements about what getName() returns, |
| // but we can at least expect it to include the relative name. |
| if (!name.contains(relName)) { |
| ProcessorTestStatus.fail("File object getName() returned " + name + |
| ", expected it to contain " + relName); |
| } |
| } |
| |
| /** |
| * Attempt to create new resources with null parentage. |
| * See <a href="http://bugs.eclipse.org/285838">Bug 285838</a>. |
| */ |
| public void testNullParents(Element e, String pkg, String relName) throws Exception { |
| FileObject fo = _filer.createResource(StandardLocation.SOURCE_OUTPUT, |
| pkg, relName + ".txt", (Element[])null); |
| PrintWriter pw = null; |
| try { |
| pw = new PrintWriter(fo.openWriter()); |
| pw.println("Hello world"); |
| } finally { |
| if (pw != null) |
| pw.close(); |
| } |
| |
| JavaFileObject jfo = _filer.createSourceFile(pkg + "/" + relName, (Element[])null); |
| pw = null; |
| try { |
| pw = new PrintWriter(jfo.openWriter()); |
| pw.println("package " + pkg + ";\npublic class " + relName + "{ }"); |
| } finally { |
| if (pw != null) |
| pw.close(); |
| } |
| } |
| |
| /** |
| * Test the toUri() method on various file objects. |
| */ |
| public void testURI(Element e, String pkg, String relName) throws Exception { |
| |
| // Generated non-source file |
| FileObject foGenNonSrc = _filer.createResource(StandardLocation.SOURCE_OUTPUT, |
| pkg, relName, e); |
| checkGenUri(foGenNonSrc, relName, helloStr, "generated non-source file"); |
| |
| // Generated source file |
| FileObject foGenSrc = _filer.createSourceFile("g.G", e); |
| checkGenUri(foGenSrc, "G", javaStr, "generated source file"); |
| } |
| |
| private void checkGenUri(FileObject fo, String name, String content, String category) throws Exception { |
| PrintWriter pw = null; |
| try { |
| pw = new PrintWriter(fo.openWriter()); |
| pw.print(content); |
| } finally { |
| if (pw != null) |
| pw.close(); |
| } |
| URI uri = fo.toUri(); |
| if (!uri.toString().contains(name)) { |
| ProcessorTestStatus.fail("toUri() on " + category + " returned " + uri.toString() + |
| ", expected it to contain " + name); |
| } |
| char buf[] = new char[256]; |
| Reader r = null; |
| int len = 0; |
| try { |
| r = new InputStreamReader(uri.toURL().openStream()); |
| len = r.read(buf); |
| } finally { |
| if (r != null) |
| r.close(); |
| } |
| buf = Arrays.copyOf(buf, len); |
| if (!Arrays.equals(buf, content.toCharArray())) { |
| ProcessorTestStatus.fail("toUri() on " + category + " returned " + uri.toString() + |
| ", but reading that URI produced \"" + new String(buf) + "\" instead of expected \"" + content + "\""); |
| } |
| } |
| |
| /** |
| * Attempt to get an existing resource from the SOURCE_OUTPUT. |
| */ |
| public void testGetCharContentLarge(Element e, String arg0, String arg1) throws Exception { |
| FileObject resource = _filer.getResource(StandardLocation.SOURCE_OUTPUT, arg0, arg1); |
| CharSequence actualCharContent = resource.getCharContent(true); |
| String expectedContents = largeJavaClass(); |
| if (!expectedContents.equals(actualCharContent.toString())) { |
| System.out.println("Expected getCharContent to return:\n" + expectedContents); |
| System.out.println("Actual getCharContent returned:\n" + actualCharContent); |
| ProcessorTestStatus.fail("getCharContent() did not return expected contents"); |
| } |
| } |
| |
| /** |
| * Check that the resource can be opened, examined, and its contents match |
| * {@link #checkResourceContents01(FileObject)}getResource01FileContents |
| */ |
| private void checkResourceContents01(FileObject resource, String expectedName, String expectedContents) throws Exception { |
| |
| long modTime = resource.getLastModified(); |
| if (modTime <= 0) { |
| ProcessorTestStatus.fail("resource had unexpected mod time: " + modTime); |
| } |
| |
| String actualName = resource.getName(); |
| if (!expectedName.equals(actualName)) { |
| System.out.println("Resource had unexpected name. Expected " + expectedName + |
| ", actual was " + actualName); |
| ProcessorTestStatus.fail("Resource had unexpected name"); |
| } |
| |
| InputStream stream = resource.openInputStream(); |
| if (stream.available() <= 0) { |
| ProcessorTestStatus.fail("stream contained no data"); |
| } |
| byte actualBytes[] = new byte[512]; |
| int length = stream.read(actualBytes); |
| String actualStringContents = new String(actualBytes, 0, length); |
| if (!expectedContents.equals(actualStringContents)) { |
| System.out.println("Expected stream contents:\n" + expectedContents); |
| System.out.println("Actual contents were:\n" + actualStringContents); |
| ProcessorTestStatus.fail("stream did not contain expected contents"); |
| } |
| stream.close(); |
| |
| char actualChars[] = new char[512]; |
| Reader reader = resource.openReader(true); |
| length = reader.read(actualChars, 0, actualChars.length); |
| actualStringContents = new String(actualChars, 0, length); |
| if (!expectedContents.equals(actualStringContents)) { |
| System.out.println("Expected reader contents:\n" + expectedContents); |
| System.out.println("Actual contents were:\n" + actualStringContents); |
| ProcessorTestStatus.fail("reader did not contain expected contents"); |
| } |
| reader.close(); |
| |
| CharSequence actualCharContent = resource.getCharContent(true); |
| if (!expectedContents.equals(actualCharContent.toString())) { |
| System.out.println("Expected getCharContent to return:\n" + expectedContents); |
| System.out.println("Actual getCharContent returned:\n" + actualCharContent); |
| ProcessorTestStatus.fail("getCharContent() did not return expected contents"); |
| } |
| } |
| |
| } |