Home | History | Annotate | Download | only in javapoet
      1 /*
      2  * Copyright (C) 2015 Square, 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 com.google.testing.compile.CompilationRule;
     19 import java.io.Closeable;
     20 import java.io.IOException;
     21 import java.lang.annotation.ElementType;
     22 import java.lang.annotation.Target;
     23 import java.util.Arrays;
     24 import java.util.Collection;
     25 import java.util.List;
     26 import java.util.concurrent.Callable;
     27 import java.util.concurrent.TimeoutException;
     28 import javax.lang.model.element.ExecutableElement;
     29 import javax.lang.model.element.Modifier;
     30 import javax.lang.model.element.TypeElement;
     31 import javax.lang.model.type.DeclaredType;
     32 import javax.lang.model.util.Elements;
     33 import javax.lang.model.util.Types;
     34 import org.junit.Before;
     35 import org.junit.Rule;
     36 import org.junit.Test;
     37 
     38 import static com.google.common.collect.Iterables.getOnlyElement;
     39 import static com.google.common.truth.Truth.assertThat;
     40 import static javax.lang.model.util.ElementFilter.methodsIn;
     41 import static org.junit.Assert.fail;
     42 
     43 public final class MethodSpecTest {
     44   @Rule public final CompilationRule compilation = new CompilationRule();
     45 
     46   private Elements elements;
     47   private Types types;
     48 
     49   @Before public void setUp() {
     50     elements = compilation.getElements();
     51     types = compilation.getTypes();
     52   }
     53 
     54   private TypeElement getElement(Class<?> clazz) {
     55     return elements.getTypeElement(clazz.getCanonicalName());
     56   }
     57 
     58   private ExecutableElement findFirst(Collection<ExecutableElement> elements, String name) {
     59     for (ExecutableElement executableElement : elements) {
     60       if (executableElement.getSimpleName().toString().equals(name)) {
     61         return executableElement;
     62       }
     63     }
     64     throw new IllegalArgumentException(name + " not found in " + elements);
     65   }
     66 
     67   @Test public void nullAnnotationsAddition() {
     68     try {
     69       MethodSpec.methodBuilder("doSomething").addAnnotations(null);
     70       fail();
     71     } catch (IllegalArgumentException expected) {
     72       assertThat(expected).hasMessageThat().isEqualTo("annotationSpecs == null");
     73     }
     74   }
     75 
     76   @Test public void nullTypeVariablesAddition() {
     77     try {
     78       MethodSpec.methodBuilder("doSomething").addTypeVariables(null);
     79       fail();
     80     } catch (IllegalArgumentException expected) {
     81       assertThat(expected).hasMessageThat().isEqualTo("typeVariables == null");
     82     }
     83   }
     84 
     85   @Test public void nullParametersAddition() {
     86     try {
     87       MethodSpec.methodBuilder("doSomething").addParameters(null);
     88       fail();
     89     } catch (IllegalArgumentException expected) {
     90       assertThat(expected).hasMessageThat().isEqualTo("parameterSpecs == null");
     91     }
     92   }
     93 
     94   @Test public void nullExceptionsAddition() {
     95     try {
     96       MethodSpec.methodBuilder("doSomething").addExceptions(null);
     97       fail();
     98     } catch (IllegalArgumentException expected) {
     99       assertThat(expected).hasMessageThat().isEqualTo("exceptions == null");
    100     }
    101   }
    102 
    103   @Target(ElementType.PARAMETER)
    104   @interface Nullable {
    105   }
    106 
    107   abstract static class Everything {
    108     @Deprecated protected abstract <T extends Runnable & Closeable> Runnable everything(
    109         @Nullable String thing, List<? extends T> things) throws IOException, SecurityException;
    110   }
    111 
    112   abstract static class Generics {
    113     <T, R, V extends Throwable> T run(R param) throws V {
    114       return null;
    115     }
    116   }
    117 
    118   abstract static class HasAnnotation {
    119     @Override public abstract String toString();
    120   }
    121 
    122   interface Throws<R extends RuntimeException> {
    123     void fail() throws R;
    124   }
    125 
    126   interface ExtendsOthers extends Callable<Integer>, Comparable<ExtendsOthers>,
    127       Throws<IllegalStateException> {
    128   }
    129 
    130   interface ExtendsIterableWithDefaultMethods extends Iterable<Object> {
    131   }
    132 
    133   final class FinalClass {
    134     void method() {
    135     }
    136   }
    137 
    138   abstract static class InvalidOverrideMethods {
    139     final void finalMethod() {
    140     }
    141 
    142     private void privateMethod() {
    143     }
    144 
    145     static void staticMethod() {
    146     }
    147   }
    148 
    149   @Test public void overrideEverything() {
    150     TypeElement classElement = getElement(Everything.class);
    151     ExecutableElement methodElement = getOnlyElement(methodsIn(classElement.getEnclosedElements()));
    152     MethodSpec method = MethodSpec.overriding(methodElement).build();
    153     assertThat(method.toString()).isEqualTo(""
    154         + "@java.lang.Override\n"
    155         + "protected <T extends java.lang.Runnable & java.io.Closeable> java.lang.Runnable "
    156         + "everything(\n"
    157         + "    java.lang.String arg0, java.util.List<? extends T> arg1) throws java.io.IOException,\n"
    158         + "    java.lang.SecurityException {\n"
    159         + "}\n");
    160   }
    161 
    162   @Test public void overrideGenerics() {
    163     TypeElement classElement = getElement(Generics.class);
    164     ExecutableElement methodElement = getOnlyElement(methodsIn(classElement.getEnclosedElements()));
    165     MethodSpec method = MethodSpec.overriding(methodElement)
    166         .addStatement("return null")
    167         .build();
    168     assertThat(method.toString()).isEqualTo(""
    169         + "@java.lang.Override\n"
    170         + "<T, R, V extends java.lang.Throwable> T run(R param) throws V {\n"
    171         + "  return null;\n"
    172         + "}\n");
    173   }
    174 
    175   @Test public void overrideDoesNotCopyOverrideAnnotation() {
    176     TypeElement classElement = getElement(HasAnnotation.class);
    177     ExecutableElement exec = getOnlyElement(methodsIn(classElement.getEnclosedElements()));
    178     MethodSpec method = MethodSpec.overriding(exec).build();
    179     assertThat(method.toString()).isEqualTo(""
    180         + "@java.lang.Override\n"
    181         + "public java.lang.String toString() {\n"
    182         + "}\n");
    183   }
    184 
    185   @Test public void overrideDoesNotCopyDefaultModifier() {
    186     TypeElement classElement = getElement(ExtendsIterableWithDefaultMethods.class);
    187     DeclaredType classType = (DeclaredType) classElement.asType();
    188     List<ExecutableElement> methods = methodsIn(elements.getAllMembers(classElement));
    189     ExecutableElement exec = findFirst(methods, "spliterator");
    190     MethodSpec method = MethodSpec.overriding(exec, classType, types).build();
    191     assertThat(method.toString()).isEqualTo(""
    192         + "@java.lang.Override\n"
    193         + "public java.util.Spliterator<java.lang.Object> spliterator() {\n"
    194         + "}\n");
    195   }
    196 
    197   @Test public void overrideExtendsOthersWorksWithActualTypeParameters() {
    198     TypeElement classElement = getElement(ExtendsOthers.class);
    199     DeclaredType classType = (DeclaredType) classElement.asType();
    200     List<ExecutableElement> methods = methodsIn(elements.getAllMembers(classElement));
    201     ExecutableElement exec = findFirst(methods, "call");
    202     MethodSpec method = MethodSpec.overriding(exec, classType, types).build();
    203     assertThat(method.toString()).isEqualTo(""
    204         + "@java.lang.Override\n"
    205         + "public java.lang.Integer call() throws java.lang.Exception {\n"
    206         + "}\n");
    207     exec = findFirst(methods, "compareTo");
    208     method = MethodSpec.overriding(exec, classType, types).build();
    209     assertThat(method.toString()).isEqualTo(""
    210         + "@java.lang.Override\n"
    211         + "public int compareTo(" + ExtendsOthers.class.getCanonicalName() + " arg0) {\n"
    212         + "}\n");
    213     exec = findFirst(methods, "fail");
    214     method = MethodSpec.overriding(exec, classType, types).build();
    215     assertThat(method.toString()).isEqualTo(""
    216         + "@java.lang.Override\n"
    217         + "public void fail() throws java.lang.IllegalStateException {\n"
    218         + "}\n");
    219   }
    220 
    221   @Test public void overrideFinalClassMethod() {
    222     TypeElement classElement = getElement(FinalClass.class);
    223     List<ExecutableElement> methods = methodsIn(elements.getAllMembers(classElement));
    224     try {
    225       MethodSpec.overriding(findFirst(methods, "method"));
    226       fail();
    227     } catch (IllegalArgumentException expected) {
    228       assertThat(expected).hasMessageThat().isEqualTo(
    229           "Cannot override method on final class com.squareup.javapoet.MethodSpecTest.FinalClass");
    230     }
    231   }
    232 
    233   @Test public void overrideInvalidModifiers() {
    234     TypeElement classElement = getElement(InvalidOverrideMethods.class);
    235     List<ExecutableElement> methods = methodsIn(elements.getAllMembers(classElement));
    236     try {
    237       MethodSpec.overriding(findFirst(methods, "finalMethod"));
    238       fail();
    239     } catch (IllegalArgumentException expected) {
    240       assertThat(expected).hasMessageThat().isEqualTo("cannot override method with modifiers: [final]");
    241     }
    242     try {
    243       MethodSpec.overriding(findFirst(methods, "privateMethod"));
    244       fail();
    245     } catch (IllegalArgumentException expected) {
    246       assertThat(expected).hasMessageThat().isEqualTo("cannot override method with modifiers: [private]");
    247     }
    248     try {
    249       MethodSpec.overriding(findFirst(methods, "staticMethod"));
    250       fail();
    251     } catch (IllegalArgumentException expected) {
    252       assertThat(expected).hasMessageThat().isEqualTo("cannot override method with modifiers: [static]");
    253     }
    254   }
    255 
    256   @Test public void equalsAndHashCode() {
    257     MethodSpec a = MethodSpec.constructorBuilder().build();
    258     MethodSpec b = MethodSpec.constructorBuilder().build();
    259     assertThat(a.equals(b)).isTrue();
    260     assertThat(a.hashCode()).isEqualTo(b.hashCode());
    261     a = MethodSpec.methodBuilder("taco").build();
    262     b = MethodSpec.methodBuilder("taco").build();
    263     assertThat(a.equals(b)).isTrue();
    264     assertThat(a.hashCode()).isEqualTo(b.hashCode());
    265     TypeElement classElement = getElement(Everything.class);
    266     ExecutableElement methodElement = getOnlyElement(methodsIn(classElement.getEnclosedElements()));
    267     a = MethodSpec.overriding(methodElement).build();
    268     b = MethodSpec.overriding(methodElement).build();
    269     assertThat(a.equals(b)).isTrue();
    270     assertThat(a.hashCode()).isEqualTo(b.hashCode());
    271   }
    272 
    273   @Test public void duplicateExceptionsIgnored() {
    274     ClassName ioException = ClassName.get(IOException.class);
    275     ClassName timeoutException = ClassName.get(TimeoutException.class);
    276     MethodSpec methodSpec = MethodSpec.methodBuilder("duplicateExceptions")
    277       .addException(ioException)
    278       .addException(timeoutException)
    279       .addException(timeoutException)
    280       .addException(ioException)
    281       .build();
    282     assertThat(methodSpec.exceptions).isEqualTo(Arrays.asList(ioException, timeoutException));
    283     assertThat(methodSpec.toBuilder().addException(ioException).build().exceptions)
    284       .isEqualTo(Arrays.asList(ioException, timeoutException));
    285   }
    286 
    287   @Test public void nullIsNotAValidMethodName() {
    288     try {
    289       MethodSpec.methodBuilder(null);
    290       fail("NullPointerException expected");
    291     } catch (NullPointerException e) {
    292       assertThat(e.getMessage()).isEqualTo("name == null");
    293     }
    294   }
    295 
    296   @Test public void addModifiersVarargsShouldNotBeNull() {
    297     try {
    298       MethodSpec.methodBuilder("taco")
    299               .addModifiers((Modifier[]) null);
    300       fail("NullPointerException expected");
    301     } catch (NullPointerException e) {
    302       assertThat(e.getMessage()).isEqualTo("modifiers == null");
    303     }
    304   }
    305 }
    306