blob: c0f87e8a73b25a0785dab6aa103860223bcda5c3 [file] [log] [blame]
/*
* Copyright (C) 2007 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.signature.cts.tests;
import static org.junit.Assert.assertEquals;
import android.signature.cts.AnnotationChecker;
import android.signature.cts.ApiComplianceChecker;
import android.signature.cts.ClassProvider;
import android.signature.cts.FailureType;
import android.signature.cts.JDiffClassDescription;
import android.signature.cts.ResultObserver;
import android.signature.cts.tests.data.AbstractClass;
import android.signature.cts.tests.data.AbstractClassWithCtor;
import android.signature.cts.tests.data.ComplexEnum;
import android.signature.cts.tests.data.ExtendedNormalInterface;
import android.signature.cts.tests.data.NormalClass;
import android.signature.cts.tests.data.NormalInterface;
import java.lang.reflect.Modifier;
import java.util.function.Consumer;
import org.junit.Test;
import org.junit.runners.JUnit4;
import org.junit.runner.RunWith;
/**
* Test class for JDiffClassDescription.
*/
@RunWith(JUnit4.class)
public class ApiComplianceCheckerTest extends ApiPresenceCheckerTest<ApiComplianceChecker> {
@Override
protected ApiComplianceChecker createChecker(ResultObserver resultObserver,
ClassProvider provider) {
return new ApiComplianceChecker(resultObserver, provider);
}
@Override
void runWithApiChecker(
ResultObserver resultObserver, Consumer<ApiComplianceChecker> consumer, String... excludedRuntimeClasses) {
super.runWithApiChecker(
resultObserver,
checker -> {
consumer.accept(checker);
checker.checkDeferred();
},
excludedRuntimeClasses);
}
@Test
public void testNormalClassCompliance() {
JDiffClassDescription clz = createClass(NormalClass.class.getSimpleName());
checkSignatureCompliance(clz);
assertEquals(clz.toSignatureString(), "public class NormalClass");
}
@Test
public void testMissingClass() {
try (ExpectFailure observer = new ExpectFailure(FailureType.MISSING_CLASS)) {
JDiffClassDescription clz = new JDiffClassDescription(
"android.signature.cts.tests.data", "NoSuchClass");
clz.setType(JDiffClassDescription.JDiffType.CLASS);
checkSignatureCompliance(clz, observer);
}
}
@Test
public void testSimpleConstructor() {
JDiffClassDescription clz = createClass(NormalClass.class.getSimpleName());
JDiffClassDescription.JDiffConstructor constructor = ctor("NormalClass", Modifier.PUBLIC);
clz.addConstructor(constructor);
checkSignatureCompliance(clz);
assertEquals(constructor.toSignatureString(), "public NormalClass()");
}
@Test
public void testOneArgConstructor() {
JDiffClassDescription clz = createClass(NormalClass.class.getSimpleName());
JDiffClassDescription.JDiffConstructor constructor = ctor("NormalClass", Modifier.PRIVATE);
constructor.addParam("java.lang.String");
clz.addConstructor(constructor);
checkSignatureCompliance(clz);
assertEquals(constructor.toSignatureString(), "private NormalClass(java.lang.String)");
}
@Test
public void testConstructorThrowsException() {
JDiffClassDescription clz = createClass(NormalClass.class.getSimpleName());
JDiffClassDescription.JDiffConstructor constructor = ctor("NormalClass", Modifier.PROTECTED);
constructor.addParam("java.lang.String");
constructor.addParam("java.lang.String");
constructor.addException("android.signature.cts.tests.data.NormalException");
clz.addConstructor(constructor);
checkSignatureCompliance(clz);
assertEquals(constructor.toSignatureString(),
"protected NormalClass(java.lang.String, java.lang.String) " +
"throws android.signature.cts.tests.data.NormalException");
}
@Test
public void testPackageProtectedConstructor() {
JDiffClassDescription clz = createClass(NormalClass.class.getSimpleName());
JDiffClassDescription.JDiffConstructor constructor = ctor("NormalClass", 0);
constructor.addParam("java.lang.String");
constructor.addParam("java.lang.String");
constructor.addParam("java.lang.String");
clz.addConstructor(constructor);
checkSignatureCompliance(clz);
assertEquals(constructor.toSignatureString(),
"NormalClass(java.lang.String, java.lang.String, java.lang.String)");
}
@Test
public void testStaticMethod() {
JDiffClassDescription clz = createClass(NormalClass.class.getSimpleName());
JDiffClassDescription.JDiffMethod method = method("staticMethod",
Modifier.STATIC | Modifier.PUBLIC, "void");
clz.addMethod(method);
checkSignatureCompliance(clz);
assertEquals(method.toSignatureString(), "public static void staticMethod()");
}
@Test
public void testSyncMethod() {
JDiffClassDescription clz = createClass(NormalClass.class.getSimpleName());
JDiffClassDescription.JDiffMethod method = method("syncMethod",
Modifier.SYNCHRONIZED | Modifier.PUBLIC, "void");
clz.addMethod(method);
checkSignatureCompliance(clz);
assertEquals(method.toSignatureString(), "public synchronized void syncMethod()");
}
@Test
public void testPackageProtectMethod() {
JDiffClassDescription clz = createClass(NormalClass.class.getSimpleName());
JDiffClassDescription.JDiffMethod method = method("packageProtectedMethod", 0, "boolean");
clz.addMethod(method);
checkSignatureCompliance(clz);
assertEquals(method.toSignatureString(), "boolean packageProtectedMethod()");
}
@Test
public void testPrivateMethod() {
JDiffClassDescription clz = createClass(NormalClass.class.getSimpleName());
JDiffClassDescription.JDiffMethod method = method("privateMethod", Modifier.PRIVATE,
"void");
clz.addMethod(method);
checkSignatureCompliance(clz);
assertEquals(method.toSignatureString(), "private void privateMethod()");
}
@Test
public void testProtectedMethod() {
JDiffClassDescription clz = createClass(NormalClass.class.getSimpleName());
JDiffClassDescription.JDiffMethod method = method("protectedMethod", Modifier.PROTECTED,
"java.lang.String");
clz.addMethod(method);
checkSignatureCompliance(clz);
assertEquals(method.toSignatureString(), "protected java.lang.String protectedMethod()");
}
@Test
public void testThrowsMethod() {
JDiffClassDescription clz = createClass(NormalClass.class.getSimpleName());
JDiffClassDescription.JDiffMethod method = method("throwsMethod", Modifier.PUBLIC, "void");
method.addException("android.signature.cts.tests.data.NormalException");
clz.addMethod(method);
checkSignatureCompliance(clz);
assertEquals(method.toSignatureString(), "public void throwsMethod() " +
"throws android.signature.cts.tests.data.NormalException");
}
@Test
public void testNativeMethod() {
JDiffClassDescription clz = createClass(NormalClass.class.getSimpleName());
JDiffClassDescription.JDiffMethod method = method("nativeMethod",
Modifier.PUBLIC | Modifier.NATIVE, "void");
clz.addMethod(method);
checkSignatureCompliance(clz);
assertEquals(method.toSignatureString(), "public native void nativeMethod()");
}
/**
* Check that a varargs method is treated as compliant.
*/
@Test
public void testVarargsMethod() {
JDiffClassDescription clz = createClass(NormalClass.class.getSimpleName());
JDiffClassDescription.JDiffMethod method = method("varargs",
Modifier.PUBLIC, "void");
method.addParam("java.lang.String...");
clz.addMethod(method);
assertEquals(method.toSignatureString(), "public void varargs(java.lang.String...)");
checkSignatureCompliance(clz);
}
/**
* Check that a clone method (which produces a special method that is marked as {@code bridge}
* and {@code synthetic}) is treated as compliant.
*/
@Test
public void testCloneMethod() {
JDiffClassDescription clz = createClass(NormalClass.class.getSimpleName());
// The generic method:
// NormalClass clone() throws CloneNotSupportedException
JDiffClassDescription.JDiffMethod method = method("clone",
Modifier.PUBLIC, NormalClass.class.getName());
method.addException(CloneNotSupportedException.class.getName());
clz.addMethod(method);
assertEquals(method.toSignatureString(),
"public android.signature.cts.tests.data.NormalClass clone()"
+ " throws java.lang.CloneNotSupportedException");
// The synthetic bridge method:
// Object clone() throws CloneNotSupportedException
method = method("clone",
Modifier.PUBLIC, Object.class.getName());
method.addException(CloneNotSupportedException.class.getName());
clz.addMethod(method);
assertEquals(method.toSignatureString(),
"public java.lang.Object clone()"
+ " throws java.lang.CloneNotSupportedException");
checkSignatureCompliance(clz);
}
@Test
public void testFinalField() {
JDiffClassDescription clz = createClass(NormalClass.class.getSimpleName());
JDiffClassDescription.JDiffField field = new JDiffClassDescription.JDiffField(
"FINAL_FIELD", "java.lang.String", Modifier.PUBLIC | Modifier.FINAL, VALUE);
clz.addField(field);
checkSignatureCompliance(clz);
assertEquals(field.toSignatureString(), "public final java.lang.String FINAL_FIELD");
}
@Test
public void testStaticField() {
JDiffClassDescription clz = createClass(NormalClass.class.getSimpleName());
JDiffClassDescription.JDiffField field = new JDiffClassDescription.JDiffField(
"STATIC_FIELD", "java.lang.String", Modifier.PUBLIC | Modifier.STATIC, VALUE);
clz.addField(field);
checkSignatureCompliance(clz);
assertEquals(field.toSignatureString(), "public static java.lang.String STATIC_FIELD");
}
@Test
public void testVolatileFiled() {
JDiffClassDescription clz = createClass(NormalClass.class.getSimpleName());
JDiffClassDescription.JDiffField field = new JDiffClassDescription.JDiffField(
"VOLATILE_FIELD", "java.lang.String", Modifier.PUBLIC | Modifier.VOLATILE, VALUE);
clz.addField(field);
checkSignatureCompliance(clz);
assertEquals(field.toSignatureString(), "public volatile java.lang.String VOLATILE_FIELD");
}
@Test
public void testTransientField() {
JDiffClassDescription clz = createClass(NormalClass.class.getSimpleName());
JDiffClassDescription.JDiffField field = new JDiffClassDescription.JDiffField(
"TRANSIENT_FIELD", "java.lang.String",
Modifier.PUBLIC | Modifier.TRANSIENT, VALUE);
clz.addField(field);
checkSignatureCompliance(clz);
assertEquals(field.toSignatureString(),
"public transient java.lang.String TRANSIENT_FIELD");
}
@Test
public void testPackageField() {
JDiffClassDescription clz = createClass(NormalClass.class.getSimpleName());
JDiffClassDescription.JDiffField field = new JDiffClassDescription.JDiffField(
"PACAKGE_FIELD", "java.lang.String", 0, VALUE);
clz.addField(field);
checkSignatureCompliance(clz);
assertEquals(field.toSignatureString(), "java.lang.String PACAKGE_FIELD");
}
@Test
public void testPrivateField() {
JDiffClassDescription clz = createClass(NormalClass.class.getSimpleName());
JDiffClassDescription.JDiffField field = new JDiffClassDescription.JDiffField(
"PRIVATE_FIELD", "java.lang.String", Modifier.PRIVATE, VALUE);
clz.addField(field);
checkSignatureCompliance(clz);
assertEquals(field.toSignatureString(), "private java.lang.String PRIVATE_FIELD");
}
@Test
public void testProtectedField() {
JDiffClassDescription clz = createClass(NormalClass.class.getSimpleName());
JDiffClassDescription.JDiffField field = new JDiffClassDescription.JDiffField(
"PROTECTED_FIELD", "java.lang.String", Modifier.PROTECTED, VALUE);
clz.addField(field);
checkSignatureCompliance(clz);
assertEquals(field.toSignatureString(), "protected java.lang.String PROTECTED_FIELD");
}
@Test
public void testFieldValue() {
JDiffClassDescription clz = createClass(NormalClass.class.getSimpleName());
JDiffClassDescription.JDiffField field = new JDiffClassDescription.JDiffField(
"VALUE_FIELD", "java.lang.String",
Modifier.PUBLIC | Modifier.FINAL | Modifier.STATIC, "\u2708");
clz.addField(field);
checkSignatureCompliance(clz);
assertEquals(field.toSignatureString(), "public static final java.lang.String VALUE_FIELD");
}
@Test
public void testFieldValueChanged() {
try (ExpectFailure observer = new ExpectFailure(FailureType.MISMATCH_FIELD)) {
JDiffClassDescription clz = createClass(NormalClass.class.getSimpleName());
JDiffClassDescription.JDiffField field = new JDiffClassDescription.JDiffField(
"VALUE_FIELD", "java.lang.String",
Modifier.PUBLIC | Modifier.FINAL | Modifier.STATIC, "\"&#9992;\"");
clz.addField(field);
checkSignatureCompliance(clz, observer);
assertEquals(field.toSignatureString(),
"public static final java.lang.String VALUE_FIELD");
}
}
@Test
public void testInnerClass() {
JDiffClassDescription clz = createClass("NormalClass.InnerClass");
JDiffClassDescription.JDiffField field = new JDiffClassDescription.JDiffField(
"innerClassData", "java.lang.String", Modifier.PRIVATE, VALUE);
clz.addField(field);
checkSignatureCompliance(clz);
assertEquals(clz.toSignatureString(), "public class NormalClass.InnerClass");
}
@Test
public void testInnerInnerClass() {
JDiffClassDescription clz = createClass(
"NormalClass.InnerClass.InnerInnerClass");
JDiffClassDescription.JDiffField field = new JDiffClassDescription.JDiffField(
"innerInnerClassData", "java.lang.String", Modifier.PRIVATE, VALUE);
clz.addField(field);
checkSignatureCompliance(clz);
assertEquals(clz.toSignatureString(),
"public class NormalClass.InnerClass.InnerInnerClass");
}
@Test
public void testInnerInterface() {
JDiffClassDescription clz = new JDiffClassDescription(
"android.signature.cts.tests.data", "NormalClass.InnerInterface");
clz.setType(JDiffClassDescription.JDiffType.INTERFACE);
clz.setModifier(Modifier.PUBLIC | Modifier.STATIC | Modifier.ABSTRACT);
clz.addMethod(
method("doSomething", Modifier.PUBLIC | Modifier.ABSTRACT, "void"));
checkSignatureCompliance(clz);
assertEquals(clz.toSignatureString(), "public interface NormalClass.InnerInterface");
}
@Test
public void testInterface() {
JDiffClassDescription clz = createInterface("NormalInterface");
clz.addMethod(
method("doSomething", Modifier.ABSTRACT | Modifier.PUBLIC, "void"));
checkSignatureCompliance(clz);
assertEquals(clz.toSignatureString(), "public interface NormalInterface");
}
/**
* Always treat interfaces as if they are abstract, even when the modifiers do not specify that.
*/
@Test
public void testInterfaceAlwaysTreatAsAbstract() {
JDiffClassDescription clz = createInterface("NormalInterface");
clz.setModifier(Modifier.PUBLIC);
clz.addMethod(method("doSomething", Modifier.ABSTRACT | Modifier.PUBLIC, "void"));
checkSignatureCompliance(clz);
}
@Test
public void testComplexEnum() {
JDiffClassDescription clz = createClass(ComplexEnum.class.getSimpleName());
clz.setExtendsClass(Enum.class.getName());
clz.setModifier(Modifier.PUBLIC | Modifier.FINAL);
checkSignatureCompliance(clz);
}
@Test
public void testFinalClass() {
JDiffClassDescription clz = new JDiffClassDescription(
"android.signature.cts.tests.data", "FinalClass");
clz.setType(JDiffClassDescription.JDiffType.CLASS);
clz.setModifier(Modifier.PUBLIC | Modifier.FINAL);
checkSignatureCompliance(clz);
assertEquals(clz.toSignatureString(), "public final class FinalClass");
}
@Test
public void testRemovingFinalFromAClass() {
try (ExpectFailure observer = new ExpectFailure(FailureType.MISMATCH_CLASS)) {
JDiffClassDescription clz = createClass(NormalClass.class.getSimpleName());
clz.setModifier(Modifier.PUBLIC | Modifier.FINAL);
checkSignatureCompliance(clz, observer);
}
}
@Test
public void testRemovingFinalFromAClass_PreviousApi() {
JDiffClassDescription clz = createClass(NormalClass.class.getSimpleName());
clz.setModifier(Modifier.PUBLIC | Modifier.FINAL);
clz.setPreviousApiFlag(true);
checkSignatureCompliance(clz);
}
/**
* Test that if the API class is final but the runtime is abstract (and not final) that it is
* an error.
*
* http://b/181019981
*/
@Test
public void testRemovingFinalFromAClassSwitchToAbstract() {
try (ExpectFailure observer = new ExpectFailure(FailureType.MISMATCH_CLASS)) {
JDiffClassDescription clz = createClass(AbstractClass.class.getSimpleName());
clz.setModifier(Modifier.PUBLIC | Modifier.FINAL);
checkSignatureCompliance(clz, observer);
}
}
/**
* Test that if the API class in a previous release is final but the runtime is abstract (and
* not final) that it is not an error.
*
* http://b/181019981
*/
@Test
public void testRemovingFinalFromAClassSwitchToAbstract_PreviousApi() {
JDiffClassDescription clz = createClass(AbstractClass.class.getSimpleName());
clz.setModifier(Modifier.PUBLIC | Modifier.FINAL);
clz.setPreviousApiFlag(true);
checkSignatureCompliance(clz);
}
/**
* Test that if the API class in a previous release is final but the runtime is abstract (and
* not final) and has constructors then it is an error.
*
* http://b/181019981
*/
@Test
public void testRemovingFinalFromAClassWithCtorSwitchToAbstract_PreviousApi() {
try (ExpectFailure observer = new ExpectFailure(FailureType.MISMATCH_CLASS)) {
String simpleName = AbstractClassWithCtor.class.getSimpleName();
JDiffClassDescription clz = createClass(simpleName);
clz.setModifier(Modifier.PUBLIC | Modifier.FINAL);
clz.setPreviousApiFlag(true);
clz.addConstructor(ctor(simpleName, Modifier.PUBLIC));
checkSignatureCompliance(clz, observer);
}
}
/**
* Test the case where the API declares the method is synchronized, but it
* actually is not.
*/
@Test
public void testRemovingSync() {
JDiffClassDescription clz = createClass(NormalClass.class.getSimpleName());
JDiffClassDescription.JDiffMethod method = method("notSyncMethod",
Modifier.SYNCHRONIZED | Modifier.PUBLIC, "void");
clz.addMethod(method);
checkSignatureCompliance(clz);
}
/**
* API says method is not native, but it actually is. http://b/1839558
*/
@Test
public void testAddingNative() {
JDiffClassDescription clz = createClass(NormalClass.class.getSimpleName());
JDiffClassDescription.JDiffMethod method = method("nativeMethod", Modifier.PUBLIC, "void");
clz.addMethod(method);
checkSignatureCompliance(clz);
}
/**
* API says method is native, but actually isn't. http://b/1839558
*/
@Test
public void testRemovingNative() {
JDiffClassDescription clz = createClass(NormalClass.class.getSimpleName());
JDiffClassDescription.JDiffMethod method = method("notNativeMethod",
Modifier.NATIVE | Modifier.PUBLIC, "void");
clz.addMethod(method);
checkSignatureCompliance(clz);
}
@Test
public void testAbstractClass() {
JDiffClassDescription clz = new JDiffClassDescription(
"android.signature.cts.tests.data", "AbstractClass");
clz.setType(JDiffClassDescription.JDiffType.CLASS);
clz.setModifier(Modifier.PUBLIC | Modifier.ABSTRACT);
checkSignatureCompliance(clz);
assertEquals(clz.toSignatureString(), "public abstract class AbstractClass");
}
/**
* API lists class as abstract, reflection does not. http://b/1839622
*/
@Test
public void testRemovingAbstractFromAClass() {
try (ExpectFailure observer = new ExpectFailure(FailureType.MISMATCH_CLASS)) {
JDiffClassDescription clz = new JDiffClassDescription(
"android.signature.cts.tests.data", "NormalClass");
clz.setType(JDiffClassDescription.JDiffType.CLASS);
clz.setModifier(Modifier.PUBLIC | Modifier.ABSTRACT);
checkSignatureCompliance(clz, observer);
}
}
/**
* Previous API lists class as abstract, reflection does not. http://b/1839622
*/
@Test
public void testRemovingAbstractFromAClass_PreviousApi() {
JDiffClassDescription clz = new JDiffClassDescription(
"android.signature.cts.tests.data", "NormalClass");
clz.setType(JDiffClassDescription.JDiffType.CLASS);
clz.setModifier(Modifier.PUBLIC | Modifier.ABSTRACT);
clz.setPreviousApiFlag(true);
checkSignatureCompliance(clz);
}
/**
* reflection lists class as abstract, api does not. http://b/1839622
*/
@Test
public void testAddingAbstractToAClass() {
try (ExpectFailure observer = new ExpectFailure(FailureType.MISMATCH_CLASS)) {
JDiffClassDescription clz = createClass("AbstractClass");
checkSignatureCompliance(clz, observer);
}
}
/**
* The current API lists the class as being final but the runtime class does not so they are
* incompatible.
*/
@Test
public void testAddingFinalToAClass() {
try (ExpectFailure observer = new ExpectFailure(FailureType.MISMATCH_CLASS)) {
JDiffClassDescription clz = createClass("FinalClass");
checkSignatureCompliance(clz, observer);
}
}
/**
* A previously released API lists the class as being final but the runtime class does not.
*
* <p>While adding a final modifier to a class is not strictly backwards compatible it is when
* the class has no accessible constructors and so cannot be instantiated or extended, as is the
* case in this test.</p>
*/
@Test
public void testAddingFinalToAClassNoCtor_PreviousApi() {
JDiffClassDescription clz = createClass("FinalClass");
clz.setPreviousApiFlag(true);
checkSignatureCompliance(clz);
}
/**
* A previously released API lists the class as being final but the runtime class does not.
*
* <p>Adding a final modifier to a class is not backwards compatible when the class has some
* accessible constructors and so could be instantiated and/or extended, as is the case of this
* class.</p>
*/
@Test
public void testAddingFinalToAClassWithCtor_PreviousApi() {
try (ExpectFailure observer = new ExpectFailure(FailureType.MISMATCH_CLASS)) {
String simpleName = "FinalClassWithCtor";
JDiffClassDescription clz = createClass(simpleName);
clz.setPreviousApiFlag(true);
clz.addConstructor(ctor(simpleName, Modifier.PUBLIC));
checkSignatureCompliance(clz, observer);
}
}
/**
* The current API lists the class as being static but the runtime class does not so they are
* incompatible.
*/
@Test
public void testAddingStaticToInnerClass() {
try (ExpectFailure observer = new ExpectFailure(FailureType.MISMATCH_CLASS)) {
JDiffClassDescription clz = createClass("AbstractClass.StaticNestedClass");
checkSignatureCompliance(clz, observer);
}
}
/**
* A previously released API lists the class as being static but the runtime class does not.
*
* <p>While adding a static modifier to a class is not strictly backwards compatible it is when
* the class has no accessible constructors and so cannot be instantiated or extended, as is the
* case in this test.</p>
*/
@Test
public void testAddingStaticToInnerClassNoCtor_PreviousApi() {
JDiffClassDescription clz = createClass("AbstractClass.StaticNestedClass");
clz.setPreviousApiFlag(true);
checkSignatureCompliance(clz);
}
/**
* A previously released API lists the class as being static but the runtime class does not.
*
* <p>Adding a static modifier to a class is not backwards compatible when the class has some
* accessible constructors and so could be instantiated and/or extended, as is the case of this
* class.</p>
*/
@Test
public void testAddingStaticToInnerClassWithCtor_PreviousApi() {
try (ExpectFailure observer = new ExpectFailure(FailureType.MISMATCH_CLASS)) {
String simpleName = "AbstractClass.StaticNestedClassWithCtor";
JDiffClassDescription clz = createClass(simpleName);
clz.setPreviousApiFlag(true);
clz.addConstructor(ctor(simpleName, Modifier.PUBLIC));
checkSignatureCompliance(clz, observer);
}
}
/**
* Compatible (no change):
*
* public abstract void AbstractClass#abstractMethod()
* -> public abstract void AbstractClass#abstractMethod()
*/
@Test
public void testAbstractMethod() {
JDiffClassDescription clz = createAbstractClass(AbstractClass.class.getSimpleName());
JDiffClassDescription.JDiffMethod method = method("abstractMethod",
Modifier.PUBLIC | Modifier.ABSTRACT, "void");
clz.addMethod(method);
checkSignatureCompliance(clz);
}
/**
* Incompatible (provide implementation for abstract method):
*
* public abstract void Normal#notSyncMethod()
* -> public void Normal#notSyncMethod()
*/
@Test
public void testRemovingAbstractFromMethod() {
try (ExpectFailure observer = new ExpectFailure(FailureType.MISMATCH_METHOD)) {
JDiffClassDescription clz = createClass(NormalClass.class.getSimpleName());
JDiffClassDescription.JDiffMethod method = method("notSyncMethod",
Modifier.PUBLIC | Modifier.ABSTRACT, "void");
clz.addMethod(method);
checkSignatureCompliance(clz, observer);
}
}
/**
* A previously released API lists the method as being abstract but the runtime class does not.
*
* <p>While adding an abstract modifier to a method is not strictly backwards compatible it is
* when the class has no accessible constructors and so cannot be instantiated or extended, as
* is the case in this test.</p>
*/
@Test
public void testRemovingAbstractFromMethodOnClassNoCtor_PreviousApi() {
JDiffClassDescription clz = createClass(NormalClass.class.getSimpleName());
JDiffClassDescription.JDiffMethod method = method("notSyncMethod",
Modifier.PUBLIC | Modifier.ABSTRACT, "void");
clz.addMethod(method);
clz.setPreviousApiFlag(true);
checkSignatureCompliance(clz);
}
/**
* Not compatible (overridden method is not overridable anymore):
*
* public abstract void AbstractClass#finalMethod()
* -> public final void AbstractClass#finalMethod()
*/
@Test
public void testAbstractToFinalMethod() {
JDiffClassDescription clz = createAbstractClass(AbstractClass.class.getSimpleName());
JDiffClassDescription.JDiffMethod method = method("finalMethod",
Modifier.PUBLIC | Modifier.ABSTRACT, "void");
clz.addMethod(method);
try (ExpectFailure observer = new ExpectFailure(FailureType.MISMATCH_METHOD)) {
checkSignatureCompliance(clz, observer);
}
}
/**
* Not compatible (previously implemented method becomes abstract):
*
* public void AbstractClass#abstractMethod()
* -> public abstract void AbstractClass#abstractMethod()
*/
@Test
public void testAddingAbstractToMethod() {
JDiffClassDescription clz = createAbstractClass(AbstractClass.class.getSimpleName());
JDiffClassDescription.JDiffMethod method = method("abstractMethod",
Modifier.PUBLIC, "void");
clz.addMethod(method);
try (ExpectFailure observer = new ExpectFailure(FailureType.MISMATCH_METHOD)) {
checkSignatureCompliance(clz, observer);
}
}
@Test
public void testFinalMethod() {
JDiffClassDescription clz = createClass(NormalClass.class.getSimpleName());
JDiffClassDescription.JDiffMethod method = method("finalMethod",
Modifier.PUBLIC | Modifier.FINAL, "void");
clz.addMethod(method);
checkSignatureCompliance(clz);
assertEquals(method.toSignatureString(), "public final void finalMethod()");
}
/**
* Final Class, API lists methods as non-final, reflection has it as final.
* http://b/1839589
*/
@Test
public void testAddingFinalToAMethodInAFinalClass() {
JDiffClassDescription clz = new JDiffClassDescription(
"android.signature.cts.tests.data", "FinalClass");
clz.setType(JDiffClassDescription.JDiffType.CLASS);
clz.setModifier(Modifier.PUBLIC | Modifier.FINAL);
JDiffClassDescription.JDiffMethod method = method("finalMethod", Modifier.PUBLIC, "void");
clz.addMethod(method);
checkSignatureCompliance(clz);
}
/**
* Final Class, API lists methods as final, reflection has it as non-final.
* http://b/1839589
*/
@Test
public void testRemovingFinalToAMethodInAFinalClass() {
JDiffClassDescription clz = new JDiffClassDescription(
"android.signature.cts.tests.data", "FinalClass");
clz.setType(JDiffClassDescription.JDiffType.CLASS);
clz.setModifier(Modifier.PUBLIC | Modifier.FINAL);
JDiffClassDescription.JDiffMethod method = method("nonFinalMethod",
Modifier.PUBLIC | Modifier.FINAL, "void");
clz.addMethod(method);
checkSignatureCompliance(clz);
}
/**
* non-final Class, API lists methods as non-final, reflection has it as
* final. http://b/1839589
*/
@Test
public void testAddingFinalToAMethodInANonFinalClass() {
try (ExpectFailure observer = new ExpectFailure(FailureType.MISMATCH_METHOD)) {
JDiffClassDescription clz = createClass("NormalClass");
JDiffClassDescription.JDiffMethod method = method("finalMethod", Modifier.PUBLIC,
"void");
clz.addMethod(method);
checkSignatureCompliance(clz, observer);
}
}
@Test
public void testExtendedNormalInterface() {
try (NoFailures observer = new NoFailures()) {
runWithApiChecker(observer, checker -> {
JDiffClassDescription iface = createInterface(
NormalInterface.class.getSimpleName());
iface.addMethod(method("doSomething", Modifier.PUBLIC, "void"));
checker.addBaseClass(iface);
JDiffClassDescription clz =
createInterface(ExtendedNormalInterface.class.getSimpleName());
clz.addMethod(
method("doSomethingElse", Modifier.PUBLIC | Modifier.ABSTRACT, "void"));
clz.addImplInterface(iface.getAbsoluteClassName());
checker.checkSignatureCompliance(clz);
});
}
}
@Test
public void testAddingRuntimeMethodToInterface() {
try (ExpectFailure observer = new ExpectFailure(FailureType.MISMATCH_INTERFACE_METHOD)) {
runWithApiChecker(observer, checker -> {
JDiffClassDescription iface = createInterface(
ExtendedNormalInterface.class.getSimpleName());
iface.addMethod(method("doSomething", Modifier.PUBLIC | Modifier.ABSTRACT, "void"));
checker.checkSignatureCompliance(iface);
});
}
}
@Test
public void testAddingRuntimeMethodToInterface_PreviousApi() {
try (NoFailures observer = new NoFailures()) {
runWithApiChecker(observer, checker -> {
JDiffClassDescription iface = createInterface(
ExtendedNormalInterface.class.getSimpleName());
iface.addMethod(method("doSomething", Modifier.PUBLIC | Modifier.ABSTRACT, "void"));
iface.setPreviousApiFlag(true);
checker.checkSignatureCompliance(iface);
});
}
}
}