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
This commit is contained in:
Marcono1234 2022-08-23 23:19:47 +02:00 committed by GitHub
parent 7f77ad4ff6
commit 325f37cd6b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 87 additions and 2 deletions

View File

@ -16,9 +16,13 @@
package com.google.gson.common;
import org.junit.Assert;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Collection;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import org.junit.Assert;
/**
* Handy asserts that we wish were present in {@link Assert}
@ -49,4 +53,53 @@ public class MoreAsserts {
Assert.assertFalse(a.equals(null));
Assert.assertFalse(a.equals(new Object()));
}
private static boolean isProtectedOrPublic(Method method) {
int modifiers = method.getModifiers();
return Modifier.isProtected(modifiers) || Modifier.isPublic(modifiers);
}
private static String getMethodSignature(Method method) {
StringBuilder builder = new StringBuilder(method.getName());
builder.append('(');
String sep = "";
for (Class<?> paramType : method.getParameterTypes()) {
builder.append(sep).append(paramType.getName());
sep = ",";
}
builder.append(')');
return builder.toString();
}
/**
* Asserts that {@code subClass} overrides all protected and public methods declared by
* {@code baseClass} except for the ones whose signatures are in {@code ignoredMethods}.
*/
public static void assertOverridesMethods(Class<?> baseClass, Class<?> subClass, List<String> ignoredMethods) {
Set<String> requiredOverriddenMethods = new LinkedHashSet<>();
for (Method method : baseClass.getDeclaredMethods()) {
// Note: Do not filter out `final` methods; maybe they should not be `final` and subclass needs
// to override them
if (isProtectedOrPublic(method)) {
requiredOverriddenMethods.add(getMethodSignature(method));
}
}
for (Method method : subClass.getDeclaredMethods()) {
requiredOverriddenMethods.remove(getMethodSignature(method));
}
for (String ignoredMethod : ignoredMethods) {
boolean foundIgnored = requiredOverriddenMethods.remove(ignoredMethod);
if (!foundIgnored) {
throw new IllegalArgumentException("Method '" + ignoredMethod + "' does not exist or is already overridden");
}
}
if (!requiredOverriddenMethods.isEmpty()) {
Assert.fail(subClass.getSimpleName() + " must override these methods: " + requiredOverriddenMethods);
}
}
}

View File

@ -19,9 +19,14 @@ import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonNull;
import com.google.gson.JsonObject;
import com.google.gson.common.MoreAsserts;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonToken;
import com.google.gson.stream.MalformedJsonException;
import java.io.IOException;
import java.io.Reader;
import java.util.Arrays;
import java.util.List;
import junit.framework.TestCase;
@SuppressWarnings("resource")
@ -80,4 +85,14 @@ public class JsonTreeReaderTest extends TestCase {
expected.getMessage());
}
}
/**
* {@link JsonTreeReader} effectively replaces the complete reading logic of {@link JsonReader} to
* read from a {@link JsonElement} instead of a {@link Reader}. Therefore all relevant methods of
* {@code JsonReader} must be overridden.
*/
public void testOverrides() {
List<String> ignoredMethods = Arrays.asList("setLenient(boolean)", "isLenient()");
MoreAsserts.assertOverridesMethods(JsonReader.class, JsonTreeReader.class, ignoredMethods);
}
}

View File

@ -16,8 +16,14 @@
package com.google.gson.internal.bind;
import com.google.gson.JsonElement;
import com.google.gson.JsonNull;
import com.google.gson.common.MoreAsserts;
import com.google.gson.stream.JsonWriter;
import java.io.IOException;
import java.io.Writer;
import java.util.Arrays;
import java.util.List;
import junit.framework.TestCase;
@SuppressWarnings("resource")
@ -243,4 +249,15 @@ public final class JsonTreeWriterTest extends TestCase {
} catch (UnsupportedOperationException expected) {
}
}
/**
* {@link JsonTreeWriter} effectively replaces the complete writing logic of {@link JsonWriter} to
* create a {@link JsonElement} tree instead of writing to a {@link Writer}. Therefore all relevant
* methods of {@code JsonWriter} must be overridden.
*/
public void testOverrides() {
List<String> ignoredMethods = Arrays.asList("setLenient(boolean)", "isLenient()", "setIndent(java.lang.String)",
"setHtmlSafe(boolean)", "isHtmlSafe()", "setSerializeNulls(boolean)", "getSerializeNulls()");
MoreAsserts.assertOverridesMethods(JsonWriter.class, JsonTreeWriter.class, ignoredMethods);
}
}