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