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