• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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