Skip to content

Commit 810e356

Browse files
authored
Add shrinker test for interface implementation being deserialized (#2845)
For R8 this currently fails, see TODOs in the new code.
1 parent 00ae397 commit 810e356

File tree

3 files changed

+58
-5
lines changed

3 files changed

+58
-5
lines changed
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package com.example;
2+
3+
import com.google.gson.annotations.SerializedName;
4+
5+
/** Interface whose implementation class is only referenced for deserialization */
6+
public interface InterfaceWithImplementation {
7+
String getValue();
8+
9+
// Implementation class which is only referenced in `TypeToken`, but nowhere else
10+
public static class Implementation implements InterfaceWithImplementation {
11+
public Implementation() {}
12+
13+
@SerializedName("s")
14+
public String s;
15+
16+
@Override
17+
public String getValue() {
18+
return s;
19+
}
20+
}
21+
}

test-shrinker/src/main/java/com/example/Main.java

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,8 @@ public static void runTests(BiConsumer<String, String> outputConsumer) {
5353
testJsonAdapterAnnotation(outputConsumer);
5454

5555
testGenericClasses(outputConsumer);
56+
57+
testDeserializingInterfaceImpl(outputConsumer);
5658
}
5759

5860
private static void testTypeTokenWriteRead(
@@ -290,4 +292,25 @@ private static void testGenericClasses(BiConsumer<String, String> outputConsumer
290292
"{\"g\": {\"t\": 1}}", new TypeToken<GenericUsingGenericClass<DummyClass>>() {})
291293
.toString());
292294
}
295+
296+
private static void testDeserializingInterfaceImpl(BiConsumer<String, String> outputConsumer) {
297+
Gson gson = new Gson();
298+
TestExecutor.run(
299+
outputConsumer,
300+
"Read: Interface implementation",
301+
() -> {
302+
try {
303+
// Use the interface type here
304+
List<? extends InterfaceWithImplementation> list =
305+
gson.fromJson(
306+
"[{\"s\": \"value\"}]",
307+
// This is the only place where the implementation class is referenced
308+
new TypeToken<List<InterfaceWithImplementation.Implementation>>() {});
309+
return list.get(0).getValue();
310+
} catch (ClassCastException e) {
311+
// TODO: R8 causes exception, see https://github.com/google/gson/issues/2658
312+
return "ClassCastException";
313+
}
314+
});
315+
}
293316
}

test-shrinker/src/test/java/com/google/gson/it/ShrinkingIT.java

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
import static com.google.common.truth.Truth.assertThat;
2020
import static org.junit.Assert.assertThrows;
2121
import static org.junit.Assert.fail;
22-
import static org.junit.Assume.assumeTrue;
22+
import static org.junit.Assume.assumeFalse;
2323

2424
import com.example.UnusedClass;
2525
import java.lang.reflect.InvocationTargetException;
@@ -61,6 +61,11 @@ public void verifyJarExists() {
6161
}
6262
}
6363

64+
/** Returns whether the test is currently running for ProGuard, instead of R8 */
65+
private boolean isTestingProGuard() {
66+
return jarToTest.equals(PROGUARD_RESULT_PATH);
67+
}
68+
6469
@FunctionalInterface
6570
interface TestAction {
6671
void run(Class<?> c) throws Exception;
@@ -216,6 +221,10 @@ public void test() throws Exception {
216221
"Read: Using Generic TypeToken",
217222
"{g={t=read-1}}",
218223
"===",
224+
"Read: Interface implementation",
225+
// TODO: Currently only works for ProGuard but not R8
226+
isTestingProGuard() ? "value" : "ClassCastException",
227+
"===",
219228
""));
220229
}
221230

@@ -226,7 +235,7 @@ public void testNoSerializedName_NoArgsConstructor() throws Exception {
226235
c -> {
227236
Method m = c.getMethod("runTestNoArgsConstructor");
228237

229-
if (jarToTest.equals(PROGUARD_RESULT_PATH)) {
238+
if (isTestingProGuard()) {
230239
Object result = m.invoke(null);
231240
assertThat(result).isEqualTo("value");
232241
} else {
@@ -251,7 +260,7 @@ public void testNoSerializedName_NoArgsConstructorNoJdkUnsafe() throws Exception
251260
c -> {
252261
Method m = c.getMethod("runTestNoJdkUnsafe");
253262

254-
if (jarToTest.equals(PROGUARD_RESULT_PATH)) {
263+
if (isTestingProGuard()) {
255264
Object result = m.invoke(null);
256265
assertThat(result).isEqualTo("value");
257266
} else {
@@ -278,7 +287,7 @@ public void testNoSerializedName_HasArgsConstructor() throws Exception {
278287
c -> {
279288
Method m = c.getMethod("runTestHasArgsConstructor");
280289

281-
if (jarToTest.equals(PROGUARD_RESULT_PATH)) {
290+
if (isTestingProGuard()) {
282291
Object result = m.invoke(null);
283292
assertThat(result).isEqualTo("value");
284293
} else {
@@ -300,7 +309,7 @@ public void testNoSerializedName_HasArgsConstructor() throws Exception {
300309
public void testUnusedClassRemoved() throws Exception {
301310
// For some reason this test only works for R8 but not for ProGuard; ProGuard keeps the unused
302311
// class
303-
assumeTrue(jarToTest.equals(R8_RESULT_PATH));
312+
assumeFalse(isTestingProGuard());
304313

305314
String className = UnusedClass.class.getName();
306315
ClassNotFoundException e =

0 commit comments

Comments
 (0)