1 /* 2 * Copyright (C) 2014 Google, Inc. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 package com.squareup.javapoet; 17 18 import static com.google.common.base.Charsets.*; 19 import static com.google.common.base.Preconditions.*; 20 21 import java.util.Locale; 22 import java.util.Set; 23 import java.util.concurrent.atomic.AtomicReference; 24 25 import javax.annotation.processing.AbstractProcessor; 26 import javax.annotation.processing.ProcessingEnvironment; 27 import javax.annotation.processing.Processor; 28 import javax.annotation.processing.RoundEnvironment; 29 import javax.lang.model.SourceVersion; 30 import javax.lang.model.element.TypeElement; 31 import javax.lang.model.util.Elements; 32 import javax.lang.model.util.Types; 33 import javax.tools.DiagnosticCollector; 34 import javax.tools.JavaCompiler; 35 import javax.tools.JavaFileManager; 36 import javax.tools.JavaFileObject; 37 38 import org.eclipse.jdt.internal.compiler.tool.EclipseCompiler; 39 import org.junit.Rule; 40 import org.junit.rules.TestRule; 41 import org.junit.runner.Description; 42 import org.junit.runner.RunWith; 43 import org.junit.runners.JUnit4; 44 import org.junit.runners.model.Statement; 45 46 import com.google.common.collect.ImmutableList; 47 import com.google.common.collect.ImmutableSet; 48 49 @RunWith(JUnit4.class) 50 public final class TypesEclipseTest extends AbstractTypesTest { 51 /** 52 * A {@link JUnit4} {@link Rule} that executes tests such that a instances of {@link Elements} and 53 * {@link Types} are available during execution. 54 * 55 * <p>To use this rule in a test, just add the following field: <pre> {@code 56 * @Rule public CompilationRule compilationRule = new CompilationRule();} 57 * 58 * @author Gregory Kick 59 */ 60 public static final class CompilationRule implements TestRule { 61 private Elements elements; 62 private Types types; 63 64 @Override apply(final Statement base, Description description)65 public Statement apply(final Statement base, Description description) { 66 return new Statement() { 67 @Override public void evaluate() throws Throwable { 68 final AtomicReference<Throwable> thrown = new AtomicReference<>(); 69 boolean successful = compile(ImmutableList.of(new AbstractProcessor() { 70 @Override 71 public SourceVersion getSupportedSourceVersion() { 72 return SourceVersion.latest(); 73 } 74 75 @Override 76 public Set<String> getSupportedAnnotationTypes() { 77 return ImmutableSet.of("*"); 78 } 79 80 @Override 81 public synchronized void init(ProcessingEnvironment processingEnv) { 82 super.init(processingEnv); 83 elements = processingEnv.getElementUtils(); 84 types = processingEnv.getTypeUtils(); 85 } 86 87 @Override 88 public boolean process(Set<? extends TypeElement> annotations, 89 RoundEnvironment roundEnv) { 90 // just run the test on the last round after compilation is over 91 if (roundEnv.processingOver()) { 92 try { 93 base.evaluate(); 94 } catch (Throwable e) { 95 thrown.set(e); 96 } 97 } 98 return false; 99 } 100 })); 101 checkState(successful); 102 Throwable t = thrown.get(); 103 if (t != null) { 104 throw t; 105 } 106 } 107 }; 108 } 109 110 /** 111 * Returns the {@link Elements} instance associated with the current execution of the rule. 112 * 113 * @throws IllegalStateException if this method is invoked outside the execution of the rule. 114 */ getElements()115 public Elements getElements() { 116 checkState(elements != null, "Not running within the rule"); 117 return elements; 118 } 119 120 /** 121 * Returns the {@link Types} instance associated with the current execution of the rule. 122 * 123 * @throws IllegalStateException if this method is invoked outside the execution of the rule. 124 */ getTypes()125 public Types getTypes() { 126 checkState(elements != null, "Not running within the rule"); 127 return types; 128 } 129 compile(Iterable<? extends Processor> processors)130 static private boolean compile(Iterable<? extends Processor> processors) { 131 JavaCompiler compiler = new EclipseCompiler(); 132 DiagnosticCollector<JavaFileObject> diagnosticCollector = 133 new DiagnosticCollector<>(); 134 JavaFileManager fileManager = compiler.getStandardFileManager(diagnosticCollector, Locale.getDefault(), UTF_8); 135 JavaCompiler.CompilationTask task = compiler.getTask( 136 null, 137 fileManager, 138 diagnosticCollector, 139 ImmutableSet.of(), 140 ImmutableSet.of(TypesEclipseTest.class.getCanonicalName()), 141 ImmutableSet.of()); 142 task.setProcessors(processors); 143 return task.call(); 144 } 145 } 146 147 @Rule public final CompilationRule compilation = new CompilationRule(); 148 149 @Override 150 protected Elements getElements() { 151 return compilation.getElements(); 152 } 153 154 @Override 155 protected Types getTypes() { 156 return compilation.getTypes(); 157 } 158 } 159