Skip to content

Commit 325f37c

Browse files
authored
Verify that JsonTreeReader and JsonTreeWriter override all methods (#2181)
* Verify that JsonTreeReader and JsonTreeWriter override all methods If those classes do not override one of the JsonReader or JsonWriter methods the user might encounter an AssertionError. * Address review feedback
1 parent 7f77ad4 commit 325f37c

File tree

3 files changed

+87
-2
lines changed

3 files changed

+87
-2
lines changed

gson/src/test/java/com/google/gson/common/MoreAsserts.java

Lines changed: 55 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,13 @@
1616

1717
package com.google.gson.common;
1818

19-
import org.junit.Assert;
20-
19+
import java.lang.reflect.Method;
20+
import java.lang.reflect.Modifier;
2121
import java.util.Collection;
22+
import java.util.LinkedHashSet;
23+
import java.util.List;
24+
import java.util.Set;
25+
import org.junit.Assert;
2226

2327
/**
2428
* Handy asserts that we wish were present in {@link Assert}
@@ -49,4 +53,53 @@ public static void assertEqualsAndHashCode(Object a, Object b) {
4953
Assert.assertFalse(a.equals(null));
5054
Assert.assertFalse(a.equals(new Object()));
5155
}
56+
57+
private static boolean isProtectedOrPublic(Method method) {
58+
int modifiers = method.getModifiers();
59+
return Modifier.isProtected(modifiers) || Modifier.isPublic(modifiers);
60+
}
61+
62+
private static String getMethodSignature(Method method) {
63+
StringBuilder builder = new StringBuilder(method.getName());
64+
builder.append('(');
65+
66+
String sep = "";
67+
for (Class<?> paramType : method.getParameterTypes()) {
68+
builder.append(sep).append(paramType.getName());
69+
sep = ",";
70+
}
71+
72+
builder.append(')');
73+
return builder.toString();
74+
}
75+
76+
/**
77+
* Asserts that {@code subClass} overrides all protected and public methods declared by
78+
* {@code baseClass} except for the ones whose signatures are in {@code ignoredMethods}.
79+
*/
80+
public static void assertOverridesMethods(Class<?> baseClass, Class<?> subClass, List<String> ignoredMethods) {
81+
Set<String> requiredOverriddenMethods = new LinkedHashSet<>();
82+
for (Method method : baseClass.getDeclaredMethods()) {
83+
// Note: Do not filter out `final` methods; maybe they should not be `final` and subclass needs
84+
// to override them
85+
if (isProtectedOrPublic(method)) {
86+
requiredOverriddenMethods.add(getMethodSignature(method));
87+
}
88+
}
89+
90+
for (Method method : subClass.getDeclaredMethods()) {
91+
requiredOverriddenMethods.remove(getMethodSignature(method));
92+
}
93+
94+
for (String ignoredMethod : ignoredMethods) {
95+
boolean foundIgnored = requiredOverriddenMethods.remove(ignoredMethod);
96+
if (!foundIgnored) {
97+
throw new IllegalArgumentException("Method '" + ignoredMethod + "' does not exist or is already overridden");
98+
}
99+
}
100+
101+
if (!requiredOverriddenMethods.isEmpty()) {
102+
Assert.fail(subClass.getSimpleName() + " must override these methods: " + requiredOverriddenMethods);
103+
}
104+
}
52105
}

gson/src/test/java/com/google/gson/internal/bind/JsonTreeReaderTest.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,14 @@
1919
import com.google.gson.JsonElement;
2020
import com.google.gson.JsonNull;
2121
import com.google.gson.JsonObject;
22+
import com.google.gson.common.MoreAsserts;
23+
import com.google.gson.stream.JsonReader;
2224
import com.google.gson.stream.JsonToken;
2325
import com.google.gson.stream.MalformedJsonException;
2426
import java.io.IOException;
27+
import java.io.Reader;
28+
import java.util.Arrays;
29+
import java.util.List;
2530
import junit.framework.TestCase;
2631

2732
@SuppressWarnings("resource")
@@ -80,4 +85,14 @@ public JsonElement deepCopy() {
8085
expected.getMessage());
8186
}
8287
}
88+
89+
/**
90+
* {@link JsonTreeReader} effectively replaces the complete reading logic of {@link JsonReader} to
91+
* read from a {@link JsonElement} instead of a {@link Reader}. Therefore all relevant methods of
92+
* {@code JsonReader} must be overridden.
93+
*/
94+
public void testOverrides() {
95+
List<String> ignoredMethods = Arrays.asList("setLenient(boolean)", "isLenient()");
96+
MoreAsserts.assertOverridesMethods(JsonReader.class, JsonTreeReader.class, ignoredMethods);
97+
}
8398
}

gson/src/test/java/com/google/gson/internal/bind/JsonTreeWriterTest.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,14 @@
1616

1717
package com.google.gson.internal.bind;
1818

19+
import com.google.gson.JsonElement;
1920
import com.google.gson.JsonNull;
21+
import com.google.gson.common.MoreAsserts;
22+
import com.google.gson.stream.JsonWriter;
2023
import java.io.IOException;
24+
import java.io.Writer;
25+
import java.util.Arrays;
26+
import java.util.List;
2127
import junit.framework.TestCase;
2228

2329
@SuppressWarnings("resource")
@@ -243,4 +249,15 @@ public void testJsonValue() throws IOException {
243249
} catch (UnsupportedOperationException expected) {
244250
}
245251
}
252+
253+
/**
254+
* {@link JsonTreeWriter} effectively replaces the complete writing logic of {@link JsonWriter} to
255+
* create a {@link JsonElement} tree instead of writing to a {@link Writer}. Therefore all relevant
256+
* methods of {@code JsonWriter} must be overridden.
257+
*/
258+
public void testOverrides() {
259+
List<String> ignoredMethods = Arrays.asList("setLenient(boolean)", "isLenient()", "setIndent(java.lang.String)",
260+
"setHtmlSafe(boolean)", "isHtmlSafe()", "setSerializeNulls(boolean)", "getSerializeNulls()");
261+
MoreAsserts.assertOverridesMethods(JsonWriter.class, JsonTreeWriter.class, ignoredMethods);
262+
}
246263
}

0 commit comments

Comments
 (0)