Add shrinker-test examples with never-referenced constructors (#2478)
* Add shrinker-test examples with never-referenced constructors R8 can do some optimizations that assume values of types whose constructors are never referenced are `null`. (A bit confused why the rules here are sufficient to avoid that optimization in the case of a class whose sole constructor has arguments and is unreferenced, since the keep rule only refers to `<init>()` instead of `<init>(...)`. Does R8 have special behavior for `<init>()` where attempting to keep that makes it assume a class is constructable even if it doesn't have a no-args constructor?) Also rename some test classes/names to refer to `NoArgs` instead of `Default` constructors. The examples here care about whether a no-argument constructor is present. While a no-argument constructor (explicitly defined or not) is used by default in some circumstances, the Java documentation consistently uses "default constructor" to refer only to constructors whose implementation is implicitly provided by the complier (all default constructors are no-argument constructors, but not vice versa). * Update shrinker-test/src/main/java/com/example/ClassWithNoArgsConstructor.java Accept review suggestion Co-authored-by: Marcono1234 <Marcono1234@users.noreply.github.com> * Update shrinker-test/src/main/java/com/example/NoSerializedNameMain.java Accept review suggestion Co-authored-by: Marcono1234 <Marcono1234@users.noreply.github.com> * Further adjust test class/method names for consistency * Update shrinker-test/src/main/java/com/example/NoSerializedNameMain.java Co-authored-by: Marcono1234 <Marcono1234@users.noreply.github.com> --------- Co-authored-by: Marcono1234 <Marcono1234@users.noreply.github.com>
This commit is contained in:
parent
4654f5906c
commit
ad5cd31498
@ -16,9 +16,9 @@
|
|||||||
public static void runTests(...);
|
public static void runTests(...);
|
||||||
}
|
}
|
||||||
-keep class com.example.NoSerializedNameMain {
|
-keep class com.example.NoSerializedNameMain {
|
||||||
public static java.lang.String runTest();
|
public static java.lang.String runTestNoArgsConstructor();
|
||||||
public static java.lang.String runTestNoJdkUnsafe();
|
public static java.lang.String runTestNoJdkUnsafe();
|
||||||
public static java.lang.String runTestNoDefaultConstructor();
|
public static java.lang.String runTestHasArgsConstructor();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
4
shrinker-test/proguard.pro
vendored
4
shrinker-test/proguard.pro
vendored
@ -6,12 +6,12 @@
|
|||||||
# Unlike R8, ProGuard does not perform aggressive optimization which makes classes abstract,
|
# Unlike R8, ProGuard does not perform aggressive optimization which makes classes abstract,
|
||||||
# therefore for ProGuard can successfully perform deserialization, and for that need to
|
# therefore for ProGuard can successfully perform deserialization, and for that need to
|
||||||
# preserve the field names
|
# preserve the field names
|
||||||
-keepclassmembernames class com.example.NoSerializedNameMain$TestClass {
|
-keepclassmembernames class com.example.NoSerializedNameMain$TestClassNoArgsConstructor {
|
||||||
<fields>;
|
<fields>;
|
||||||
}
|
}
|
||||||
-keepclassmembernames class com.example.NoSerializedNameMain$TestClassNotAbstract {
|
-keepclassmembernames class com.example.NoSerializedNameMain$TestClassNotAbstract {
|
||||||
<fields>;
|
<fields>;
|
||||||
}
|
}
|
||||||
-keepclassmembernames class com.example.NoSerializedNameMain$TestClassWithoutDefaultConstructor {
|
-keepclassmembernames class com.example.NoSerializedNameMain$TestClassHasArgsConstructor {
|
||||||
<fields>;
|
<fields>;
|
||||||
}
|
}
|
||||||
|
@ -10,8 +10,8 @@
|
|||||||
-keep,allowshrinking,allowoptimization,allowobfuscation,allowaccessmodification class com.example.GenericClasses$GenericUsingGenericClass
|
-keep,allowshrinking,allowoptimization,allowobfuscation,allowaccessmodification class com.example.GenericClasses$GenericUsingGenericClass
|
||||||
|
|
||||||
# Don't obfuscate class name, to check it in exception message
|
# Don't obfuscate class name, to check it in exception message
|
||||||
-keep,allowshrinking,allowoptimization class com.example.NoSerializedNameMain$TestClass
|
-keep,allowshrinking,allowoptimization class com.example.NoSerializedNameMain$TestClassNoArgsConstructor
|
||||||
-keep,allowshrinking,allowoptimization class com.example.NoSerializedNameMain$TestClassWithoutDefaultConstructor
|
-keep,allowshrinking,allowoptimization class com.example.NoSerializedNameMain$TestClassHasArgsConstructor
|
||||||
|
|
||||||
# This rule has the side-effect that R8 still removes the no-args constructor, but does not make the class abstract
|
# This rule has the side-effect that R8 still removes the no-args constructor, but does not make the class abstract
|
||||||
-keep class com.example.NoSerializedNameMain$TestClassNotAbstract {
|
-keep class com.example.NoSerializedNameMain$TestClassNotAbstract {
|
||||||
|
@ -3,15 +3,15 @@ package com.example;
|
|||||||
import com.google.gson.annotations.SerializedName;
|
import com.google.gson.annotations.SerializedName;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class without no-args default constructor, but with field annotated with
|
* Class without no-args constructor, but with field annotated with
|
||||||
* {@link SerializedName}.
|
* {@link SerializedName}.
|
||||||
*/
|
*/
|
||||||
public class ClassWithoutDefaultConstructor {
|
public class ClassWithHasArgsConstructor {
|
||||||
@SerializedName("myField")
|
@SerializedName("myField")
|
||||||
public int i;
|
public int i;
|
||||||
|
|
||||||
// Specify explicit constructor with args to remove implicit no-args default constructor
|
// Specify explicit constructor with args to remove implicit no-args default constructor
|
||||||
public ClassWithoutDefaultConstructor(int i) {
|
public ClassWithHasArgsConstructor(int i) {
|
||||||
this.i = i;
|
this.i = i;
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -3,14 +3,14 @@ package com.example;
|
|||||||
import com.google.gson.annotations.SerializedName;
|
import com.google.gson.annotations.SerializedName;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class with no-args default constructor and with field annotated with
|
* Class with no-args constructor and with field annotated with
|
||||||
* {@link SerializedName}.
|
* {@link SerializedName}.
|
||||||
*/
|
*/
|
||||||
public class ClassWithDefaultConstructor {
|
public class ClassWithNoArgsConstructor {
|
||||||
@SerializedName("myField")
|
@SerializedName("myField")
|
||||||
public int i;
|
public int i;
|
||||||
|
|
||||||
public ClassWithDefaultConstructor() {
|
public ClassWithNoArgsConstructor() {
|
||||||
i = -3;
|
i = -3;
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -0,0 +1,19 @@
|
|||||||
|
package com.example;
|
||||||
|
|
||||||
|
import com.google.gson.annotations.SerializedName;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class without no-args constructor, but with field annotated with
|
||||||
|
* {@link SerializedName}. The constructor should not actually be used in the
|
||||||
|
* code, but this shouldn't lead to R8 concluding that values of the type are
|
||||||
|
* not constructible and therefore must be null.
|
||||||
|
*/
|
||||||
|
public class ClassWithUnreferencedHasArgsConstructor {
|
||||||
|
@SerializedName("myField")
|
||||||
|
public int i;
|
||||||
|
|
||||||
|
// Specify explicit constructor with args to remove implicit no-args default constructor
|
||||||
|
public ClassWithUnreferencedHasArgsConstructor(int i) {
|
||||||
|
this.i = i;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,18 @@
|
|||||||
|
package com.example;
|
||||||
|
|
||||||
|
import com.google.gson.annotations.SerializedName;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class with no-args constructor and with field annotated with
|
||||||
|
* {@link SerializedName}. The constructor should not actually be used in the
|
||||||
|
* code, but this shouldn't lead to R8 concluding that values of the type are
|
||||||
|
* not constructible and therefore must be null.
|
||||||
|
*/
|
||||||
|
public class ClassWithUnreferencedNoArgsConstructor {
|
||||||
|
@SerializedName("myField")
|
||||||
|
public int i;
|
||||||
|
|
||||||
|
public ClassWithUnreferencedNoArgsConstructor() {
|
||||||
|
i = -3;
|
||||||
|
}
|
||||||
|
}
|
@ -32,7 +32,11 @@ public class Main {
|
|||||||
testNamedFields(outputConsumer);
|
testNamedFields(outputConsumer);
|
||||||
testSerializedName(outputConsumer);
|
testSerializedName(outputConsumer);
|
||||||
|
|
||||||
testNoDefaultConstructor(outputConsumer);
|
testConstructorNoArgs(outputConsumer);
|
||||||
|
testConstructorHasArgs(outputConsumer);
|
||||||
|
testUnreferencedConstructorNoArgs(outputConsumer);
|
||||||
|
testUnreferencedConstructorHasArgs(outputConsumer);
|
||||||
|
|
||||||
testNoJdkUnsafe(outputConsumer);
|
testNoJdkUnsafe(outputConsumer);
|
||||||
|
|
||||||
testEnum(outputConsumer);
|
testEnum(outputConsumer);
|
||||||
@ -90,12 +94,57 @@ public class Main {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void testNoDefaultConstructor(BiConsumer<String, String> outputConsumer) {
|
private static void testConstructorNoArgs(BiConsumer<String, String> outputConsumer) {
|
||||||
Gson gson = new GsonBuilder().setPrettyPrinting().create();
|
Gson gson = new GsonBuilder().setPrettyPrinting().create();
|
||||||
TestExecutor.run(outputConsumer, "Write: No default constructor", () -> toJson(gson, new ClassWithoutDefaultConstructor(2)));
|
TestExecutor.run(outputConsumer, "Write: No args constructor", () -> toJson(
|
||||||
|
gson, new ClassWithNoArgsConstructor()));
|
||||||
|
TestExecutor.run(outputConsumer, "Read: No args constructor; initial constructor value", () -> {
|
||||||
|
ClassWithNoArgsConstructor deserialized = fromJson(gson, "{}", ClassWithNoArgsConstructor.class);
|
||||||
|
return Integer.toString(deserialized.i);
|
||||||
|
});
|
||||||
|
TestExecutor.run(outputConsumer, "Read: No args constructor; custom value", () -> {
|
||||||
|
ClassWithNoArgsConstructor deserialized = fromJson(gson, "{\"myField\": 3}", ClassWithNoArgsConstructor.class);
|
||||||
|
return Integer.toString(deserialized.i);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void testConstructorHasArgs(BiConsumer<String, String> outputConsumer) {
|
||||||
|
Gson gson = new GsonBuilder().setPrettyPrinting().create();
|
||||||
|
TestExecutor.run(outputConsumer, "Write: Constructor with args", () -> toJson(
|
||||||
|
gson, new ClassWithHasArgsConstructor(2)));
|
||||||
// This most likely relies on JDK Unsafe (unless the shrinker rewrites the constructor in some way)
|
// This most likely relies on JDK Unsafe (unless the shrinker rewrites the constructor in some way)
|
||||||
TestExecutor.run(outputConsumer, "Read: No default constructor", () -> {
|
TestExecutor.run(outputConsumer, "Read: Constructor with args", () -> {
|
||||||
ClassWithoutDefaultConstructor deserialized = fromJson(gson, "{\"myField\": 3}", ClassWithoutDefaultConstructor.class);
|
ClassWithHasArgsConstructor deserialized = fromJson(
|
||||||
|
gson, "{\"myField\": 3}", ClassWithHasArgsConstructor.class);
|
||||||
|
return Integer.toString(deserialized.i);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void testUnreferencedConstructorNoArgs(BiConsumer<String, String> outputConsumer) {
|
||||||
|
Gson gson = new GsonBuilder().setPrettyPrinting().create();
|
||||||
|
// No write because we're not referencing this class's constructor.
|
||||||
|
|
||||||
|
// This runs the no-args constructor.
|
||||||
|
TestExecutor.run(outputConsumer, "Read: Unreferenced no args constructor; initial constructor value", () -> {
|
||||||
|
ClassWithUnreferencedNoArgsConstructor deserialized = fromJson(
|
||||||
|
gson, "{}", ClassWithUnreferencedNoArgsConstructor.class);
|
||||||
|
return Integer.toString(deserialized.i);
|
||||||
|
});
|
||||||
|
TestExecutor.run(outputConsumer, "Read: Unreferenced no args constructor; custom value", () -> {
|
||||||
|
ClassWithUnreferencedNoArgsConstructor deserialized = fromJson(
|
||||||
|
gson, "{\"myField\": 3}", ClassWithUnreferencedNoArgsConstructor.class);
|
||||||
|
return Integer.toString(deserialized.i);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void testUnreferencedConstructorHasArgs(BiConsumer<String, String> outputConsumer) {
|
||||||
|
Gson gson = new GsonBuilder().setPrettyPrinting().create();
|
||||||
|
// No write because we're not referencing this class's constructor.
|
||||||
|
|
||||||
|
// This most likely relies on JDK Unsafe (unless the shrinker rewrites the constructor in some way)
|
||||||
|
TestExecutor.run(outputConsumer, "Read: Unreferenced constructor with args", () -> {
|
||||||
|
ClassWithUnreferencedHasArgsConstructor deserialized = fromJson(
|
||||||
|
gson, "{\"myField\": 3}", ClassWithUnreferencedHasArgsConstructor.class);
|
||||||
return Integer.toString(deserialized.i);
|
return Integer.toString(deserialized.i);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -103,11 +152,13 @@ public class Main {
|
|||||||
private static void testNoJdkUnsafe(BiConsumer<String, String> outputConsumer) {
|
private static void testNoJdkUnsafe(BiConsumer<String, String> outputConsumer) {
|
||||||
Gson gson = new GsonBuilder().disableJdkUnsafe().create();
|
Gson gson = new GsonBuilder().disableJdkUnsafe().create();
|
||||||
TestExecutor.run(outputConsumer, "Read: No JDK Unsafe; initial constructor value", () -> {
|
TestExecutor.run(outputConsumer, "Read: No JDK Unsafe; initial constructor value", () -> {
|
||||||
ClassWithDefaultConstructor deserialized = fromJson(gson, "{}", ClassWithDefaultConstructor.class);
|
ClassWithNoArgsConstructor deserialized = fromJson(
|
||||||
|
gson, "{}", ClassWithNoArgsConstructor.class);
|
||||||
return Integer.toString(deserialized.i);
|
return Integer.toString(deserialized.i);
|
||||||
});
|
});
|
||||||
TestExecutor.run(outputConsumer, "Read: No JDK Unsafe; custom value", () -> {
|
TestExecutor.run(outputConsumer, "Read: No JDK Unsafe; custom value", () -> {
|
||||||
ClassWithDefaultConstructor deserialized = fromJson(gson, "{\"myField\": 3}", ClassWithDefaultConstructor.class);
|
ClassWithNoArgsConstructor deserialized = fromJson(
|
||||||
|
gson, "{\"myField\": 3}", ClassWithNoArgsConstructor.class);
|
||||||
return Integer.toString(deserialized.i);
|
return Integer.toString(deserialized.i);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -10,7 +10,8 @@ import com.google.gson.GsonBuilder;
|
|||||||
* therefore not matched by the default {@code gson.pro} rules.
|
* therefore not matched by the default {@code gson.pro} rules.
|
||||||
*/
|
*/
|
||||||
public class NoSerializedNameMain {
|
public class NoSerializedNameMain {
|
||||||
static class TestClass {
|
static class TestClassNoArgsConstructor {
|
||||||
|
// Has a no-args default constructor.
|
||||||
public String s;
|
public String s;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -19,37 +20,40 @@ public class NoSerializedNameMain {
|
|||||||
public String s;
|
public String s;
|
||||||
}
|
}
|
||||||
|
|
||||||
static class TestClassWithoutDefaultConstructor {
|
static class TestClassHasArgsConstructor {
|
||||||
public String s;
|
public String s;
|
||||||
|
|
||||||
// Specify explicit constructor with args to remove implicit no-args default constructor
|
// Specify explicit constructor with args to remove implicit no-args default constructor
|
||||||
public TestClassWithoutDefaultConstructor(String s) {
|
public TestClassHasArgsConstructor(String s) {
|
||||||
this.s = s;
|
this.s = s;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Main entrypoint, called by {@code ShrinkingIT.testNoSerializedName_DefaultConstructor()}.
|
* Main entrypoint, called by {@code ShrinkingIT.testNoSerializedName_NoArgsConstructor()}.
|
||||||
*/
|
*/
|
||||||
public static String runTest() {
|
public static String runTestNoArgsConstructor() {
|
||||||
TestClass deserialized = new Gson().fromJson("{\"s\":\"value\"}", same(TestClass.class));
|
TestClassNoArgsConstructor deserialized = new Gson().fromJson(
|
||||||
|
"{\"s\":\"value\"}", same(TestClassNoArgsConstructor.class));
|
||||||
return deserialized.s;
|
return deserialized.s;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Main entrypoint, called by {@code ShrinkingIT.testNoSerializedName_DefaultConstructorNoJdkUnsafe()}.
|
* Main entrypoint, called by {@code ShrinkingIT.testNoSerializedName_NoArgsConstructorNoJdkUnsafe()}.
|
||||||
*/
|
*/
|
||||||
public static String runTestNoJdkUnsafe() {
|
public static String runTestNoJdkUnsafe() {
|
||||||
Gson gson = new GsonBuilder().disableJdkUnsafe().create();
|
Gson gson = new GsonBuilder().disableJdkUnsafe().create();
|
||||||
TestClassNotAbstract deserialized = gson.fromJson("{\"s\": \"value\"}", same(TestClassNotAbstract.class));
|
TestClassNotAbstract deserialized = gson.fromJson(
|
||||||
|
"{\"s\": \"value\"}", same(TestClassNotAbstract.class));
|
||||||
return deserialized.s;
|
return deserialized.s;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Main entrypoint, called by {@code ShrinkingIT.testNoSerializedName_NoDefaultConstructor()}.
|
* Main entrypoint, called by {@code ShrinkingIT.testNoSerializedName_HasArgsConstructor()}.
|
||||||
*/
|
*/
|
||||||
public static String runTestNoDefaultConstructor() {
|
public static String runTestHasArgsConstructor() {
|
||||||
TestClassWithoutDefaultConstructor deserialized = new Gson().fromJson("{\"s\":\"value\"}", same(TestClassWithoutDefaultConstructor.class));
|
TestClassHasArgsConstructor deserialized = new Gson().fromJson(
|
||||||
|
"{\"s\":\"value\"}", same(TestClassHasArgsConstructor.class));
|
||||||
return deserialized.s;
|
return deserialized.s;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -129,12 +129,32 @@ public class ShrinkingIT {
|
|||||||
"Read: SerializedName",
|
"Read: SerializedName",
|
||||||
"3",
|
"3",
|
||||||
"===",
|
"===",
|
||||||
"Write: No default constructor",
|
"Write: No args constructor",
|
||||||
|
"{",
|
||||||
|
" \"myField\": -3",
|
||||||
|
"}",
|
||||||
|
"===",
|
||||||
|
"Read: No args constructor; initial constructor value",
|
||||||
|
"-3",
|
||||||
|
"===",
|
||||||
|
"Read: No args constructor; custom value",
|
||||||
|
"3",
|
||||||
|
"===",
|
||||||
|
"Write: Constructor with args",
|
||||||
"{",
|
"{",
|
||||||
" \"myField\": 2",
|
" \"myField\": 2",
|
||||||
"}",
|
"}",
|
||||||
"===",
|
"===",
|
||||||
"Read: No default constructor",
|
"Read: Constructor with args",
|
||||||
|
"3",
|
||||||
|
"===",
|
||||||
|
"Read: Unreferenced no args constructor; initial constructor value",
|
||||||
|
"-3",
|
||||||
|
"===",
|
||||||
|
"Read: Unreferenced no args constructor; custom value",
|
||||||
|
"3",
|
||||||
|
"===",
|
||||||
|
"Read: Unreferenced constructor with args",
|
||||||
"3",
|
"3",
|
||||||
"===",
|
"===",
|
||||||
"Read: No JDK Unsafe; initial constructor value",
|
"Read: No JDK Unsafe; initial constructor value",
|
||||||
@ -191,9 +211,9 @@ public class ShrinkingIT {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testNoSerializedName_DefaultConstructor() throws Exception {
|
public void testNoSerializedName_NoArgsConstructor() throws Exception {
|
||||||
runTest("com.example.NoSerializedNameMain", c -> {
|
runTest("com.example.NoSerializedNameMain", c -> {
|
||||||
Method m = c.getMethod("runTest");
|
Method m = c.getMethod("runTestNoArgsConstructor");
|
||||||
|
|
||||||
if (jarToTest.equals(PROGUARD_RESULT_PATH)) {
|
if (jarToTest.equals(PROGUARD_RESULT_PATH)) {
|
||||||
Object result = m.invoke(null);
|
Object result = m.invoke(null);
|
||||||
@ -203,7 +223,7 @@ public class ShrinkingIT {
|
|||||||
Exception e = assertThrows(InvocationTargetException.class, () -> m.invoke(null));
|
Exception e = assertThrows(InvocationTargetException.class, () -> m.invoke(null));
|
||||||
assertThat(e).hasCauseThat().hasMessageThat().isEqualTo(
|
assertThat(e).hasCauseThat().hasMessageThat().isEqualTo(
|
||||||
"Abstract classes can't be instantiated! Adjust the R8 configuration or register an InstanceCreator"
|
"Abstract classes can't be instantiated! Adjust the R8 configuration or register an InstanceCreator"
|
||||||
+ " or a TypeAdapter for this type. Class name: com.example.NoSerializedNameMain$TestClass"
|
+ " or a TypeAdapter for this type. Class name: com.example.NoSerializedNameMain$TestClassNoArgsConstructor"
|
||||||
+ "\nSee https://github.com/google/gson/blob/main/Troubleshooting.md#r8-abstract-class"
|
+ "\nSee https://github.com/google/gson/blob/main/Troubleshooting.md#r8-abstract-class"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -211,7 +231,7 @@ public class ShrinkingIT {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testNoSerializedName_DefaultConstructorNoJdkUnsafe() throws Exception {
|
public void testNoSerializedName_NoArgsConstructorNoJdkUnsafe() throws Exception {
|
||||||
runTest("com.example.NoSerializedNameMain", c -> {
|
runTest("com.example.NoSerializedNameMain", c -> {
|
||||||
Method m = c.getMethod("runTestNoJdkUnsafe");
|
Method m = c.getMethod("runTestNoJdkUnsafe");
|
||||||
|
|
||||||
@ -232,9 +252,9 @@ public class ShrinkingIT {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testNoSerializedName_NoDefaultConstructor() throws Exception {
|
public void testNoSerializedName_HasArgsConstructor() throws Exception {
|
||||||
runTest("com.example.NoSerializedNameMain", c -> {
|
runTest("com.example.NoSerializedNameMain", c -> {
|
||||||
Method m = c.getMethod("runTestNoDefaultConstructor");
|
Method m = c.getMethod("runTestHasArgsConstructor");
|
||||||
|
|
||||||
if (jarToTest.equals(PROGUARD_RESULT_PATH)) {
|
if (jarToTest.equals(PROGUARD_RESULT_PATH)) {
|
||||||
Object result = m.invoke(null);
|
Object result = m.invoke(null);
|
||||||
@ -244,7 +264,7 @@ public class ShrinkingIT {
|
|||||||
Exception e = assertThrows(InvocationTargetException.class, () -> m.invoke(null));
|
Exception e = assertThrows(InvocationTargetException.class, () -> m.invoke(null));
|
||||||
assertThat(e).hasCauseThat().hasMessageThat().isEqualTo(
|
assertThat(e).hasCauseThat().hasMessageThat().isEqualTo(
|
||||||
"Abstract classes can't be instantiated! Adjust the R8 configuration or register an InstanceCreator"
|
"Abstract classes can't be instantiated! Adjust the R8 configuration or register an InstanceCreator"
|
||||||
+ " or a TypeAdapter for this type. Class name: com.example.NoSerializedNameMain$TestClassWithoutDefaultConstructor"
|
+ " or a TypeAdapter for this type. Class name: com.example.NoSerializedNameMain$TestClassHasArgsConstructor"
|
||||||
+ "\nSee https://github.com/google/gson/blob/main/Troubleshooting.md#r8-abstract-class"
|
+ "\nSee https://github.com/google/gson/blob/main/Troubleshooting.md#r8-abstract-class"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user