diff options
Diffstat (limited to 'value/src/test/java/com/google')
22 files changed, 2905 insertions, 307 deletions
diff --git a/value/src/test/java/com/google/auto/value/extension/memoized/MemoizedMethodSubject.java b/value/src/test/java/com/google/auto/value/extension/memoized/MemoizedMethodSubject.java index 18c77368..5d1462d0 100644 --- a/value/src/test/java/com/google/auto/value/extension/memoized/MemoizedMethodSubject.java +++ b/value/src/test/java/com/google/auto/value/extension/memoized/MemoizedMethodSubject.java @@ -51,9 +51,6 @@ final class MemoizedMethodSubject extends Subject { javac() .withProcessors(new AutoValueProcessor(ImmutableList.of(new MemoizeExtension()))) .compile(file); - assertThat(compilation) - .hadErrorContaining(error) - .inFile(file) - .onLineContaining(actual); + assertThat(compilation).hadErrorContaining(error).inFile(file).onLineContaining(actual); } } diff --git a/value/src/test/java/com/google/auto/value/extension/memoized/MemoizedTest.java b/value/src/test/java/com/google/auto/value/extension/memoized/MemoizedTest.java index 7bd61ac7..5beb686b 100644 --- a/value/src/test/java/com/google/auto/value/extension/memoized/MemoizedTest.java +++ b/value/src/test/java/com/google/auto/value/extension/memoized/MemoizedTest.java @@ -22,6 +22,8 @@ import com.google.auto.value.AutoValue; import com.google.auto.value.AutoValue.CopyAnnotations; import com.google.auto.value.extension.memoized.MemoizedTest.HashCodeEqualsOptimization.EqualsCounter; import com.google.common.collect.ImmutableList; +import com.google.errorprone.annotations.Immutable; +import com.google.errorprone.annotations.ImmutableTypeParameter; import java.lang.reflect.AnnotatedType; import java.lang.reflect.Constructor; import java.lang.reflect.Method; @@ -123,7 +125,9 @@ public class MemoizedTest { @org.checkerframework.checker.nullness.qual.Nullable String nullableWithTypeAnnotation() { nullableWithTypeAnnotationCount++; - return "nullable derived " + stringWithTypeAnnotation() + " " + return "nullable derived " + + stringWithTypeAnnotation() + + " " + nullableWithTypeAnnotationCount; } @@ -234,8 +238,9 @@ public class MemoizedTest { @Before public void setUp() { - value = new AutoValue_MemoizedTest_Value( - "string", "stringWithTypeAnnotation", new HashCodeAndToStringCounter()); + value = + new AutoValue_MemoizedTest_Value( + "string", "stringWithTypeAnnotation", new HashCodeAndToStringCounter()); listValue = new AutoValue_MemoizedTest_ListValue<Integer, String>(0, "hello"); } @@ -375,8 +380,9 @@ public class MemoizedTest { Method nullable = AutoValue_MemoizedTest_Value.class.getDeclaredMethod("nullableWithTypeAnnotation"); AnnotatedType returnType = nullable.getAnnotatedReturnType(); - assertThat(returnType.isAnnotationPresent( - org.checkerframework.checker.nullness.qual.Nullable.class)) + assertThat( + returnType.isAnnotationPresent( + org.checkerframework.checker.nullness.qual.Nullable.class)) .isTrue(); } @@ -387,11 +393,13 @@ public class MemoizedTest { // [1] @org.checkerframework.checker.nullness.qual.Nullable String stringWithTypeAnnotation, // [2] HashCodeAndToStringCounter counter // We don't currently copy @javax.annotation.Nullable because it is not a TYPE_USE annotation. - Constructor<?> constructor = AutoValue_MemoizedTest_Value.class.getDeclaredConstructor( - String.class, String.class, HashCodeAndToStringCounter.class); + Constructor<?> constructor = + AutoValue_MemoizedTest_Value.class.getDeclaredConstructor( + String.class, String.class, HashCodeAndToStringCounter.class); AnnotatedType paramType = constructor.getAnnotatedParameterTypes()[1]; - assertThat(paramType.isAnnotationPresent( - org.checkerframework.checker.nullness.qual.Nullable.class)) + assertThat( + paramType.isAnnotationPresent( + org.checkerframework.checker.nullness.qual.Nullable.class)) .isTrue(); } @@ -448,8 +456,8 @@ public class MemoizedTest { assertThat(memoizedHashCodeAndFinalEqualsMethod.equals(second)).isTrue(); assertThat(memoizedHashCodeAndFinalEqualsMethod.hashCodeCount).isEqualTo(0); - memoizedHashCodeAndFinalEqualsMethod.hashCode(); - memoizedHashCodeAndFinalEqualsMethod.hashCode(); + int unused1 = memoizedHashCodeAndFinalEqualsMethod.hashCode(); + int unused2 = memoizedHashCodeAndFinalEqualsMethod.hashCode(); assertThat(memoizedHashCodeAndFinalEqualsMethod.hashCodeCount).isEqualTo(1); } @@ -465,8 +473,7 @@ public class MemoizedTest { @AutoValue abstract static class ResourceUriPath<InputT> extends AbstractTypePath<InputT, ResourceUri> { - static <InputT> ResourceUriPath<InputT> create( - TypeEdgeIterable<InputT, ResourceUri> edges) { + static <InputT> ResourceUriPath<InputT> create(TypeEdgeIterable<InputT, ResourceUri> edges) { return new AutoValue_MemoizedTest_ResourceUriPath<>(edges); } @@ -482,4 +489,27 @@ public class MemoizedTest { ResourceUriPath.create(new TypeEdgeIterable<String, ResourceUri>() {}); assertThat(path.edges()).isNotNull(); } + + @Immutable + @AutoValue + abstract static class Unchanging<@ImmutableTypeParameter T> { + abstract T value(); + + @Override + @Memoized + public abstract int hashCode(); + + static <@ImmutableTypeParameter T> Unchanging<T> of(T value) { + return new AutoValue_MemoizedTest_Unchanging<T>(value); + } + } + + @Test + public void copiedTypeAnnotations() { + for (Class<?> c = Unchanging.of("foo").getClass(); c != Object.class; c = c.getSuperclass()) { + assertThat(c.getTypeParameters()).hasLength(1); + assertThat(c.getTypeParameters()[0].isAnnotationPresent(ImmutableTypeParameter.class)) + .isTrue(); + } + } } diff --git a/value/src/test/java/com/google/auto/value/extension/serializable/processor/SerializableAutoValueExtensionTest.java b/value/src/test/java/com/google/auto/value/extension/serializable/processor/SerializableAutoValueExtensionTest.java index 1879d1e5..064406da 100644 --- a/value/src/test/java/com/google/auto/value/extension/serializable/processor/SerializableAutoValueExtensionTest.java +++ b/value/src/test/java/com/google/auto/value/extension/serializable/processor/SerializableAutoValueExtensionTest.java @@ -415,4 +415,38 @@ public final class SerializableAutoValueExtensionTest { assertThat(actualAutoValue).isEqualTo(autoValue); } + + /** + * Type that may result in nested lambdas in the generated code. Including this allows us to + * verify that we handle those correctly, in particular not reusing a lambda parameter name in + * another lambda nested inside the first one. + */ + @SerializableAutoValue + @AutoValue + abstract static class ComplexType implements Serializable { + abstract ImmutableMap<String, ImmutableMap<String, Optional<String>>> a(); + + static ComplexType.Builder builder() { + return new AutoValue_SerializableAutoValueExtensionTest_ComplexType.Builder(); + } + + @AutoValue.Builder + abstract static class Builder { + abstract ComplexType.Builder setA( + ImmutableMap<String, ImmutableMap<String, Optional<String>>> a); + + abstract ComplexType build(); + } + } + + @Test + public void complexType() { + ImmutableMap<String, ImmutableMap<String, Optional<String>>> map = + ImmutableMap.of("foo", ImmutableMap.of("bar", Optional.of("baz"))); + ComplexType complexType = ComplexType.builder().setA(map).build(); + + ComplexType reserialized = SerializableTester.reserialize(complexType); + + assertThat(reserialized).isEqualTo(complexType); + } } diff --git a/value/src/test/java/com/google/auto/value/extension/serializable/serializer/utils/FakeSerializerFactory.java b/value/src/test/java/com/google/auto/value/extension/serializable/serializer/utils/FakeSerializerFactory.java index 388977fb..162d7cf6 100644 --- a/value/src/test/java/com/google/auto/value/extension/serializable/serializer/utils/FakeSerializerFactory.java +++ b/value/src/test/java/com/google/auto/value/extension/serializable/serializer/utils/FakeSerializerFactory.java @@ -42,6 +42,15 @@ public final class FakeSerializerFactory implements SerializerFactory { return new FakeIdentitySerializer(type, isIdentity); } + // This doesn't follow the contract, and always returns the same string for a given prefix. + // That means it will be wrong if two identifiers with the same prefix are in the same scope in + // the generated code, but for our purposes in this fake it is OK, and means we don't have to + // hardwire knowledge of the uniqueness algorithm into golden text in tests. + @Override + public CodeBlock newIdentifier(String prefix) { + return CodeBlock.of("$L$$", prefix); + } + private static class FakeIdentitySerializer implements Serializer { private final TypeMirror typeMirror; diff --git a/value/src/test/java/com/google/auto/value/extension/toprettystring/ToPrettyStringTest.java b/value/src/test/java/com/google/auto/value/extension/toprettystring/ToPrettyStringTest.java new file mode 100644 index 00000000..f5197472 --- /dev/null +++ b/value/src/test/java/com/google/auto/value/extension/toprettystring/ToPrettyStringTest.java @@ -0,0 +1,961 @@ +/* + * Copyright 2021 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.auto.value.extension.toprettystring; + +import static com.google.common.truth.Truth.assertThat; + +import com.google.auto.value.AutoValue; +import com.google.auto.value.extension.toprettystring.ToPrettyStringTest.CollectionSubtypesWithFixedTypeParameters.StringList; +import com.google.auto.value.extension.toprettystring.ToPrettyStringTest.CollectionSubtypesWithFixedTypeParameters.StringMap; +import com.google.auto.value.extension.toprettystring.ToPrettyStringTest.PropertyHasToPrettyString.HasToPrettyString; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableMultimap; +import com.google.common.collect.Multimap; +import com.google.common.primitives.ImmutableIntArray; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import javax.annotation.Nullable; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +@SuppressWarnings("AutoValueImmutableFields") +@RunWith(JUnit4.class) +public class ToPrettyStringTest { + @AutoValue + abstract static class Primitives { + abstract int i(); + + abstract long l(); + + abstract byte b(); + + abstract short s(); + + abstract char c(); + + abstract float f(); + + abstract double d(); + + abstract boolean bool(); + + @ToPrettyString + abstract String toPrettyString(); + } + + @Test + public void primitives() { + Primitives valueType = + new AutoValue_ToPrettyStringTest_Primitives( + 1, 2L, (byte) 3, (short) 4, 'C', 6.6f, 7.7, false); + + assertThat(valueType.toPrettyString()) + .isEqualTo( + "Primitives {" + + "\n i = 1," + + "\n l = 2," + + "\n b = 3," + + "\n s = 4," + + "\n c = C," + + "\n f = 6.6," + + "\n d = 7.7," + + "\n bool = false," + + "\n}"); + } + + @AutoValue + abstract static class PrimitiveArray { + @Nullable + @SuppressWarnings("mutable") + abstract long[] longs(); + + @ToPrettyString + abstract String toPrettyString(); + } + + @Test + public void primitiveArray() { + PrimitiveArray valueType = + new AutoValue_ToPrettyStringTest_PrimitiveArray(new long[] {1L, 2L, 10L, 200L}); + + assertThat(valueType.toPrettyString()) + .isEqualTo( + "PrimitiveArray {" + + "\n longs = [" + + "\n 1," + + "\n 2," + + "\n 10," + + "\n 200," + + "\n ]," + + "\n}"); + } + + @Test + public void primitiveArray_empty() { + PrimitiveArray valueType = new AutoValue_ToPrettyStringTest_PrimitiveArray(new long[0]); + + assertThat(valueType.toPrettyString()) + .isEqualTo( + "PrimitiveArray {" // force newline + + "\n longs = []," + + "\n}"); + } + + @Test + public void primitiveArray_null() { + PrimitiveArray valueType = new AutoValue_ToPrettyStringTest_PrimitiveArray(null); + + assertThat(valueType.toPrettyString()) + .isEqualTo( + "PrimitiveArray {" // force newline + + "\n longs = null," + + "\n}"); + } + + @AutoValue + abstract static class PrettyCollection { + @Nullable + abstract Collection<Object> collection(); + + @ToPrettyString + abstract String toPrettyString(); + } + + @Test + public void prettyCollection() { + PrettyCollection valueType = + new AutoValue_ToPrettyStringTest_PrettyCollection(ImmutableList.of("hello", "world")); + + assertThat(valueType.toPrettyString()) + .isEqualTo( + "PrettyCollection {" + + "\n collection = [" + + "\n hello," + + "\n world," + + "\n ]," + + "\n}"); + } + + @Test + public void prettyCollection_elementsWithNewlines() { + PrettyCollection valueType = + new AutoValue_ToPrettyStringTest_PrettyCollection( + ImmutableList.of("hello\nworld\nnewline")); + + assertThat(valueType.toPrettyString()) + .isEqualTo( + "PrettyCollection {" + + "\n collection = [" + + "\n hello" + + "\n world" + + "\n newline," + + "\n ]," + + "\n}"); + } + + @Test + public void prettyCollection_empty() { + PrettyCollection valueType = + new AutoValue_ToPrettyStringTest_PrettyCollection(ImmutableList.of()); + + assertThat(valueType.toPrettyString()) + .isEqualTo( + "PrettyCollection {" // force newline + + "\n collection = []," + + "\n}"); + } + + @Test + public void prettyCollection_null() { + PrettyCollection valueType = new AutoValue_ToPrettyStringTest_PrettyCollection(null); + + assertThat(valueType.toPrettyString()) + .isEqualTo( + "PrettyCollection {" // force newline + + "\n collection = null," + + "\n}"); + } + + @AutoValue + abstract static class NestedCollection { + @Nullable + abstract Collection<Collection<Object>> nestedCollection(); + + @ToPrettyString + abstract String toPrettyString(); + } + + @Test + public void nestedCollection() { + NestedCollection valueType = + new AutoValue_ToPrettyStringTest_NestedCollection( + Arrays.asList( + ImmutableList.of("hello", "world"), + ImmutableList.of("hello2", "world2"), + null, + Arrays.asList("not null", null))); + + assertThat(valueType.toPrettyString()) + .isEqualTo( + "NestedCollection {" + + "\n nestedCollection = [" + + "\n [" + + "\n hello," + + "\n world," + + "\n ]," + + "\n [" + + "\n hello2," + + "\n world2," + + "\n ]," + + "\n null," + + "\n [" + + "\n not null," + + "\n null," + + "\n ]," + + "\n ]," + + "\n}"); + } + + @Test + public void nestedCollection_elementsWithNewlines() { + NestedCollection valueType = + new AutoValue_ToPrettyStringTest_NestedCollection( + ImmutableList.of( + ImmutableList.of((Object) "hello\nworld\nnewline", "hello2\nworld2\nnewline2"))); + + assertThat(valueType.toPrettyString()) + .isEqualTo( + "NestedCollection {" + + "\n nestedCollection = [" + + "\n [" + + "\n hello" + + "\n world" + + "\n newline," + + "\n hello2" + + "\n world2" + + "\n newline2," + + "\n ]," + + "\n ]," + + "\n}"); + } + + @Test + public void nestedCollection_empty() { + NestedCollection valueType = + new AutoValue_ToPrettyStringTest_NestedCollection(ImmutableList.of()); + + assertThat(valueType.toPrettyString()) + .isEqualTo( + "NestedCollection {" // force newline + + "\n nestedCollection = []," + + "\n}"); + } + + @Test + public void nestedCollection_nestedEmpty() { + NestedCollection valueType = + new AutoValue_ToPrettyStringTest_NestedCollection( + ImmutableList.of(ImmutableList.of(), ImmutableList.of())); + + assertThat(valueType.toPrettyString()) + .isEqualTo( + "NestedCollection {" + + "\n nestedCollection = [" + + "\n []," + + "\n []," + + "\n ]," + + "\n}"); + } + + @Test + public void nestedCollection_null() { + NestedCollection valueType = new AutoValue_ToPrettyStringTest_NestedCollection(null); + + assertThat(valueType.toPrettyString()) + .isEqualTo( + "NestedCollection {" // force newline + + "\n nestedCollection = null," + + "\n}"); + } + + @AutoValue + abstract static class ImmutablePrimitiveArray { + @Nullable + abstract ImmutableIntArray immutableIntArray(); + + @ToPrettyString + abstract String toPrettyString(); + } + + @Test + public void immutablePrimitiveArray() { + ImmutablePrimitiveArray valueType = + new AutoValue_ToPrettyStringTest_ImmutablePrimitiveArray(ImmutableIntArray.of(1, 2)); + + assertThat(valueType.toPrettyString()) + .isEqualTo( + "ImmutablePrimitiveArray {" + + "\n immutableIntArray = [" + + "\n 1," + + "\n 2," + + "\n ]," + + "\n}"); + } + + @Test + public void immutablePrimitiveArray_empty() { + ImmutablePrimitiveArray valueType = + new AutoValue_ToPrettyStringTest_ImmutablePrimitiveArray(ImmutableIntArray.of()); + + assertThat(valueType.toPrettyString()) + .isEqualTo( + "ImmutablePrimitiveArray {" // force newline + + "\n immutableIntArray = []," + + "\n}"); + } + + @Test + public void immutablePrimitiveArray_null() { + ImmutablePrimitiveArray valueType = + new AutoValue_ToPrettyStringTest_ImmutablePrimitiveArray(null); + + assertThat(valueType.toPrettyString()) + .isEqualTo( + "ImmutablePrimitiveArray {" // force newline + + "\n immutableIntArray = null," + + "\n}"); + } + + @AutoValue + abstract static class PrettyMap { + @Nullable + abstract Map<Object, Object> map(); + + @ToPrettyString + abstract String toPrettyString(); + } + + @Test + public void prettyMap() { + PrettyMap valueType = new AutoValue_ToPrettyStringTest_PrettyMap(ImmutableMap.of(1, 2)); + + assertThat(valueType.toPrettyString()) + .isEqualTo( + "PrettyMap {" // force newline + + "\n map = {" + + "\n 1: 2," + + "\n }," + + "\n}"); + } + + @Test + public void prettyMap_keysAndValuesWithNewlines() { + PrettyMap valueType = + new AutoValue_ToPrettyStringTest_PrettyMap( + ImmutableMap.of( + "key1\nnewline", "value1\nnewline", "key2\nnewline", "value2\nnewline")); + + assertThat(valueType.toPrettyString()) + .isEqualTo( + "PrettyMap {" + + "\n map = {" + + "\n key1" + + "\n newline: value1" + + "\n newline," + + "\n key2" + + "\n newline: value2" + + "\n newline," + + "\n }," + + "\n}"); + } + + @Test + public void prettyMap_empty() { + PrettyMap valueType = new AutoValue_ToPrettyStringTest_PrettyMap(ImmutableMap.of()); + + assertThat(valueType.toPrettyString()) + .isEqualTo( + "PrettyMap {" // force newline + + "\n map = {}," + + "\n}"); + } + + @Test + public void prettyMap_null() { + PrettyMap valueType = new AutoValue_ToPrettyStringTest_PrettyMap(null); + + assertThat(valueType.toPrettyString()) + .isEqualTo( + "PrettyMap {" // force newline + + "\n map = null," + + "\n}"); + } + + @AutoValue + abstract static class MapOfMaps { + @Nullable + abstract Map<Map<Object, Object>, Map<Object, Object>> mapOfMaps(); + + @ToPrettyString + abstract String toPrettyString(); + } + + private static <K, V> Map<K, V> mapWithNulls(K k, V v) { + Map<K, V> map = new LinkedHashMap<>(); + map.put(k, v); + return map; + } + + @Test + public void mapOfMaps() { + Map<Map<Object, Object>, Map<Object, Object>> mapOfMaps = new LinkedHashMap<>(); + mapOfMaps.put(ImmutableMap.of("k1_k", "k1_v"), ImmutableMap.of("v1_k", "v1_v")); + mapOfMaps.put(ImmutableMap.of("k2_k", "k2_v"), ImmutableMap.of("v2_k", "v2_v")); + mapOfMaps.put(mapWithNulls("keyForNullValue", null), mapWithNulls(null, "valueForNullKey")); + mapOfMaps.put(null, ImmutableMap.of("nullKeyKey", "nullKeyValue")); + mapOfMaps.put(ImmutableMap.of("nullValueKey", "nullValueValue"), null); + mapOfMaps.put( + ImmutableMap.of("keyForMapOfNullsKey", "keyForMapOfNullsValue"), mapWithNulls(null, null)); + MapOfMaps valueType = new AutoValue_ToPrettyStringTest_MapOfMaps(mapOfMaps); + assertThat(valueType.toPrettyString()) + .isEqualTo( + "MapOfMaps {" + + "\n mapOfMaps = {" + + "\n {" + + "\n k1_k: k1_v," + + "\n }: {" + + "\n v1_k: v1_v," + + "\n }," + + "\n {" + + "\n k2_k: k2_v," + + "\n }: {" + + "\n v2_k: v2_v," + + "\n }," + + "\n {" + + "\n keyForNullValue: null," + + "\n }: {" + + "\n null: valueForNullKey," + + "\n }," + + "\n null: {" + + "\n nullKeyKey: nullKeyValue," + + "\n }," + + "\n {" + + "\n nullValueKey: nullValueValue," + + "\n }: null," + + "\n {" + + "\n keyForMapOfNullsKey: keyForMapOfNullsValue," + + "\n }: {" + + "\n null: null," + + "\n }," + + "\n }," + + "\n}"); + } + + @Test + public void mapOfMaps_elementsWithNewlines() { + MapOfMaps valueType = + new AutoValue_ToPrettyStringTest_MapOfMaps( + ImmutableMap.of( + ImmutableMap.of((Object) "k_k\nnewline", (Object) "k_v\nnewline"), + ImmutableMap.of((Object) "v_k\nnewline", (Object) "v_v\nnewline"))); + + assertThat(valueType.toPrettyString()) + .isEqualTo( + "MapOfMaps {" + + "\n mapOfMaps = {" + + "\n {" + + "\n k_k" + + "\n newline: k_v" + + "\n newline," + + "\n }: {" + + "\n v_k" + + "\n newline: v_v" + + "\n newline," + + "\n }," + + "\n }," + + "\n}"); + } + + @Test + public void mapOfMaps_empty() { + MapOfMaps valueType = new AutoValue_ToPrettyStringTest_MapOfMaps(ImmutableMap.of()); + + assertThat(valueType.toPrettyString()) + .isEqualTo( + "MapOfMaps {" // force newline + + "\n mapOfMaps = {}," + + "\n}"); + } + + @Test + public void mapOfMaps_nestedEmpty() { + MapOfMaps valueType = + new AutoValue_ToPrettyStringTest_MapOfMaps( + ImmutableMap.of(ImmutableMap.of(), ImmutableMap.of())); + + assertThat(valueType.toPrettyString()) + .isEqualTo( + "MapOfMaps {" // force newline + + "\n mapOfMaps = {" + + "\n {}: {}," + + "\n }," + + "\n}"); + } + + @Test + public void mapOfMaps_null() { + MapOfMaps valueType = new AutoValue_ToPrettyStringTest_MapOfMaps(null); + + assertThat(valueType.toPrettyString()) + .isEqualTo( + "MapOfMaps {" // force newline + + "\n mapOfMaps = null," + + "\n}"); + } + + @AutoValue + abstract static class PrettyMultimap { + @Nullable + abstract Multimap<Object, Object> multimap(); + + @ToPrettyString + abstract String toPrettyString(); + } + + @Test + public void prettyMultimap() { + PrettyMultimap valueType = + new AutoValue_ToPrettyStringTest_PrettyMultimap( + ImmutableMultimap.builder().putAll("k", "v1", "v2").build()); + + assertThat(valueType.toPrettyString()) + .isEqualTo( + "PrettyMultimap {" // force newline + + "\n multimap = {" + + "\n k: [" + + "\n v1," + + "\n v2," + + "\n ]," + + "\n }," + + "\n}"); + } + + @Test + public void prettyMultimap_keysAndValuesWithNewlines() { + PrettyMultimap valueType = + new AutoValue_ToPrettyStringTest_PrettyMultimap( + ImmutableMultimap.builder() + .putAll("key\nnewline", "value1\nnewline", "value2\nnewline") + .build()); + + assertThat(valueType.toPrettyString()) + .isEqualTo( + "PrettyMultimap {" + + "\n multimap = {" + + "\n key" + + "\n newline: [" + + "\n value1" + + "\n newline," + + "\n value2" + + "\n newline," + + "\n ]," + + "\n }," + + "\n}"); + } + + @Test + public void prettyMultimap_empty() { + PrettyMultimap valueType = + new AutoValue_ToPrettyStringTest_PrettyMultimap(ImmutableMultimap.of()); + + assertThat(valueType.toPrettyString()) + .isEqualTo( + "PrettyMultimap {" // force newline + + "\n multimap = {}," + + "\n}"); + } + + @Test + public void prettyMultimap_null() { + PrettyMultimap valueType = new AutoValue_ToPrettyStringTest_PrettyMultimap(null); + + assertThat(valueType.toPrettyString()) + .isEqualTo( + "PrettyMultimap {" // force newline + + "\n multimap = null," + + "\n}"); + } + + @AutoValue + abstract static class JavaOptional { + @Nullable + abstract java.util.Optional<Object> optional(); + + @ToPrettyString + abstract String toPrettyString(); + } + + @Test + public void javaOptional_present() { + JavaOptional valueType = + new AutoValue_ToPrettyStringTest_JavaOptional(java.util.Optional.of("hello, world")); + + assertThat(valueType.toPrettyString()) + .isEqualTo( + "JavaOptional {" // force newline + + "\n optional = hello, world," + + "\n}"); + } + + @Test + public void javaOptional_empty() { + JavaOptional valueType = + new AutoValue_ToPrettyStringTest_JavaOptional(java.util.Optional.empty()); + + assertThat(valueType.toPrettyString()) + .isEqualTo( + "JavaOptional {" // force newline + + "\n optional = <empty>," + + "\n}"); + } + + @Test + public void javaOptional_valueWithNewlines() { + JavaOptional valueType = + new AutoValue_ToPrettyStringTest_JavaOptional( + java.util.Optional.of("optional\nwith\nnewline")); + + assertThat(valueType.toPrettyString()) + .isEqualTo( + "JavaOptional {" // force newline + + "\n optional = optional" + + "\n with" + + "\n newline," + + "\n}"); + } + + @Test + public void javaOptional_null() { + @SuppressWarnings("NullOptional") + JavaOptional valueType = new AutoValue_ToPrettyStringTest_JavaOptional(null); + + assertThat(valueType.toPrettyString()) + .isEqualTo( + "JavaOptional {" // force newline + + "\n optional = null," + + "\n}"); + } + + @AutoValue + abstract static class GuavaOptional { + @Nullable + abstract com.google.common.base.Optional<Object> optional(); + + @ToPrettyString + abstract String toPrettyString(); + } + + @Test + public void guavaOptional_present() { + GuavaOptional valueType = + new AutoValue_ToPrettyStringTest_GuavaOptional( + com.google.common.base.Optional.of("hello, world")); + + assertThat(valueType.toPrettyString()) + .isEqualTo( + "GuavaOptional {" // force newline + + "\n optional = hello, world," + + "\n}"); + } + + @Test + public void guavaOptional_absent() { + GuavaOptional valueType = + new AutoValue_ToPrettyStringTest_GuavaOptional(com.google.common.base.Optional.absent()); + + assertThat(valueType.toPrettyString()) + .isEqualTo( + "GuavaOptional {" // force newline + + "\n optional = <absent>," + + "\n}"); + } + + @Test + public void guavaOptional_valueWithNewlines() { + GuavaOptional valueType = + new AutoValue_ToPrettyStringTest_GuavaOptional( + com.google.common.base.Optional.of("optional\nwith\nnewline")); + + assertThat(valueType.toPrettyString()) + .isEqualTo( + "GuavaOptional {" // force newline + + "\n optional = optional" + + "\n with" + + "\n newline," + + "\n}"); + } + + @Test + public void guavaOptional_null() { + @SuppressWarnings("NullOptional") + GuavaOptional valueType = new AutoValue_ToPrettyStringTest_GuavaOptional(null); + + assertThat(valueType.toPrettyString()) + .isEqualTo( + "GuavaOptional {" // force newline + + "\n optional = null," + + "\n}"); + } + + @AutoValue + abstract static class NestAllTheThings { + @Nullable + abstract com.google.common.base.Optional< + java.util.Optional< + List< // open list + Map<ImmutableIntArray, Multimap<int[][], Object>> + // close list + >>> + value(); + + @ToPrettyString + abstract String toPrettyString(); + } + + @Test + public void nestAllTheThings() { + NestAllTheThings valueType = + new AutoValue_ToPrettyStringTest_NestAllTheThings( + com.google.common.base.Optional.of( + java.util.Optional.of( + ImmutableList.of( + ImmutableMap.of( + ImmutableIntArray.of(-1, -2, -3), + ImmutableMultimap.of( + new int[][] {{1, 2}, {3, 4, 5}, {}}, "value\nwith\nnewline")))))); + assertThat(valueType.toPrettyString()) + .isEqualTo( + "NestAllTheThings {" + + "\n value = [" + + "\n {" + + "\n [" + + "\n -1," + + "\n -2," + + "\n -3," + + "\n ]: {" + + "\n [" + + "\n [" + + "\n 1," + + "\n 2," + + "\n ]," + + "\n [" + + "\n 3," + + "\n 4," + + "\n 5," + + "\n ]," + + "\n []," + + "\n ]: [" + + "\n value" + + "\n with" + + "\n newline," + + "\n ]," + + "\n }," + + "\n }," + + "\n ]," + + "\n}"); + } + + @AutoValue + abstract static class WithCustomName { + abstract int i(); + + @ToPrettyString + abstract String customName(); + } + + @Test + public void withCustomName() { + WithCustomName valueType = new AutoValue_ToPrettyStringTest_WithCustomName(1); + + assertThat(valueType.customName()) + .isEqualTo( + "WithCustomName {" // force newline + + "\n i = 1," + + "\n}"); + } + + @AutoValue + abstract static class OverridesToString { + abstract int i(); + + @ToPrettyString + @Override + public abstract String toString(); + } + + @Test + public void overridesToString() { + OverridesToString valueType = new AutoValue_ToPrettyStringTest_OverridesToString(1); + + assertThat(valueType.toString()) + .isEqualTo( + "OverridesToString {" // force newline + + "\n i = 1," + + "\n}"); + } + + @AutoValue + abstract static class PropertyHasToPrettyString { + static class HasToPrettyString<A> { + @Override + public String toString() { + throw new AssertionError(); + } + + @ToPrettyString + String toPrettyString() { + return "custom\n@ToPrettyString\nmethod"; + } + } + + static class HasInheritedToPrettyString extends HasToPrettyString<String> {} + + interface HasToPrettyStringInInterface { + @ToPrettyString + default String toPrettyString() { + return "custom\n@ToPrettyString\nmethod\ninterface"; + } + } + + static class HasToPrettyStringFromSuperInterface implements HasToPrettyStringInInterface {} + + abstract HasToPrettyString<String> parameterizedWithString(); + + abstract HasToPrettyString<Void> parameterizedWithVoid(); + + abstract HasInheritedToPrettyString superclass(); + + abstract HasToPrettyStringFromSuperInterface superinterface(); + + @ToPrettyString + abstract String toPrettyString(); + } + + @Test + public void propertyHasToPrettyString() { + PropertyHasToPrettyString valueType = + new AutoValue_ToPrettyStringTest_PropertyHasToPrettyString( + new PropertyHasToPrettyString.HasToPrettyString<>(), + new PropertyHasToPrettyString.HasToPrettyString<>(), + new PropertyHasToPrettyString.HasInheritedToPrettyString(), + new PropertyHasToPrettyString.HasToPrettyStringFromSuperInterface()); + + assertThat(valueType.toPrettyString()) + .isEqualTo( + "PropertyHasToPrettyString {" + + "\n parameterizedWithString = custom" + + "\n @ToPrettyString" + + "\n method," + + "\n parameterizedWithVoid = custom" + + "\n @ToPrettyString" + + "\n method," + + "\n superclass = custom" + + "\n @ToPrettyString" + + "\n method," + + "\n superinterface = custom" + + "\n @ToPrettyString" + + "\n method" + + "\n interface," + + "\n}"); + } + + @AutoValue + abstract static class CollectionSubtypesWithFixedTypeParameters { + static class StringList extends ArrayList<String> {} + + static class StringMap extends LinkedHashMap<String, String> {} + + abstract StringList list(); + + abstract StringMap map(); + + @ToPrettyString + abstract String toPrettyString(); + } + + @Test + public void fixedTypeParameters() { + StringList stringList = new StringList(); + stringList.addAll(ImmutableList.of("a", "b", "c")); + StringMap stringMap = new StringMap(); + stringMap.putAll(ImmutableMap.of("A", "a", "B", "b")); + CollectionSubtypesWithFixedTypeParameters valueType = + new AutoValue_ToPrettyStringTest_CollectionSubtypesWithFixedTypeParameters( + stringList, stringMap); + + assertThat(valueType.toPrettyString()) + .isEqualTo( + "CollectionSubtypesWithFixedTypeParameters {" + + "\n list = [" + + "\n a," + + "\n b," + + "\n c," + + "\n ]," + + "\n map = {" + + "\n A: a," + + "\n B: b," + + "\n }," + + "\n}"); + } + + @AutoValue + abstract static class JavaBeans { + abstract int getInt(); + + abstract boolean isBoolean(); + + abstract String getNotAJavaIdentifier(); + + @ToPrettyString + abstract String toPrettyString(); + } + + @Test + public void javaBeans() { + JavaBeans valueType = new AutoValue_ToPrettyStringTest_JavaBeans(4, false, "not"); + + assertThat(valueType.toPrettyString()) + .isEqualTo( + "JavaBeans {" + + "\n int = 4," + + "\n boolean = false," + + "\n notAJavaIdentifier = not," + + "\n}"); + + // Check to make sure that we use the same property names that AutoValue does. This is mostly + // defensive, since in some scenarios AutoValue considers the property names of a java bean as + // having the prefix removed. + assertThat(valueType.toString()) + .isEqualTo("JavaBeans{int=4, boolean=false, notAJavaIdentifier=not}"); + } +} diff --git a/value/src/test/java/com/google/auto/value/extension/toprettystring/ToPrettyStringValidatorTest.java b/value/src/test/java/com/google/auto/value/extension/toprettystring/ToPrettyStringValidatorTest.java new file mode 100644 index 00000000..6c51be1d --- /dev/null +++ b/value/src/test/java/com/google/auto/value/extension/toprettystring/ToPrettyStringValidatorTest.java @@ -0,0 +1,240 @@ +/* + * Copyright 2021 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.auto.value.extension.toprettystring; + +import static com.google.testing.compile.CompilationSubject.assertThat; +import static com.google.testing.compile.Compiler.javac; + +import com.google.auto.value.extension.toprettystring.processor.ToPrettyStringValidator; +import com.google.testing.compile.Compilation; +import com.google.testing.compile.JavaFileObjects; +import javax.tools.JavaFileObject; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +@RunWith(JUnit4.class) +public class ToPrettyStringValidatorTest { + @Test + public void cannotBeStatic() { + JavaFileObject file = + JavaFileObjects.forSourceLines( + "test.Test", + "package test;", + "", + "import com.google.auto.value.extension.toprettystring.ToPrettyString;", + "", + "class Test {", + " @ToPrettyString", + " static String toPretty() {", + " return new String();", + " }", + "}", + ""); + Compilation compilation = compile(file); + + assertThat(compilation).failed(); + assertThat(compilation).hadErrorCount(1); + assertThat(compilation) + .hadErrorContaining("must be instance methods") + .inFile(file) + .onLineContaining("static String toPretty()"); + } + + @Test + public void mustReturnString() { + JavaFileObject file = + JavaFileObjects.forSourceLines( + "test.Test", + "package test;", + "", + "import com.google.auto.value.extension.toprettystring.ToPrettyString;", + "", + "class Test {", + " @ToPrettyString", + " CharSequence toPretty() {", + " return new String();", + " }", + "}", + ""); + Compilation compilation = compile(file); + + assertThat(compilation).failed(); + assertThat(compilation).hadErrorCount(1); + assertThat(compilation) + .hadErrorContaining("must return String") + .inFile(file) + .onLineContaining("CharSequence toPretty()"); + } + + @Test + public void noParameters() { + JavaFileObject file = + JavaFileObjects.forSourceLines( + "test.Test", + "package test;", + "", + "import com.google.auto.value.extension.toprettystring.ToPrettyString;", + "", + "class Test {", + " @ToPrettyString", + " String toPretty(String value) {", + " return value;", + " }", + "}", + ""); + Compilation compilation = compile(file); + + assertThat(compilation).failed(); + assertThat(compilation).hadErrorCount(1); + assertThat(compilation) + .hadErrorContaining("cannot have parameters") + .inFile(file) + .onLineContaining("String toPretty(String value)"); + } + + @Test + public void onlyOneToPrettyStringMethod_sameClass() { + JavaFileObject file = + JavaFileObjects.forSourceLines( + "test.Test", + "package test;", + "", + "import com.google.auto.value.extension.toprettystring.ToPrettyString;", + "", + "class Test {", + " @ToPrettyString", + " String toPretty1() {", + " return new String();", + " }", + "", + " @ToPrettyString", + " String toPretty2() {", + " return new String();", + " }", + "}", + ""); + Compilation compilation = compile(file); + + assertThat(compilation).failed(); + assertThat(compilation).hadErrorCount(1); + assertThat(compilation) + .hadErrorContaining( + error( + "test.Test has multiple @ToPrettyString methods:", + " - test.Test.toPretty1()", + " - test.Test.toPretty2()")) + .inFile(file) + .onLineContaining("class Test"); + } + + @Test + public void onlyOneToPrettyStringMethod_superclass() { + JavaFileObject superclass = + JavaFileObjects.forSourceLines( + "test.Superclass", + "package test;", + "", + "import com.google.auto.value.extension.toprettystring.ToPrettyString;", + "", + "class Superclass {", + " @ToPrettyString", + " String toPretty1() {", + " return new String();", + " }", + "}", + ""); + JavaFileObject subclass = + JavaFileObjects.forSourceLines( + "test.Subclass", + "package test;", + "", + "import com.google.auto.value.extension.toprettystring.ToPrettyString;", + "", + "class Subclass extends Superclass {", + " @ToPrettyString", + " String toPretty2() {", + " return new String();", + " }", + "}", + ""); + Compilation compilation = compile(superclass, subclass); + + assertThat(compilation).failed(); + assertThat(compilation).hadErrorCount(1); + assertThat(compilation) + .hadErrorContaining( + error( + "test.Subclass has multiple @ToPrettyString methods:", + " - test.Superclass.toPretty1()", + " - test.Subclass.toPretty2()")) + .inFile(subclass) + .onLineContaining("class Subclass"); + } + + @Test + public void onlyOneToPrettyStringMethod_superinterface() { + JavaFileObject superinterface = + JavaFileObjects.forSourceLines( + "test.Superinterface", + "package test;", + "", + "import com.google.auto.value.extension.toprettystring.ToPrettyString;", + "", + "interface Superinterface {", + " @ToPrettyString", + " default String toPretty1() {", + " return new String();", + " }", + "}", + ""); + JavaFileObject subclass = + JavaFileObjects.forSourceLines( + "test.Subclass", + "package test;", + "", + "import com.google.auto.value.extension.toprettystring.ToPrettyString;", + "", + "class Subclass implements Superinterface {", + " @ToPrettyString", + " String toPretty2() {", + " return new String();", + " }", + "}", + ""); + Compilation compilation = compile(superinterface, subclass); + + assertThat(compilation).failed(); + assertThat(compilation).hadErrorCount(1); + assertThat(compilation) + .hadErrorContaining( + error( + "test.Subclass has multiple @ToPrettyString methods:", + " - test.Superinterface.toPretty1()", + " - test.Subclass.toPretty2()")) + .inFile(subclass) + .onLineContaining("class Subclass"); + } + + private static Compilation compile(JavaFileObject... javaFileObjects) { + return javac().withProcessors(new ToPrettyStringValidator()).compile(javaFileObjects); + } + + private static String error(String... lines) { + return String.join("\n ", lines); + } +} diff --git a/value/src/test/java/com/google/auto/value/processor/AutoAnnotationCompilationTest.java b/value/src/test/java/com/google/auto/value/processor/AutoAnnotationCompilationTest.java index b8a36eac..1f79a074 100644 --- a/value/src/test/java/com/google/auto/value/processor/AutoAnnotationCompilationTest.java +++ b/value/src/test/java/com/google/auto/value/processor/AutoAnnotationCompilationTest.java @@ -74,11 +74,13 @@ public class AutoAnnotationCompilationTest { "", "import com.example.annotations.MyAnnotation;", "import com.example.enums.MyEnum;", + "import java.io.Serializable;", GeneratedImport.importGeneratedAnnotationType(), "", "@Generated(\"" + AutoAnnotationProcessor.class.getName() + "\")", "final class AutoAnnotation_AnnotationFactory_newMyAnnotation", - " implements MyAnnotation {", + " implements MyAnnotation, Serializable {", + " private static final long serialVersionUID = -7473814294717163169L;", " private final MyEnum value;", " private static final int defaultedValue = 23;", "", @@ -129,6 +131,7 @@ public class AutoAnnotationCompilationTest { Compilation compilation = javac() .withProcessors(new AutoAnnotationProcessor()) + .withOptions("-A" + Nullables.NULLABLE_OPTION + "=") .compile(annotationFactoryJavaFile, myAnnotationJavaFile, myEnumJavaFile); assertThat(compilation).succeededWithoutWarnings(); assertThat(compilation) @@ -157,11 +160,13 @@ public class AutoAnnotationCompilationTest { JavaFileObject expectedOutput = JavaFileObjects.forSourceLines( "AutoAnnotation_AnnotationFactory_newMyAnnotation", + "import java.io.Serializable;", GeneratedImport.importGeneratedAnnotationType(), "", "@Generated(\"" + AutoAnnotationProcessor.class.getName() + "\")", "final class AutoAnnotation_AnnotationFactory_newMyAnnotation", - " implements MyAnnotation {", + " implements MyAnnotation, Serializable {", + " private static final long serialVersionUID = 0L;", " AutoAnnotation_AnnotationFactory_newMyAnnotation() {", " }", "", @@ -191,6 +196,7 @@ public class AutoAnnotationCompilationTest { Compilation compilation = javac() .withProcessors(new AutoAnnotationProcessor()) + .withOptions("-A" + Nullables.NULLABLE_OPTION + "=") .compile(annotationFactoryJavaFile, myAnnotationJavaFile); assertThat(compilation).succeededWithoutWarnings(); assertThat(compilation) @@ -237,12 +243,14 @@ public class AutoAnnotationCompilationTest { "package com.example.factories;", "", "import com.example.annotations.MyAnnotation;", + "import java.io.Serializable", "import java.util.Arrays;", GeneratedImport.importGeneratedAnnotationType(), "", "@Generated(\"" + AutoAnnotationProcessor.class.getName() + "\")", - "final class AutoAnnotation_AnnotationFactory_newMyAnnotation implements MyAnnotation" - + " {", + "final class AutoAnnotation_AnnotationFactory_newMyAnnotation implements MyAnnotation," + + " Serializable {", + " private static final long serialVersionUID = -8116050813861599066L;", " private final int[] value;", "", " AutoAnnotation_AnnotationFactory_newMyAnnotation(int[] value) {", @@ -288,6 +296,7 @@ public class AutoAnnotationCompilationTest { Compilation compilation = javac() .withProcessors(new AutoAnnotationProcessor()) + .withOptions("-A" + Nullables.NULLABLE_OPTION + "=") .compile(annotationFactoryJavaFile, myAnnotationJavaFile, gwtCompatibleJavaFile); assertThat(compilation).succeededWithoutWarnings(); assertThat(compilation) @@ -343,6 +352,7 @@ public class AutoAnnotationCompilationTest { "", "import com.example.annotations.MyAnnotation;", "import com.example.enums.MyEnum;", + "import java.io.Serializable;", "import java.util.Arrays;", "import java.util.Collection;", "import java.util.List;", @@ -350,8 +360,9 @@ public class AutoAnnotationCompilationTest { GeneratedImport.importGeneratedAnnotationType(), "", "@Generated(\"" + AutoAnnotationProcessor.class.getName() + "\")", - "final class AutoAnnotation_AnnotationFactory_newMyAnnotation implements MyAnnotation" - + " {", + "final class AutoAnnotation_AnnotationFactory_newMyAnnotation implements MyAnnotation," + + " Serializable {", + " private static final long serialVersionUID = -2102364343628921304L;", " private final int[] value;", " private final MyEnum[] enums;", "", @@ -426,6 +437,7 @@ public class AutoAnnotationCompilationTest { Compilation compilation = javac() .withProcessors(new AutoAnnotationProcessor()) + .withOptions("-A" + Nullables.NULLABLE_OPTION + "=") .compile(annotationFactoryJavaFile, myEnumJavaFile, myAnnotationJavaFile); assertThat(compilation).succeededWithoutWarnings(); assertThat(compilation) @@ -457,9 +469,7 @@ public class AutoAnnotationCompilationTest { " @NotAutoAnnotation Empty notNewEmpty() {}", "}"); Compilation compilation = - javac() - .withProcessors(new AutoAnnotationProcessor()) - .compile(erroneousJavaFileObject); + javac().withProcessors(new AutoAnnotationProcessor()).compile(erroneousJavaFileObject); assertThat(compilation) .hadErrorContaining("NotAutoAnnotation") .inFile(erroneousJavaFileObject) diff --git a/value/src/test/java/com/google/auto/value/processor/AutoAnnotationErrorsTest.java b/value/src/test/java/com/google/auto/value/processor/AutoAnnotationErrorsTest.java index 55a4c5db..094b570d 100644 --- a/value/src/test/java/com/google/auto/value/processor/AutoAnnotationErrorsTest.java +++ b/value/src/test/java/com/google/auto/value/processor/AutoAnnotationErrorsTest.java @@ -57,9 +57,7 @@ public class AutoAnnotationErrorsTest { " }", "}"); Compilation compilation = - javac() - .withProcessors(new AutoAnnotationProcessor()) - .compile(TEST_ANNOTATION, testSource); + javac().withProcessors(new AutoAnnotationProcessor()).compile(TEST_ANNOTATION, testSource); assertThat(compilation).succeededWithoutWarnings(); } @@ -79,9 +77,7 @@ public class AutoAnnotationErrorsTest { " }", "}"); Compilation compilation = - javac() - .withProcessors(new AutoAnnotationProcessor()) - .compile(TEST_ANNOTATION, testSource); + javac().withProcessors(new AutoAnnotationProcessor()).compile(TEST_ANNOTATION, testSource); assertThat(compilation) .hadErrorContaining("must be static") .inFile(testSource) @@ -103,9 +99,7 @@ public class AutoAnnotationErrorsTest { " }", "}"); Compilation compilation = - javac() - .withProcessors(new AutoAnnotationProcessor()) - .compile(TEST_ANNOTATION, testSource); + javac().withProcessors(new AutoAnnotationProcessor()).compile(TEST_ANNOTATION, testSource); assertThat(compilation) .hadErrorContaining("must be an annotation type, not java.lang.String") .inFile(testSource) @@ -132,9 +126,7 @@ public class AutoAnnotationErrorsTest { " }", "}"); Compilation compilation = - javac() - .withProcessors(new AutoAnnotationProcessor()) - .compile(TEST_ANNOTATION, testSource); + javac().withProcessors(new AutoAnnotationProcessor()).compile(TEST_ANNOTATION, testSource); assertThat(compilation) .hadErrorContaining("@AutoAnnotation methods cannot be overloaded") .inFile(testSource) @@ -196,9 +188,7 @@ public class AutoAnnotationErrorsTest { " }", "}"); Compilation compilation = - javac() - .withProcessors(new AutoAnnotationProcessor()) - .compile(TEST_ANNOTATION, testSource); + javac().withProcessors(new AutoAnnotationProcessor()).compile(TEST_ANNOTATION, testSource); assertThat(compilation) .hadErrorContaining("method parameter 'fred' must have the same name") .inFile(testSource) @@ -221,9 +211,7 @@ public class AutoAnnotationErrorsTest { " }", "}"); Compilation compilation = - javac() - .withProcessors(new AutoAnnotationProcessor()) - .compile(TEST_ANNOTATION, testSource); + javac().withProcessors(new AutoAnnotationProcessor()).compile(TEST_ANNOTATION, testSource); assertThat(compilation) .hadErrorContaining( "method parameter 'value' has type java.lang.String " @@ -267,9 +255,7 @@ public class AutoAnnotationErrorsTest { " }", "}"); Compilation compilation = - javac() - .withProcessors(new AutoAnnotationProcessor()) - .compile(testSource, testAnnotation); + javac().withProcessors(new AutoAnnotationProcessor()).compile(testSource, testAnnotation); assertThat(compilation) .hadErrorContaining( "method parameter 'value' has type " @@ -296,9 +282,7 @@ public class AutoAnnotationErrorsTest { " }", "}"); Compilation compilation = - javac() - .withProcessors(new AutoAnnotationProcessor()) - .compile(TEST_ANNOTATION, testSource); + javac().withProcessors(new AutoAnnotationProcessor()).compile(TEST_ANNOTATION, testSource); assertThat(compilation) .hadErrorContaining( "method parameter 'other' must have the same name as a member of " @@ -323,9 +307,7 @@ public class AutoAnnotationErrorsTest { " }", "}"); Compilation compilation = - javac() - .withProcessors(new AutoAnnotationProcessor()) - .compile(TEST_ANNOTATION, testSource); + javac().withProcessors(new AutoAnnotationProcessor()).compile(TEST_ANNOTATION, testSource); assertThat(compilation) .hadErrorContaining("method needs a parameter with name 'value' and type int") .inFile(testSource) @@ -357,9 +339,7 @@ public class AutoAnnotationErrorsTest { " }", "}"); Compilation compilation = - javac() - .withProcessors(new AutoAnnotationProcessor()) - .compile(annotationSource, testSource); + javac().withProcessors(new AutoAnnotationProcessor()).compile(annotationSource, testSource); assertThat(compilation) .hadErrorContaining( "@AutoAnnotation cannot yet supply a default value for annotation-valued member " @@ -399,10 +379,7 @@ public class AutoAnnotationErrorsTest { " }", "}"); Compilation compilation = - javac() - .withProcessors(new AutoAnnotationProcessor()) - .compile(annotationSource, testSource); - assertThat(compilation) - .hadErrorContaining("variable value$ is already defined in constructor"); + javac().withProcessors(new AutoAnnotationProcessor()).compile(annotationSource, testSource); + assertThat(compilation).hadErrorContaining("variable value$ is already defined in constructor"); } } diff --git a/value/src/test/java/com/google/auto/value/processor/AutoBuilderCompilationTest.java b/value/src/test/java/com/google/auto/value/processor/AutoBuilderCompilationTest.java new file mode 100644 index 00000000..50b6b271 --- /dev/null +++ b/value/src/test/java/com/google/auto/value/processor/AutoBuilderCompilationTest.java @@ -0,0 +1,875 @@ +/* + * Copyright 2021 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.google.auto.value.processor; + +import static com.google.common.base.StandardSystemProperty.JAVA_SPECIFICATION_VERSION; +import static com.google.common.truth.TruthJUnit.assume; +import static com.google.testing.compile.CompilationSubject.assertThat; +import static com.google.testing.compile.Compiler.javac; + +import com.google.testing.compile.Compilation; +import com.google.testing.compile.JavaFileObjects; +import javax.tools.JavaFileObject; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +@RunWith(JUnit4.class) +public final class AutoBuilderCompilationTest { + private static final JavaFileObject EXPECTED_SIMPLE_OUTPUT = + JavaFileObjects.forSourceLines( + "foo.bar.AutoBuilder_Baz_Builder", + "package foo.bar;", + "", + GeneratedImport.importGeneratedAnnotationType(), + "", + "@Generated(\"" + AutoBuilderProcessor.class.getName() + "\")", + "class AutoBuilder_Baz_Builder implements Baz.Builder {", + " private Integer anInt;", + " private String aString;", + "", + " AutoBuilder_Baz_Builder() {}", + "", + " @Override public Baz.Builder setAnInt(int anInt) {", + " this.anInt = anInt;", + " return this;", + " }", + "", + " @Override public Baz.Builder setAString(String aString) {", + " if (aString == null) {", + " throw new NullPointerException(\"Null aString\");", + " }", + " this.aString = aString;", + " return this;", + " }", + "", + " @Override", + " public Baz build() {", + " if (this.anInt == null", + " || this.aString == null) {", + " StringBuilder missing = new StringBuilder();", + " if (this.anInt == null) {", + " missing.append(\" anInt\");", + " }", + " if (this.aString == null) {", + " missing.append(\" aString\");", + " }", + " throw new IllegalStateException(\"Missing required properties:\" + missing);", + " }", + " return new Baz(", + " this.anInt,", + " this.aString);", + " }", + "}"); + + @Test + public void simpleSuccess() { + JavaFileObject javaFileObject = + JavaFileObjects.forSourceLines( + "foo.bar.Baz", + "package foo.bar;", + "", + "import com.google.auto.value.AutoBuilder;", + "", + "public class Baz {", + " private final int anInt;", + " private final String aString;", + "", + " public Baz(int anInt, String aString) {", + " this.anInt = anInt;", + " this.aString = aString;", + " }", + "", + " public int anInt() {", + " return anInt;", + " }", + "", + " public String aString() {", + " return aString;", + " }", + "", + " public static Builder builder() {", + " return new AutoBuilder_Baz_Builder();", + " }", + "", + " @AutoBuilder", + " public interface Builder {", + " Builder setAnInt(int x);", + " Builder setAString(String x);", + " Baz build();", + " }", + "}"); + Compilation compilation = + javac() + .withProcessors(new AutoBuilderProcessor()) + .withOptions("-Acom.google.auto.value.AutoBuilderIsUnstable") + .compile(javaFileObject); + assertThat(compilation) + .generatedSourceFile("foo.bar.AutoBuilder_Baz_Builder") + .hasSourceEquivalentTo(EXPECTED_SIMPLE_OUTPUT); + } + + @Test + public void simpleRecord() { + double version = Double.parseDouble(JAVA_SPECIFICATION_VERSION.value()); + assume().that(version).isAtLeast(16.0); + JavaFileObject javaFileObject = + JavaFileObjects.forSourceLines( + "foo.bar.Baz", + "package foo.bar;", + "", + "import com.google.auto.value.AutoBuilder;", + "", + "public record Baz(int anInt, String aString) {", + " public static Builder builder() {", + " return new AutoBuilder_Baz_Builder();", + " }", + "", + " @AutoBuilder", + " public interface Builder {", + " Builder setAnInt(int x);", + " Builder setAString(String x);", + " Baz build();", + " }", + "}"); + Compilation compilation = + javac() + .withProcessors(new AutoBuilderProcessor()) + .withOptions("-Acom.google.auto.value.AutoBuilderIsUnstable") + .compile(javaFileObject); + assertThat(compilation) + .generatedSourceFile("foo.bar.AutoBuilder_Baz_Builder") + .hasSourceEquivalentTo(EXPECTED_SIMPLE_OUTPUT); + } + + @Test + public void buildOtherPackage() { + JavaFileObject built = + JavaFileObjects.forSourceLines( + "com.example.Built", + "package com.example;", + "", + "public class Built {", + " private final int anInt;", + " private final String aString;", + "", + " public Built(int anInt, String aString) {", + " this.anInt = anInt;", + " this.aString = aString;", + " }", + "}"); + JavaFileObject builder = + JavaFileObjects.forSourceLines( + "foo.bar.Builder", + "package foo.bar;", + "", + "import com.example.Built;", + "import com.google.auto.value.AutoBuilder;", + "", + "@AutoBuilder(ofClass = Built.class)", + "public interface Builder {", + " public static Builder builder() {", + " return new AutoBuilder_Builder();", + " }", + "", + " Builder setAnInt(int x);", + " Builder setAString(String x);", + " Built build();", + "}"); + Compilation compilation = + javac() + .withProcessors(new AutoBuilderProcessor()) + .withOptions("-Acom.google.auto.value.AutoBuilderIsUnstable") + .compile(built, builder); + assertThat(compilation).succeededWithoutWarnings(); + assertThat(compilation).generatedSourceFile("foo.bar.AutoBuilder_Builder"); + } + + @Test + public void autoBuilderOnEnum() { + JavaFileObject javaFileObject = + JavaFileObjects.forSourceLines( + "foo.bar.Baz", + "package foo.bar;", + "", + "import com.google.auto.value.AutoBuilder;", + "", + "@AutoBuilder", + "public enum Baz {", + " ZIG, ZAG, DUSTIN,", + "}"); + Compilation compilation = + javac() + .withProcessors(new AutoBuilderProcessor()) + .withOptions("-Acom.google.auto.value.AutoBuilderIsUnstable") + .compile(javaFileObject); + assertThat(compilation).failed(); + assertThat(compilation) + .hadErrorContaining( + "[AutoBuilderWrongType] @AutoBuilder only applies to classes and interfaces") + .inFile(javaFileObject) + .onLineContaining("enum Baz"); + } + + @Test + public void autoBuilderPrivate() { + JavaFileObject javaFileObject = + JavaFileObjects.forSourceLines( + "foo.bar.Baz", + "package foo.bar;", + "", + "import com.google.auto.value.AutoBuilder;", + "", + "public class Baz {", + " @AutoBuilder", + " private interface Builder {", + " Baz build();", + " }", + "}"); + Compilation compilation = + javac() + .withProcessors(new AutoBuilderProcessor()) + .withOptions("-Acom.google.auto.value.AutoBuilderIsUnstable") + .compile(javaFileObject); + assertThat(compilation).failed(); + assertThat(compilation) + .hadErrorContaining("[AutoBuilderPrivate] @AutoBuilder class must not be private") + .inFile(javaFileObject) + .onLineContaining("interface Builder"); + } + + @Test + public void autoBuilderNestedInPrivate() { + JavaFileObject javaFileObject = + JavaFileObjects.forSourceLines( + "foo.bar.Baz", + "package foo.bar;", + "", + "import com.google.auto.value.AutoBuilder;", + "", + "public class Baz {", + " private static class Private {", + " @AutoBuilder", + " public interface Builder {", + " Baz build();", + " }", + " }", + "}"); + Compilation compilation = + javac() + .withProcessors(new AutoBuilderProcessor()) + .withOptions("-Acom.google.auto.value.AutoBuilderIsUnstable") + .compile(javaFileObject); + assertThat(compilation).failed(); + assertThat(compilation) + .hadErrorContaining( + "[AutoBuilderInPrivate] @AutoBuilder class must not be nested in a private class") + .inFile(javaFileObject) + .onLineContaining("interface Builder"); + } + + @Test + public void autoBuilderInner() { + JavaFileObject javaFileObject = + JavaFileObjects.forSourceLines( + "foo.bar.Baz", + "package foo.bar;", + "", + "import com.google.auto.value.AutoBuilder;", + "", + "public class Baz {", + " @AutoBuilder", + " abstract class Builder {", + " abstract Baz build();", + " }", + "}"); + Compilation compilation = + javac() + .withProcessors(new AutoBuilderProcessor()) + .withOptions("-Acom.google.auto.value.AutoBuilderIsUnstable") + .compile(javaFileObject); + assertThat(compilation).failed(); + assertThat(compilation) + .hadErrorContaining("[AutoBuilderInner] Nested @AutoBuilder class must be static") + .inFile(javaFileObject) + .onLineContaining("class Builder"); + } + + @Test + public void innerConstructor() { + JavaFileObject javaFileObject = + JavaFileObjects.forSourceLines( + "foo.bar.Baz", + "package foo.bar;", + "", + "import com.google.auto.value.AutoBuilder;", + "", + "public class Baz {", + " class Inner {}", + "", + " @AutoBuilder(ofClass = Inner.class)", + " interface Builder {", + " abstract Inner build();", + " }", + "}"); + Compilation compilation = + javac() + .withProcessors(new AutoBuilderProcessor()) + .withOptions("-Acom.google.auto.value.AutoBuilderIsUnstable") + .compile(javaFileObject); + assertThat(compilation).failed(); + assertThat(compilation) + .hadErrorContaining("[AutoBuilderInner] Nested @AutoBuilder ofClass class must be static") + .inFile(javaFileObject) + .onLineContaining("interface Builder"); + } + + @Test + public void noVisibleConstructor() { + JavaFileObject javaFileObject = + JavaFileObjects.forSourceLines( + "foo.bar.Baz", + "package foo.bar;", + "", + "import com.google.auto.value.AutoBuilder;", + "", + "public class Baz {", + " @AutoBuilder(ofClass = System.class)", + " interface Builder {", + " abstract System build();", + " }", + "}"); + Compilation compilation = + javac() + .withProcessors(new AutoBuilderProcessor()) + .withOptions("-Acom.google.auto.value.AutoBuilderIsUnstable") + .compile(javaFileObject); + assertThat(compilation).failed(); + assertThat(compilation) + .hadErrorContaining("[AutoBuilderNoVisible] No visible constructor for java.lang.System") + .inFile(javaFileObject) + .onLineContaining("interface Builder"); + } + + @Test + public void noVisibleMethod() { + JavaFileObject javaFileObject = + JavaFileObjects.forSourceLines( + "foo.bar.Baz", + "package foo.bar;", + "", + "import com.google.auto.value.AutoBuilder;", + "", + "public class Baz {", + " private static Baz of() {", + " return new Baz();", + " }", + "", + " @AutoBuilder(callMethod = \"of\")", + " interface Builder {", + " abstract Baz build();", + " }", + "}"); + Compilation compilation = + javac() + .withProcessors(new AutoBuilderProcessor()) + .withOptions("-Acom.google.auto.value.AutoBuilderIsUnstable") + .compile(javaFileObject); + assertThat(compilation).failed(); + assertThat(compilation) + .hadErrorContaining( + "[AutoBuilderNoVisible] No visible static method named \"of\" for foo.bar.Baz") + .inFile(javaFileObject) + .onLineContaining("interface Builder"); + } + + @Test + public void methodNotStatic() { + JavaFileObject javaFileObject = + JavaFileObjects.forSourceLines( + "foo.bar.Baz", + "package foo.bar;", + "", + "import com.google.auto.value.AutoBuilder;", + "", + "public class Baz {", + " Baz of() {", + " return this;", + " }", + "", + " @AutoBuilder(callMethod = \"of\")", + " interface Builder {", + " abstract Baz build();", + " }", + "}"); + Compilation compilation = + javac() + .withProcessors(new AutoBuilderProcessor()) + .withOptions("-Acom.google.auto.value.AutoBuilderIsUnstable") + .compile(javaFileObject); + assertThat(compilation).failed(); + assertThat(compilation) + .hadErrorContaining( + "[AutoBuilderNoVisible] No visible static method named \"of\" for foo.bar.Baz") + .inFile(javaFileObject) + .onLineContaining("interface Builder"); + } + + @Test + public void noMatchingConstructor() { + JavaFileObject javaFileObject = + JavaFileObjects.forSourceLines( + "foo.bar.Baz", + "package foo.bar;", + "", + "import com.google.auto.value.AutoBuilder;", + "", + "public class Baz {", + " public Baz(int notMe) {}", + "", + " public Baz(String notMeEither) {}", + "", + " @AutoBuilder", + " interface Builder {", + " Builder setBuh(String x);", + " abstract Baz build();", + " }", + "}"); + Compilation compilation = + javac() + .withProcessors(new AutoBuilderProcessor()) + .withOptions("-Acom.google.auto.value.AutoBuilderIsUnstable") + .compile(javaFileObject); + assertThat(compilation).failed(); + assertThat(compilation) + .hadErrorContaining( + "[AutoBuilderNoMatch] Property names do not correspond to the parameter names of any" + + " constructor:\n" + + " Baz(int notMe)\n" + + " Baz(java.lang.String notMeEither)") + .inFile(javaFileObject) + .onLineContaining("interface Builder"); + } + + @Test + public void twoMatchingConstructors() { + JavaFileObject javaFileObject = + JavaFileObjects.forSourceLines( + "foo.bar.Baz", + "package foo.bar;", + "", + "import com.google.auto.value.AutoBuilder;", + "", + "public class Baz {", + " public Baz() {}", + "", + " public Baz(int buh) {}", + "", + " public Baz(String buh) {}", + "", + " @AutoBuilder", + " interface Builder {", + " Builder setBuh(String x);", + " abstract Baz build();", + " }", + "}"); + Compilation compilation = + javac() + .withProcessors(new AutoBuilderProcessor()) + .withOptions("-Acom.google.auto.value.AutoBuilderIsUnstable") + .compile(javaFileObject); + assertThat(compilation).failed(); + assertThat(compilation) + .hadErrorContaining( + "[AutoBuilderAmbiguous] Property names correspond to more than one constructor:\n" + + " Baz(int buh)\n" + + " Baz(java.lang.String buh)") + .inFile(javaFileObject) + .onLineContaining("interface Builder"); + } + + @Test + public void constructInterface() { + JavaFileObject javaFileObject = + JavaFileObjects.forSourceLines( + "foo.bar.Baz", + "package foo.bar;", + "", + "import com.google.auto.value.AutoBuilder;", + "", + "public interface Baz {", + " @AutoBuilder", + " interface Builder {", + " abstract Baz build();", + " }", + "}"); + Compilation compilation = + javac() + .withProcessors(new AutoBuilderProcessor()) + .withOptions("-Acom.google.auto.value.AutoBuilderIsUnstable") + .compile(javaFileObject); + assertThat(compilation).failed(); + assertThat(compilation) + .hadErrorContaining( + "[AutoBuilderEnclosing] @AutoBuilder must specify ofClass=Something.class or it must" + + " be nested inside the class to be built; actually nested inside interface" + + " foo.bar.Baz") + .inFile(javaFileObject) + .onLineContaining("interface Builder"); + } + + @Test + public void inconsistentSetPrefix() { + JavaFileObject javaFileObject = + JavaFileObjects.forSourceLines( + "foo.bar.Baz", + "package foo.bar;", + "", + "import com.google.auto.value.AutoBuilder;", + "", + "class Baz {", + " Baz(int one, int two) {}", + "", + " @AutoBuilder", + " interface Builder {", + " abstract Builder one(int x);", + " abstract Builder setTwo(int x);", + " abstract Baz build();", + " }", + "}"); + Compilation compilation = + javac() + .withProcessors(new AutoBuilderProcessor()) + .withOptions("-Acom.google.auto.value.AutoBuilderIsUnstable") + .compile(javaFileObject); + assertThat(compilation).failed(); + assertThat(compilation) + .hadErrorContaining( + "[AutoBuilderSetNotSet] If any setter methods use the setFoo convention then all must") + .inFile(javaFileObject) + .onLineContaining("Builder one(int x)"); + } + + @Test + public void missingSetter() { + JavaFileObject javaFileObject = + JavaFileObjects.forSourceLines( + "foo.bar.Baz", + "package foo.bar;", + "", + "import com.google.auto.value.AutoBuilder;", + "", + "class Baz {", + " Baz(int one, int two) {}", + "", + " @AutoBuilder", + " interface Builder {", + " abstract Builder one(int x);", + " abstract Baz build();", + " }", + "}"); + Compilation compilation = + javac() + .withProcessors(new AutoBuilderProcessor()) + .withOptions("-Acom.google.auto.value.AutoBuilderIsUnstable") + .compile(javaFileObject); + assertThat(compilation).failed(); + assertThat(compilation) + .hadErrorContaining( + "[AutoBuilderBuilderMissingMethod] Expected a method with this signature:" + + " foo.bar.Baz.Builder two(int), or a twoBuilder() method") + .inFile(javaFileObject) + .onLineContaining("interface Builder"); + } + + @Test + public void tooManyArgs() { + JavaFileObject javaFileObject = + JavaFileObjects.forSourceLines( + "foo.bar.Baz", + "package foo.bar;", + "", + "import com.google.auto.value.AutoBuilder;", + "", + "class Baz {", + " Baz(int one, int two) {}", + "", + " @AutoBuilder", + " interface Builder {", + " abstract Builder one(int x);", + " abstract Builder two(int x);", + " abstract Builder many(int x, int y);", + " abstract Baz build();", + " }", + "}"); + Compilation compilation = + javac() + .withProcessors(new AutoBuilderProcessor()) + .withOptions("-Acom.google.auto.value.AutoBuilderIsUnstable") + .compile(javaFileObject); + assertThat(compilation).failed(); + assertThat(compilation) + .hadErrorContaining("[AutoBuilderBuilderArgs] Builder methods must have 0 or 1 parameters") + .inFile(javaFileObject) + .onLineContaining("many(int x, int y)"); + } + + @Test + public void alienNoArgMethod() { + JavaFileObject javaFileObject = + JavaFileObjects.forSourceLines( + "foo.bar.Baz", + "package foo.bar;", + "", + "import com.google.auto.value.AutoBuilder;", + "", + "class Baz {", + " Baz(int one, int two) {}", + "", + " @AutoBuilder", + " interface Builder {", + " abstract Builder one(int x);", + " abstract Builder two(int x);", + " abstract String alien();", + " abstract Baz build();", + " }", + "}"); + Compilation compilation = + javac() + .withProcessors(new AutoBuilderProcessor()) + .withOptions("-Acom.google.auto.value.AutoBuilderIsUnstable") + .compile(javaFileObject); + assertThat(compilation).failed(); + assertThat(compilation) + .hadErrorContaining( + "[AutoBuilderBuilderNoArg] Method without arguments should be a build method returning" + + " foo.bar.Baz, or a getter method with the same name and type as a parameter of" + + " Baz(int one, int two), or fooBuilder() where foo is a parameter of Baz(int" + + " one, int two)") + .inFile(javaFileObject) + .onLineContaining("String alien()"); + } + + @Test + public void alienOneArgMethod() { + JavaFileObject javaFileObject = + JavaFileObjects.forSourceLines( + "foo.bar.Baz", + "package foo.bar;", + "", + "import com.google.auto.value.AutoBuilder;", + "", + "class Baz {", + " Baz(int one, int two) {}", + "", + " @AutoBuilder", + " interface Builder {", + " abstract Builder one(int x);", + " abstract Builder two(int x);", + " abstract Builder three(int x);", + " abstract Baz build();", + " }", + "}"); + Compilation compilation = + javac() + .withProcessors(new AutoBuilderProcessor()) + .withOptions("-Acom.google.auto.value.AutoBuilderIsUnstable") + .compile(javaFileObject); + assertThat(compilation).failed(); + assertThat(compilation) + .hadErrorContaining( + "[AutoBuilderBuilderWhatProp] Method three does not correspond to " + + "a parameter of Baz(int one, int two)") + .inFile(javaFileObject) + .onLineContaining("three(int x)"); + } + + @Test + public void setterReturnType() { + JavaFileObject javaFileObject = + JavaFileObjects.forSourceLines( + "foo.bar.Baz", + "package foo.bar;", + "", + "import com.google.auto.value.AutoBuilder;", + "", + "class Baz {", + " Baz(int one, int two) {}", + "", + " @AutoBuilder", + " interface Builder {", + " abstract Builder one(int x);", + " abstract void two(int x);", + " abstract Baz build();", + " }", + "}"); + Compilation compilation = + javac() + .withProcessors(new AutoBuilderProcessor()) + .withOptions("-Acom.google.auto.value.AutoBuilderIsUnstable") + .compile(javaFileObject); + assertThat(compilation).failed(); + assertThat(compilation) + .hadErrorContaining( + "[AutoBuilderBuilderRet] Setter methods must return foo.bar.Baz.Builder") + .inFile(javaFileObject) + .onLineContaining("two(int x)"); + } + + @Test + public void nullableSetterForNonNullableParameter() { + JavaFileObject javaFileObject = + JavaFileObjects.forSourceLines( + "foo.bar.Baz", + "package foo.bar;", + "", + "import com.google.auto.value.AutoBuilder;", + "import org.checkerframework.checker.nullness.qual.Nullable;", + "", + "class Baz {", + " Baz(String thing) {}", + "", + " @AutoBuilder", + " interface Builder {", + " abstract Builder thing(@Nullable String x);", + " abstract Baz build();", + " }", + "}"); + JavaFileObject nullableFileObject = + JavaFileObjects.forSourceLines( + "org.checkerframework.checker.nullness.qual.Nullable", + "package org.jspecify.nullness;", + "", + "import java.lang.annotation.ElementType;", + "import java.lang.annotation.Target;", + "", + "@Target(ElementType.TYPE_USE)", + "public @interface Nullable {}"); + Compilation compilation = + javac() + .withProcessors(new AutoBuilderProcessor()) + .withOptions("-Acom.google.auto.value.AutoBuilderIsUnstable") + .compile(javaFileObject, nullableFileObject); + assertThat(compilation).failed(); + assertThat(compilation) + .hadErrorContaining( + "[AutoBuilderNullNotNull] Parameter of setter method is @Nullable but parameter" + + " \"thing\" of Baz(java.lang.String thing) is not") + .inFile(javaFileObject) + .onLineContaining("thing(@Nullable String x)"); + } + + @Test + public void setterWrongType() { + JavaFileObject javaFileObject = + JavaFileObjects.forSourceLines( + "foo.bar.Baz", + "package foo.bar;", + "", + "import com.google.auto.value.AutoBuilder;", + "", + "class Baz {", + " Baz(int up, int down) {}", + "", + " @AutoBuilder", + " interface Builder {", + " abstract Builder up(int x);", + " abstract Builder down(String x);", + " abstract Baz build();", + " }", + "}"); + Compilation compilation = + javac() + .withProcessors(new AutoBuilderProcessor()) + .withOptions("-Acom.google.auto.value.AutoBuilderIsUnstable") + .compile(javaFileObject); + assertThat(compilation).failed(); + assertThat(compilation) + .hadErrorContaining( + "[AutoBuilderGetVsSet] Parameter type java.lang.String of setter method should be int" + + " to match parameter \"down\" of Baz(int up, int down)") + .inFile(javaFileObject) + .onLineContaining("down(String x)"); + } + + @Test + public void setterWrongTypeEvenWithConversion() { + JavaFileObject javaFileObject = + JavaFileObjects.forSourceLines( + "foo.bar.Baz", + "package foo.bar;", + "", + "import com.google.auto.value.AutoBuilder;", + "import java.util.Optional;", + "", + "class Baz {", + " Baz(Optional<String> maybe) {}", + "", + " @AutoBuilder", + " interface Builder {", + " abstract Builder maybe(int x);", + " abstract Baz build();", + " }", + "}"); + Compilation compilation = + javac() + .withProcessors(new AutoBuilderProcessor()) + .withOptions("-Acom.google.auto.value.AutoBuilderIsUnstable") + .compile(javaFileObject); + assertThat(compilation).failed(); + assertThat(compilation) + .hadErrorContaining( + "[AutoBuilderGetVsSetOrConvert] Parameter type int of setter method should be" + + " java.util.Optional<java.lang.String> to match parameter \"maybe\" of" + + " Baz(java.util.Optional<java.lang.String> maybe), or it should be a type that" + + " can be passed to Optional.of to produce java.util.Optional<java.lang.String>") + .inFile(javaFileObject) + .onLineContaining("maybe(int x)"); + } + + @Test + public void typeParamMismatch() { + JavaFileObject javaFileObject = + JavaFileObjects.forSourceLines( + "foo.bar.Baz", + "package foo.bar;", + "", + "import com.google.auto.value.AutoBuilder;", + "import java.util.Optional;", + "", + "class Baz<T> {", + " Baz(T param) {}", + "", + " @AutoBuilder", + " interface Builder<E> {", + " abstract Builder<E> param(E param);", + " abstract Baz<E> build();", + " }", + "}"); + Compilation compilation = + javac() + .withProcessors(new AutoBuilderProcessor()) + .withOptions("-Acom.google.auto.value.AutoBuilderIsUnstable") + .compile(javaFileObject); + assertThat(compilation).failed(); + assertThat(compilation) + .hadErrorContaining( + "[AutoBuilderTypeParams] Builder type parameters <E> must match type parameters <T> of" + + " Baz(T param)") + .inFile(javaFileObject) + .onLineContaining("interface Builder<E>"); + } +} diff --git a/value/src/test/java/com/google/auto/value/processor/AutoOneOfCompilationTest.java b/value/src/test/java/com/google/auto/value/processor/AutoOneOfCompilationTest.java index 63e84199..788b543a 100644 --- a/value/src/test/java/com/google/auto/value/processor/AutoOneOfCompilationTest.java +++ b/value/src/test/java/com/google/auto/value/processor/AutoOneOfCompilationTest.java @@ -43,7 +43,9 @@ public class AutoOneOfCompilationTest { "import java.io.Serializable;", "", "@AutoOneOf(TaskResult.Kind.class)", - "public abstract class TaskResult<V, T extends Throwable> {", + "public abstract class TaskResult<V, T extends Throwable> implements Serializable {", + " private static final long serialVersionUID = 1234L;", + "", " public enum Kind {VALUE, EXCEPTION, EMPTY}", " public abstract Kind getKind();", "", @@ -92,6 +94,8 @@ public class AutoOneOfCompilationTest { " // Parent class that each implementation will inherit from.", " private abstract static class Parent_<V, T extends Throwable> " + "extends TaskResult<V, T> {", + " private static final long serialVersionUID = 1234L;", + "", " @Override", " public V value() {", " throw new UnsupportedOperationException(getKind().toString());", @@ -111,6 +115,8 @@ public class AutoOneOfCompilationTest { " // Implementation when the contained property is \"value\".", " private static final class Impl_value<V, T extends Throwable> " + "extends Parent_<V, T> {", + " private static final long serialVersionUID = 1234L;", + "", " private final V value;", "", " Impl_value(V value) {", @@ -152,6 +158,8 @@ public class AutoOneOfCompilationTest { " // Implementation when the contained property is \"exception\".", " private static final class Impl_exception<V, T extends Throwable> " + "extends Parent_<V, T> {", + " private static final long serialVersionUID = 1234L;", + "", " private final Throwable exception;", "", " Impl_exception(Throwable exception) {", @@ -193,6 +201,8 @@ public class AutoOneOfCompilationTest { " // Implementation when the contained property is \"empty\".", " private static final class Impl_empty<V, T extends Throwable> " + "extends Parent_<V, T> {", + " private static final long serialVersionUID = 1234L;", + "", " static final Impl_empty<?, ?> INSTANCE = new Impl_empty<>();", "", " private Impl_empty() {}", @@ -200,6 +210,10 @@ public class AutoOneOfCompilationTest { " @Override", " public void empty() {}", "", + " private Object readResolve() {", + " return INSTANCE;", + " }", + "", " @Override", " public String toString() {", " return \"TaskResult{empty}\";", @@ -223,7 +237,8 @@ public class AutoOneOfCompilationTest { Compilation compilation = javac() .withProcessors(new AutoOneOfProcessor()) - .withOptions("-Xlint:-processing", "-implicit:none") + .withOptions( + "-Xlint:-processing", "-implicit:none", "-A" + Nullables.NULLABLE_OPTION + "=") .compile(javaFileObject); assertThat(compilation).succeededWithoutWarnings(); assertThat(compilation) @@ -307,7 +322,8 @@ public class AutoOneOfCompilationTest { Compilation compilation = javac() .withProcessors(new AutoOneOfProcessor()) - .withOptions("-Xlint:-processing", "-implicit:none") + .withOptions( + "-Xlint:-processing", "-implicit:none", "-A" + Nullables.NULLABLE_OPTION + "=") .compile(javaFileObject); assertThat(compilation).succeededWithoutWarnings(); assertThat(compilation) diff --git a/value/src/test/java/com/google/auto/value/processor/AutoValueCompilationTest.java b/value/src/test/java/com/google/auto/value/processor/AutoValueCompilationTest.java index e46c9f51..ab6690fd 100644 --- a/value/src/test/java/com/google/auto/value/processor/AutoValueCompilationTest.java +++ b/value/src/test/java/com/google/auto/value/processor/AutoValueCompilationTest.java @@ -57,8 +57,8 @@ public class AutoValueCompilationTest { public void simpleSuccess() { // Positive test case that ensures we generate the expected code for at least one case. // Most AutoValue code-generation tests are functional, meaning that we check that the generated - // code does the right thing rather than checking what it looks like, but this test is a sanity - // check that we are not generating correct but weird code. + // code does the right thing rather than checking what it looks like, but this test checks that + // we are not generating correct but weird code. JavaFileObject javaFileObject = JavaFileObjects.forSourceLines( "foo.bar.Baz", @@ -118,7 +118,10 @@ public class AutoValueCompilationTest { " }", "}"); Compilation compilation = - javac().withProcessors(new AutoValueProcessor()).compile(javaFileObject); + javac() + .withProcessors(new AutoValueProcessor()) + .withOptions("-A" + Nullables.NULLABLE_OPTION + "=") + .compile(javaFileObject); assertThat(compilation) .generatedSourceFile("foo.bar.AutoValue_Baz") .hasSourceEquivalentTo(expectedOutput); @@ -216,7 +219,10 @@ public class AutoValueCompilationTest { " }", "}"); Compilation compilation = - javac().withProcessors(new AutoValueProcessor()).compile(javaFileObject); + javac() + .withProcessors(new AutoValueProcessor()) + .withOptions("-A" + Nullables.NULLABLE_OPTION + "=") + .compile(javaFileObject); assertThat(compilation) .generatedSourceFile("foo.bar.AutoValue_Baz") .hasSourceEquivalentTo(expectedOutput); @@ -335,7 +341,6 @@ public class AutoValueCompilationTest { " return false;", " }", "", - " @Override", " public int hashCode() {", " int h$ = 1;", @@ -348,7 +353,8 @@ public class AutoValueCompilationTest { Compilation compilation = javac() .withProcessors(new AutoValueProcessor()) - .withOptions("-Xlint:-processing", "-implicit:none") + .withOptions( + "-Xlint:-processing", "-implicit:none", "-A" + Nullables.NULLABLE_OPTION + "=") .compile(annotFileObject, outerFileObject, nestyFileObject); assertThat(compilation).succeededWithoutWarnings(); assertThat(compilation) @@ -1129,10 +1135,10 @@ public class AutoValueCompilationTest { " return this.anInt == that.anInt()", " && Arrays.equals(this.aByteArray, " + "(that instanceof AutoValue_Baz) " - + "? ((AutoValue_Baz) that).aByteArray : that.aByteArray())", + + "? ((AutoValue_Baz<?>) that).aByteArray : that.aByteArray())", " && Arrays.equals(this.aNullableIntArray, " + "(that instanceof AutoValue_Baz) " - + "? ((AutoValue_Baz) that).aNullableIntArray : that.aNullableIntArray())", + + "? ((AutoValue_Baz<?>) that).aNullableIntArray : that.aNullableIntArray())", " && this.aList.equals(that.aList())", " && this.anImmutableList.equals(that.anImmutableList())", " && this.anOptionalString.equals(that.anOptionalString())", @@ -1312,17 +1318,19 @@ public class AutoValueCompilationTest { + "NestedAutoValue.builder();", " this.aNestedAutoValue = aNestedAutoValue$builder.build();", " }", - " String missing = \"\";", - " if (this.anInt == null) {", - " missing += \" anInt\";", - " }", - " if (this.aByteArray == null) {", - " missing += \" aByteArray\";", - " }", - " if (this.aList == null) {", - " missing += \" aList\";", - " }", - " if (!missing.isEmpty()) {", + " if (this.anInt == null", + " || this.aByteArray == null", + " || this.aList == null) {", + " StringBuilder missing = new StringBuilder();", + " if (this.anInt == null) {", + " missing.append(\" anInt\");", + " }", + " if (this.aByteArray == null) {", + " missing.append(\" aByteArray\");", + " }", + " if (this.aList == null) {", + " missing.append(\" aList\");", + " }", " throw new IllegalStateException(\"Missing required properties:\" + missing);", " }", " return new AutoValue_Baz<T>(", @@ -1339,7 +1347,8 @@ public class AutoValueCompilationTest { Compilation compilation = javac() .withProcessors(new AutoValueProcessor()) - .withOptions("-Xlint:-processing", "-implicit:none") + .withOptions( + "-Xlint:-processing", "-implicit:none", "-A" + Nullables.NULLABLE_OPTION + "=") .compile(javaFileObject, nestedJavaFileObject); assertThat(compilation).succeededWithoutWarnings(); assertThat(compilation) @@ -1587,7 +1596,7 @@ public class AutoValueCompilationTest { assertThat(compilation) .hadErrorContaining( "Parameter type java.lang.String of setter method should be int " - + "to match getter foo.bar.Baz.blim") + + "to match property method foo.bar.Baz.blim()") .inFile(javaFileObject) .onLineContaining("Builder blim(String x)"); } @@ -1620,10 +1629,10 @@ public class AutoValueCompilationTest { .compile(javaFileObject); assertThat(compilation) .hadErrorContaining( - "Parameter type java.lang.String of setter method should be " - + "com.google.common.collect.ImmutableList<java.lang.String> to match getter " - + "foo.bar.Baz.blam, or it should be a type that can be passed to " - + "ImmutableList.copyOf") + "Parameter type java.lang.String of setter method should be" + + " com.google.common.collect.ImmutableList<java.lang.String> to match property" + + " method foo.bar.Baz.blam(), or it should be a type that can be passed to" + + " ImmutableList.copyOf") .inFile(javaFileObject) .onLineContaining("Builder blam(String x)"); } @@ -1657,11 +1666,11 @@ public class AutoValueCompilationTest { .compile(javaFileObject); assertThat(compilation) .hadErrorContaining( - "Parameter type java.util.Collection<java.lang.Integer> of setter method should be " - + "com.google.common.collect.ImmutableList<java.lang.String> to match getter " - + "foo.bar.Baz.blam, or it should be a type that can be passed to " - + "ImmutableList.copyOf to produce " - + "com.google.common.collect.ImmutableList<java.lang.String>") + "Parameter type java.util.Collection<java.lang.Integer> of setter method should be" + + " com.google.common.collect.ImmutableList<java.lang.String> to match property" + + " method foo.bar.Baz.blam(), or it should be a type that can be passed to" + + " ImmutableList.copyOf to produce" + + " com.google.common.collect.ImmutableList<java.lang.String>") .inFile(javaFileObject) .onLineContaining("Builder blam(Collection<Integer> x)"); } @@ -1694,7 +1703,7 @@ public class AutoValueCompilationTest { assertThat(compilation) .hadErrorContaining( "Parameter type java.lang.String of setter method should be int " - + "to match getter foo.bar.Baz.getBlim") + + "to match property method foo.bar.Baz.getBlim()") .inFile(javaFileObject) .onLineContaining("Builder blim(String x)"); } @@ -1768,7 +1777,8 @@ public class AutoValueCompilationTest { .withProcessors(new AutoValueProcessor(), new AutoValueBuilderProcessor()) .compile(javaFileObject); assertThat(compilation) - .hadErrorContaining("Method does not correspond to a property of foo.bar.Item") + .hadErrorContaining( + "Method setTitle does not correspond to a property method of foo.bar.Item") .inFile(javaFileObject) .onLineContaining("Builder setTitle(String title)"); assertThat(compilation) @@ -1802,7 +1812,7 @@ public class AutoValueCompilationTest { .withProcessors(new AutoValueProcessor(), new AutoValueBuilderProcessor()) .compile(javaFileObject); assertThat(compilation) - .hadErrorContaining("Method does not correspond to a property of foo.bar.Baz") + .hadErrorContaining("Method blim does not correspond to a property method of foo.bar.Baz") .inFile(javaFileObject) .onLineContaining("Builder blim(int x)"); } @@ -1839,6 +1849,35 @@ public class AutoValueCompilationTest { } @Test + public void autoValueBuilderSetterReturnType() { + JavaFileObject javaFileObject = + JavaFileObjects.forSourceLines( + "foo.bar.Baz", + "package foo.bar;", + "", + "import com.google.auto.value.AutoValue;", + "", + "@AutoValue", + "public abstract class Baz {", + " abstract int blim();", + "", + " @AutoValue.Builder", + " public interface Builder {", + " void blim(int x);", + " Baz build();", + " }", + "}"); + Compilation compilation = + javac() + .withProcessors(new AutoValueProcessor(), new AutoValueBuilderProcessor()) + .compile(javaFileObject); + assertThat(compilation) + .hadErrorContaining("Setter methods must return foo.bar.Baz.Builder") + .inFile(javaFileObject) + .onLineContaining("void blim(int x)"); + } + + @Test public void autoValueBuilderWrongTypeGetter() { JavaFileObject javaFileObject = JavaFileObjects.forSourceLines( @@ -1866,10 +1905,15 @@ public class AutoValueCompilationTest { .withProcessors(new AutoValueProcessor(), new AutoValueBuilderProcessor()) .compile(javaFileObject); assertThat(compilation) - .hadErrorContaining( - "Method matches a property of foo.bar.Baz but has return type T instead of U") + .hadErrorContainingMatch( + "Method matches a property of foo\\.bar\\.Baz<T, ?U> but has return type T instead of" + + " U") .inFile(javaFileObject) .onLineContaining("T blam()"); + // The <T, ?U> is because we're depending on TypeMirror.toString(), and the JDK actually spells + // this as <T,U> with no space. While it's not completely sound to expect a given string from + // TypeMirror.toString(), in practice it's hard to imagine that it would be anything other + // than "foo.bar.Baz<T,U>" or "foo.bar.Baz<T, U>" given the specification. } @Test @@ -1929,9 +1973,9 @@ public class AutoValueCompilationTest { .withProcessors(new AutoValueProcessor(), new AutoValueBuilderProcessor()) .compile(javaFileObject); assertThat(compilation) - .hadErrorContaining("Property strings has a property builder so it cannot be @Nullable") + .hadErrorContaining("Property strings is @Nullable so it cannot have a property builder") .inFile(javaFileObject) - .onLineContaining("@Nullable ImmutableList<String> strings()"); + .onLineContaining("stringsBuilder()"); } @Test @@ -1963,9 +2007,9 @@ public class AutoValueCompilationTest { .withProcessors(new AutoValueProcessor(), new AutoValueBuilderProcessor()) .compile(javaFileObject); assertThat(compilation) - .hadErrorContaining("Property strings has a property builder so it cannot be @Nullable") + .hadErrorContaining("Property strings is @Nullable so it cannot have a property builder") .inFile(javaFileObject) - .onLineContaining("@Nullable ImmutableList<String> strings()"); + .onLineContaining("stringsBuilder()"); } @Test @@ -2451,8 +2495,8 @@ public class AutoValueCompilationTest { assertThat(compilation) .hadErrorContaining( "Method without arguments should be a build method returning foo.bar.Baz, or a getter" - + " method with the same name and type as a getter method of foo.bar.Baz, or" - + " fooBuilder() where foo() or getFoo() is a getter method of foo.bar.Baz") + + " method with the same name and type as a property method of foo.bar.Baz, or" + + " fooBuilder() where foo() or getFoo() is a property method of foo.bar.Baz") .inFile(javaFileObject) .onLineContaining("Builder whut()"); } @@ -2481,7 +2525,7 @@ public class AutoValueCompilationTest { .withProcessors(new AutoValueProcessor(), new AutoValueBuilderProcessor()) .compile(javaFileObject); assertThat(compilation) - .hadErrorContaining("Method does not correspond to a property of foo.bar.Baz") + .hadErrorContaining("Method whut does not correspond to a property method of foo.bar.Baz") .inFile(javaFileObject) .onLineContaining("void whut(String x)"); } @@ -2539,7 +2583,8 @@ public class AutoValueCompilationTest { .compile(javaFileObject); assertThat(compilation) .hadErrorContaining( - "Builder must have a single no-argument method returning foo.bar.Baz<T>") + "Builder must have a single no-argument method, typically called build(), that returns" + + " foo.bar.Baz<T>") .inFile(javaFileObject) .onLineContaining("public interface Builder<T>"); } @@ -2569,11 +2614,15 @@ public class AutoValueCompilationTest { .withProcessors(new AutoValueProcessor(), new AutoValueBuilderProcessor()) .compile(javaFileObject); assertThat(compilation) - .hadErrorContaining("Builder must have a single no-argument method returning foo.bar.Baz") + .hadErrorContaining( + "Builder must have a single no-argument method, typically called build(), that returns" + + " foo.bar.Baz") .inFile(javaFileObject) .onLineContaining("Baz build()"); assertThat(compilation) - .hadErrorContaining("Builder must have a single no-argument method returning foo.bar.Baz") + .hadErrorContaining( + "Builder must have a single no-argument method, typically called build(), that returns" + + " foo.bar.Baz") .inFile(javaFileObject) .onLineContaining("Baz create()"); } @@ -3286,7 +3335,7 @@ public class AutoValueCompilationTest { "}"); private static final String GENERATED_PROPERTY_TYPE = String.join( - "\n", + "\n", // "package foo.baz;", "", "public class GeneratedPropertyType {}"); @@ -3315,18 +3364,14 @@ public class AutoValueCompilationTest { GENERATED_TYPES.forEach( (typeName, source) -> { try { - JavaFileObject generated = - processingEnv - .getFiler() - .createSourceFile(typeName); + JavaFileObject generated = processingEnv.getFiler().createSourceFile(typeName); try (Writer writer = generated.openWriter()) { writer.write(source); } } catch (IOException e) { throw new UncheckedIOException(e); } - } - ); + }); } return false; } @@ -3337,7 +3382,41 @@ public class AutoValueCompilationTest { } } + // This is a regression test for the problem described in + // https://github.com/google/auto/issues/1087. + @Test + public void kotlinMetadataAnnotationsAreImplicitlyExcludedFromCopying() { + JavaFileObject metadata = + JavaFileObjects.forSourceLines( + "kotlin.Metadata", "package kotlin;", "", "public @interface Metadata {", "}"); + JavaFileObject test = + JavaFileObjects.forSourceLines( + "foo.bar.Test", + "package foo.bar;", + "", + "import com.google.auto.value.AutoValue;", + "import kotlin.Metadata;", + "", + "@AutoValue.CopyAnnotations", + "@Metadata", + "@AutoValue", + "public abstract class Test {", + " public abstract String string();", + "}"); + AutoValueProcessor autoValueProcessor = new AutoValueProcessor(); + Compilation compilation = + javac() + .withProcessors(autoValueProcessor) + .withOptions("-Xlint:-processing", "-implicit:none") + .compile(test, metadata); + assertThat(compilation).succeededWithoutWarnings(); + assertThat(compilation) + .generatedSourceFile("foo.bar.AutoValue_Test") + .contentsAsUtf8String() + .doesNotContain("kotlin.Metadata"); + } + private String sorted(String... imports) { - return Arrays.stream(imports).sorted().collect(joining("\n")); - } + return Arrays.stream(imports).sorted().collect(joining("\n")); + } } diff --git a/value/src/test/java/com/google/auto/value/processor/ExtensionTest.java b/value/src/test/java/com/google/auto/value/processor/ExtensionTest.java index ce9eeed0..56eaad25 100644 --- a/value/src/test/java/com/google/auto/value/processor/ExtensionTest.java +++ b/value/src/test/java/com/google/auto/value/processor/ExtensionTest.java @@ -172,6 +172,7 @@ public class ExtensionTest { Compilation compilation = javac() .withProcessors(new AutoValueProcessor(ImmutableList.of(new FooExtension()))) + .withOptions("-A" + Nullables.NULLABLE_OPTION + "=") .compile(javaFileObject); assertThat(compilation).succeededWithoutWarnings(); assertThat(compilation) @@ -250,9 +251,7 @@ public class ExtensionTest { " abstract String dizzle();", "}"); Compilation compilation = - javac() - .withProcessors(new AutoValueProcessor(ImmutableList.of(ext1, ext2))) - .compile(impl); + javac().withProcessors(new AutoValueProcessor(ImmutableList.of(ext1, ext2))).compile(impl); assertThat(compilation) .hadErrorContaining("wants to consume a method that was already consumed") .inFile(impl) @@ -596,10 +595,9 @@ public class ExtensionTest { "}"); Compilation compilation = javac() - .withProcessors(new AutoValueProcessor(ImmutableList.of(new FooExtension()))) - .compile(javaFileObject); - assertThat(compilation) - .hadErrorContaining("writeToParcel"); + .withProcessors(new AutoValueProcessor(ImmutableList.of(new FooExtension()))) + .compile(javaFileObject); + assertThat(compilation).hadErrorContaining("writeToParcel"); assertThat(compilation) .hadWarningContaining( "Abstract method is neither a property getter nor a Builder converter, " @@ -647,13 +645,12 @@ public class ExtensionTest { "public abstract class Baz {", "}"); Compilation compilation = - javac() - .withProcessors(new AutoValueProcessor(badJarLoader)) - .compile(javaFileObject); + javac().withProcessors(new AutoValueProcessor(badJarLoader)).compile(javaFileObject); assertThat(compilation).succeeded(); - assertThat(compilation).hadWarningContaining( - "This may be due to a corrupt jar file in the compiler's classpath.\n " - + ServiceConfigurationError.class.getName()); + assertThat(compilation) + .hadWarningContaining( + "This may be due to a corrupt jar file in the compiler's classpath.\n " + + ServiceConfigurationError.class.getName()); assertThat(compilation).generatedSourceFile("foo.bar.AutoValue_Baz"); } @@ -857,8 +854,12 @@ public class ExtensionTest { String sideClassName = "Side_" + context.autoValueClass().getSimpleName(); String sideClass = "" // - + "package " + context.packageName() + ";\n" - + "class " + sideClassName + " {}\n"; + + "package " + + context.packageName() + + ";\n" + + "class " + + sideClassName + + " {}\n"; Filer filer = context.processingEnvironment().getFiler(); try { String sideClassFqName = context.packageName() + "." + sideClassName; @@ -912,25 +913,27 @@ public class ExtensionTest { @Test public void propertyTypes() { - JavaFileObject parent = JavaFileObjects.forSourceLines( - "foo.bar.Parent", - "package foo.bar;", - "", - "import java.util.List;", - "", - "interface Parent<T> {", - " T thing();", - " List<T> list();", - "}"); - JavaFileObject autoValueClass = JavaFileObjects.forSourceLines( - "foo.bar.Baz", - "package foo.bar;", - "", - "import com.google.auto.value.AutoValue;", - "", - "@AutoValue", - "abstract class Baz implements Parent<String> {", - "}"); + JavaFileObject parent = + JavaFileObjects.forSourceLines( + "foo.bar.Parent", + "package foo.bar;", + "", + "import java.util.List;", + "", + "interface Parent<T> {", + " T thing();", + " List<T> list();", + "}"); + JavaFileObject autoValueClass = + JavaFileObjects.forSourceLines( + "foo.bar.Baz", + "package foo.bar;", + "", + "import com.google.auto.value.AutoValue;", + "", + "@AutoValue", + "abstract class Baz implements Parent<String> {", + "}"); ContextChecker checker = context -> { assertThat(context.builder()).isEmpty(); @@ -955,15 +958,16 @@ public class ExtensionTest { @Test public void finalAutoValueClassName() { - JavaFileObject autoValueClass = JavaFileObjects.forSourceLines( - "foo.bar.Baz", - "package foo.bar;", - "", - "import com.google.auto.value.AutoValue;", - "", - "@AutoValue", - "abstract class Baz {", - "}"); + JavaFileObject autoValueClass = + JavaFileObjects.forSourceLines( + "foo.bar.Baz", + "package foo.bar;", + "", + "import com.google.auto.value.AutoValue;", + "", + "@AutoValue", + "abstract class Baz {", + "}"); ContextChecker checker = context -> { assertThat(context.finalAutoValueClassName()).isEqualTo("foo.bar.AutoValue_Baz"); @@ -982,43 +986,45 @@ public class ExtensionTest { @Test public void builderContext() { - JavaFileObject parent = JavaFileObjects.forSourceLines( - "foo.bar.Parent", - "package foo.bar;", - "", - "import com.google.common.collect.ImmutableList;", - "", - "interface Parent<T> {", - " T thing();", - " ImmutableList<T> list();", - "}"); - JavaFileObject autoValueClass = JavaFileObjects.forSourceLines( - "foo.bar.Baz", - "package foo.bar;", - "", - "import com.google.auto.value.AutoValue;", - "import com.google.common.collect.ImmutableList;", - "", - "@AutoValue", - "abstract class Baz implements Parent<String> {", - " static Builder builder() {", - " return new AutoValue_Baz.Builder();", - " }", - "", - " abstract Builder toBuilder();", - "", - " @AutoValue.Builder", - " abstract static class Builder {", - " abstract Builder setThing(String x);", - " abstract Builder setList(Iterable<String> x);", - " abstract Builder setList(ImmutableList<String> x);", - " abstract ImmutableList.Builder<String> listBuilder();", - " abstract Baz autoBuild();", - " Baz build() {", - " return autoBuild();", - " }", - " }", - "}"); + JavaFileObject parent = + JavaFileObjects.forSourceLines( + "foo.bar.Parent", + "package foo.bar;", + "", + "import com.google.common.collect.ImmutableList;", + "", + "interface Parent<T> {", + " T thing();", + " ImmutableList<T> list();", + "}"); + JavaFileObject autoValueClass = + JavaFileObjects.forSourceLines( + "foo.bar.Baz", + "package foo.bar;", + "", + "import com.google.auto.value.AutoValue;", + "import com.google.common.collect.ImmutableList;", + "", + "@AutoValue", + "abstract class Baz implements Parent<String> {", + " static Builder builder() {", + " return new AutoValue_Baz.Builder();", + " }", + "", + " abstract Builder toBuilder();", + "", + " @AutoValue.Builder", + " abstract static class Builder {", + " abstract Builder setThing(String x);", + " abstract Builder setList(Iterable<String> x);", + " abstract Builder setList(ImmutableList<String> x);", + " abstract ImmutableList.Builder<String> listBuilder();", + " abstract Baz autoBuild();", + " Baz build() {", + " return autoBuild();", + " }", + " }", + "}"); ContextChecker checker = context -> { assertThat(context.builder()).isPresent(); @@ -1075,27 +1081,102 @@ public class ExtensionTest { } @Test + public void builderContextWithInheritance() { + JavaFileObject parent = + JavaFileObjects.forSourceLines( + "foo.bar.Parent", + "package foo.bar;", + "", + "interface Parent<BuilderT> {", + " BuilderT toBuilder();", + " interface Builder<T, BuilderT, BuiltT> {", + " BuilderT setThing(T x);", + " BuiltT build();", + " }", + "}"); + JavaFileObject autoValueClass = + JavaFileObjects.forSourceLines( + "foo.bar.Baz", + "package foo.bar;", + "", + "import com.google.auto.value.AutoValue;", + "", + "@AutoValue", + "abstract class Baz<T> implements Parent<Baz.Builder<T>> {", + " abstract T thing();", + " static <T> Builder<T> builder() {", + " return new AutoValue_Baz.Builder<>();", + " }", + "", + " @AutoValue.Builder", + " abstract static class Builder<T> implements Parent.Builder<T, Builder<T>, Baz<T>> {", + " }", + "}"); + ContextChecker checker = + context -> { + assertThat(context.builder()).isPresent(); + BuilderContext builderContext = context.builder().get(); + + assertThat(builderContext.builderType().getQualifiedName().toString()) + .isEqualTo("foo.bar.Baz.Builder"); + + Set<ExecutableElement> builderMethods = builderContext.builderMethods(); + assertThat(builderMethods).hasSize(1); + ExecutableElement builderMethod = Iterables.getOnlyElement(builderMethods); + assertThat(builderMethod.getSimpleName().toString()).isEqualTo("builder"); + + Set<ExecutableElement> toBuilderMethods = builderContext.toBuilderMethods(); + assertThat(toBuilderMethods).hasSize(1); + ExecutableElement toBuilderMethod = Iterables.getOnlyElement(toBuilderMethods); + assertThat(toBuilderMethod.getSimpleName().toString()).isEqualTo("toBuilder"); + + Optional<ExecutableElement> buildMethod = builderContext.buildMethod(); + assertThat(buildMethod).isPresent(); + assertThat(buildMethod.get().getSimpleName().toString()).isEqualTo("build"); + assertThat(buildMethod.get().getParameters()).isEmpty(); + assertThat(buildMethod.get().getReturnType().toString()).isEqualTo("BuiltT"); + + ExecutableElement autoBuildMethod = builderContext.autoBuildMethod(); + assertThat(autoBuildMethod).isEqualTo(buildMethod.get()); + + Map<String, Set<ExecutableElement>> setters = builderContext.setters(); + assertThat(setters.keySet()).containsExactly("thing"); + Set<ExecutableElement> thingSetters = setters.get("thing"); + assertThat(thingSetters).hasSize(1); + ExecutableElement thingSetter = Iterables.getOnlyElement(thingSetters); + assertThat(thingSetter.getSimpleName().toString()).isEqualTo("setThing"); + }; + ContextCheckingExtension extension = new ContextCheckingExtension(checker); + Compilation compilation = + javac() + .withProcessors(new AutoValueProcessor(ImmutableList.of(extension))) + .compile(autoValueClass, parent); + assertThat(compilation).succeededWithoutWarnings(); + } + + @Test public void oddBuilderContext() { - JavaFileObject autoValueClass = JavaFileObjects.forSourceLines( - "foo.bar.Baz", - "package foo.bar;", - "", - "import com.google.auto.value.AutoValue;", - "import com.google.common.collect.ImmutableList;", - "", - "@AutoValue", - "abstract class Baz {", - " abstract String string();", - "", - " @AutoValue.Builder", - " abstract static class Builder {", - " abstract Builder setString(String x);", - " abstract Baz oddBuild();", - " Baz build(int butNotReallyBecauseOfThisParameter) {", - " return null;", - " }", - " }", - "}"); + JavaFileObject autoValueClass = + JavaFileObjects.forSourceLines( + "foo.bar.Baz", + "package foo.bar;", + "", + "import com.google.auto.value.AutoValue;", + "import com.google.common.collect.ImmutableList;", + "", + "@AutoValue", + "abstract class Baz {", + " abstract String string();", + "", + " @AutoValue.Builder", + " abstract static class Builder {", + " abstract Builder setString(String x);", + " abstract Baz oddBuild();", + " Baz build(int butNotReallyBecauseOfThisParameter) {", + " return null;", + " }", + " }", + "}"); ContextChecker checker = context -> { assertThat(context.builder()).isPresent(); @@ -1126,25 +1207,26 @@ public class ExtensionTest { // https://github.com/google/auto/issues/809 @Test public void propertyErrorShouldNotCrash() { - JavaFileObject autoValueClass = JavaFileObjects.forSourceLines( - "test.Test", - "package test;", - "import com.google.auto.value.AutoValue;", - "import java.util.List;", - "", - "@AutoValue", - "public abstract class Test {", - " abstract Integer property();", - " abstract List<String> listProperty();", - "", - " @AutoValue.Builder", - " public interface Builder {", - " Builder property(Integer property);", - " Builder listProperty(List<String> listProperty);", - " Builder listProperty(Integer listPropertyValues);", - " Test build();", - " }", - "}"); + JavaFileObject autoValueClass = + JavaFileObjects.forSourceLines( + "test.Test", + "package test;", + "import com.google.auto.value.AutoValue;", + "import java.util.List;", + "", + "@AutoValue", + "public abstract class Test {", + " abstract Integer property();", + " abstract List<String> listProperty();", + "", + " @AutoValue.Builder", + " public interface Builder {", + " Builder property(Integer property);", + " Builder listProperty(List<String> listProperty);", + " Builder listProperty(Integer listPropertyValues);", + " Test build();", + " }", + "}"); // We don't actually expect the extension to be invoked. Previously it was, and that led to a // NullPointerException when calling .setters() in the checker. ContextChecker checker = diff --git a/value/src/test/java/com/google/auto/value/processor/GeneratedDoesNotExistTest.java b/value/src/test/java/com/google/auto/value/processor/GeneratedDoesNotExistTest.java index f3d3d611..18cca5e4 100644 --- a/value/src/test/java/com/google/auto/value/processor/GeneratedDoesNotExistTest.java +++ b/value/src/test/java/com/google/auto/value/processor/GeneratedDoesNotExistTest.java @@ -48,6 +48,8 @@ import org.junit.runners.Parameterized.Parameters; */ @RunWith(Parameterized.class) public class GeneratedDoesNotExistTest { + private static final ImmutableList<String> STANDARD_OPTIONS = + ImmutableList.of("-A" + Nullables.NULLABLE_OPTION + "="); @Parameters(name = "{0}") public static Collection<Object[]> data() { @@ -57,12 +59,16 @@ public class GeneratedDoesNotExistTest { // TODO(b/72513371): use --release 8 once compile-testing supports that params.add( new Object[] { - ImmutableList.of(), "javax.annotation.processing.Generated", + STANDARD_OPTIONS, "javax.annotation.processing.Generated", }); } params.add( new Object[] { - ImmutableList.of("-source", "8", "-target", "8"), "javax.annotation.Generated", + ImmutableList.<String>builder() + .addAll(STANDARD_OPTIONS) + .add("-source", "8", "-target", "8") + .build(), + "javax.annotation.Generated", }); return params.build(); } @@ -223,9 +229,9 @@ public class GeneratedDoesNotExistTest { Processor noGeneratedProcessor = partialProxy(Processor.class, handler); Compilation compilation = javac() - .withOptions(javacOptions) - .withProcessors(noGeneratedProcessor) - .compile(javaFileObject); + .withOptions(javacOptions) + .withProcessors(noGeneratedProcessor) + .compile(javaFileObject); assertThat(compilation).succeededWithoutWarnings(); assertThat(compilation) .generatedSourceFile("foo.bar.AutoValue_Baz") diff --git a/value/src/test/java/com/google/auto/value/processor/IncrementalExtensionTest.java b/value/src/test/java/com/google/auto/value/processor/IncrementalExtensionTest.java index 27cb0936..472f62db 100644 --- a/value/src/test/java/com/google/auto/value/processor/IncrementalExtensionTest.java +++ b/value/src/test/java/com/google/auto/value/processor/IncrementalExtensionTest.java @@ -22,6 +22,7 @@ import com.google.auto.value.extension.AutoValueExtension; import com.google.auto.value.extension.AutoValueExtension.IncrementalExtensionType; import com.google.auto.value.extension.memoized.processor.MemoizeExtension; import com.google.auto.value.extension.serializable.processor.SerializableAutoValueExtension; +import com.google.auto.value.extension.toprettystring.processor.ToPrettyStringExtension; import com.google.common.collect.ImmutableList; import javax.annotation.processing.ProcessingEnvironment; import net.ltgt.gradle.incap.IncrementalAnnotationProcessorType; @@ -46,7 +47,10 @@ public class IncrementalExtensionTest { // different <?>. assertThat(builtInExtensions) .comparingElementsUsing(transforming(e -> (Object) e.getClass(), "is class")) - .containsExactly(MemoizeExtension.class, SerializableAutoValueExtension.class); + .containsExactly( + MemoizeExtension.class, + SerializableAutoValueExtension.class, + ToPrettyStringExtension.class); AutoValueProcessor processor = new AutoValueProcessor(builtInExtensions); assertThat(processor.getSupportedOptions()) @@ -58,10 +62,12 @@ public class IncrementalExtensionTest { AutoValueExtension nonIsolatingExtension = new NonIsolatingExtension(); assertThat(nonIsolatingExtension.incrementalType((ProcessingEnvironment) null)) .isEqualTo(IncrementalExtensionType.UNKNOWN); - ImmutableList<AutoValueExtension> extensions = ImmutableList.<AutoValueExtension>builder() - .addAll(AutoValueProcessor.extensionsFromLoader(AutoValueProcessor.class.getClassLoader())) - .add(nonIsolatingExtension) - .build(); + ImmutableList<AutoValueExtension> extensions = + ImmutableList.<AutoValueExtension>builder() + .addAll( + AutoValueProcessor.extensionsFromLoader(AutoValueProcessor.class.getClassLoader())) + .add(nonIsolatingExtension) + .build(); AutoValueProcessor processor = new AutoValueProcessor(extensions); assertThat(processor.getSupportedOptions()) @@ -73,10 +79,12 @@ public class IncrementalExtensionTest { AutoValueExtension isolatingExtension = new IsolatingExtension(); assertThat(isolatingExtension.incrementalType((ProcessingEnvironment) null)) .isEqualTo(IncrementalExtensionType.ISOLATING); - ImmutableList<AutoValueExtension> extensions = ImmutableList.<AutoValueExtension>builder() - .addAll(AutoValueProcessor.extensionsFromLoader(AutoValueProcessor.class.getClassLoader())) - .add(isolatingExtension) - .build(); + ImmutableList<AutoValueExtension> extensions = + ImmutableList.<AutoValueExtension>builder() + .addAll( + AutoValueProcessor.extensionsFromLoader(AutoValueProcessor.class.getClassLoader())) + .add(isolatingExtension) + .build(); AutoValueProcessor processor = new AutoValueProcessor(extensions); assertThat(processor.getSupportedOptions()) diff --git a/value/src/test/java/com/google/auto/value/processor/NullablesTest.java b/value/src/test/java/com/google/auto/value/processor/NullablesTest.java new file mode 100644 index 00000000..9e345f53 --- /dev/null +++ b/value/src/test/java/com/google/auto/value/processor/NullablesTest.java @@ -0,0 +1,179 @@ +/* + * Copyright 2021 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.google.auto.value.processor; + +import static com.google.common.base.StandardSystemProperty.JAVA_SPECIFICATION_VERSION; +import static com.google.common.truth.OptionalSubject.optionals; +import static com.google.common.truth.Truth.assertThat; +import static com.google.common.truth.TruthJUnit.assume; +import static com.google.testing.compile.CompilationSubject.assertThat; +import static java.util.stream.Collectors.partitioningBy; + +import com.google.auto.common.MoreTypes; +import com.google.common.collect.ImmutableList; +import com.google.common.truth.Expect; +import com.google.testing.compile.Compilation; +import com.google.testing.compile.Compiler; +import com.google.testing.compile.JavaFileObjects; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import java.util.List; +import java.util.Map; +import java.util.Set; +import javax.annotation.processing.AbstractProcessor; +import javax.annotation.processing.RoundEnvironment; +import javax.annotation.processing.SupportedAnnotationTypes; +import javax.lang.model.SourceVersion; +import javax.lang.model.element.AnnotationMirror; +import javax.lang.model.element.ExecutableElement; +import javax.lang.model.element.TypeElement; +import javax.lang.model.type.DeclaredType; +import javax.lang.model.util.ElementFilter; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +@RunWith(JUnit4.class) +public class NullablesTest { + @Rule public Expect expect = Expect.create(); + + @Target(ElementType.TYPE_USE) + @Retention(RetentionPolicy.RUNTIME) + public @interface Nullable {} + + @Target(ElementType.TYPE_USE) + @Retention(RetentionPolicy.RUNTIME) + public @interface Irrelevant {} + + // The class here has various methods that we will examine with + // Nullables.nullableMentionedInMethods to ensure that we do indeed detect @Nullable annotations + // in various contexts. + // This test is a lot more complicated than it should be. Ideally we would just have this Methods + // class be an actual class nested inside this test, and we would use CompilationRule to get + // the corresponding TypeElement so we could check the various methods. Unfortunately, if we + // do that then we get a TypeElement where all type annotations have disappeared because of + // https://bugs.openjdk.java.net/browse/JDK-8225377. So instead we have to use Compiler to compile + // the code here with a special annotation processor that will check the annotations of the + // just-compiled class. Since the processor is running as part of the same compilation, we don't + // lose the type annotations. + + private static final ImmutableList<String> METHOD_LINES = + ImmutableList.of( + // Methods in this class whose names begin with "no" do not mention @Nullable anywhere.", + // All other methods do.", + "package foo.bar;", + "", + "import " + Irrelevant.class.getCanonicalName() + ";", + "import " + Nullable.class.getCanonicalName() + ";", + "import java.util.List;", + "", + "abstract class Methods {", + " void noAnnotations() {}", + " abstract int noAnnotationsEither(int x);", + " abstract @Irrelevant String noRelevantAnnotations(@Irrelevant int x);", + " abstract @Nullable String nullableString();", + " abstract String @Nullable [] nullableArrayOfString();", + " abstract @Nullable String[] arrayOfNullableString();", + " abstract @Nullable String @Nullable [] nullableArrayOfNullableString();", + " abstract List<@Nullable String> listOfNullableString();", + " abstract List<? extends @Nullable Object> listOfExtendsNullable();", + " abstract List<? super @Nullable Number> listOfSuperNullable();", + " abstract <T extends @Nullable Object> T nullableTypeParamBound();", + " abstract <T> @Nullable T nullableTypeParamRef();", + " void nullableParam(@Nullable String x) {}", + " void nullableParamBound(List<? extends @Nullable String> x) {}", + "}"); + + @Test + public void nullableMentionedInMethods() { + // Sadly we can't rely on JDK 8 to handle type annotations correctly. + // Some versions do, some don't. So skip the test unless we are on at least JDK 9. + double javaVersion = Double.parseDouble(JAVA_SPECIFICATION_VERSION.value()); + assume().that(javaVersion).isAtLeast(9.0); + NullableProcessor processor = new NullableProcessor(expect); + Compilation compilation = + Compiler.javac() + .withProcessors(processor) + .compile(JavaFileObjects.forSourceLines("foo.bar.Methods", METHOD_LINES)); + assertThat(compilation).succeededWithoutWarnings(); + assertThat(processor.ran).isTrue(); + // If any `expect` calls failed then the test will fail now because of the Expect rule. + } + + @SupportedAnnotationTypes("*") + private static class NullableProcessor extends AbstractProcessor { + + private final Expect expect; + boolean ran; + + NullableProcessor(Expect expect) { + this.expect = expect; + } + + @Override + public SourceVersion getSupportedSourceVersion() { + return SourceVersion.latestSupported(); + } + + @Override + public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) { + if (roundEnv.processingOver()) { + TypeElement methodsElement = + processingEnv.getElementUtils().getTypeElement("foo.bar.Methods"); + expect.that(methodsElement).isNotNull(); + + List<ExecutableElement> methods = + ElementFilter.methodsIn(methodsElement.getEnclosedElements()); + Map<Boolean, List<ExecutableElement>> partitionedMethods = + methods.stream() + .collect(partitioningBy(p -> !p.getSimpleName().toString().startsWith("no"))); + List<ExecutableElement> nullableMethods = partitionedMethods.get(true); + List<ExecutableElement> notNullableMethods = partitionedMethods.get(false); + + expect + .about(optionals()) + .that(Nullables.nullableMentionedInMethods(notNullableMethods)) + .isEmpty(); + + TypeElement nullableElement = + processingEnv.getElementUtils().getTypeElement(Nullable.class.getCanonicalName()); + expect.that(nullableElement).isNotNull(); + DeclaredType nullableType = MoreTypes.asDeclared(nullableElement.asType()); + + for (ExecutableElement nullableMethod : nullableMethods) { + // Make a list with all the methods that don't have @Nullable plus one method that does. + ImmutableList<ExecutableElement> notNullablePlusNullable = + ImmutableList.<ExecutableElement>builder() + .addAll(notNullableMethods) + .add(nullableMethod) + .build(); + expect + .withMessage("method %s should have @Nullable", nullableMethod) + .about(optionals()) + .that( + Nullables.nullableMentionedInMethods(notNullablePlusNullable) + .map(AnnotationMirror::getAnnotationType)) + .hasValue(nullableType); + } + ran = true; + } + return false; + } + } +} diff --git a/value/src/test/java/com/google/auto/value/processor/PropertyAnnotationsTest.java b/value/src/test/java/com/google/auto/value/processor/PropertyAnnotationsTest.java index 2c3bea0d..48d8cd6e 100644 --- a/value/src/test/java/com/google/auto/value/processor/PropertyAnnotationsTest.java +++ b/value/src/test/java/com/google/auto/value/processor/PropertyAnnotationsTest.java @@ -15,12 +15,13 @@ */ package com.google.auto.value.processor; -import static com.google.common.truth.Truth.assertAbout; -import static com.google.testing.compile.JavaSourceSubjectFactory.javaSource; +import static com.google.testing.compile.CompilationSubject.assertThat; +import static com.google.testing.compile.Compiler.javac; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSortedSet; +import com.google.testing.compile.Compilation; import com.google.testing.compile.JavaFileObjects; import java.lang.annotation.ElementType; import java.lang.annotation.Inherited; @@ -40,8 +41,7 @@ import org.junit.runners.JUnit4; */ @RunWith(JUnit4.class) public class PropertyAnnotationsTest { - private static final String TEST_ANNOTATION = - "@PropertyAnnotationsTest.TestAnnotation"; + private static final String TEST_ANNOTATION = "@PropertyAnnotationsTest.TestAnnotation"; private static final String TEST_ARRAY_ANNOTATION = "@PropertyAnnotationsTest.TestArrayAnnotation"; @@ -272,12 +272,15 @@ public class PropertyAnnotationsTest { .addMethodAnnotations(expectedMethodAnnotations) .build(); - assertAbout(javaSource()) - .that(javaFileObject) - .processedWith(new AutoValueProcessor()) - .compilesWithoutError() - .and() - .generatesSources(expectedOutput); + Compilation compilation = + javac() + .withOptions("-A" + Nullables.NULLABLE_OPTION + "=") + .withProcessors(new AutoValueProcessor()) + .compile(javaFileObject); + assertThat(compilation).succeeded(); + assertThat(compilation) + .generatedSourceFile("foo.bar.AutoValue_Baz") + .hasSourceEquivalentTo(expectedOutput); } @Test @@ -445,10 +448,12 @@ public class PropertyAnnotationsTest { assertGeneratedMatches( getImports(PropertyAnnotationsTest.class), ImmutableList.of( - TEST_ARRAY_ANNOTATION + "(testEnums = {PropertyAnnotationsTest.TestEnum.A," + TEST_ARRAY_ANNOTATION + + "(testEnums = {PropertyAnnotationsTest.TestEnum.A," + " PropertyAnnotationsTest.TestEnum.B})"), ImmutableList.of( - TEST_ARRAY_ANNOTATION + "(testEnums = {PropertyAnnotationsTest.TestEnum.A," + TEST_ARRAY_ANNOTATION + + "(testEnums = {PropertyAnnotationsTest.TestEnum.A," + " PropertyAnnotationsTest.TestEnum.B})")); } @@ -512,12 +517,15 @@ public class PropertyAnnotationsTest { .addFieldAnnotations("@Deprecated", "@PropertyAnnotationsTest.InheritedAnnotation") .build(); - assertAbout(javaSource()) - .that(inputFile) - .processedWith(new AutoValueProcessor()) - .compilesWithoutError() - .and() - .generatesSources(outputFile); + Compilation compilation = + javac() + .withOptions("-A" + Nullables.NULLABLE_OPTION) + .withProcessors(new AutoValueProcessor()) + .compile(inputFile); + assertThat(compilation).succeeded(); + assertThat(compilation) + .generatedSourceFile("foo.bar.AutoValue_Baz") + .hasSourceEquivalentTo(outputFile); } /** @@ -545,16 +553,17 @@ public class PropertyAnnotationsTest { .setImports(getImports(PropertyAnnotationsTest.class)) .addFieldAnnotations("@Deprecated", "@PropertyAnnotationsTest.InheritedAnnotation") .addMethodAnnotations( - "@Deprecated", - "@PropertyAnnotationsTest.InheritedAnnotation", - "@Baz.MethodsOnly") + "@Deprecated", "@PropertyAnnotationsTest.InheritedAnnotation", "@Baz.MethodsOnly") .build(); - assertAbout(javaSource()) - .that(inputFile) - .processedWith(new AutoValueProcessor()) - .compilesWithoutError() - .and() - .generatesSources(outputFile); + Compilation compilation = + javac() + .withOptions("-A" + Nullables.NULLABLE_OPTION + "=") + .withProcessors(new AutoValueProcessor()) + .compile(inputFile); + assertThat(compilation).succeeded(); + assertThat(compilation) + .generatedSourceFile("foo.bar.AutoValue_Baz") + .hasSourceEquivalentTo(outputFile); } } diff --git a/value/src/test/java/com/google/auto/value/processor/PropertyNamesTest.java b/value/src/test/java/com/google/auto/value/processor/PropertyNamesTest.java index 7af240c6..d488e599 100644 --- a/value/src/test/java/com/google/auto/value/processor/PropertyNamesTest.java +++ b/value/src/test/java/com/google/auto/value/processor/PropertyNamesTest.java @@ -34,14 +34,12 @@ public class PropertyNamesTest { .put("x", "x") .put("", "") .build(); - + @Test public void decapitalizeLikeJavaBeans() { - NORMAL_CASES - .forEach( - (input, output) -> { - expect.that(PropertyNames.decapitalizeLikeJavaBeans(input)).isEqualTo(output); - }); + NORMAL_CASES.forEach( + (input, output) -> + expect.that(PropertyNames.decapitalizeLikeJavaBeans(input)).isEqualTo(output)); expect.that(PropertyNames.decapitalizeLikeJavaBeans(null)).isNull(); expect.that(PropertyNames.decapitalizeLikeJavaBeans("HTMLPage")).isEqualTo("HTMLPage"); expect.that(PropertyNames.decapitalizeLikeJavaBeans("OAuth")).isEqualTo("OAuth"); @@ -49,11 +47,9 @@ public class PropertyNamesTest { @Test public void decapitalizeNormally() { - NORMAL_CASES - .forEach( - (input, output) -> { - expect.that(PropertyNames.decapitalizeNormally(input)).isEqualTo(output); - }); + NORMAL_CASES.forEach( + (input, output) -> + expect.that(PropertyNames.decapitalizeNormally(input)).isEqualTo(output)); expect.that(PropertyNames.decapitalizeNormally(null)).isNull(); expect.that(PropertyNames.decapitalizeNormally("HTMLPage")).isEqualTo("hTMLPage"); expect.that(PropertyNames.decapitalizeNormally("OAuth")).isEqualTo("oAuth"); diff --git a/value/src/test/java/com/google/auto/value/processor/ReformatterTest.java b/value/src/test/java/com/google/auto/value/processor/ReformatterTest.java index 98d31b48..48ecf5be 100644 --- a/value/src/test/java/com/google/auto/value/processor/ReformatterTest.java +++ b/value/src/test/java/com/google/auto/value/processor/ReformatterTest.java @@ -15,7 +15,7 @@ */ package com.google.auto.value.processor; -import static org.junit.Assert.assertEquals; +import static com.google.common.truth.Truth.assertThat; import org.junit.Test; import org.junit.runner.RunWith; @@ -29,7 +29,7 @@ import org.junit.runners.JUnit4; @RunWith(JUnit4.class) public class ReformatterTest { @Test - public void testSimple() { + public void simple() { String input = "\n" + "package com.latin.declension; \n" @@ -50,7 +50,7 @@ public class ReformatterTest { + "\n" + " Eodem ( Eadem eodem ) { }\n"; String output = - "package com.latin.declension;\n" + "package com.latin.declension;\n" + "\n" + "public class Idem {\n" + "\n" @@ -62,11 +62,11 @@ public class ReformatterTest { + " }\n" + "\n" + " Eodem (Eadem eodem) { }\n"; - assertEquals(output, Reformatter.fixup(input)); + assertThat(Reformatter.fixup(input)).isEqualTo(output); } @Test - public void testSpecialSpaces() { + public void specialSpaces() { String input = "\n" + "package com.example.whatever;\n" @@ -79,7 +79,7 @@ public class ReformatterTest { + " static final char QUOTE2 = '\\\"' ;\n" + "}\n"; String output = - "package com.example.whatever;\n" + "package com.example.whatever;\n" + "\n" + "public class SomeClass {\n" + " static final String STRING = \" hello world \\n\";\n" @@ -88,13 +88,66 @@ public class ReformatterTest { + " static final char QUOTE = '\"';\n" + " static final char QUOTE2 = '\\\"';\n" + "}\n"; - assertEquals(output, Reformatter.fixup(input)); + assertThat(Reformatter.fixup(input)).isEqualTo(output); } @Test public void noTrailingNewline() { String input = "package com.example.whatever;\n\npublic class SomeClass {}"; String output = input + "\n"; - assertEquals(output, Reformatter.fixup(input)); + assertThat(Reformatter.fixup(input)).isEqualTo(output); + } + + @Test + public void indent() { + String input = + " class Test {\n" + + "private final int field;\n" + + "\n" + + "Test(\n" + + "@Interesting Integer field,\n" + + "boolean ignored) {\n" + + "this.field = field;\n" + + "}\n" + + "\n" + + "@Override\n" + + "public boolean equals(Object x) {\n" + + "return x instanceof Test\n" + + "&& ((Test) x).field == field;\n" + + "// interesting\n" + + "}\n" + + "\n" + + "@Override\n" + + "public String toString() {\n" + + "return \"Test{\"\n" + + "+ \"field=\" + field\n" + + "+ \"}\";\n" + + "}\n" + + "}\n"; + String output = + "class Test {\n" + + " private final int field;\n" + + "\n" + + " Test(\n" + + " @Interesting Integer field,\n" + + " boolean ignored) {\n" + + " this.field = field;\n" + + " }\n" + + "\n" + + " @Override\n" + + " public boolean equals(Object x) {\n" + + " return x instanceof Test\n" + + " && ((Test) x).field == field;\n" + + " // interesting\n" + + " }\n" + + "\n" + + " @Override\n" + + " public String toString() {\n" + + " return \"Test{\"\n" + + " + \"field=\" + field\n" + + " + \"}\";\n" + + " }\n" + + "}\n"; + assertThat(Reformatter.fixup(input)).isEqualTo(output); } } diff --git a/value/src/test/java/com/google/auto/value/processor/SimpleServiceLoaderTest.java b/value/src/test/java/com/google/auto/value/processor/SimpleServiceLoaderTest.java index fab18056..5e2d230d 100644 --- a/value/src/test/java/com/google/auto/value/processor/SimpleServiceLoaderTest.java +++ b/value/src/test/java/com/google/auto/value/processor/SimpleServiceLoaderTest.java @@ -28,6 +28,8 @@ import java.io.PrintWriter; import java.net.URL; import java.net.URLClassLoader; import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.Collections; import java.util.Enumeration; import java.util.List; import java.util.ServiceConfigurationError; @@ -54,6 +56,35 @@ public final class SimpleServiceLoaderTest { assertThat(classes).containsExactly(String.class, StringBuilder.class).inOrder(); } + // Sometimes you can have the same jar appear more than once in the classpath, perhaps in + // different versions. In that case we don't want to instantiate the same class more than once. + // This test checks that we don't. + @Test + public void loadWithDuplicates() throws Exception { + ClassLoader loader1 = + loaderForJarWithEntries( + CharSequence.class.getName(), String.class.getName(), StringBuilder.class.getName()); + ClassLoader loader2 = + loaderForJarWithEntries( + CharSequence.class.getName(), String.class.getName(), StringBuilder.class.getName()); + ClassLoader combinedLoader = + new ClassLoader() { + @Override + public Enumeration<URL> getResources(String name) throws IOException { + List<URL> urls = new ArrayList<>(Collections.list(loader1.getResources(name))); + urls.addAll(Collections.list(loader2.getResources(name))); + return Collections.enumeration(urls); + } + }; + + ImmutableList<CharSequence> providers = + SimpleServiceLoader.load(CharSequence.class, combinedLoader); + + assertThat(providers).contains(""); + List<Class<?>> classes = providers.stream().map(Object::getClass).collect(toList()); + assertThat(classes).containsExactly(String.class, StringBuilder.class).inOrder(); + } + @Test public void blankLinesAndComments() throws Exception { ClassLoader loader = diff --git a/value/src/test/java/com/google/auto/value/processor/SimplifyWithAnnotationsTest.java b/value/src/test/java/com/google/auto/value/processor/SimplifyWithAnnotationsTest.java index 39e2dc0e..7bc67790 100644 --- a/value/src/test/java/com/google/auto/value/processor/SimplifyWithAnnotationsTest.java +++ b/value/src/test/java/com/google/auto/value/processor/SimplifyWithAnnotationsTest.java @@ -157,8 +157,7 @@ public class SimplifyWithAnnotationsTest { void testTypeSpellings(TypeElement testClass) { ExecutableElement witness = - ElementFilter.methodsIn(testClass.getEnclosedElements()) - .stream() + ElementFilter.methodsIn(testClass.getEnclosedElements()).stream() .filter(m -> m.getSimpleName().contentEquals("witness")) .collect(onlyElement()); if (witness.getReturnType().getAnnotationMirrors().isEmpty()) { diff --git a/value/src/test/java/com/google/auto/value/processor/TypeEncoderTest.java b/value/src/test/java/com/google/auto/value/processor/TypeEncoderTest.java index c31f711e..83951e0a 100644 --- a/value/src/test/java/com/google/auto/value/processor/TypeEncoderTest.java +++ b/value/src/test/java/com/google/auto/value/processor/TypeEncoderTest.java @@ -292,7 +292,7 @@ public class TypeEncoderTest { TypeMirror multipleBoundsMirror = multipleBoundsElement.asType(); String text = "`import`\n"; text += "{" + TypeEncoder.encode(multipleBoundsMirror) + "}"; - text += "{" + TypeEncoder.formalTypeParametersString(multipleBoundsElement) + "}"; + text += "{" + TypeEncoder.typeParametersString(multipleBoundsElement.getTypeParameters()) + "}"; String myPackage = getClass().getPackage().getName(); String decoded = TypeEncoder.decode(text, elementUtils, typeUtils, myPackage, baseWithoutContainedTypes()); @@ -306,6 +306,7 @@ public class TypeEncoderTest { @SuppressWarnings("ClassCanBeStatic") static class Outer<T extends Number> { class InnerWithoutTypeParam {} + class Middle<U> { class InnerWithTypeParam<V> {} } diff --git a/value/src/test/java/com/google/auto/value/processor/TypeVariablesTest.java b/value/src/test/java/com/google/auto/value/processor/TypeVariablesTest.java index 078ef513..895bed25 100644 --- a/value/src/test/java/com/google/auto/value/processor/TypeVariablesTest.java +++ b/value/src/test/java/com/google/auto/value/processor/TypeVariablesTest.java @@ -115,10 +115,12 @@ public class TypeVariablesTest { abstract static class Outer<T, U extends T> { abstract Map<T, U> getFoo(); + abstract List<? extends T> getBar(); abstract static class Inner<T, U extends T> { abstract void setFoo(Map<T, U> foo); + abstract void setBar(List<? extends T> bar); } } @@ -165,13 +167,15 @@ public class TypeVariablesTest { List<ExecutableElement> immutableMapMethods = ElementFilter.methodsIn(immutableMap.getEnclosedElements()); ExecutableElement copyOf = methodNamed(immutableMapMethods, "copyOf", erasedMap); - expect.that( - TypeVariables.canAssignStaticMethodResult( - copyOf, immutableMapStringInteger, immutableMapStringNumber, typeUtils)) + expect + .that( + TypeVariables.canAssignStaticMethodResult( + copyOf, immutableMapStringInteger, immutableMapStringNumber, typeUtils)) .isTrue(); - expect.that( - TypeVariables.canAssignStaticMethodResult( - copyOf, immutableMapStringNumber, immutableMapStringInteger, typeUtils)) + expect + .that( + TypeVariables.canAssignStaticMethodResult( + copyOf, immutableMapStringNumber, immutableMapStringInteger, typeUtils)) .isFalse(); } @@ -184,7 +188,9 @@ public class TypeVariablesTest { return methods.stream() .filter(m -> m.getSimpleName().contentEquals(name)) .filter(m -> m.getParameters().size() == 1) - .filter(m -> typeUtils.isSameType( + .filter( + m -> + typeUtils.isSameType( erasedParameterType, typeUtils.erasure(m.getParameters().get(0).asType()))) .findFirst() .get(); |