diff --git a/gson/src/test/java/com/google/gson/common/MoreAsserts.java b/gson/src/test/java/com/google/gson/common/MoreAsserts.java index f409480d67..f69802cb2f 100644 --- a/gson/src/test/java/com/google/gson/common/MoreAsserts.java +++ b/gson/src/test/java/com/google/gson/common/MoreAsserts.java @@ -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 static void assertEqualsAndHashCode(Object a, Object b) { 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 ignoredMethods) { + Set 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); + } + } } diff --git a/gson/src/test/java/com/google/gson/internal/bind/JsonTreeReaderTest.java b/gson/src/test/java/com/google/gson/internal/bind/JsonTreeReaderTest.java index dd4daf05ba..4e6a218efb 100644 --- a/gson/src/test/java/com/google/gson/internal/bind/JsonTreeReaderTest.java +++ b/gson/src/test/java/com/google/gson/internal/bind/JsonTreeReaderTest.java @@ -19,9 +19,14 @@ 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 JsonElement deepCopy() { 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 ignoredMethods = Arrays.asList("setLenient(boolean)", "isLenient()"); + MoreAsserts.assertOverridesMethods(JsonReader.class, JsonTreeReader.class, ignoredMethods); + } } diff --git a/gson/src/test/java/com/google/gson/internal/bind/JsonTreeWriterTest.java b/gson/src/test/java/com/google/gson/internal/bind/JsonTreeWriterTest.java index 0207b1674a..ce91664ecf 100644 --- a/gson/src/test/java/com/google/gson/internal/bind/JsonTreeWriterTest.java +++ b/gson/src/test/java/com/google/gson/internal/bind/JsonTreeWriterTest.java @@ -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 void testJsonValue() throws IOException { } 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 ignoredMethods = Arrays.asList("setLenient(boolean)", "isLenient()", "setIndent(java.lang.String)", + "setHtmlSafe(boolean)", "isHtmlSafe()", "setSerializeNulls(boolean)", "getSerializeNulls()"); + MoreAsserts.assertOverridesMethods(JsonWriter.class, JsonTreeWriter.class, ignoredMethods); + } }