1 /* 2 * Copyright 2012 Google LLC 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.google.auto.value; 17 18 import static com.google.common.truth.Truth.assertThat; 19 import static com.google.common.truth.Truth.assertWithMessage; 20 import static com.google.common.truth.Truth8.assertThat; 21 import static com.google.testing.compile.CompilationSubject.assertThat; 22 import static org.junit.Assert.assertThrows; 23 import static org.junit.Assume.assumeTrue; 24 25 import com.google.common.collect.ImmutableList; 26 import com.google.common.collect.Iterables; 27 import com.google.common.testing.EqualsTester; 28 import com.google.testing.compile.Compilation; 29 import com.google.testing.compile.Compiler; 30 import com.google.testing.compile.JavaFileObjects; 31 import java.lang.annotation.Annotation; 32 import java.lang.annotation.ElementType; 33 import java.lang.annotation.Retention; 34 import java.lang.annotation.RetentionPolicy; 35 import java.lang.annotation.Target; 36 import java.lang.reflect.AnnotatedType; 37 import java.lang.reflect.Constructor; 38 import java.lang.reflect.Method; 39 import java.lang.reflect.TypeVariable; 40 import java.util.Arrays; 41 import java.util.List; 42 import java.util.Optional; 43 import java.util.Set; 44 import java.util.function.Predicate; 45 import javax.annotation.processing.AbstractProcessor; 46 import javax.annotation.processing.RoundEnvironment; 47 import javax.annotation.processing.SupportedAnnotationTypes; 48 import javax.annotation.processing.SupportedSourceVersion; 49 import javax.lang.model.SourceVersion; 50 import javax.lang.model.element.AnnotationMirror; 51 import javax.lang.model.element.ExecutableElement; 52 import javax.lang.model.element.TypeElement; 53 import javax.lang.model.util.ElementFilter; 54 import javax.tools.Diagnostic; 55 import javax.tools.JavaFileObject; 56 import org.junit.BeforeClass; 57 import org.junit.Test; 58 import org.junit.runner.RunWith; 59 import org.junit.runners.JUnit4; 60 61 /** 62 * Tests for constructs new in Java 8, such as type annotations. 63 * 64 * @author Till Brychcy 65 * @author emcmanus@google.com (Éamonn McManus) 66 */ 67 @RunWith(JUnit4.class) 68 public class AutoValueJava8Test { 69 private static boolean javacHandlesTypeAnnotationsCorrectly; 70 71 // This is appalling. Some versions of javac do not correctly report annotations on type uses in 72 // certain cases, for example on type variables or arrays. Since some of the tests here are for 73 // exactly that, we compile a test program with a test annotation processor to see whether we 74 // might be in the presence of such a javac, and if so we skip the tests that would fail because 75 // of the bug. This isn't completely sound because we can't be entirely sure that the javac that 76 // Compiler.javac() finds is the same as the javac that was used to build this test (and therefore 77 // run AutoValueProcessor), but it's better than just ignoring the tests outright. 78 @BeforeClass setUpClass()79 public static void setUpClass() { 80 JavaFileObject javaFileObject = 81 JavaFileObjects.forSourceLines( 82 "Test", 83 "import java.lang.annotation.ElementType;", 84 "import java.lang.annotation.Retention;", 85 "import java.lang.annotation.RetentionPolicy;", 86 "import java.lang.annotation.Target;", 87 "public abstract class Test<T> {", 88 " @Retention(RetentionPolicy.RUNTIME)", 89 " @Target(ElementType.TYPE_USE)", 90 " public @interface Nullable {}", 91 "", 92 " public abstract @Nullable T t();", 93 "}"); 94 Compilation compilation = 95 Compiler.javac().withProcessors(new BugTestProcessor()).compile(javaFileObject); 96 if (compilation.errors().isEmpty()) { 97 javacHandlesTypeAnnotationsCorrectly = true; 98 } else { 99 assertThat(compilation).hadErrorCount(1); 100 assertThat(compilation).hadErrorContaining(JAVAC_HAS_BUG_ERROR); 101 } 102 } 103 104 private static final String JAVAC_HAS_BUG_ERROR = "javac has the type-annotation bug"; 105 106 @SupportedAnnotationTypes("*") 107 @SupportedSourceVersion(SourceVersion.RELEASE_8) 108 private static class BugTestProcessor extends AbstractProcessor { 109 @Override process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv)110 public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) { 111 if (roundEnv.processingOver()) { 112 test(); 113 } 114 return false; 115 } 116 test()117 private void test() { 118 TypeElement test = processingEnv.getElementUtils().getTypeElement("Test"); 119 List<ExecutableElement> methods = ElementFilter.methodsIn(test.getEnclosedElements()); 120 ExecutableElement t = Iterables.getOnlyElement(methods); 121 assertThat(t.getSimpleName().toString()).isEqualTo("t"); 122 List<? extends AnnotationMirror> typeAnnotations = t.getReturnType().getAnnotationMirrors(); 123 if (typeAnnotations.isEmpty()) { 124 processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, JAVAC_HAS_BUG_ERROR); 125 return; 126 } 127 AnnotationMirror typeAnnotation = Iterables.getOnlyElement(typeAnnotations); 128 assertThat(typeAnnotation.getAnnotationType().toString()).contains("Nullable"); 129 } 130 } 131 132 @Retention(RetentionPolicy.RUNTIME) 133 @Target(ElementType.TYPE_USE) 134 public @interface Nullable {} 135 136 @AutoValue 137 abstract static class NullableProperties { nullableString()138 abstract @Nullable String nullableString(); 139 randomInt()140 abstract int randomInt(); 141 create(@ullable String nullableString, int randomInt)142 static NullableProperties create(@Nullable String nullableString, int randomInt) { 143 return new AutoValue_AutoValueJava8Test_NullableProperties(nullableString, randomInt); 144 } 145 } 146 147 @Test testNullablePropertiesCanBeNull()148 public void testNullablePropertiesCanBeNull() { 149 NullableProperties instance = NullableProperties.create(null, 23); 150 assertThat(instance.nullableString()).isNull(); 151 assertThat(instance.randomInt()).isEqualTo(23); 152 assertThat(instance.toString()) 153 .isEqualTo("NullableProperties{nullableString=null, randomInt=23}"); 154 } 155 156 @AutoAnnotation nullable()157 static Nullable nullable() { 158 return new AutoAnnotation_AutoValueJava8Test_nullable(); 159 } 160 161 @Test testNullablePropertyImplementationIsNullable()162 public void testNullablePropertyImplementationIsNullable() throws NoSuchMethodException { 163 Method method = 164 AutoValue_AutoValueJava8Test_NullableProperties.class.getDeclaredMethod("nullableString"); 165 assertThat(method.getAnnotatedReturnType().getAnnotations()) 166 .asList() 167 .contains(nullable()); 168 } 169 170 @Test testNullablePropertyConstructorParameterIsNullable()171 public void testNullablePropertyConstructorParameterIsNullable() throws NoSuchMethodException { 172 Constructor<?> constructor = 173 AutoValue_AutoValueJava8Test_NullableProperties.class.getDeclaredConstructor( 174 String.class, int.class); 175 try { 176 assertThat(constructor.getAnnotatedParameterTypes()[0].getAnnotations()) 177 .asList() 178 .contains(nullable()); 179 } catch (AssertionError e) { 180 if (javacHandlesTypeAnnotationsCorrectly) { 181 throw e; 182 } 183 } 184 } 185 186 @AutoValue 187 abstract static class NullablePropertiesNotCopied { 188 @AutoValue.CopyAnnotations(exclude = Nullable.class) nullableString()189 abstract @Nullable String nullableString(); 190 randomInt()191 abstract int randomInt(); 192 create(String notNullableAfterAll, int randomInt)193 NullablePropertiesNotCopied create(String notNullableAfterAll, int randomInt) { 194 return new AutoValue_AutoValueJava8Test_NullablePropertiesNotCopied( 195 notNullableAfterAll, randomInt); 196 } 197 } 198 199 @Test testExcludedNullablePropertyImplementation()200 public void testExcludedNullablePropertyImplementation() throws NoSuchMethodException { 201 Method method = AutoValue_AutoValueJava8Test_NullablePropertiesNotCopied.class 202 .getDeclaredMethod("nullableString"); 203 assertThat(method.getAnnotatedReturnType().getAnnotations()) 204 .asList() 205 .doesNotContain(nullable()); 206 } 207 208 @Test testExcludedNullablePropertyConstructorParameter()209 public void testExcludedNullablePropertyConstructorParameter() throws NoSuchMethodException { 210 Constructor<?> constructor = 211 AutoValue_AutoValueJava8Test_NullablePropertiesNotCopied.class.getDeclaredConstructor( 212 String.class, int.class); 213 try { 214 assertThat(constructor.getAnnotatedParameterTypes()[0].getAnnotations()) 215 .asList() 216 .doesNotContain(nullable()); 217 } catch (AssertionError e) { 218 if (javacHandlesTypeAnnotationsCorrectly) { 219 throw e; 220 } 221 } 222 } 223 224 @AutoValue 225 abstract static class NullableNonNullable { nullableString()226 abstract @Nullable String nullableString(); 227 otherNullableString()228 abstract @Nullable String otherNullableString(); 229 nonNullableString()230 abstract String nonNullableString(); 231 create( String nullableString, String otherNullableString, String nonNullableString)232 static NullableNonNullable create( 233 String nullableString, String otherNullableString, String nonNullableString) { 234 return new AutoValue_AutoValueJava8Test_NullableNonNullable( 235 nullableString, otherNullableString, nonNullableString); 236 } 237 } 238 239 @Test testEqualsWithNullable()240 public void testEqualsWithNullable() throws Exception { 241 NullableNonNullable everythingNull = 242 NullableNonNullable.create(null, null, "nonNullableString"); 243 NullableNonNullable somethingNull = 244 NullableNonNullable.create(null, "otherNullableString", "nonNullableString"); 245 NullableNonNullable nothingNull = 246 NullableNonNullable.create("nullableString", "otherNullableString", "nonNullableString"); 247 NullableNonNullable nothingNullAgain = 248 NullableNonNullable.create("nullableString", "otherNullableString", "nonNullableString"); 249 new EqualsTester() 250 .addEqualityGroup(everythingNull) 251 .addEqualityGroup(somethingNull) 252 .addEqualityGroup(nothingNull, nothingNullAgain) 253 .testEquals(); 254 } 255 256 public static class Nested {} 257 258 @Retention(RetentionPolicy.RUNTIME) 259 @Target(ElementType.TYPE_USE) 260 public @interface OtherTypeAnnotation {} 261 262 @AutoAnnotation otherTypeAnnotation()263 public static OtherTypeAnnotation otherTypeAnnotation() { 264 return new AutoAnnotation_AutoValueJava8Test_otherTypeAnnotation(); 265 } 266 267 @AutoValue 268 abstract static class NestedNullableProperties { nullableThing()269 abstract @Nullable @OtherTypeAnnotation Nested nullableThing(); 270 randomInt()271 abstract int randomInt(); 272 builder()273 static Builder builder() { 274 return new AutoValue_AutoValueJava8Test_NestedNullableProperties.Builder(); 275 } 276 277 @AutoValue.Builder 278 abstract static class Builder { setNullableThing(@ullable @therTypeAnnotation Nested thing)279 abstract Builder setNullableThing(@Nullable @OtherTypeAnnotation Nested thing); 280 setRandomInt(int x)281 abstract Builder setRandomInt(int x); 282 build()283 abstract NestedNullableProperties build(); 284 } 285 } 286 287 @Test testNestedNullablePropertiesCanBeNull()288 public void testNestedNullablePropertiesCanBeNull() { 289 NestedNullableProperties instance = NestedNullableProperties.builder().setRandomInt(23).build(); 290 assertThat(instance.nullableThing()).isNull(); 291 assertThat(instance.randomInt()).isEqualTo(23); 292 assertThat(instance.toString()) 293 .isEqualTo("NestedNullableProperties{nullableThing=null, randomInt=23}"); 294 } 295 296 @Test testNestedNullablePropertiesAreCopied()297 public void testNestedNullablePropertiesAreCopied() throws Exception { 298 try { 299 Method generatedGetter = 300 AutoValue_AutoValueJava8Test_NestedNullableProperties.class.getDeclaredMethod( 301 "nullableThing"); 302 Annotation[] getterAnnotations = generatedGetter.getAnnotatedReturnType().getAnnotations(); 303 assertThat(getterAnnotations).asList().containsAtLeast(nullable(), otherTypeAnnotation()); 304 305 Method generatedSetter = 306 AutoValue_AutoValueJava8Test_NestedNullableProperties.Builder.class.getDeclaredMethod( 307 "setNullableThing", Nested.class); 308 Annotation[] setterAnnotations = 309 generatedSetter.getAnnotatedParameterTypes()[0].getAnnotations(); 310 assertThat(setterAnnotations).asList().containsAtLeast(nullable(), otherTypeAnnotation()); 311 } catch (AssertionError e) { 312 if (javacHandlesTypeAnnotationsCorrectly) { 313 throw e; 314 } 315 } 316 } 317 318 @AutoValue 319 @SuppressWarnings("AutoValueImmutableFields") 320 abstract static class PrimitiveArrays { 321 @SuppressWarnings("mutable") booleans()322 abstract boolean[] booleans(); 323 324 @SuppressWarnings("mutable") ints()325 abstract int @Nullable [] ints(); 326 create(boolean[] booleans, int[] ints)327 static PrimitiveArrays create(boolean[] booleans, int[] ints) { 328 // Real code would likely clone these parameters, but here we want to check that the 329 // generated constructor rejects a null value for booleans. 330 return new AutoValue_AutoValueJava8Test_PrimitiveArrays(booleans, ints); 331 } 332 } 333 334 @Test testPrimitiveArrays()335 public void testPrimitiveArrays() { 336 PrimitiveArrays object0 = PrimitiveArrays.create(new boolean[0], new int[0]); 337 boolean[] booleans = {false, true, true, false}; 338 int[] ints = {6, 28, 496, 8128, 33550336}; 339 PrimitiveArrays object1 = PrimitiveArrays.create(booleans.clone(), ints.clone()); 340 PrimitiveArrays object2 = PrimitiveArrays.create(booleans.clone(), ints.clone()); 341 new EqualsTester().addEqualityGroup(object1, object2).addEqualityGroup(object0).testEquals(); 342 // EqualsTester also exercises hashCode(). We clone the arrays above to ensure that using the 343 // default Object.hashCode() will fail. 344 345 String expectedString = 346 "PrimitiveArrays{booleans=" 347 + Arrays.toString(booleans) 348 + ", " 349 + "ints=" 350 + Arrays.toString(ints) 351 + "}"; 352 assertThat(object1.toString()).isEqualTo(expectedString); 353 354 assertThat(object1.ints()).isSameInstanceAs(object1.ints()); 355 } 356 357 @Test testNullablePrimitiveArrays()358 public void testNullablePrimitiveArrays() { 359 assumeTrue(javacHandlesTypeAnnotationsCorrectly); 360 PrimitiveArrays object0 = PrimitiveArrays.create(new boolean[0], null); 361 boolean[] booleans = {false, true, true, false}; 362 PrimitiveArrays object1 = PrimitiveArrays.create(booleans.clone(), null); 363 PrimitiveArrays object2 = PrimitiveArrays.create(booleans.clone(), null); 364 new EqualsTester().addEqualityGroup(object1, object2).addEqualityGroup(object0).testEquals(); 365 366 String expectedString = 367 "PrimitiveArrays{booleans=" + Arrays.toString(booleans) + ", " + "ints=null}"; 368 assertThat(object1.toString()).isEqualTo(expectedString); 369 370 assertThat(object1.booleans()).isSameInstanceAs(object1.booleans()); 371 assertThat(object1.booleans()).isEqualTo(booleans); 372 object1.booleans()[0] ^= true; 373 assertThat(object1.booleans()).isNotEqualTo(booleans); 374 } 375 376 @Test testNotNullablePrimitiveArrays()377 public void testNotNullablePrimitiveArrays() { 378 NullPointerException e = 379 assertThrows(NullPointerException.class, () -> PrimitiveArrays.create(null, new int[0])); 380 assertThat(e).hasMessageThat().contains("booleans"); 381 } 382 383 @AutoValue 384 public abstract static class NullablePropertyWithBuilder { notNullable()385 public abstract String notNullable(); 386 nullable()387 public abstract @Nullable String nullable(); 388 builder()389 public static Builder builder() { 390 return new AutoValue_AutoValueJava8Test_NullablePropertyWithBuilder.Builder(); 391 } 392 393 @AutoValue.Builder 394 public interface Builder { notNullable(String s)395 Builder notNullable(String s); 396 nullable(@ullable String s)397 Builder nullable(@Nullable String s); 398 build()399 NullablePropertyWithBuilder build(); 400 } 401 } 402 403 @Test testOmitNullableWithBuilder()404 public void testOmitNullableWithBuilder() { 405 NullablePropertyWithBuilder instance1 = 406 NullablePropertyWithBuilder.builder().notNullable("hello").build(); 407 assertThat(instance1.notNullable()).isEqualTo("hello"); 408 assertThat(instance1.nullable()).isNull(); 409 410 NullablePropertyWithBuilder instance2 = 411 NullablePropertyWithBuilder.builder().notNullable("hello").nullable(null).build(); 412 assertThat(instance2.notNullable()).isEqualTo("hello"); 413 assertThat(instance2.nullable()).isNull(); 414 assertThat(instance1).isEqualTo(instance2); 415 416 NullablePropertyWithBuilder instance3 = 417 NullablePropertyWithBuilder.builder().notNullable("hello").nullable("world").build(); 418 assertThat(instance3.notNullable()).isEqualTo("hello"); 419 assertThat(instance3.nullable()).isEqualTo("world"); 420 421 IllegalStateException e = 422 assertThrows( 423 IllegalStateException.class, () -> NullablePropertyWithBuilder.builder().build()); 424 assertThat(e).hasMessageThat().contains("notNullable"); 425 } 426 427 @AutoValue 428 public abstract static class OptionalPropertyWithNullableBuilder { notOptional()429 public abstract String notOptional(); 430 optional()431 public abstract Optional<String> optional(); 432 builder()433 public static Builder builder() { 434 return new AutoValue_AutoValueJava8Test_OptionalPropertyWithNullableBuilder.Builder(); 435 } 436 437 @AutoValue.Builder 438 public interface Builder { notOptional(String s)439 Builder notOptional(String s); 440 optional(@ullable String s)441 Builder optional(@Nullable String s); 442 build()443 OptionalPropertyWithNullableBuilder build(); 444 } 445 } 446 447 @Test testOmitOptionalWithNullableBuilder()448 public void testOmitOptionalWithNullableBuilder() { 449 OptionalPropertyWithNullableBuilder instance1 = 450 OptionalPropertyWithNullableBuilder.builder().notOptional("hello").build(); 451 assertThat(instance1.notOptional()).isEqualTo("hello"); 452 assertThat(instance1.optional()).isEmpty(); 453 454 OptionalPropertyWithNullableBuilder instance2 = 455 OptionalPropertyWithNullableBuilder.builder().notOptional("hello").optional(null).build(); 456 assertThat(instance2.notOptional()).isEqualTo("hello"); 457 assertThat(instance2.optional()).isEmpty(); 458 assertThat(instance1).isEqualTo(instance2); 459 460 OptionalPropertyWithNullableBuilder instance3 = 461 OptionalPropertyWithNullableBuilder.builder() 462 .notOptional("hello") 463 .optional("world") 464 .build(); 465 assertThat(instance3.notOptional()).isEqualTo("hello"); 466 assertThat(instance3.optional()).hasValue("world"); 467 468 assertThrows( 469 IllegalStateException.class, () -> OptionalPropertyWithNullableBuilder.builder().build()); 470 } 471 472 @AutoValue 473 public abstract static class NullableOptionalPropertyWithNullableBuilder { optional()474 public abstract @Nullable Optional<String> optional(); 475 builder()476 public static Builder builder() { 477 return new AutoValue_AutoValueJava8Test_NullableOptionalPropertyWithNullableBuilder.Builder(); 478 } 479 480 @AutoValue.Builder 481 public interface Builder { optional(@ullable String s)482 Builder optional(@Nullable String s); 483 build()484 NullableOptionalPropertyWithNullableBuilder build(); 485 } 486 } 487 488 @Test testNullableOptional()489 public void testNullableOptional() { 490 NullableOptionalPropertyWithNullableBuilder instance1 = 491 NullableOptionalPropertyWithNullableBuilder.builder().build(); 492 assertThat(instance1.optional()).isNull(); 493 494 NullableOptionalPropertyWithNullableBuilder instance2 = 495 NullableOptionalPropertyWithNullableBuilder.builder().optional(null).build(); 496 assertThat(instance2.optional()).isEmpty(); 497 498 NullableOptionalPropertyWithNullableBuilder instance3 = 499 NullableOptionalPropertyWithNullableBuilder.builder().optional("haruspex").build(); 500 assertThat(instance3.optional()).hasValue("haruspex"); 501 } 502 503 @AutoValue 504 @SuppressWarnings("AutoValueImmutableFields") 505 public abstract static class BuilderWithUnprefixedGetters<T extends Comparable<T>> { list()506 public abstract ImmutableList<T> list(); 507 t()508 public abstract @Nullable T t(); 509 510 @SuppressWarnings("mutable") ints()511 public abstract int[] ints(); 512 noGetter()513 public abstract int noGetter(); 514 builder()515 public static <T extends Comparable<T>> Builder<T> builder() { 516 return new AutoValue_AutoValueJava8Test_BuilderWithUnprefixedGetters.Builder<T>(); 517 } 518 519 @AutoValue.Builder 520 public interface Builder<T extends Comparable<T>> { setList(ImmutableList<T> list)521 Builder<T> setList(ImmutableList<T> list); 522 setT(T t)523 Builder<T> setT(T t); 524 setInts(int[] ints)525 Builder<T> setInts(int[] ints); 526 setNoGetter(int x)527 Builder<T> setNoGetter(int x); 528 list()529 ImmutableList<T> list(); 530 t()531 T t(); 532 ints()533 int[] ints(); 534 build()535 BuilderWithUnprefixedGetters<T> build(); 536 } 537 } 538 539 @Test testBuilderWithUnprefixedGetter()540 public void testBuilderWithUnprefixedGetter() { 541 assumeTrue(javacHandlesTypeAnnotationsCorrectly); 542 ImmutableList<String> names = ImmutableList.of("fred", "jim"); 543 int[] ints = {6, 28, 496, 8128, 33550336}; 544 int noGetter = -1; 545 546 BuilderWithUnprefixedGetters.Builder<String> builder = BuilderWithUnprefixedGetters.builder(); 547 assertThat(builder.t()).isNull(); 548 IllegalStateException e1 = assertThrows(IllegalStateException.class, () -> builder.list()); 549 assertThat(e1).hasMessageThat().isEqualTo("Property \"list\" has not been set"); 550 IllegalStateException e2 = assertThrows(IllegalStateException.class, () -> builder.ints()); 551 assertThat(e2).hasMessageThat().isEqualTo("Property \"ints\" has not been set"); 552 553 builder.setList(names); 554 assertThat(builder.list()).isSameInstanceAs(names); 555 builder.setInts(ints); 556 assertThat(builder.ints()).isEqualTo(ints); 557 // The array is not cloned by the getter, so the client can modify it (but shouldn't). 558 ints[0] = 0; 559 assertThat(builder.ints()[0]).isEqualTo(0); 560 ints[0] = 6; 561 562 BuilderWithUnprefixedGetters<String> instance = builder.setNoGetter(noGetter).build(); 563 assertThat(instance.list()).isSameInstanceAs(names); 564 assertThat(instance.t()).isNull(); 565 assertThat(instance.ints()).isEqualTo(ints); 566 assertThat(instance.noGetter()).isEqualTo(noGetter); 567 } 568 569 @AutoValue 570 @SuppressWarnings("AutoValueImmutableFields") 571 public abstract static class BuilderWithPrefixedGetters<T extends Comparable<T>> { getList()572 public abstract ImmutableList<T> getList(); 573 getT()574 public abstract T getT(); 575 576 @SuppressWarnings("mutable") getInts()577 public abstract int @Nullable [] getInts(); 578 getNoGetter()579 public abstract int getNoGetter(); 580 builder()581 public static <T extends Comparable<T>> Builder<T> builder() { 582 return new AutoValue_AutoValueJava8Test_BuilderWithPrefixedGetters.Builder<T>(); 583 } 584 585 @AutoValue.Builder 586 public abstract static class Builder<T extends Comparable<T>> { setList(ImmutableList<T> list)587 public abstract Builder<T> setList(ImmutableList<T> list); 588 setT(T t)589 public abstract Builder<T> setT(T t); 590 setInts(int[] ints)591 public abstract Builder<T> setInts(int[] ints); 592 setNoGetter(int x)593 public abstract Builder<T> setNoGetter(int x); 594 getList()595 abstract ImmutableList<T> getList(); 596 getT()597 abstract T getT(); 598 getInts()599 abstract int[] getInts(); 600 build()601 public abstract BuilderWithPrefixedGetters<T> build(); 602 } 603 } 604 605 @Test testBuilderWithPrefixedGetter()606 public void testBuilderWithPrefixedGetter() { 607 assumeTrue(javacHandlesTypeAnnotationsCorrectly); 608 ImmutableList<String> names = ImmutableList.of("fred", "jim"); 609 String name = "sheila"; 610 int noGetter = -1; 611 612 BuilderWithPrefixedGetters.Builder<String> builder = BuilderWithPrefixedGetters.builder(); 613 assertThat(builder.getInts()).isNull(); 614 IllegalStateException e = assertThrows(IllegalStateException.class, () -> builder.getList()); 615 assertThat(e).hasMessageThat().isEqualTo("Property \"list\" has not been set"); 616 617 builder.setList(names); 618 assertThat(builder.getList()).isSameInstanceAs(names); 619 builder.setT(name); 620 assertThat(builder.getInts()).isNull(); 621 622 BuilderWithPrefixedGetters<String> instance = builder.setNoGetter(noGetter).build(); 623 assertThat(instance.getList()).isSameInstanceAs(names); 624 assertThat(instance.getT()).isEqualTo(name); 625 assertThat(instance.getInts()).isNull(); 626 assertThat(instance.getNoGetter()).isEqualTo(noGetter); 627 } 628 629 // This class tests the case where an annotation is both a method annotation and a type 630 // annotation. If we weren't careful, we might emit it twice in the generated code. 631 @AutoValue 632 abstract static class FunkyNullable { 633 @Target({ElementType.METHOD, ElementType.TYPE_USE}) 634 @interface Nullable {} 635 foo()636 abstract @Nullable String foo(); 637 bar()638 abstract Optional<String> bar(); 639 builder()640 static Builder builder() { 641 return new AutoValue_AutoValueJava8Test_FunkyNullable.Builder(); 642 } 643 644 @AutoValue.Builder 645 interface Builder { setFoo(@ullable String foo)646 Builder setFoo(@Nullable String foo); 647 setBar(@ullable String bar)648 Builder setBar(@Nullable String bar); 649 build()650 FunkyNullable build(); 651 } 652 } 653 654 @Test testFunkyNullable()655 public void testFunkyNullable() { 656 FunkyNullable explicitNull = FunkyNullable.builder().setFoo(null).setBar(null).build(); 657 FunkyNullable implicitNull = FunkyNullable.builder().build(); 658 assertThat(explicitNull).isEqualTo(implicitNull); 659 } 660 661 @AutoValue 662 abstract static class EqualsNullable { 663 @Target({ElementType.TYPE_USE, ElementType.TYPE_PARAMETER}) 664 @Retention(RetentionPolicy.RUNTIME) 665 @interface Nullable {} 666 foo()667 abstract String foo(); 668 create(String foo)669 static EqualsNullable create(String foo) { 670 return new AutoValue_AutoValueJava8Test_EqualsNullable(foo); 671 } 672 673 @Override equals(@ullable Object x)674 public abstract boolean equals(@Nullable Object x); 675 676 @Override hashCode()677 public abstract int hashCode(); 678 } 679 680 /** 681 * Tests that a type annotation on the parameter of {@code equals(Object)} is copied into the 682 * implementation class. 683 */ 684 @Test testEqualsNullable()685 public void testEqualsNullable() throws ReflectiveOperationException { 686 EqualsNullable x = EqualsNullable.create("foo"); 687 Class<? extends EqualsNullable> implClass = x.getClass(); 688 Method equals = implClass.getDeclaredMethod("equals", Object.class); 689 AnnotatedType[] parameterTypes = equals.getAnnotatedParameterTypes(); 690 assertThat(parameterTypes[0].isAnnotationPresent(EqualsNullable.Nullable.class)).isTrue(); 691 } 692 693 @AutoValue 694 abstract static class AnnotatedTypeParameter<@Nullable T> { thing()695 abstract @Nullable T thing(); 696 create(T thing)697 static <@Nullable T> AnnotatedTypeParameter<T> create(T thing) { 698 return new AutoValue_AutoValueJava8Test_AnnotatedTypeParameter<T>(thing); 699 } 700 } 701 702 /** 703 * Tests that an annotation on a type parameter of an {@code @AutoValue} class is copied to the 704 * implementation class. 705 */ 706 @Test testTypeAnnotationCopiedToImplementation()707 public void testTypeAnnotationCopiedToImplementation() { 708 @Nullable String nullableString = "blibby"; 709 AnnotatedTypeParameter<@Nullable String> x = AnnotatedTypeParameter.create(nullableString); 710 Class<?> c = x.getClass(); 711 assertThat(c.getTypeParameters()).hasLength(1); 712 TypeVariable<?> typeParameter = c.getTypeParameters()[0]; 713 assertWithMessage(typeParameter.toString()) 714 .that(typeParameter.getAnnotations()) 715 .asList() 716 .contains(nullable()); 717 } 718 719 @AutoValue 720 abstract static class AnnotatedTypeParameterWithBuilder<@Nullable T> { thing()721 abstract @Nullable T thing(); 722 builder()723 static <@Nullable T> Builder<T> builder() { 724 return new AutoValue_AutoValueJava8Test_AnnotatedTypeParameterWithBuilder.Builder<T>(); 725 } 726 727 @AutoValue.Builder 728 abstract static class Builder<@Nullable T> { setThing(T thing)729 abstract Builder<T> setThing(T thing); 730 build()731 abstract AnnotatedTypeParameterWithBuilder<T> build(); 732 } 733 } 734 735 /** 736 * Tests that an annotation on a type parameter of an {@code @AutoValue} builder is copied to the 737 * implementation class. 738 */ 739 @Test testTypeAnnotationOnBuilderCopiedToImplementation()740 public void testTypeAnnotationOnBuilderCopiedToImplementation() { 741 AnnotatedTypeParameterWithBuilder.Builder<@Nullable String> builder = 742 AnnotatedTypeParameterWithBuilder.builder(); 743 Class<?> c = builder.getClass(); 744 assertThat(c.getTypeParameters()).hasLength(1); 745 TypeVariable<?> typeParameter = c.getTypeParameters()[0]; 746 assertWithMessage(typeParameter.toString()) 747 .that(typeParameter.getAnnotations()) 748 .asList() 749 .contains(nullable()); 750 } 751 752 // b/127701294 753 @AutoValue 754 abstract static class OptionalOptional { maybeJustMaybe()755 abstract Optional<Optional<String>> maybeJustMaybe(); 756 builder()757 static Builder builder() { 758 return new AutoValue_AutoValueJava8Test_OptionalOptional.Builder(); 759 } 760 761 @AutoValue.Builder 762 abstract static class Builder { maybeJustMaybe(Optional<String> maybe)763 abstract Builder maybeJustMaybe(Optional<String> maybe); build()764 abstract OptionalOptional build(); 765 } 766 } 767 768 @Test testOptionalOptional_empty()769 public void testOptionalOptional_empty() { 770 OptionalOptional empty = OptionalOptional.builder().build(); 771 assertThat(empty.maybeJustMaybe()).isEmpty(); 772 } 773 774 @Test testOptionalOptional_ofEmpty()775 public void testOptionalOptional_ofEmpty() { 776 OptionalOptional ofEmpty = OptionalOptional.builder().maybeJustMaybe(Optional.empty()).build(); 777 assertThat(ofEmpty.maybeJustMaybe()).hasValue(Optional.empty()); 778 } 779 780 @Test testOptionalOptional_ofSomething()781 public void testOptionalOptional_ofSomething() { 782 OptionalOptional ofSomething = 783 OptionalOptional.builder().maybeJustMaybe(Optional.of("foo")).build(); 784 assertThat(ofSomething.maybeJustMaybe()).hasValue(Optional.of("foo")); 785 } 786 787 @AutoValue 788 abstract static class OptionalExtends { predicate()789 abstract Optional<? extends Predicate<? super Integer>> predicate(); 790 builder()791 static Builder builder() { 792 return new AutoValue_AutoValueJava8Test_OptionalExtends.Builder(); 793 } 794 795 @AutoValue.Builder 796 abstract static class Builder { setPredicate(Predicate<? super Integer> predicate)797 abstract Builder setPredicate(Predicate<? super Integer> predicate); build()798 abstract OptionalExtends build(); 799 } 800 } 801 802 @Test testOptionalExtends()803 public void testOptionalExtends() { 804 Predicate<Number> predicate = n -> n.toString().equals("0"); 805 OptionalExtends t = OptionalExtends.builder().setPredicate(predicate).build(); 806 assertThat(t.predicate()).hasValue(predicate); 807 } 808 } 809