• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2018 Google LLC
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package com.google.auto.value;
17 
18 import static com.google.common.truth.Truth.assertThat;
19 import static org.junit.Assert.fail;
20 
21 import com.google.common.testing.EqualsTester;
22 import java.io.ByteArrayInputStream;
23 import java.io.ByteArrayOutputStream;
24 import java.io.ObjectInputStream;
25 import java.io.ObjectOutputStream;
26 import java.io.Serializable;
27 import java.lang.annotation.Retention;
28 import java.lang.annotation.RetentionPolicy;
29 import java.util.concurrent.ExecutionException;
30 import org.junit.Test;
31 import org.junit.runner.RunWith;
32 import org.junit.runners.JUnit4;
33 
34 /** @author emcmanus@google.com (Éamonn McManus) */
35 @RunWith(JUnit4.class)
36 public class AutoOneOfTest {
37   @AutoValue
38   public abstract static class Dog {
name()39     public abstract String name();
40 
create(String name)41     public static Dog create(String name) {
42       return new AutoValue_AutoOneOfTest_Dog(name);
43     }
44 
bark()45     public void bark() {}
46   }
47 
48   @AutoValue
49   public abstract static class Cat {
create()50     public static Cat create() {
51       return new AutoValue_AutoOneOfTest_Cat();
52     }
53 
meow()54     public void meow() {}
55   }
56 
57   @AutoValue
58   public abstract static class TigerShark {
create()59     public static TigerShark create() {
60       return new AutoValue_AutoOneOfTest_TigerShark();
61     }
62 
chomp()63     public void chomp() {}
64   }
65 
66   @AutoOneOf(Pet.Kind.class)
67   public abstract static class Pet {
68 
create(Dog dog)69     public static Pet create(Dog dog) {
70       return AutoOneOf_AutoOneOfTest_Pet.dog(dog);
71     }
72 
create(Cat cat)73     public static Pet create(Cat cat) {
74       return AutoOneOf_AutoOneOfTest_Pet.cat(cat);
75     }
76 
create(TigerShark shark)77     public static Pet create(TigerShark shark) {
78       return AutoOneOf_AutoOneOfTest_Pet.tigerShark(shark);
79     }
80 
dog()81     public abstract Dog dog();
82 
cat()83     public abstract Cat cat();
84 
tigerShark()85     public abstract TigerShark tigerShark();
86 
87     public enum Kind {
88       DOG,
89       CAT,
90       TIGER_SHARK
91     }
92 
getKind()93     public abstract Kind getKind();
94   }
95 
96   @Test
equality()97   public void equality() {
98     Dog marvin1 = Dog.create("Marvin");
99     Pet petMarvin1 = Pet.create(marvin1);
100     Dog marvin2 = Dog.create("Marvin");
101     Pet petMarvin2 = Pet.create(marvin2);
102     Dog isis = Dog.create("Isis");
103     Pet petIsis = Pet.create(isis);
104     Cat cat = Cat.create();
105     new EqualsTester()
106         .addEqualityGroup(marvin1, marvin2)
107         .addEqualityGroup(petMarvin1, petMarvin2)
108         .addEqualityGroup(petIsis)
109         .addEqualityGroup(cat)
110         .addEqualityGroup("foo")
111         .testEquals();
112   }
113 
114   @Test
getCorrectType()115   public void getCorrectType() {
116     Dog marvin = Dog.create("Marvin");
117     Pet petMarvin = Pet.create(marvin);
118     assertThat(petMarvin.dog()).isSameInstanceAs(marvin);
119   }
120 
121   @Test
getWrongType()122   public void getWrongType() {
123     Cat cat = Cat.create();
124     Pet petCat = Pet.create(cat);
125     try {
126       petCat.tigerShark();
127       fail();
128     } catch (UnsupportedOperationException e) {
129       assertThat(e).hasMessageThat().containsMatch("(?i:cat)");
130     }
131   }
132 
133   @Test
string()134   public void string() {
135     Dog marvin = Dog.create("Marvin");
136     Pet petMarvin = Pet.create(marvin);
137     assertThat(petMarvin.toString()).isEqualTo("Pet{dog=Dog{name=Marvin}}");
138   }
139 
140   @Test
getKind()141   public void getKind() {
142     Dog marvin = Dog.create("Marvin");
143     Pet petMarvin = Pet.create(marvin);
144     Cat cat = Cat.create();
145     Pet petCat = Pet.create(cat);
146     TigerShark shark = TigerShark.create();
147     Pet petShark = Pet.create(shark);
148     assertThat(petMarvin.getKind()).isEqualTo(Pet.Kind.DOG);
149     assertThat(petCat.getKind()).isEqualTo(Pet.Kind.CAT);
150     assertThat(petShark.getKind()).isEqualTo(Pet.Kind.TIGER_SHARK);
151   }
152 
153   @Test
cannotBeNull()154   public void cannotBeNull() {
155     try {
156       Pet.create((Dog) null);
157       fail();
158     } catch (NullPointerException expected) {
159     }
160   }
161 
162   // Package-private case.
163 
164   @AutoOneOf(IntegerOrString.Kind.class)
165   abstract static class IntegerOrString {
166     enum Kind {
167       INTEGER,
168       STRING
169     }
170 
getKind()171     abstract Kind getKind();
172 
integer()173     abstract int integer();
174 
string()175     abstract String string();
176 
of(int x)177     static IntegerOrString of(int x) {
178       return AutoOneOf_AutoOneOfTest_IntegerOrString.integer(x);
179     }
180 
of(String x)181     static IntegerOrString of(String x) {
182       return AutoOneOf_AutoOneOfTest_IntegerOrString.string(x);
183     }
184   }
185 
186   @Test
packagePrivate()187   public void packagePrivate() {
188     IntegerOrString integer = IntegerOrString.of(23);
189     IntegerOrString string = IntegerOrString.of("23");
190     assertThat(integer.getKind()).isEqualTo(IntegerOrString.Kind.INTEGER);
191     assertThat(string.getKind()).isEqualTo(IntegerOrString.Kind.STRING);
192     assertThat(integer.integer()).isEqualTo(23);
193     assertThat(string.string()).isEqualTo("23");
194     assertThat(integer).isNotEqualTo(string);
195     try {
196       integer.string();
197       fail();
198     } catch (UnsupportedOperationException e) {
199       assertThat(e).hasMessageThat().containsMatch("(?i:integer)");
200     }
201   }
202 
203   @AutoOneOf(Pet.Kind.class)
204   public abstract static class PetWithGet {
getDog()205     public abstract Dog getDog();
206 
getCat()207     public abstract Cat getCat();
208 
getTigerShark()209     public abstract TigerShark getTigerShark();
210 
create(Dog dog)211     public static PetWithGet create(Dog dog) {
212       return AutoOneOf_AutoOneOfTest_PetWithGet.dog(dog);
213     }
214 
create(Cat cat)215     public static PetWithGet create(Cat cat) {
216       return AutoOneOf_AutoOneOfTest_PetWithGet.cat(cat);
217     }
218 
create(TigerShark shark)219     public static PetWithGet create(TigerShark shark) {
220       return AutoOneOf_AutoOneOfTest_PetWithGet.tigerShark(shark);
221     }
222 
getKind()223     public abstract Pet.Kind getKind();
224   }
225 
226   @Test
getPrefix()227   public void getPrefix() {
228     Dog marvin = Dog.create("Marvin");
229     PetWithGet petMarvin = PetWithGet.create(marvin);
230     assertThat(petMarvin.toString()).isEqualTo("PetWithGet{dog=Dog{name=Marvin}}");
231   }
232 
233   @AutoOneOf(Primitive.Kind.class)
234   public abstract static class Primitive {
235     public enum Kind {
236       A_BYTE,
237       A_SHORT,
238       AN_INT,
239       A_LONG,
240       A_FLOAT,
241       A_DOUBLE,
242       A_CHAR,
243       A_BOOLEAN
244     }
245 
getKind()246     public abstract Kind getKind();
247 
aByte()248     public abstract byte aByte();
249 
aShort()250     public abstract short aShort();
251 
anInt()252     public abstract int anInt();
253 
aLong()254     public abstract long aLong();
255 
aFloat()256     public abstract float aFloat();
257 
aDouble()258     public abstract double aDouble();
259 
aChar()260     public abstract char aChar();
261 
aBoolean()262     public abstract boolean aBoolean();
263 
of(byte x)264     public static Primitive of(byte x) {
265       return AutoOneOf_AutoOneOfTest_Primitive.aByte(x);
266     }
267 
of(short x)268     public static Primitive of(short x) {
269       return AutoOneOf_AutoOneOfTest_Primitive.aShort(x);
270     }
271 
of(int x)272     public static Primitive of(int x) {
273       return AutoOneOf_AutoOneOfTest_Primitive.anInt(x);
274     }
275 
of(long x)276     public static Primitive of(long x) {
277       return AutoOneOf_AutoOneOfTest_Primitive.aLong(x);
278     }
279 
of(float x)280     public static Primitive of(float x) {
281       return AutoOneOf_AutoOneOfTest_Primitive.aFloat(x);
282     }
283 
of(double x)284     public static Primitive of(double x) {
285       return AutoOneOf_AutoOneOfTest_Primitive.aDouble(x);
286     }
287 
of(char x)288     public static Primitive of(char x) {
289       return AutoOneOf_AutoOneOfTest_Primitive.aChar(x);
290     }
291 
of(boolean x)292     public static Primitive of(boolean x) {
293       return AutoOneOf_AutoOneOfTest_Primitive.aBoolean(x);
294     }
295   }
296 
297   @Test
primitive()298   public void primitive() {
299     Primitive primitive = Primitive.of(17);
300     assertThat(primitive.anInt()).isEqualTo(17);
301     assertThat(primitive.toString()).isEqualTo("Primitive{anInt=17}");
302   }
303 
304   @AutoOneOf(OneOfOne.Kind.class)
305   public abstract static class OneOfOne {
306     public enum Kind {
307       DOG
308     }
309 
getDog()310     public abstract Dog getDog();
311 
create(Dog dog)312     public static OneOfOne create(Dog dog) {
313       return AutoOneOf_AutoOneOfTest_OneOfOne.dog(dog);
314     }
315 
getKind()316     public abstract Kind getKind();
317   }
318 
319   @Test
oneOfOne()320   public void oneOfOne() {
321     Dog marvin = Dog.create("Marvin");
322     OneOfOne oneOfMarvin = OneOfOne.create(marvin);
323     assertThat(oneOfMarvin.toString()).isEqualTo("OneOfOne{dog=Dog{name=Marvin}}");
324     assertThat(oneOfMarvin.getKind()).isEqualTo(OneOfOne.Kind.DOG);
325   }
326 
327   // We allow this for consistency, even though it's obviously pretty useless.
328   // The generated code might be rubbish, but it compiles. No concrete implementation is generated
329   // so there isn't really anything to test beyond that it compiles.
330   @AutoOneOf(OneOfNone.Kind.class)
331   public abstract static class OneOfNone {
332     public enum Kind {}
333 
getKind()334     public abstract Kind getKind();
335   }
336 
337   // Testing generics. Typically generics will be a bit messy because the @AutoOneOf class must
338   // have type parameters for every property that needs them, even though any given property
339   // might not use all the type parameters.
340   @AutoOneOf(TaskResult.Kind.class)
341   public abstract static class TaskResult<V extends Serializable> {
342     public enum Kind {
343       VALUE,
344       EXCEPTION
345     }
346 
getKind()347     public abstract Kind getKind();
348 
value()349     public abstract V value();
350 
exception()351     public abstract Throwable exception();
352 
get()353     public V get() throws ExecutionException {
354       switch (getKind()) {
355         case VALUE:
356           return value();
357         case EXCEPTION:
358           throw new ExecutionException(exception());
359       }
360       throw new AssertionError(getKind());
361     }
362 
value(V value)363     static <V extends Serializable> TaskResult<V> value(V value) {
364       return AutoOneOf_AutoOneOfTest_TaskResult.value(value);
365     }
366 
exception(Throwable exception)367     static TaskResult<?> exception(Throwable exception) {
368       return AutoOneOf_AutoOneOfTest_TaskResult.exception(exception);
369     }
370   }
371 
372   @Test
taskResultValue()373   public void taskResultValue() throws Exception {
374     TaskResult<String> result = TaskResult.value("foo");
375     assertThat(result.get()).isEqualTo("foo");
376   }
377 
378   @Test
taskResultException()379   public void taskResultException() {
380     Exception exception = new IllegalArgumentException("oops");
381     TaskResult<?> result = TaskResult.exception(exception);
382     try {
383       result.get();
384       fail();
385     } catch (ExecutionException e) {
386       assertThat(e).hasCauseThat().isEqualTo(exception);
387     }
388   }
389 
390   @AutoOneOf(CustomToString.Kind.class)
391   public abstract static class CustomToString {
392     public enum Kind {
393       ACE
394     }
395 
getKind()396     public abstract Kind getKind();
397 
ace()398     public abstract String ace();
399 
ace(String ace)400     public static CustomToString ace(String ace) {
401       return AutoOneOf_AutoOneOfTest_CustomToString.ace(ace);
402     }
403 
404     @Override
toString()405     public String toString() {
406       return "blim";
407     }
408   }
409 
410   // If you have an explicit toString() method, we won't override it.
411   @Test
customToString()412   public void customToString() {
413     CustomToString x = CustomToString.ace("ceg");
414     assertThat(x.toString()).isEqualTo("blim");
415   }
416 
417   @AutoOneOf(AbstractToString.Kind.class)
418   public abstract static class AbstractToString {
419     public enum Kind {
420       ACE
421     }
422 
getKind()423     public abstract Kind getKind();
424 
ace()425     public abstract String ace();
426 
ace(String ace)427     public static AbstractToString ace(String ace) {
428       return AutoOneOf_AutoOneOfTest_AbstractToString.ace(ace);
429     }
430 
431     @Override
toString()432     public abstract String toString();
433   }
434 
435   // If you have an explicit abstract toString() method, we will implement it.
436   @Test
abstractToString()437   public void abstractToString() {
438     AbstractToString x = AbstractToString.ace("ceg");
439     assertThat(x.toString()).isEqualTo("AbstractToString{ace=ceg}");
440   }
441 
442   // "package" is a reserved word. You probably don't want to have a property with that name,
443   // but if you insist, you can get one by using getFoo()-style methods. We leak our renaming
444   // scheme here (package0) and for users that that bothers they can just avoid having properties
445   // that are reserved words.
446   @AutoOneOf(LetterOrPackage.Kind.class)
447   public abstract static class LetterOrPackage {
448     public enum Kind {
449       LETTER,
450       PACKAGE
451     }
452 
getKind()453     public abstract Kind getKind();
454 
getLetter()455     public abstract String getLetter();
456 
getPackage()457     public abstract String getPackage();
458 
ofLetter(String letter)459     public static LetterOrPackage ofLetter(String letter) {
460       return AutoOneOf_AutoOneOfTest_LetterOrPackage.letter(letter);
461     }
462 
ofPackage(String pkg)463     public static LetterOrPackage ofPackage(String pkg) {
464       return AutoOneOf_AutoOneOfTest_LetterOrPackage.package0(pkg);
465     }
466   }
467 
468   @Test
reservedWordProperty()469   public void reservedWordProperty() {
470     LetterOrPackage pkg = LetterOrPackage.ofPackage("pacquet");
471     assertThat(pkg.toString()).isEqualTo("LetterOrPackage{package=pacquet}");
472   }
473 
474   @AutoOneOf(ArrayValue.Kind.class)
475   public abstract static class ArrayValue {
476     public enum Kind {
477       STRING,
478       INTS
479     }
480 
getKind()481     public abstract Kind getKind();
482 
string()483     public abstract String string();
484 
485     @SuppressWarnings("mutable")
ints()486     public abstract int[] ints();
487 
ofString(String string)488     public static ArrayValue ofString(String string) {
489       return AutoOneOf_AutoOneOfTest_ArrayValue.string(string);
490     }
491 
ofInts(int[] ints)492     public static ArrayValue ofInts(int[] ints) {
493       return AutoOneOf_AutoOneOfTest_ArrayValue.ints(ints);
494     }
495   }
496 
497   @Test
arrayValues()498   public void arrayValues() {
499     ArrayValue string = ArrayValue.ofString("foo");
500     ArrayValue ints1 = ArrayValue.ofInts(new int[] {17, 23});
501     ArrayValue ints2 = ArrayValue.ofInts(new int[] {17, 23});
502     new EqualsTester().addEqualityGroup(string).addEqualityGroup(ints1, ints2).testEquals();
503   }
504 
505   @Retention(RetentionPolicy.RUNTIME)
506   public @interface CopyTest {
value()507     int value();
508   }
509 
510   @AutoOneOf(AnnotationNotCopied.Kind.class)
511   @CopyTest(23)
512   public abstract static class AnnotationNotCopied {
513     public enum Kind {
514       ACE
515     }
516 
getKind()517     public abstract Kind getKind();
518 
ace()519     public abstract String ace();
520 
ace(String ace)521     public static AnnotationNotCopied ace(String ace) {
522       return AutoOneOf_AutoOneOfTest_AnnotationNotCopied.ace(ace);
523     }
524   }
525 
526   @Test
classAnnotationsNotCopiedByDefault()527   public void classAnnotationsNotCopiedByDefault() {
528     assertThat(AnnotationNotCopied.class.isAnnotationPresent(CopyTest.class)).isTrue();
529     AnnotationNotCopied ace = AnnotationNotCopied.ace("ace");
530     assertThat(ace.getClass().isAnnotationPresent(CopyTest.class)).isFalse();
531   }
532 
533   @AutoOneOf(AnnotationCopied.Kind.class)
534   @CopyTest(23)
535   @AutoValue.CopyAnnotations
536   public abstract static class AnnotationCopied {
537     public enum Kind {
538       ACE
539     }
540 
getKind()541     public abstract Kind getKind();
542 
ace()543     public abstract String ace();
544 
ace(String ace)545     public static AnnotationCopied ace(String ace) {
546       return AutoOneOf_AutoOneOfTest_AnnotationCopied.ace(ace);
547     }
548   }
549 
550   @Test
classAnnotationsCopiedIfCopyAnnotations()551   public void classAnnotationsCopiedIfCopyAnnotations() {
552     assertThat(AnnotationCopied.class.isAnnotationPresent(CopyTest.class)).isTrue();
553     AnnotationCopied ace = AnnotationCopied.ace("ace");
554     assertThat(ace.getClass().isAnnotationPresent(CopyTest.class)).isTrue();
555     assertThat(ace.getClass().getAnnotation(CopyTest.class).value()).isEqualTo(23);
556   }
557 
558   @AutoOneOf(MaybeEmpty.Kind.class)
559   public abstract static class MaybeEmpty implements Serializable {
560     private static final long serialVersionUID = 1L;
561 
562     public enum Kind {
563       EMPTY,
564       STRING,
565     }
566 
getKind()567     public abstract Kind getKind();
568 
empty()569     public abstract void empty();
570 
string()571     public abstract String string();
572 
ofEmpty()573     public static MaybeEmpty ofEmpty() {
574       return AutoOneOf_AutoOneOfTest_MaybeEmpty.empty();
575     }
576 
ofString(String s)577     public static MaybeEmpty ofString(String s) {
578       return AutoOneOf_AutoOneOfTest_MaybeEmpty.string(s);
579     }
580   }
581 
582   @Test
voidPropertyIsSingleton()583   public void voidPropertyIsSingleton() {
584     MaybeEmpty empty1 = MaybeEmpty.ofEmpty();
585     MaybeEmpty empty2 = MaybeEmpty.ofEmpty();
586     assertThat(empty1).isSameInstanceAs(empty2);
587   }
588 
589   @Test
voidPropertyRemainsSingletonWhenDeserialized()590   public void voidPropertyRemainsSingletonWhenDeserialized() throws Exception {
591     MaybeEmpty empty1 = MaybeEmpty.ofEmpty();
592     ByteArrayOutputStream baos = new ByteArrayOutputStream();
593     // We're still compiling this with -source 6, so we can't use try-with-resources.
594     ObjectOutputStream dos = new ObjectOutputStream(baos);
595     dos.writeObject(empty1);
596     dos.close();
597     ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
598     ObjectInputStream ois = new ObjectInputStream(bais);
599     MaybeEmpty empty2 = (MaybeEmpty) ois.readObject();
600     assertThat(empty2).isSameInstanceAs(empty1);
601   }
602 
603   @Test
voidPropertyToString()604   public void voidPropertyToString() {
605     MaybeEmpty empty = MaybeEmpty.ofEmpty();
606     assertThat(empty.toString()).isEqualTo("MaybeEmpty{empty}");
607   }
608 
609   @Test
voidPropertyHashCodeIsIdentity()610   public void voidPropertyHashCodeIsIdentity() {
611     MaybeEmpty empty = MaybeEmpty.ofEmpty();
612     assertThat(empty.hashCode()).isEqualTo(System.identityHashCode(empty));
613   }
614 
615   @Test
voidPropertyGetterDoesNothing()616   public void voidPropertyGetterDoesNothing() {
617     MaybeEmpty empty = MaybeEmpty.ofEmpty();
618     empty.empty();
619   }
620 
621   @Test
voidPropertyNotEqualToNonVoid()622   public void voidPropertyNotEqualToNonVoid() {
623     MaybeEmpty empty = MaybeEmpty.ofEmpty();
624     MaybeEmpty notEmpty = MaybeEmpty.ofString("foo");
625     assertThat(empty).isNotEqualTo(notEmpty);
626     assertThat(notEmpty).isNotEqualTo(empty);
627   }
628 
629   @Test
voidPropertyWrongType()630   public void voidPropertyWrongType() {
631     MaybeEmpty notEmpty = MaybeEmpty.ofString("foo");
632     try {
633       notEmpty.empty();
634       fail();
635     } catch (UnsupportedOperationException e) {
636       assertThat(e).hasMessageThat().containsMatch("(?i:string)");
637     }
638   }
639 
640   @AutoOneOf(OneOfArray.Kind.class)
641   public abstract static class OneOfArray {
642     public enum Kind {
643       INTS
644     }
645 
getKind()646     public abstract Kind getKind();
647 
648     @SuppressWarnings("mutable")
ints()649     public abstract int[] ints();
650 
ofInts(int[] s)651     public static OneOfArray ofInts(int[] s) {
652       return AutoOneOf_AutoOneOfTest_OneOfArray.ints(s);
653     }
654   }
655 
656   @Test
arrayToString()657   public void arrayToString() {
658     OneOfArray oneOfArray = OneOfArray.ofInts(new int[] {1, 2});
659     assertThat(oneOfArray.toString()).isEqualTo("OneOfArray{ints=[1, 2]}");
660   }
661 }
662