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 setUp()49 @Before public void setUp() { 50 elements = compilation.getElements(); 51 types = compilation.getTypes(); 52 } 53 getElement(Class<?> clazz)54 private TypeElement getElement(Class<?> clazz) { 55 return elements.getTypeElement(clazz.getCanonicalName()); 56 } 57 findFirst(Collection<ExecutableElement> elements, String name)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 nullAnnotationsAddition()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 nullTypeVariablesAddition()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 nullParametersAddition()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 nullExceptionsAddition()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 { everything( @ullable String thing, List<? extends T> things)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 { run(R param)113 <T, R, V extends Throwable> T run(R param) throws V { 114 return null; 115 } 116 } 117 118 abstract static class HasAnnotation { toString()119 @Override public abstract String toString(); 120 } 121 122 interface Throws<R extends RuntimeException> { fail()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 { method()134 void method() { 135 } 136 } 137 138 abstract static class InvalidOverrideMethods { finalMethod()139 final void finalMethod() { 140 } 141 privateMethod()142 private void privateMethod() { 143 } 144 staticMethod()145 static void staticMethod() { 146 } 147 } 148 overrideEverything()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 overrideGenerics()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 overrideDoesNotCopyOverrideAnnotation()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 overrideDoesNotCopyDefaultModifier()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 overrideExtendsOthersWorksWithActualTypeParameters()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 overrideFinalClassMethod()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 overrideInvalidModifiers()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 equalsAndHashCode()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 duplicateExceptionsIgnored()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 nullIsNotAValidMethodName()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 addModifiersVarargsShouldNotBeNull()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