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