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