diff --git a/pom.xml b/pom.xml index 99ef7854d..fc734325a 100644 --- a/pom.xml +++ b/pom.xml @@ -81,6 +81,12 @@ 1.10.16 test + + org.eclipse.jdt.core.compiler + ecj + 4.4.2 + test + diff --git a/src/main/java/com/squareup/javapoet/TypeVariableName.java b/src/main/java/com/squareup/javapoet/TypeVariableName.java index 910f61f42..4d89a9170 100644 --- a/src/main/java/com/squareup/javapoet/TypeVariableName.java +++ b/src/main/java/com/squareup/javapoet/TypeVariableName.java @@ -16,17 +16,13 @@ package com.squareup.javapoet; import java.io.IOException; -import java.lang.reflect.Method; import java.lang.reflect.Type; import java.lang.reflect.TypeVariable; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; -import javax.lang.model.element.NestingKind; -import javax.lang.model.element.TypeElement; -import javax.lang.model.type.DeclaredType; -import javax.lang.model.type.TypeKind; +import javax.lang.model.element.TypeParameterElement; import javax.lang.model.type.TypeMirror; import static com.squareup.javapoet.Util.checkArgument; @@ -80,8 +76,13 @@ public static TypeVariableName get(String name, Type... bounds) { /** Returns type variable equivalent to {@code mirror}. */ public static TypeVariableName get(javax.lang.model.type.TypeVariable mirror) { - String name = mirror.asElement().getSimpleName().toString(); - List boundsMirrors = typeVariableBounds(mirror); + return get((TypeParameterElement) mirror.asElement()); + } + + /** Returns type variable equivalent to {@code element}. */ + public static TypeVariableName get(TypeParameterElement element) { + String name = element.getSimpleName().toString(); + List boundsMirrors = element.getBounds(); List boundsTypeNames = new ArrayList<>(); for (TypeMirror typeMirror : boundsMirrors) { @@ -91,40 +92,6 @@ public static TypeVariableName get(javax.lang.model.type.TypeVariable mirror) { return new TypeVariableName(name, boundsTypeNames); } - /** - * Returns a list of type mirrors representing the unpacked bounds of {@code typeVariable}. This - * is made gnarly by the need to unpack Java 8's new IntersectionType with reflection. We don't - * have that type in Java 7, and {@link TypeVariable}'s array of bounds is sufficient anyway. - */ - @SuppressWarnings("unchecked") // Gross things in support of Java 7 and Java 8. - private static List typeVariableBounds( - javax.lang.model.type.TypeVariable typeVariable) { - TypeMirror upperBound = typeVariable.getUpperBound(); - - // On Java 8, unwrap an intersection type into its component bounds. - if ("INTERSECTION".equals(upperBound.getKind().name())) { - try { - Method method = upperBound.getClass().getMethod("getBounds"); - return (List) method.invoke(upperBound); - } catch (Exception e) { - throw new RuntimeException(e); - } - } - - // On Java 7, intersection types exist but without explicit API. Use a (clumsy) heuristic. - if (upperBound.getKind() == TypeKind.DECLARED) { - TypeElement upperBoundElement = (TypeElement) ((DeclaredType) upperBound).asElement(); - if (upperBoundElement.getNestingKind() == NestingKind.ANONYMOUS) { - List result = new ArrayList<>(); - result.add(upperBoundElement.getSuperclass()); - result.addAll(upperBoundElement.getInterfaces()); - return result; - } - } - - return Collections.singletonList(upperBound); - } - /** Returns type variable equivalent to {@code type}. */ public static TypeVariableName get(TypeVariable type) { return new TypeVariableName(type.getName(), TypeName.list(type.getBounds())); diff --git a/src/test/java/com/squareup/javapoet/AbstractTypesTest.java b/src/test/java/com/squareup/javapoet/AbstractTypesTest.java new file mode 100644 index 000000000..bc37632b2 --- /dev/null +++ b/src/test/java/com/squareup/javapoet/AbstractTypesTest.java @@ -0,0 +1,220 @@ +/* + * Copyright (C) 2014 Google, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.squareup.javapoet; + +import static com.google.common.truth.Truth.assertThat; +import static org.junit.Assert.*; + +import java.io.Serializable; +import java.nio.charset.Charset; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import javax.lang.model.element.TypeElement; +import javax.lang.model.element.TypeParameterElement; +import javax.lang.model.type.DeclaredType; +import javax.lang.model.type.TypeKind; +import javax.lang.model.type.TypeMirror; +import javax.lang.model.type.WildcardType; +import javax.lang.model.util.Elements; +import javax.lang.model.util.Types; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +import com.google.testing.compile.CompilationRule; + +public abstract class AbstractTypesTest { + protected abstract Elements getElements(); + protected abstract Types getTypes(); + + private TypeElement getElement(Class clazz) { + return getElements().getTypeElement(clazz.getCanonicalName()); + } + + private TypeMirror getMirror(Class clazz) { + return getElement(clazz).asType(); + } + + @Test public void getBasicTypeMirror() { + assertThat(TypeName.get(getMirror(Object.class))) + .isEqualTo(ClassName.get(Object.class)); + assertThat(TypeName.get(getMirror(Charset.class))) + .isEqualTo(ClassName.get(Charset.class)); + assertThat(TypeName.get(getMirror(AbstractTypesTest.class))) + .isEqualTo(ClassName.get(AbstractTypesTest.class)); + } + + @Test public void getParameterizedTypeMirror() { + DeclaredType setType = + getTypes().getDeclaredType(getElement(Set.class), getMirror(Object.class)); + assertThat(TypeName.get(setType)) + .isEqualTo(ParameterizedTypeName.get(ClassName.get(Set.class), ClassName.OBJECT)); + } + + static class Parameterized< + Simple, + ExtendsClass extends Number, + ExtendsInterface extends Runnable, + ExtendsTypeVariable extends Simple, + Intersection extends Number & Runnable, + IntersectionOfInterfaces extends Runnable & Serializable> {} + + @Test public void getTypeVariableTypeMirror() { + List typeVariables = + getElement(Parameterized.class).getTypeParameters(); + + // Members of converted types use ClassName and not Class. + ClassName number = ClassName.get(Number.class); + ClassName runnable = ClassName.get(Runnable.class); + ClassName serializable = ClassName.get(Serializable.class); + + assertThat(TypeName.get(typeVariables.get(0).asType())) + .isEqualTo(TypeVariableName.get("Simple")); + assertThat(TypeName.get(typeVariables.get(1).asType())) + .isEqualTo(TypeVariableName.get("ExtendsClass", number)); + assertThat(TypeName.get(typeVariables.get(2).asType())) + .isEqualTo(TypeVariableName.get("ExtendsInterface", runnable)); + assertThat(TypeName.get(typeVariables.get(3).asType())) + .isEqualTo(TypeVariableName.get("ExtendsTypeVariable", TypeVariableName.get("Simple"))); + assertThat(TypeName.get(typeVariables.get(4).asType())) + .isEqualTo(TypeVariableName.get("Intersection", number, runnable)); + assertThat(TypeName.get(typeVariables.get(5).asType())) + .isEqualTo(TypeVariableName.get("IntersectionOfInterfaces", runnable, serializable)); + assertThat(((TypeVariableName) TypeName.get(typeVariables.get(4).asType())).bounds) + .containsExactly(number, runnable); + } + + @Test public void getPrimitiveTypeMirror() { + assertThat(TypeName.get(getTypes().getPrimitiveType(TypeKind.BOOLEAN))) + .isEqualTo(TypeName.BOOLEAN); + assertThat(TypeName.get(getTypes().getPrimitiveType(TypeKind.BYTE))) + .isEqualTo(TypeName.BYTE); + assertThat(TypeName.get(getTypes().getPrimitiveType(TypeKind.SHORT))) + .isEqualTo(TypeName.SHORT); + assertThat(TypeName.get(getTypes().getPrimitiveType(TypeKind.INT))) + .isEqualTo(TypeName.INT); + assertThat(TypeName.get(getTypes().getPrimitiveType(TypeKind.LONG))) + .isEqualTo(TypeName.LONG); + assertThat(TypeName.get(getTypes().getPrimitiveType(TypeKind.CHAR))) + .isEqualTo(TypeName.CHAR); + assertThat(TypeName.get(getTypes().getPrimitiveType(TypeKind.FLOAT))) + .isEqualTo(TypeName.FLOAT); + assertThat(TypeName.get(getTypes().getPrimitiveType(TypeKind.DOUBLE))) + .isEqualTo(TypeName.DOUBLE); + } + + @Test public void getArrayTypeMirror() { + assertThat(TypeName.get(getTypes().getArrayType(getMirror(Object.class)))) + .isEqualTo(ArrayTypeName.of(ClassName.OBJECT)); + } + + @Test public void getVoidTypeMirror() { + assertThat(TypeName.get(getTypes().getNoType(TypeKind.VOID))) + .isEqualTo(TypeName.VOID); + } + + @Test public void getNullTypeMirror() { + try { + TypeName.get(getTypes().getNullType()); + fail(); + } catch (IllegalArgumentException expected) { + } + } + + @Test public void parameterizedType() throws Exception { + ParameterizedTypeName type = ParameterizedTypeName.get(Map.class, String.class, Long.class); + assertThat(type.toString()).isEqualTo("java.util.Map"); + } + + @Test public void arrayType() throws Exception { + ArrayTypeName type = ArrayTypeName.of(String.class); + assertThat(type.toString()).isEqualTo("java.lang.String[]"); + } + + @Test public void wildcardExtendsType() throws Exception { + WildcardTypeName type = WildcardTypeName.subtypeOf(CharSequence.class); + assertThat(type.toString()).isEqualTo("? extends java.lang.CharSequence"); + } + + @Test public void wildcardExtendsObject() throws Exception { + WildcardTypeName type = WildcardTypeName.subtypeOf(Object.class); + assertThat(type.toString()).isEqualTo("?"); + } + + @Test public void wildcardSuperType() throws Exception { + WildcardTypeName type = WildcardTypeName.supertypeOf(String.class); + assertThat(type.toString()).isEqualTo("? super java.lang.String"); + } + + @Test public void wildcardMirrorNoBounds() throws Exception { + WildcardType wildcard = getTypes().getWildcardType(null, null); + TypeName type = TypeName.get(wildcard); + assertThat(type.toString()).isEqualTo("?"); + } + + @Test public void wildcardMirrorExtendsType() throws Exception { + Types types = getTypes(); + Elements elements = getElements(); + TypeMirror charSequence = elements.getTypeElement(CharSequence.class.getName()).asType(); + WildcardType wildcard = types.getWildcardType(charSequence, null); + TypeName type = TypeName.get(wildcard); + assertThat(type.toString()).isEqualTo("? extends java.lang.CharSequence"); + } + + @Test public void wildcardMirrorSuperType() throws Exception { + Types types = getTypes(); + Elements elements = getElements(); + TypeMirror string = elements.getTypeElement(String.class.getName()).asType(); + WildcardType wildcard = types.getWildcardType(null, string); + TypeName type = TypeName.get(wildcard); + assertThat(type.toString()).isEqualTo("? super java.lang.String"); + } + + @Test public void typeVariable() throws Exception { + TypeVariableName type = TypeVariableName.get("T", CharSequence.class); + assertThat(type.toString()).isEqualTo("T"); // (Bounds are only emitted in declaration.) + } + + @Test public void box() throws Exception { + assertThat(TypeName.INT.box()).isEqualTo(ClassName.get(Integer.class)); + assertThat(TypeName.VOID.box()).isEqualTo(ClassName.get(Void.class)); + assertThat(ClassName.get(Integer.class).box()).isEqualTo(ClassName.get(Integer.class)); + assertThat(ClassName.get(Void.class).box()).isEqualTo(ClassName.get(Void.class)); + assertThat(TypeName.OBJECT.box()).isEqualTo(TypeName.OBJECT); + assertThat(ClassName.get(String.class).box()).isEqualTo(ClassName.get(String.class)); + } + + @Test public void unbox() throws Exception { + assertThat(TypeName.INT).isEqualTo(TypeName.INT.unbox()); + assertThat(TypeName.VOID).isEqualTo(TypeName.VOID.unbox()); + assertThat(ClassName.get(Integer.class).unbox()).isEqualTo(TypeName.INT.unbox()); + assertThat(ClassName.get(Void.class).unbox()).isEqualTo(TypeName.VOID.unbox()); + try { + TypeName.OBJECT.unbox(); + fail(); + } catch (UnsupportedOperationException expected) { + } + try { + ClassName.get(String.class).unbox(); + fail(); + } catch (UnsupportedOperationException expected) { + } + } +} diff --git a/src/test/java/com/squareup/javapoet/TypesEclipseTest.java b/src/test/java/com/squareup/javapoet/TypesEclipseTest.java new file mode 100644 index 000000000..96b846b22 --- /dev/null +++ b/src/test/java/com/squareup/javapoet/TypesEclipseTest.java @@ -0,0 +1,158 @@ +/* + * Copyright (C) 2014 Google, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.squareup.javapoet; + +import static com.google.common.base.Charsets.*; +import static com.google.common.base.Preconditions.*; + +import java.util.Locale; +import java.util.Set; +import java.util.concurrent.atomic.AtomicReference; + +import javax.annotation.processing.AbstractProcessor; +import javax.annotation.processing.ProcessingEnvironment; +import javax.annotation.processing.Processor; +import javax.annotation.processing.RoundEnvironment; +import javax.lang.model.SourceVersion; +import javax.lang.model.element.TypeElement; +import javax.lang.model.util.Elements; +import javax.lang.model.util.Types; +import javax.tools.DiagnosticCollector; +import javax.tools.JavaCompiler; +import javax.tools.JavaFileManager; +import javax.tools.JavaFileObject; + +import org.eclipse.jdt.internal.compiler.tool.EclipseCompiler; +import org.junit.Rule; +import org.junit.rules.TestRule; +import org.junit.runner.Description; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; +import org.junit.runners.model.Statement; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableSet; + +@RunWith(JUnit4.class) +public final class TypesEclipseTest extends AbstractTypesTest { + /** + * A {@link JUnit4} {@link Rule} that executes tests such that a instances of {@link Elements} and + * {@link Types} are available during execution. + * + *

To use this rule in a test, just add the following field:

   {@code
+   *   @Rule public CompilationRule compilationRule = new CompilationRule();}
+   *
+   * @author Gregory Kick
+   */
+  public static final class CompilationRule implements TestRule {
+    private Elements elements;
+    private Types types;
+
+    @Override
+    public Statement apply(final Statement base, Description description) {
+      return new Statement() {
+        @Override public void evaluate() throws Throwable {
+          final AtomicReference thrown = new AtomicReference();
+          boolean successful = compile(ImmutableList.of(new AbstractProcessor() {
+            @Override
+            public SourceVersion getSupportedSourceVersion() {
+              return SourceVersion.latest();
+            }
+
+            @Override
+            public Set getSupportedAnnotationTypes() {
+              return ImmutableSet.of("*");
+            }
+
+            @Override
+            public synchronized void init(ProcessingEnvironment processingEnv) {
+              super.init(processingEnv);
+              elements = processingEnv.getElementUtils();
+              types = processingEnv.getTypeUtils();
+            }
+
+            @Override
+            public boolean process(Set annotations,
+                RoundEnvironment roundEnv) {
+              // just run the test on the last round after compilation is over
+              if (roundEnv.processingOver()) {
+                try {
+                  base.evaluate();
+                } catch (Throwable e) {
+                  thrown.set(e);
+                }
+              }
+              return false;
+            }
+          }));
+          checkState(successful);
+          Throwable t = thrown.get();
+          if (t != null) {
+            throw t;
+          }
+        }
+      };
+    }
+
+    /**
+     * Returns the {@link Elements} instance associated with the current execution of the rule.
+     *
+     * @throws IllegalStateException if this method is invoked outside the execution of the rule.
+     */
+    public Elements getElements() {
+      checkState(elements != null, "Not running within the rule");
+      return elements;
+    }
+
+    /**
+     * Returns the {@link Types} instance associated with the current execution of the rule.
+     *
+     * @throws IllegalStateException if this method is invoked outside the execution of the rule.
+     */
+    public Types getTypes() {
+      checkState(elements != null, "Not running within the rule");
+      return types;
+    }
+
+    static private boolean compile(Iterable processors) {
+      JavaCompiler compiler = new EclipseCompiler();
+      DiagnosticCollector diagnosticCollector =
+          new DiagnosticCollector();
+      JavaFileManager fileManager = compiler.getStandardFileManager(diagnosticCollector, Locale.getDefault(), UTF_8);
+      JavaCompiler.CompilationTask task = compiler.getTask(
+          null,
+          fileManager,
+          diagnosticCollector,
+          ImmutableSet.of(),
+          ImmutableSet.of(TypesEclipseTest.class.getCanonicalName()),
+          ImmutableSet.of());
+      task.setProcessors(processors);
+      return task.call();
+    }
+  }
+
+  @Rule public final CompilationRule compilation = new CompilationRule();
+
+  @Override
+  protected Elements getElements() {
+    return compilation.getElements();
+  }
+
+  @Override
+  protected Types getTypes() {
+    return compilation.getTypes();
+  }
+}
diff --git a/src/test/java/com/squareup/javapoet/TypesTest.java b/src/test/java/com/squareup/javapoet/TypesTest.java
index 6c11c9693..2455ae585 100644
--- a/src/test/java/com/squareup/javapoet/TypesTest.java
+++ b/src/test/java/com/squareup/javapoet/TypesTest.java
@@ -15,203 +15,26 @@
  */
 package com.squareup.javapoet;
 
-import com.google.testing.compile.CompilationRule;
-import java.io.Serializable;
-import java.nio.charset.Charset;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import javax.lang.model.element.TypeElement;
-import javax.lang.model.element.TypeParameterElement;
-import javax.lang.model.type.DeclaredType;
-import javax.lang.model.type.TypeKind;
-import javax.lang.model.type.TypeMirror;
-import javax.lang.model.type.WildcardType;
 import javax.lang.model.util.Elements;
 import javax.lang.model.util.Types;
+
 import org.junit.Rule;
-import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.JUnit4;
 
-import static com.google.common.truth.Truth.assertThat;
-import static org.junit.Assert.fail;
+import com.google.testing.compile.CompilationRule;
 
 @RunWith(JUnit4.class)
-public final class TypesTest {
+public final class TypesTest extends AbstractTypesTest {
   @Rule public final CompilationRule compilation = new CompilationRule();
 
-  private TypeElement getElement(Class clazz) {
-    return compilation.getElements().getTypeElement(clazz.getCanonicalName());
-  }
-
-  private TypeMirror getMirror(Class clazz) {
-    return getElement(clazz).asType();
-  }
-
-  @Test public void getBasicTypeMirror() {
-    assertThat(TypeName.get(getMirror(Object.class)))
-        .isEqualTo(ClassName.get(Object.class));
-    assertThat(TypeName.get(getMirror(Charset.class)))
-        .isEqualTo(ClassName.get(Charset.class));
-    assertThat(TypeName.get(getMirror(TypesTest.class)))
-        .isEqualTo(ClassName.get(TypesTest.class));
-  }
-
-  @Test public void getParameterizedTypeMirror() {
-    DeclaredType setType =
-        compilation.getTypes().getDeclaredType(getElement(Set.class), getMirror(Object.class));
-    assertThat(TypeName.get(setType))
-        .isEqualTo(ParameterizedTypeName.get(ClassName.get(Set.class), ClassName.OBJECT));
-  }
-
-  static class Parameterized<
-      Simple,
-      ExtendsClass extends Number,
-      ExtendsInterface extends Runnable,
-      ExtendsTypeVariable extends Simple,
-      Intersection extends Number & Runnable,
-      IntersectionOfInterfaces extends Runnable & Serializable> {}
-
-  @Test public void getTypeVariableTypeMirror() {
-    List typeVariables =
-        getElement(Parameterized.class).getTypeParameters();
-
-    // Members of converted types use ClassName and not Class.
-    ClassName number = ClassName.get(Number.class);
-    ClassName runnable = ClassName.get(Runnable.class);
-    ClassName serializable = ClassName.get(Serializable.class);
-
-    assertThat(TypeName.get(typeVariables.get(0).asType()))
-        .isEqualTo(TypeVariableName.get("Simple"));
-    assertThat(TypeName.get(typeVariables.get(1).asType()))
-        .isEqualTo(TypeVariableName.get("ExtendsClass", number));
-    assertThat(TypeName.get(typeVariables.get(2).asType()))
-        .isEqualTo(TypeVariableName.get("ExtendsInterface", runnable));
-    assertThat(TypeName.get(typeVariables.get(3).asType()))
-        .isEqualTo(TypeVariableName.get("ExtendsTypeVariable", TypeVariableName.get("Simple")));
-    assertThat(TypeName.get(typeVariables.get(4).asType()))
-        .isEqualTo(TypeVariableName.get("Intersection", number, runnable));
-    assertThat(TypeName.get(typeVariables.get(5).asType()))
-        .isEqualTo(TypeVariableName.get("IntersectionOfInterfaces", runnable, serializable));
-    assertThat(((TypeVariableName) TypeName.get(typeVariables.get(4).asType())).bounds)
-        .containsExactly(number, runnable);
-  }
-
-  @Test public void getPrimitiveTypeMirror() {
-    assertThat(TypeName.get(compilation.getTypes().getPrimitiveType(TypeKind.BOOLEAN)))
-        .isEqualTo(TypeName.BOOLEAN);
-    assertThat(TypeName.get(compilation.getTypes().getPrimitiveType(TypeKind.BYTE)))
-        .isEqualTo(TypeName.BYTE);
-    assertThat(TypeName.get(compilation.getTypes().getPrimitiveType(TypeKind.SHORT)))
-        .isEqualTo(TypeName.SHORT);
-    assertThat(TypeName.get(compilation.getTypes().getPrimitiveType(TypeKind.INT)))
-        .isEqualTo(TypeName.INT);
-    assertThat(TypeName.get(compilation.getTypes().getPrimitiveType(TypeKind.LONG)))
-        .isEqualTo(TypeName.LONG);
-    assertThat(TypeName.get(compilation.getTypes().getPrimitiveType(TypeKind.CHAR)))
-        .isEqualTo(TypeName.CHAR);
-    assertThat(TypeName.get(compilation.getTypes().getPrimitiveType(TypeKind.FLOAT)))
-        .isEqualTo(TypeName.FLOAT);
-    assertThat(TypeName.get(compilation.getTypes().getPrimitiveType(TypeKind.DOUBLE)))
-        .isEqualTo(TypeName.DOUBLE);
-  }
-
-  @Test public void getArrayTypeMirror() {
-    assertThat(TypeName.get(compilation.getTypes().getArrayType(getMirror(Object.class))))
-        .isEqualTo(ArrayTypeName.of(ClassName.OBJECT));
-  }
-
-  @Test public void getVoidTypeMirror() {
-    assertThat(TypeName.get(compilation.getTypes().getNoType(TypeKind.VOID)))
-        .isEqualTo(TypeName.VOID);
-  }
-
-  @Test public void getNullTypeMirror() {
-    try {
-      TypeName.get(compilation.getTypes().getNullType());
-      fail();
-    } catch (IllegalArgumentException expected) {
-    }
-  }
-
-  @Test public void parameterizedType() throws Exception {
-    ParameterizedTypeName type = ParameterizedTypeName.get(Map.class, String.class, Long.class);
-    assertThat(type.toString()).isEqualTo("java.util.Map");
-  }
-
-  @Test public void arrayType() throws Exception {
-    ArrayTypeName type = ArrayTypeName.of(String.class);
-    assertThat(type.toString()).isEqualTo("java.lang.String[]");
-  }
-
-  @Test public void wildcardExtendsType() throws Exception {
-    WildcardTypeName type = WildcardTypeName.subtypeOf(CharSequence.class);
-    assertThat(type.toString()).isEqualTo("? extends java.lang.CharSequence");
-  }
-
-  @Test public void wildcardExtendsObject() throws Exception {
-    WildcardTypeName type = WildcardTypeName.subtypeOf(Object.class);
-    assertThat(type.toString()).isEqualTo("?");
-  }
-
-  @Test public void wildcardSuperType() throws Exception {
-    WildcardTypeName type = WildcardTypeName.supertypeOf(String.class);
-    assertThat(type.toString()).isEqualTo("? super java.lang.String");
-  }
-
-  @Test public void wildcardMirrorNoBounds() throws Exception {
-    WildcardType wildcard = compilation.getTypes().getWildcardType(null, null);
-    TypeName type = TypeName.get(wildcard);
-    assertThat(type.toString()).isEqualTo("?");
-  }
-
-  @Test public void wildcardMirrorExtendsType() throws Exception {
-    Types types = compilation.getTypes();
-    Elements elements = compilation.getElements();
-    TypeMirror charSequence = elements.getTypeElement(CharSequence.class.getName()).asType();
-    WildcardType wildcard = types.getWildcardType(charSequence, null);
-    TypeName type = TypeName.get(wildcard);
-    assertThat(type.toString()).isEqualTo("? extends java.lang.CharSequence");
-  }
-
-  @Test public void wildcardMirrorSuperType() throws Exception {
-    Types types = compilation.getTypes();
-    Elements elements = compilation.getElements();
-    TypeMirror string = elements.getTypeElement(String.class.getName()).asType();
-    WildcardType wildcard = types.getWildcardType(null, string);
-    TypeName type = TypeName.get(wildcard);
-    assertThat(type.toString()).isEqualTo("? super java.lang.String");
-  }
-
-  @Test public void typeVariable() throws Exception {
-    TypeVariableName type = TypeVariableName.get("T", CharSequence.class);
-    assertThat(type.toString()).isEqualTo("T"); // (Bounds are only emitted in declaration.)
-  }
-
-  @Test public void box() throws Exception {
-    assertThat(TypeName.INT.box()).isEqualTo(ClassName.get(Integer.class));
-    assertThat(TypeName.VOID.box()).isEqualTo(ClassName.get(Void.class));
-    assertThat(ClassName.get(Integer.class).box()).isEqualTo(ClassName.get(Integer.class));
-    assertThat(ClassName.get(Void.class).box()).isEqualTo(ClassName.get(Void.class));
-    assertThat(TypeName.OBJECT.box()).isEqualTo(TypeName.OBJECT);
-    assertThat(ClassName.get(String.class).box()).isEqualTo(ClassName.get(String.class));
+  @Override
+  protected Elements getElements() {
+    return compilation.getElements();
   }
 
-  @Test public void unbox() throws Exception {
-    assertThat(TypeName.INT).isEqualTo(TypeName.INT.unbox());
-    assertThat(TypeName.VOID).isEqualTo(TypeName.VOID.unbox());
-    assertThat(ClassName.get(Integer.class).unbox()).isEqualTo(TypeName.INT.unbox());
-    assertThat(ClassName.get(Void.class).unbox()).isEqualTo(TypeName.VOID.unbox());
-    try {
-      TypeName.OBJECT.unbox();
-      fail();
-    } catch (UnsupportedOperationException expected) {
-    }
-    try {
-      ClassName.get(String.class).unbox();
-      fail();
-    } catch (UnsupportedOperationException expected) {
-    }
+  @Override
+  protected Types getTypes() {
+    return compilation.getTypes();
   }
 }