1 /*
2  * Copyright 2020 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.extension.serializable.processor;
17 
18 import static com.google.common.truth.Truth.assertThat;
19 import static com.google.common.truth.Truth8.assertThat;
20 import static org.junit.Assert.assertThrows;
21 
22 import com.google.auto.value.AutoValue;
23 import com.google.auto.value.extension.memoized.Memoized;
24 import com.google.auto.value.extension.serializable.SerializableAutoValue;
25 import com.google.common.collect.ImmutableList;
26 import com.google.common.collect.ImmutableMap;
27 import com.google.common.testing.SerializableTester;
28 import java.io.ByteArrayOutputStream;
29 import java.io.NotSerializableException;
30 import java.io.ObjectOutputStream;
31 import java.io.Serializable;
32 import java.util.Optional;
33 import org.junit.Test;
34 import org.junit.runner.RunWith;
35 import org.junit.runners.JUnit4;
36 
37 @RunWith(JUnit4.class)
38 public final class SerializableAutoValueExtensionTest {
39   private static final String A = "a";
40   private static final int B = 1;
41   private static final String C = "c";
42   private static final int D = 2;
43 
44   @SerializableAutoValue
45   @AutoValue
46   abstract static class DummySerializableAutoValue implements Serializable {
47     // Primitive fields
a()48     abstract String a();
49 
b()50     abstract int b();
51 
52     // Optional fields
optionalC()53     abstract Optional<String> optionalC();
54 
optionalD()55     abstract Optional<Integer> optionalD();
56 
builder()57     static DummySerializableAutoValue.Builder builder() {
58       return new AutoValue_SerializableAutoValueExtensionTest_DummySerializableAutoValue.Builder();
59     }
60 
61     @AutoValue.Builder
62     abstract static class Builder {
setA(String value)63       abstract DummySerializableAutoValue.Builder setA(String value);
64 
setB(int value)65       abstract DummySerializableAutoValue.Builder setB(int value);
66 
setOptionalC(String value)67       abstract DummySerializableAutoValue.Builder setOptionalC(String value);
68 
setOptionalD(int value)69       abstract DummySerializableAutoValue.Builder setOptionalD(int value);
70 
build()71       abstract DummySerializableAutoValue build();
72     }
73   }
74 
75   @Test
allFieldsAreSet_noEmpty()76   public void allFieldsAreSet_noEmpty() {
77     DummySerializableAutoValue autoValue =
78         DummySerializableAutoValue.builder()
79             .setA(A)
80             .setB(B)
81             .setOptionalC(C)
82             .setOptionalD(D)
83             .build();
84 
85     assertThat(autoValue.a()).isEqualTo(A);
86     assertThat(autoValue.b()).isEqualTo(B);
87     assertThat(autoValue.optionalC()).hasValue(C);
88     assertThat(autoValue.optionalD()).hasValue(D);
89   }
90 
91   @Test
allFieldsAreSet_withMixedEmpty()92   public void allFieldsAreSet_withMixedEmpty() {
93     DummySerializableAutoValue autoValue =
94         DummySerializableAutoValue.builder().setA(A).setB(B).setOptionalC(C).build();
95 
96     assertThat(autoValue.a()).isEqualTo(A);
97     assertThat(autoValue.b()).isEqualTo(B);
98     assertThat(autoValue.optionalC()).hasValue(C);
99     assertThat(autoValue.optionalD()).isEmpty();
100   }
101 
102   @Test
allFieldsAreSet_withEmpty()103   public void allFieldsAreSet_withEmpty() {
104     DummySerializableAutoValue autoValue =
105         DummySerializableAutoValue.builder().setA(A).setB(B).build();
106 
107     assertThat(autoValue.a()).isEqualTo(A);
108     assertThat(autoValue.b()).isEqualTo(B);
109     assertThat(autoValue.optionalC()).isEmpty();
110     assertThat(autoValue.optionalD()).isEmpty();
111   }
112 
113   @Test
allFieldsAreSerialized_noEmpty()114   public void allFieldsAreSerialized_noEmpty() {
115     DummySerializableAutoValue autoValue =
116         DummySerializableAutoValue.builder()
117             .setA(A)
118             .setB(B)
119             .setOptionalC(C)
120             .setOptionalD(D)
121             .build();
122 
123     DummySerializableAutoValue actualAutoValue = SerializableTester.reserialize(autoValue);
124 
125     assertThat(actualAutoValue).isEqualTo(autoValue);
126   }
127 
128   @Test
allFieldsAreSerialized_withEmpty()129   public void allFieldsAreSerialized_withEmpty() {
130     DummySerializableAutoValue autoValue =
131         DummySerializableAutoValue.builder().setA(A).setB(B).build();
132 
133     DummySerializableAutoValue actualAutoValue = SerializableTester.reserialize(autoValue);
134 
135     assertThat(actualAutoValue).isEqualTo(autoValue);
136   }
137 
138   @Test
allFieldsAreSerialized_withMixedEmpty()139   public void allFieldsAreSerialized_withMixedEmpty() {
140     DummySerializableAutoValue autoValue =
141         DummySerializableAutoValue.builder().setA(A).setB(B).setOptionalC(C).build();
142 
143     DummySerializableAutoValue actualAutoValue = SerializableTester.reserialize(autoValue);
144 
145     assertThat(actualAutoValue).isEqualTo(autoValue);
146   }
147 
148   @SerializableAutoValue
149   @AutoValue
150   abstract static class PrefixSerializableAutoValue implements Serializable {
151     // Primitive fields
getA()152     abstract String getA();
153 
isB()154     abstract boolean isB();
155 
156     // Optional fields
getC()157     abstract Optional<String> getC();
158 
getD()159     abstract Optional<Boolean> getD();
160 
builder()161     static PrefixSerializableAutoValue.Builder builder() {
162       return new AutoValue_SerializableAutoValueExtensionTest_PrefixSerializableAutoValue.Builder();
163     }
164 
165     @AutoValue.Builder
166     abstract static class Builder {
a(String value)167       abstract PrefixSerializableAutoValue.Builder a(String value);
168 
b(boolean value)169       abstract PrefixSerializableAutoValue.Builder b(boolean value);
170 
c(String value)171       abstract PrefixSerializableAutoValue.Builder c(String value);
172 
d(boolean value)173       abstract PrefixSerializableAutoValue.Builder d(boolean value);
174 
build()175       abstract PrefixSerializableAutoValue build();
176     }
177   }
178 
179   @Test
allPrefixFieldsAreSerialized_noEmpty()180   public void allPrefixFieldsAreSerialized_noEmpty() {
181     PrefixSerializableAutoValue autoValue =
182         PrefixSerializableAutoValue.builder().a("A").b(true).c("C").d(false).build();
183 
184     PrefixSerializableAutoValue actualAutoValue = SerializableTester.reserialize(autoValue);
185 
186     assertThat(actualAutoValue).isEqualTo(autoValue);
187   }
188 
189   @Test
allPrefixFieldsAreSerialized_WithEmpty()190   public void allPrefixFieldsAreSerialized_WithEmpty() {
191     PrefixSerializableAutoValue autoValue =
192         PrefixSerializableAutoValue.builder().a("A").b(true).build();
193 
194     PrefixSerializableAutoValue actualAutoValue = SerializableTester.reserialize(autoValue);
195 
196     assertThat(actualAutoValue).isEqualTo(autoValue);
197   }
198 
199   @SerializableAutoValue
200   @AutoValue
201   abstract static class NotSerializable {
create()202     static NotSerializable create() {
203       return new AutoValue_SerializableAutoValueExtensionTest_NotSerializable(Optional.of("A"));
204     }
205 
optionalA()206     abstract Optional<String> optionalA();
207   }
208 
209   @Test
missingImplementsSerializableThrowsException()210   public void missingImplementsSerializableThrowsException() throws Exception {
211     NotSerializable autoValue = NotSerializable.create();
212     ByteArrayOutputStream bo = new ByteArrayOutputStream();
213     ObjectOutputStream so = new ObjectOutputStream(bo);
214 
215     assertThrows(NotSerializableException.class, () -> so.writeObject(autoValue));
216   }
217 
218   @AutoValue
219   abstract static class NotSerializableNoAnnotation implements Serializable {
create()220     static NotSerializableNoAnnotation create() {
221       return new AutoValue_SerializableAutoValueExtensionTest_NotSerializableNoAnnotation(
222           Optional.of("A"));
223     }
224 
optionalA()225     abstract Optional<String> optionalA();
226   }
227 
228   @Test
missingSerializableAutoValueAnnotationThrowsException()229   public void missingSerializableAutoValueAnnotationThrowsException() throws Exception {
230     NotSerializableNoAnnotation autoValue = NotSerializableNoAnnotation.create();
231     ByteArrayOutputStream bo = new ByteArrayOutputStream();
232     ObjectOutputStream so = new ObjectOutputStream(bo);
233 
234     assertThrows(NotSerializableException.class, () -> so.writeObject(autoValue));
235   }
236 
237   @SerializableAutoValue
238   @AutoValue
239   // Technically all type parameters should extend serializable, but for the purposes of testing,
240   // only one type parameter is bounded.
241   abstract static class HasTypeParameters<T extends Serializable, S> implements Serializable {
a()242     abstract T a();
243 
optionalB()244     abstract Optional<S> optionalB();
245 
builder()246     static <T extends Serializable, S> Builder<T, S> builder() {
247       return new AutoValue_SerializableAutoValueExtensionTest_HasTypeParameters.Builder<>();
248     }
249 
250     @AutoValue.Builder
251     abstract static class Builder<T extends Serializable, S> {
setA(T value)252       abstract Builder<T, S> setA(T value);
253 
setOptionalB(S value)254       abstract Builder<T, S> setOptionalB(S value);
255 
build()256       abstract HasTypeParameters<T, S> build();
257     }
258   }
259 
260   @Test
typeParameterizedFieldsAreSet_noEmpty()261   public void typeParameterizedFieldsAreSet_noEmpty() {
262     HasTypeParameters<String, Integer> autoValue =
263         HasTypeParameters.<String, Integer>builder().setA(A).setOptionalB(B).build();
264 
265     assertThat(autoValue.a()).isEqualTo(A);
266     assertThat(autoValue.optionalB()).hasValue(B);
267   }
268 
269   @Test
typeParameterizedFieldsAreSet_withEmpty()270   public void typeParameterizedFieldsAreSet_withEmpty() {
271     HasTypeParameters<String, Integer> autoValue =
272         HasTypeParameters.<String, Integer>builder().setA(A).build();
273 
274     assertThat(autoValue.a()).isEqualTo(A);
275     assertThat(autoValue.optionalB()).isEmpty();
276   }
277 
278   @Test
typeParameterizedFieldsAreSerializable_noEmpty()279   public void typeParameterizedFieldsAreSerializable_noEmpty() {
280     HasTypeParameters<String, Integer> autoValue =
281         HasTypeParameters.<String, Integer>builder().setA(A).setOptionalB(B).build();
282 
283     HasTypeParameters<String, Integer> actualAutoValue = SerializableTester.reserialize(autoValue);
284 
285     assertThat(actualAutoValue).isEqualTo(autoValue);
286   }
287 
288   @Test
typeParameterizedFieldsAreSerializable_withEmpty()289   public void typeParameterizedFieldsAreSerializable_withEmpty() {
290     HasTypeParameters<String, Integer> autoValue =
291         HasTypeParameters.<String, Integer>builder().setA(A).build();
292 
293     HasTypeParameters<String, Integer> actualAutoValue = SerializableTester.reserialize(autoValue);
294 
295     assertThat(actualAutoValue).isEqualTo(autoValue);
296   }
297 
298   @SerializableAutoValue
299   @AutoValue
300   abstract static class ImmutableListSerializableAutoValue implements Serializable {
payload()301     abstract ImmutableList<Optional<String>> payload();
302 
builder()303     static ImmutableListSerializableAutoValue.Builder builder() {
304       return new AutoValue_SerializableAutoValueExtensionTest_ImmutableListSerializableAutoValue
305           .Builder();
306     }
307 
308     @AutoValue.Builder
309     abstract static class Builder {
setPayload( ImmutableList<Optional<String>> payload)310       abstract ImmutableListSerializableAutoValue.Builder setPayload(
311           ImmutableList<Optional<String>> payload);
312 
build()313       abstract ImmutableListSerializableAutoValue build();
314     }
315   }
316 
317   @Test
immutableList_emptyListSerialized()318   public void immutableList_emptyListSerialized() {
319     ImmutableListSerializableAutoValue autoValue =
320         ImmutableListSerializableAutoValue.builder().setPayload(ImmutableList.of()).build();
321 
322     ImmutableListSerializableAutoValue actualAutoValue = SerializableTester.reserialize(autoValue);
323 
324     assertThat(actualAutoValue).isEqualTo(autoValue);
325   }
326 
327   @Test
immutableList_allFieldsSetAndSerialized()328   public void immutableList_allFieldsSetAndSerialized() {
329     ImmutableListSerializableAutoValue autoValue =
330         ImmutableListSerializableAutoValue.builder()
331             .setPayload(ImmutableList.of(Optional.of("a1"), Optional.of("a2")))
332             .build();
333 
334     ImmutableListSerializableAutoValue actualAutoValue = SerializableTester.reserialize(autoValue);
335 
336     assertThat(actualAutoValue).isEqualTo(autoValue);
337   }
338 
339   @SerializableAutoValue
340   @AutoValue
341   abstract static class ImmutableMapSerializableAutoValue implements Serializable {
a()342     abstract ImmutableMap<Optional<String>, String> a();
343 
b()344     abstract ImmutableMap<String, Optional<String>> b();
345 
builder()346     static ImmutableMapSerializableAutoValue.Builder builder() {
347       return new AutoValue_SerializableAutoValueExtensionTest_ImmutableMapSerializableAutoValue
348           .Builder();
349     }
350 
351     @AutoValue.Builder
352     abstract static class Builder {
setA( ImmutableMap<Optional<String>, String> a)353       abstract ImmutableMapSerializableAutoValue.Builder setA(
354           ImmutableMap<Optional<String>, String> a);
355 
setB( ImmutableMap<String, Optional<String>> b)356       abstract ImmutableMapSerializableAutoValue.Builder setB(
357           ImmutableMap<String, Optional<String>> b);
358 
build()359       abstract ImmutableMapSerializableAutoValue build();
360     }
361   }
362 
363   @Test
immutableMap_emptyMapSerialized()364   public void immutableMap_emptyMapSerialized() {
365     ImmutableMapSerializableAutoValue autoValue =
366         ImmutableMapSerializableAutoValue.builder()
367             .setA(ImmutableMap.of())
368             .setB(ImmutableMap.of())
369             .build();
370 
371     ImmutableMapSerializableAutoValue actualAutoValue = SerializableTester.reserialize(autoValue);
372 
373     assertThat(actualAutoValue).isEqualTo(autoValue);
374   }
375 
376   @Test
immutableMap_allFieldsSetAndSerialized()377   public void immutableMap_allFieldsSetAndSerialized() {
378     ImmutableMapSerializableAutoValue autoValue =
379         ImmutableMapSerializableAutoValue.builder()
380             .setA(ImmutableMap.of(Optional.of("key"), "value"))
381             .setB(ImmutableMap.of("key", Optional.of("value")))
382             .build();
383 
384     ImmutableMapSerializableAutoValue actualAutoValue = SerializableTester.reserialize(autoValue);
385 
386     assertThat(actualAutoValue).isEqualTo(autoValue);
387   }
388 
389   @SerializableAutoValue
390   @AutoValue
391   abstract static class MultiplePropertiesSameType implements Serializable {
a()392     abstract String a();
393 
b()394     abstract String b();
395 
builder()396     static MultiplePropertiesSameType.Builder builder() {
397       return new AutoValue_SerializableAutoValueExtensionTest_MultiplePropertiesSameType.Builder();
398     }
399 
400     @AutoValue.Builder
401     abstract static class Builder {
setA(String value)402       abstract MultiplePropertiesSameType.Builder setA(String value);
403 
setB(String value)404       abstract MultiplePropertiesSameType.Builder setB(String value);
405 
build()406       abstract MultiplePropertiesSameType build();
407     }
408   }
409 
410   @Test
multiplePropertiesSameType_allFieldsSerialized()411   public void multiplePropertiesSameType_allFieldsSerialized() {
412     MultiplePropertiesSameType autoValue =
413         MultiplePropertiesSameType.builder().setA("A").setB("B").build();
414 
415     MultiplePropertiesSameType actualAutoValue = SerializableTester.reserialize(autoValue);
416 
417     assertThat(actualAutoValue).isEqualTo(autoValue);
418   }
419 
420   /**
421    * Type that may result in nested lambdas in the generated code. Including this allows us to
422    * verify that we handle those correctly, in particular not reusing a lambda parameter name in
423    * another lambda nested inside the first one.
424    */
425   @SerializableAutoValue
426   @AutoValue
427   abstract static class ComplexType implements Serializable {
a()428     abstract ImmutableMap<String, ImmutableMap<String, Optional<String>>> a();
429 
builder()430     static ComplexType.Builder builder() {
431       return new AutoValue_SerializableAutoValueExtensionTest_ComplexType.Builder();
432     }
433 
434     @AutoValue.Builder
435     abstract static class Builder {
setA( ImmutableMap<String, ImmutableMap<String, Optional<String>>> a)436       abstract ComplexType.Builder setA(
437           ImmutableMap<String, ImmutableMap<String, Optional<String>>> a);
438 
build()439       abstract ComplexType build();
440     }
441   }
442 
443   @Test
complexType()444   public void complexType() {
445     ImmutableMap<String, ImmutableMap<String, Optional<String>>> map =
446         ImmutableMap.of("foo", ImmutableMap.of("bar", Optional.of("baz")));
447     ComplexType complexType = ComplexType.builder().setA(map).build();
448 
449     ComplexType reserialized = SerializableTester.reserialize(complexType);
450 
451     assertThat(reserialized).isEqualTo(complexType);
452   }
453 
454   /**
455    * Type that uses both {@code @SerializableAutoValue} and {@code @Memoized}, showing that the two
456    * extensions work correctly together.
457    */
458   @SerializableAutoValue
459   @AutoValue
460   abstract static class SerializeMemoize implements Serializable {
number()461     abstract Optional<Integer> number();
462     private transient int methodCount;
463 
464     @Memoized
negate()465     Optional<Integer> negate() {
466       methodCount++;
467       return number().map(n -> -n);
468     }
469 
builder()470     static SerializeMemoize.Builder builder() {
471       return new AutoValue_SerializableAutoValueExtensionTest_SerializeMemoize.Builder();
472     }
473 
474     @AutoValue.Builder
475     abstract static class Builder {
setNumber(Optional<Integer> number)476       abstract Builder setNumber(Optional<Integer> number);
build()477       abstract SerializeMemoize build();
478     }
479   }
480 
481   @Test
serializeMemoize()482   public void serializeMemoize() {
483     SerializeMemoize instance1 = SerializeMemoize.builder().setNumber(Optional.of(17)).build();
484     assertThat(instance1.methodCount).isEqualTo(0);
485     assertThat(instance1.negate()).hasValue(-17);
486     assertThat(instance1.methodCount).isEqualTo(1);
487     assertThat(instance1.negate()).hasValue(-17);
488     assertThat(instance1.methodCount).isEqualTo(1);
489     SerializeMemoize instance2 = SerializableTester.reserialize(instance1);
490     assertThat(instance2.methodCount).isEqualTo(0);
491     assertThat(instance2.negate()).hasValue(-17);
492     assertThat(instance2.methodCount).isEqualTo(1);
493     assertThat(instance2.negate()).hasValue(-17);
494     assertThat(instance2.methodCount).isEqualTo(1);
495 
496   }
497 }
498