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