| /* |
| * Copyright 2018 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; |
| |
| import static com.google.common.truth.Truth.assertThat; |
| import static org.junit.Assert.fail; |
| |
| import com.google.common.testing.EqualsTester; |
| import java.io.ByteArrayInputStream; |
| import java.io.ByteArrayOutputStream; |
| import java.io.ObjectInputStream; |
| import java.io.ObjectOutputStream; |
| import java.io.Serializable; |
| import java.lang.annotation.Retention; |
| import java.lang.annotation.RetentionPolicy; |
| import java.util.concurrent.ExecutionException; |
| import org.junit.Test; |
| import org.junit.runner.RunWith; |
| import org.junit.runners.JUnit4; |
| |
| /** @author emcmanus@google.com (Éamonn McManus) */ |
| @RunWith(JUnit4.class) |
| public class AutoOneOfTest { |
| @AutoValue |
| public abstract static class Dog { |
| public abstract String name(); |
| |
| public static Dog create(String name) { |
| return new AutoValue_AutoOneOfTest_Dog(name); |
| } |
| |
| public void bark() {} |
| } |
| |
| @AutoValue |
| public abstract static class Cat { |
| public static Cat create() { |
| return new AutoValue_AutoOneOfTest_Cat(); |
| } |
| |
| public void meow() {} |
| } |
| |
| @AutoValue |
| public abstract static class TigerShark { |
| public static TigerShark create() { |
| return new AutoValue_AutoOneOfTest_TigerShark(); |
| } |
| |
| public void chomp() {} |
| } |
| |
| @AutoOneOf(Pet.Kind.class) |
| public abstract static class Pet { |
| |
| public static Pet create(Dog dog) { |
| return AutoOneOf_AutoOneOfTest_Pet.dog(dog); |
| } |
| |
| public static Pet create(Cat cat) { |
| return AutoOneOf_AutoOneOfTest_Pet.cat(cat); |
| } |
| |
| public static Pet create(TigerShark shark) { |
| return AutoOneOf_AutoOneOfTest_Pet.tigerShark(shark); |
| } |
| |
| public abstract Dog dog(); |
| |
| public abstract Cat cat(); |
| |
| public abstract TigerShark tigerShark(); |
| |
| public enum Kind { |
| DOG, |
| CAT, |
| TIGER_SHARK |
| } |
| |
| public abstract Kind getKind(); |
| } |
| |
| @Test |
| public void equality() { |
| Dog marvin1 = Dog.create("Marvin"); |
| Pet petMarvin1 = Pet.create(marvin1); |
| Dog marvin2 = Dog.create("Marvin"); |
| Pet petMarvin2 = Pet.create(marvin2); |
| Dog isis = Dog.create("Isis"); |
| Pet petIsis = Pet.create(isis); |
| Cat cat = Cat.create(); |
| new EqualsTester() |
| .addEqualityGroup(marvin1, marvin2) |
| .addEqualityGroup(petMarvin1, petMarvin2) |
| .addEqualityGroup(petIsis) |
| .addEqualityGroup(cat) |
| .addEqualityGroup("foo") |
| .testEquals(); |
| } |
| |
| @Test |
| public void getCorrectType() { |
| Dog marvin = Dog.create("Marvin"); |
| Pet petMarvin = Pet.create(marvin); |
| assertThat(petMarvin.dog()).isSameInstanceAs(marvin); |
| } |
| |
| @Test |
| public void getWrongType() { |
| Cat cat = Cat.create(); |
| Pet petCat = Pet.create(cat); |
| try { |
| petCat.tigerShark(); |
| fail(); |
| } catch (UnsupportedOperationException e) { |
| assertThat(e).hasMessageThat().containsMatch("(?i:cat)"); |
| } |
| } |
| |
| @Test |
| public void string() { |
| Dog marvin = Dog.create("Marvin"); |
| Pet petMarvin = Pet.create(marvin); |
| assertThat(petMarvin.toString()).isEqualTo("Pet{dog=Dog{name=Marvin}}"); |
| } |
| |
| @Test |
| public void getKind() { |
| Dog marvin = Dog.create("Marvin"); |
| Pet petMarvin = Pet.create(marvin); |
| Cat cat = Cat.create(); |
| Pet petCat = Pet.create(cat); |
| TigerShark shark = TigerShark.create(); |
| Pet petShark = Pet.create(shark); |
| assertThat(petMarvin.getKind()).isEqualTo(Pet.Kind.DOG); |
| assertThat(petCat.getKind()).isEqualTo(Pet.Kind.CAT); |
| assertThat(petShark.getKind()).isEqualTo(Pet.Kind.TIGER_SHARK); |
| } |
| |
| @Test |
| public void cannotBeNull() { |
| try { |
| Pet.create((Dog) null); |
| fail(); |
| } catch (NullPointerException expected) { |
| } |
| } |
| |
| // Package-private case. |
| |
| @AutoOneOf(IntegerOrString.Kind.class) |
| abstract static class IntegerOrString { |
| enum Kind { |
| INTEGER, |
| STRING |
| } |
| |
| abstract Kind getKind(); |
| |
| abstract int integer(); |
| |
| abstract String string(); |
| |
| static IntegerOrString of(int x) { |
| return AutoOneOf_AutoOneOfTest_IntegerOrString.integer(x); |
| } |
| |
| static IntegerOrString of(String x) { |
| return AutoOneOf_AutoOneOfTest_IntegerOrString.string(x); |
| } |
| } |
| |
| @Test |
| public void packagePrivate() { |
| IntegerOrString integer = IntegerOrString.of(23); |
| IntegerOrString string = IntegerOrString.of("23"); |
| assertThat(integer.getKind()).isEqualTo(IntegerOrString.Kind.INTEGER); |
| assertThat(string.getKind()).isEqualTo(IntegerOrString.Kind.STRING); |
| assertThat(integer.integer()).isEqualTo(23); |
| assertThat(string.string()).isEqualTo("23"); |
| assertThat(integer).isNotEqualTo(string); |
| try { |
| integer.string(); |
| fail(); |
| } catch (UnsupportedOperationException e) { |
| assertThat(e).hasMessageThat().containsMatch("(?i:integer)"); |
| } |
| } |
| |
| @AutoOneOf(Pet.Kind.class) |
| public abstract static class PetWithGet { |
| public abstract Dog getDog(); |
| |
| public abstract Cat getCat(); |
| |
| public abstract TigerShark getTigerShark(); |
| |
| public static PetWithGet create(Dog dog) { |
| return AutoOneOf_AutoOneOfTest_PetWithGet.dog(dog); |
| } |
| |
| public static PetWithGet create(Cat cat) { |
| return AutoOneOf_AutoOneOfTest_PetWithGet.cat(cat); |
| } |
| |
| public static PetWithGet create(TigerShark shark) { |
| return AutoOneOf_AutoOneOfTest_PetWithGet.tigerShark(shark); |
| } |
| |
| public abstract Pet.Kind getKind(); |
| } |
| |
| @Test |
| public void getPrefix() { |
| Dog marvin = Dog.create("Marvin"); |
| PetWithGet petMarvin = PetWithGet.create(marvin); |
| assertThat(petMarvin.toString()).isEqualTo("PetWithGet{dog=Dog{name=Marvin}}"); |
| } |
| |
| @AutoOneOf(Primitive.Kind.class) |
| public abstract static class Primitive { |
| public enum Kind { |
| A_BYTE, |
| A_SHORT, |
| AN_INT, |
| A_LONG, |
| A_FLOAT, |
| A_DOUBLE, |
| A_CHAR, |
| A_BOOLEAN |
| } |
| |
| public abstract Kind getKind(); |
| |
| public abstract byte aByte(); |
| |
| public abstract short aShort(); |
| |
| public abstract int anInt(); |
| |
| public abstract long aLong(); |
| |
| public abstract float aFloat(); |
| |
| public abstract double aDouble(); |
| |
| public abstract char aChar(); |
| |
| public abstract boolean aBoolean(); |
| |
| public static Primitive of(byte x) { |
| return AutoOneOf_AutoOneOfTest_Primitive.aByte(x); |
| } |
| |
| public static Primitive of(short x) { |
| return AutoOneOf_AutoOneOfTest_Primitive.aShort(x); |
| } |
| |
| public static Primitive of(int x) { |
| return AutoOneOf_AutoOneOfTest_Primitive.anInt(x); |
| } |
| |
| public static Primitive of(long x) { |
| return AutoOneOf_AutoOneOfTest_Primitive.aLong(x); |
| } |
| |
| public static Primitive of(float x) { |
| return AutoOneOf_AutoOneOfTest_Primitive.aFloat(x); |
| } |
| |
| public static Primitive of(double x) { |
| return AutoOneOf_AutoOneOfTest_Primitive.aDouble(x); |
| } |
| |
| public static Primitive of(char x) { |
| return AutoOneOf_AutoOneOfTest_Primitive.aChar(x); |
| } |
| |
| public static Primitive of(boolean x) { |
| return AutoOneOf_AutoOneOfTest_Primitive.aBoolean(x); |
| } |
| } |
| |
| @Test |
| public void primitive() { |
| Primitive primitive = Primitive.of(17); |
| assertThat(primitive.anInt()).isEqualTo(17); |
| assertThat(primitive.toString()).isEqualTo("Primitive{anInt=17}"); |
| } |
| |
| @AutoOneOf(OneOfOne.Kind.class) |
| public abstract static class OneOfOne { |
| public enum Kind { |
| DOG |
| } |
| |
| public abstract Dog getDog(); |
| |
| public static OneOfOne create(Dog dog) { |
| return AutoOneOf_AutoOneOfTest_OneOfOne.dog(dog); |
| } |
| |
| public abstract Kind getKind(); |
| } |
| |
| @Test |
| public void oneOfOne() { |
| Dog marvin = Dog.create("Marvin"); |
| OneOfOne oneOfMarvin = OneOfOne.create(marvin); |
| assertThat(oneOfMarvin.toString()).isEqualTo("OneOfOne{dog=Dog{name=Marvin}}"); |
| assertThat(oneOfMarvin.getKind()).isEqualTo(OneOfOne.Kind.DOG); |
| } |
| |
| // We allow this for consistency, even though it's obviously pretty useless. |
| // The generated code might be rubbish, but it compiles. No concrete implementation is generated |
| // so there isn't really anything to test beyond that it compiles. |
| @AutoOneOf(OneOfNone.Kind.class) |
| public abstract static class OneOfNone { |
| public enum Kind {} |
| |
| public abstract Kind getKind(); |
| } |
| |
| // Testing generics. Typically generics will be a bit messy because the @AutoOneOf class must |
| // have type parameters for every property that needs them, even though any given property |
| // might not use all the type parameters. |
| @AutoOneOf(TaskResult.Kind.class) |
| public abstract static class TaskResult<V extends Serializable> { |
| public enum Kind { |
| VALUE, |
| EXCEPTION |
| } |
| |
| public abstract Kind getKind(); |
| |
| public abstract V value(); |
| |
| public abstract Throwable exception(); |
| |
| public V get() throws ExecutionException { |
| switch (getKind()) { |
| case VALUE: |
| return value(); |
| case EXCEPTION: |
| throw new ExecutionException(exception()); |
| } |
| throw new AssertionError(getKind()); |
| } |
| |
| static <V extends Serializable> TaskResult<V> value(V value) { |
| return AutoOneOf_AutoOneOfTest_TaskResult.value(value); |
| } |
| |
| static TaskResult<?> exception(Throwable exception) { |
| return AutoOneOf_AutoOneOfTest_TaskResult.exception(exception); |
| } |
| } |
| |
| @Test |
| public void taskResultValue() throws Exception { |
| TaskResult<String> result = TaskResult.value("foo"); |
| assertThat(result.get()).isEqualTo("foo"); |
| } |
| |
| @Test |
| public void taskResultException() { |
| Exception exception = new IllegalArgumentException("oops"); |
| TaskResult<?> result = TaskResult.exception(exception); |
| try { |
| result.get(); |
| fail(); |
| } catch (ExecutionException e) { |
| assertThat(e).hasCauseThat().isEqualTo(exception); |
| } |
| } |
| |
| @AutoOneOf(CustomToString.Kind.class) |
| public abstract static class CustomToString { |
| public enum Kind { |
| ACE |
| } |
| |
| public abstract Kind getKind(); |
| |
| public abstract String ace(); |
| |
| public static CustomToString ace(String ace) { |
| return AutoOneOf_AutoOneOfTest_CustomToString.ace(ace); |
| } |
| |
| @Override |
| public String toString() { |
| return "blim"; |
| } |
| } |
| |
| // If you have an explicit toString() method, we won't override it. |
| @Test |
| public void customToString() { |
| CustomToString x = CustomToString.ace("ceg"); |
| assertThat(x.toString()).isEqualTo("blim"); |
| } |
| |
| @AutoOneOf(AbstractToString.Kind.class) |
| public abstract static class AbstractToString { |
| public enum Kind { |
| ACE |
| } |
| |
| public abstract Kind getKind(); |
| |
| public abstract String ace(); |
| |
| public static AbstractToString ace(String ace) { |
| return AutoOneOf_AutoOneOfTest_AbstractToString.ace(ace); |
| } |
| |
| @Override |
| public abstract String toString(); |
| } |
| |
| // If you have an explicit abstract toString() method, we will implement it. |
| @Test |
| public void abstractToString() { |
| AbstractToString x = AbstractToString.ace("ceg"); |
| assertThat(x.toString()).isEqualTo("AbstractToString{ace=ceg}"); |
| } |
| |
| // "package" is a reserved word. You probably don't want to have a property with that name, |
| // but if you insist, you can get one by using getFoo()-style methods. We leak our renaming |
| // scheme here (package0) and for users that that bothers they can just avoid having properties |
| // that are reserved words. |
| @AutoOneOf(LetterOrPackage.Kind.class) |
| public abstract static class LetterOrPackage { |
| public enum Kind { |
| LETTER, |
| PACKAGE |
| } |
| |
| public abstract Kind getKind(); |
| |
| public abstract String getLetter(); |
| |
| public abstract String getPackage(); |
| |
| public static LetterOrPackage ofLetter(String letter) { |
| return AutoOneOf_AutoOneOfTest_LetterOrPackage.letter(letter); |
| } |
| |
| public static LetterOrPackage ofPackage(String pkg) { |
| return AutoOneOf_AutoOneOfTest_LetterOrPackage.package0(pkg); |
| } |
| } |
| |
| @Test |
| public void reservedWordProperty() { |
| LetterOrPackage pkg = LetterOrPackage.ofPackage("pacquet"); |
| assertThat(pkg.toString()).isEqualTo("LetterOrPackage{package=pacquet}"); |
| } |
| |
| @AutoOneOf(ArrayValue.Kind.class) |
| public abstract static class ArrayValue { |
| public enum Kind { |
| STRING, |
| INTS |
| } |
| |
| public abstract Kind getKind(); |
| |
| public abstract String string(); |
| |
| @SuppressWarnings("mutable") |
| public abstract int[] ints(); |
| |
| public static ArrayValue ofString(String string) { |
| return AutoOneOf_AutoOneOfTest_ArrayValue.string(string); |
| } |
| |
| public static ArrayValue ofInts(int[] ints) { |
| return AutoOneOf_AutoOneOfTest_ArrayValue.ints(ints); |
| } |
| } |
| |
| @Test |
| public void arrayValues() { |
| ArrayValue string = ArrayValue.ofString("foo"); |
| ArrayValue ints1 = ArrayValue.ofInts(new int[] {17, 23}); |
| ArrayValue ints2 = ArrayValue.ofInts(new int[] {17, 23}); |
| new EqualsTester().addEqualityGroup(string).addEqualityGroup(ints1, ints2).testEquals(); |
| } |
| |
| @Retention(RetentionPolicy.RUNTIME) |
| public @interface CopyTest { |
| int value(); |
| } |
| |
| @AutoOneOf(AnnotationNotCopied.Kind.class) |
| @CopyTest(23) |
| public abstract static class AnnotationNotCopied { |
| public enum Kind { |
| ACE |
| } |
| |
| public abstract Kind getKind(); |
| |
| public abstract String ace(); |
| |
| public static AnnotationNotCopied ace(String ace) { |
| return AutoOneOf_AutoOneOfTest_AnnotationNotCopied.ace(ace); |
| } |
| } |
| |
| @Test |
| public void classAnnotationsNotCopiedByDefault() { |
| assertThat(AnnotationNotCopied.class.isAnnotationPresent(CopyTest.class)).isTrue(); |
| AnnotationNotCopied ace = AnnotationNotCopied.ace("ace"); |
| assertThat(ace.getClass().isAnnotationPresent(CopyTest.class)).isFalse(); |
| } |
| |
| @AutoOneOf(AnnotationCopied.Kind.class) |
| @CopyTest(23) |
| @AutoValue.CopyAnnotations |
| public abstract static class AnnotationCopied { |
| public enum Kind { |
| ACE |
| } |
| |
| public abstract Kind getKind(); |
| |
| public abstract String ace(); |
| |
| public static AnnotationCopied ace(String ace) { |
| return AutoOneOf_AutoOneOfTest_AnnotationCopied.ace(ace); |
| } |
| } |
| |
| @Test |
| public void classAnnotationsCopiedIfCopyAnnotations() { |
| assertThat(AnnotationCopied.class.isAnnotationPresent(CopyTest.class)).isTrue(); |
| AnnotationCopied ace = AnnotationCopied.ace("ace"); |
| assertThat(ace.getClass().isAnnotationPresent(CopyTest.class)).isTrue(); |
| assertThat(ace.getClass().getAnnotation(CopyTest.class).value()).isEqualTo(23); |
| } |
| |
| @AutoOneOf(MaybeEmpty.Kind.class) |
| public abstract static class MaybeEmpty implements Serializable { |
| private static final long serialVersionUID = 1L; |
| |
| public enum Kind { |
| EMPTY, |
| STRING, |
| } |
| |
| public abstract Kind getKind(); |
| |
| public abstract void empty(); |
| |
| public abstract String string(); |
| |
| public static MaybeEmpty ofEmpty() { |
| return AutoOneOf_AutoOneOfTest_MaybeEmpty.empty(); |
| } |
| |
| public static MaybeEmpty ofString(String s) { |
| return AutoOneOf_AutoOneOfTest_MaybeEmpty.string(s); |
| } |
| } |
| |
| @Test |
| public void voidPropertyIsSingleton() { |
| MaybeEmpty empty1 = MaybeEmpty.ofEmpty(); |
| MaybeEmpty empty2 = MaybeEmpty.ofEmpty(); |
| assertThat(empty1).isSameInstanceAs(empty2); |
| } |
| |
| @Test |
| public void voidPropertyRemainsSingletonWhenDeserialized() throws Exception { |
| MaybeEmpty empty1 = MaybeEmpty.ofEmpty(); |
| ByteArrayOutputStream baos = new ByteArrayOutputStream(); |
| // We're still compiling this with -source 6, so we can't use try-with-resources. |
| ObjectOutputStream dos = new ObjectOutputStream(baos); |
| dos.writeObject(empty1); |
| dos.close(); |
| ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray()); |
| ObjectInputStream ois = new ObjectInputStream(bais); |
| MaybeEmpty empty2 = (MaybeEmpty) ois.readObject(); |
| assertThat(empty2).isSameInstanceAs(empty1); |
| } |
| |
| @Test |
| public void voidPropertyToString() { |
| MaybeEmpty empty = MaybeEmpty.ofEmpty(); |
| assertThat(empty.toString()).isEqualTo("MaybeEmpty{empty}"); |
| } |
| |
| @Test |
| public void voidPropertyHashCodeIsIdentity() { |
| MaybeEmpty empty = MaybeEmpty.ofEmpty(); |
| assertThat(empty.hashCode()).isEqualTo(System.identityHashCode(empty)); |
| } |
| |
| @Test |
| public void voidPropertyGetterDoesNothing() { |
| MaybeEmpty empty = MaybeEmpty.ofEmpty(); |
| empty.empty(); |
| } |
| |
| @Test |
| public void voidPropertyNotEqualToNonVoid() { |
| MaybeEmpty empty = MaybeEmpty.ofEmpty(); |
| MaybeEmpty notEmpty = MaybeEmpty.ofString("foo"); |
| assertThat(empty).isNotEqualTo(notEmpty); |
| assertThat(notEmpty).isNotEqualTo(empty); |
| } |
| |
| @Test |
| public void voidPropertyWrongType() { |
| MaybeEmpty notEmpty = MaybeEmpty.ofString("foo"); |
| try { |
| notEmpty.empty(); |
| fail(); |
| } catch (UnsupportedOperationException e) { |
| assertThat(e).hasMessageThat().containsMatch("(?i:string)"); |
| } |
| } |
| |
| @AutoOneOf(OneOfArray.Kind.class) |
| public abstract static class OneOfArray { |
| public enum Kind { |
| INTS |
| } |
| |
| public abstract Kind getKind(); |
| |
| @SuppressWarnings("mutable") |
| public abstract int[] ints(); |
| |
| public static OneOfArray ofInts(int[] s) { |
| return AutoOneOf_AutoOneOfTest_OneOfArray.ints(s); |
| } |
| } |
| |
| @Test |
| public void arrayToString() { |
| OneOfArray oneOfArray = OneOfArray.ofInts(new int[] {1, 2}); |
| assertThat(oneOfArray.toString()).isEqualTo("OneOfArray{ints=[1, 2]}"); |
| } |
| } |