• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2014 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.gwt;
17 
18 import static com.google.common.truth.Truth.assertThat;
19 import static com.google.common.truth.Truth.assertWithMessage;
20 
21 import com.google.auto.value.AutoValue;
22 import com.google.common.annotations.GwtCompatible;
23 import com.google.common.collect.ImmutableList;
24 import com.google.common.collect.ImmutableMap;
25 import com.google.common.reflect.Reflection;
26 import com.google.gwt.user.client.rpc.SerializationException;
27 import com.google.gwt.user.client.rpc.SerializationStreamWriter;
28 import java.io.Serializable;
29 import java.lang.reflect.Method;
30 import java.util.ArrayDeque;
31 import java.util.Collections;
32 import java.util.Deque;
33 import java.util.List;
34 import java.util.Map;
35 import javax.annotation.Nullable;
36 import org.junit.Test;
37 import org.junit.function.ThrowingRunnable;
38 import org.junit.runner.RunWith;
39 import org.junit.runners.JUnit4;
40 
41 /**
42  * Tests that the generated GWT serializer for GwtValueType serializes fields in the expected way.
43  *
44  * @author emcmanus@google.com (Éamonn McManus)
45  */
46 @RunWith(JUnit4.class)
47 public class CustomFieldSerializerTest {
48   @AutoValue
49   @GwtCompatible(serializable = true)
50   abstract static class ValueType implements Serializable {
string()51     abstract String string();
52 
integer()53     abstract int integer();
54 
55     @Nullable
other()56     abstract ValueType other();
57 
others()58     abstract List<ValueType> others();
59 
create(String string, int integer, @Nullable ValueType other)60     static ValueType create(String string, int integer, @Nullable ValueType other) {
61       return create(string, integer, other, Collections.<ValueType>emptyList());
62     }
63 
create( String string, int integer, @Nullable ValueType other, List<ValueType> others)64     static ValueType create(
65         String string, int integer, @Nullable ValueType other, List<ValueType> others) {
66       return new AutoValue_CustomFieldSerializerTest_ValueType(string, integer, other, others);
67     }
68   }
69 
70   private static final ValueType SIMPLE = ValueType.create("anotherstring", 1729, null);
71   private static final ValueType CONS = ValueType.create("whatever", 1296, SIMPLE);
72   private static final ValueType WITH_LIST =
73       ValueType.create("blim", 11881376, SIMPLE, ImmutableList.of(SIMPLE, CONS));
74 
75   private final MickeyMouseMock<SerializationStreamWriter> mock =
76       new MickeyMouseMock<>(SerializationStreamWriter.class);
77   private final SerializationStreamWriter streamWriter = mock.proxy();
78 
79   @Test
testCustomFieldSerializer()80   public void testCustomFieldSerializer() throws SerializationException {
81     AutoValue_CustomFieldSerializerTest_ValueType withList =
82         (AutoValue_CustomFieldSerializerTest_ValueType) WITH_LIST;
83     AutoValue_CustomFieldSerializerTest_ValueType_CustomFieldSerializer.serialize(
84         streamWriter, withList);
85     mock.verify(
86         () -> {
87           streamWriter.writeString("blim");
88           streamWriter.writeInt(11881376);
89           streamWriter.writeObject(SIMPLE);
90           streamWriter.writeObject(ImmutableList.of(SIMPLE, CONS));
91         });
92   }
93 
94   @AutoValue
95   @GwtCompatible(serializable = true)
96   abstract static class ValueTypeWithGetters implements Serializable {
getPackage()97     abstract String getPackage();
98 
isDefault()99     abstract boolean isDefault();
100 
create(String pkg, boolean dflt)101     static ValueTypeWithGetters create(String pkg, boolean dflt) {
102       return new AutoValue_CustomFieldSerializerTest_ValueTypeWithGetters(pkg, dflt);
103     }
104   }
105 
106   @Test
testCustomFieldSerializerWithGetters()107   public void testCustomFieldSerializerWithGetters() throws SerializationException {
108     AutoValue_CustomFieldSerializerTest_ValueTypeWithGetters instance =
109         (AutoValue_CustomFieldSerializerTest_ValueTypeWithGetters)
110             ValueTypeWithGetters.create("package", true);
111     AutoValue_CustomFieldSerializerTest_ValueTypeWithGetters_CustomFieldSerializer.serialize(
112         streamWriter, instance);
113     mock.verify(
114         () -> {
115           streamWriter.writeString("package");
116           streamWriter.writeBoolean(true);
117         });
118   }
119 
120   @AutoValue
121   @GwtCompatible(serializable = true)
122   abstract static class GenericValueType<K extends Comparable<K>, V extends K>
123       implements Serializable {
map()124     abstract Map<K, V> map();
125 
create(Map<K, V> map)126     static <K extends Comparable<K>, V extends K> GenericValueType<K, V> create(Map<K, V> map) {
127       return new AutoValue_CustomFieldSerializerTest_GenericValueType<K, V>(map);
128     }
129   }
130 
131   @Test
testCustomFieldSerializerGeneric()132   public void testCustomFieldSerializerGeneric() throws SerializationException {
133     Map<Integer, Integer> map = ImmutableMap.of(2, 2);
134     AutoValue_CustomFieldSerializerTest_GenericValueType<Integer, Integer> instance =
135         (AutoValue_CustomFieldSerializerTest_GenericValueType<Integer, Integer>)
136             GenericValueType.create(map);
137     AutoValue_CustomFieldSerializerTest_GenericValueType_CustomFieldSerializer.serialize(
138         streamWriter, instance);
139     mock.verify(
140         () -> {
141           streamWriter.writeObject(map);
142         });
143   }
144 
145   @AutoValue
146   @GwtCompatible(serializable = true)
147   abstract static class ValueTypeWithBuilder implements Serializable {
string()148     abstract String string();
149 
strings()150     abstract ImmutableList<String> strings();
151 
builder()152     static Builder builder() {
153       return new AutoValue_CustomFieldSerializerTest_ValueTypeWithBuilder.Builder();
154     }
155 
156     @AutoValue.Builder
157     interface Builder {
string(String x)158       Builder string(String x);
159 
strings(ImmutableList<String> x)160       Builder strings(ImmutableList<String> x);
161 
build()162       ValueTypeWithBuilder build();
163     }
164   }
165 
166   @Test
testCustomFieldSerializerWithBuilder()167   public void testCustomFieldSerializerWithBuilder() throws SerializationException {
168     AutoValue_CustomFieldSerializerTest_ValueTypeWithBuilder instance =
169         (AutoValue_CustomFieldSerializerTest_ValueTypeWithBuilder)
170             ValueTypeWithBuilder.builder().string("s").strings(ImmutableList.of("a", "b")).build();
171     AutoValue_CustomFieldSerializerTest_ValueTypeWithBuilder_CustomFieldSerializer.serialize(
172         streamWriter, instance);
173     mock.verify(
174         () -> {
175           streamWriter.writeString("s");
176           streamWriter.writeObject(ImmutableList.of("a", "b"));
177         });
178   }
179 
180   @AutoValue
181   @GwtCompatible(serializable = true)
182   abstract static class ValueTypeWithBuilderAndGetters implements Serializable {
getPackage()183     abstract String getPackage();
184 
isDefault()185     abstract boolean isDefault();
186 
builder()187     static Builder builder() {
188       return new AutoValue_CustomFieldSerializerTest_ValueTypeWithBuilderAndGetters.Builder();
189     }
190 
191     @AutoValue.Builder
192     interface Builder {
setPackage(String x)193       Builder setPackage(String x);
194 
setDefault(boolean x)195       Builder setDefault(boolean x);
196 
build()197       ValueTypeWithBuilderAndGetters build();
198     }
199   }
200 
201   @Test
testCustomFieldSerializerWithBuilderAndGetters()202   public void testCustomFieldSerializerWithBuilderAndGetters() throws SerializationException {
203     AutoValue_CustomFieldSerializerTest_ValueTypeWithBuilderAndGetters instance =
204         (AutoValue_CustomFieldSerializerTest_ValueTypeWithBuilderAndGetters)
205             ValueTypeWithBuilderAndGetters.builder().setPackage("s").setDefault(false).build();
206     AutoValue_CustomFieldSerializerTest_ValueTypeWithBuilderAndGetters_CustomFieldSerializer
207         .serialize(streamWriter, instance);
208     mock.verify(
209         () -> {
210           streamWriter.writeString("s");
211           streamWriter.writeBoolean(false);
212         });
213   }
214 
215   @AutoValue
216   @GwtCompatible(serializable = true)
217   abstract static class GenericValueTypeWithBuilder<K extends Comparable<K>, V extends K>
218       implements Serializable {
map()219     abstract Map<K, V> map();
220 
builder()221     static <K extends Comparable<K>, V extends K> Builder<K, V> builder() {
222       return new AutoValue_CustomFieldSerializerTest_GenericValueTypeWithBuilder.Builder<K, V>();
223     }
224 
225     @AutoValue.Builder
226     interface Builder<K extends Comparable<K>, V extends K> {
map(Map<K, V> map)227       Builder<K, V> map(Map<K, V> map);
228 
build()229       GenericValueTypeWithBuilder<K, V> build();
230     }
231   }
232 
233   @Test
testCustomFieldSerializerGenericWithBuilder()234   public void testCustomFieldSerializerGenericWithBuilder() throws SerializationException {
235     Map<Integer, Integer> map = ImmutableMap.of(2, 2);
236     AutoValue_CustomFieldSerializerTest_GenericValueTypeWithBuilder<Integer, Integer> instance =
237         (AutoValue_CustomFieldSerializerTest_GenericValueTypeWithBuilder<Integer, Integer>)
238             GenericValueTypeWithBuilder.<Integer, Integer>builder().map(map).build();
239     AutoValue_CustomFieldSerializerTest_GenericValueTypeWithBuilder_CustomFieldSerializer.serialize(
240         streamWriter, instance);
241     mock.verify(
242         () -> {
243           streamWriter.writeObject(map);
244         });
245   }
246 
247   @AutoValue
248   abstract static class MethodCall {
method()249     abstract String method();
250 
args()251     abstract ImmutableList<Object> args();
252 
of(String method, ImmutableList<Object> args)253     static MethodCall of(String method, ImmutableList<Object> args) {
254       return new AutoValue_CustomFieldSerializerTest_MethodCall(method, args);
255     }
256   }
257 
258   /**
259    * A trivial home-made mocking framework.
260    *
261    * <p>Mockito 5 no longer supports Java 8, and we do, so rather than pinning to an older version
262    * of Mockito we fake it with {@link Reflection}. This is only really possible because the thing
263    * we want to mock ({@link SerializationStreamWriter}) is an interface. Furthermore all its
264    * methods return void so we don't even need a way to specify what to return.
265    *
266    * <p>The idea is that you make an instance of this class, have the code under test call methods
267    * on it, then call {@link #verify} with a lambda that repeats the expected calls. If they match
268    * the actual calls then the test passes.
269    */
270   private static class MickeyMouseMock<T> {
271     private boolean recording = true;
272     private final Deque<MethodCall> methodCalls = new ArrayDeque<>();
273     private final T proxy;
274 
MickeyMouseMock(Class<T> intf)275     MickeyMouseMock(Class<T> intf) {
276       this.proxy = Reflection.newProxy(intf, this::invocationHandler);
277     }
278 
proxy()279     T proxy() {
280       return proxy;
281     }
282 
verify(ThrowingRunnable actions)283     void verify(ThrowingRunnable actions) {
284       assertThat(recording).isTrue();
285       recording = false;
286       try {
287         actions.run();
288       } catch (AssertionError e) {
289         throw e;
290       } catch (Throwable t) {
291         throw new AssertionError(t);
292       }
293       assertThat(methodCalls).isEmpty();
294     }
295 
invocationHandler(Object proxy, Method method, Object[] args)296     private Object invocationHandler(Object proxy, Method method, Object[] args) {
297       if (args == null) {
298         args = new Object[0];
299       }
300       MethodCall methodCall = MethodCall.of(method.getName(), ImmutableList.copyOf(args));
301       if (recording) {
302         methodCalls.add(methodCall);
303       } else { // verifying
304         assertWithMessage("Missing call %s", methodCall).that(methodCalls).isNotEmpty();
305         MethodCall recorded = methodCalls.removeFirst();
306         assertThat(methodCall).isEqualTo(recorded);
307       }
308       return null;
309     }
310   }
311 }
312