blob: b4ffe631e618d34d4d4fc81ed717f35dee7b8aed [file] [log] [blame]
/*
* Copyright (C) 2018 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 android.signature.cts.ApiPresenceChecker;
import android.signature.cts.ClassProvider;
import android.signature.cts.ExcludingClassProvider;
import android.signature.cts.FailureType;
import android.signature.cts.JDiffClassDescription;
import android.signature.cts.ResultObserver;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.function.Consumer;
import org.junit.Assert;
/**
* Base class for tests of implementations of {@link ApiPresenceChecker}.
*/
public abstract class ApiPresenceCheckerTest<T extends ApiPresenceChecker> {
static final String VALUE = "VALUE";
private static ClassProvider createClassProvider(String[] excludedRuntimeClasses) {
ClassProvider provider = new TestClassesProvider();
if (excludedRuntimeClasses.length != 0) {
provider = new ExcludingClassProvider(provider,
name -> Arrays.stream(excludedRuntimeClasses)
.anyMatch(myname -> myname.equals(name)));
}
return provider;
}
protected static JDiffClassDescription createClass(String name) {
JDiffClassDescription clz = new JDiffClassDescription(
"android.signature.cts.tests.data", name);
clz.setType(JDiffClassDescription.JDiffType.CLASS);
clz.setModifier(Modifier.PUBLIC);
return clz;
}
protected static JDiffClassDescription createAbstractClass(String name) {
JDiffClassDescription clz = new JDiffClassDescription(
"android.signature.cts.tests.data", name);
clz.setType(JDiffClassDescription.JDiffType.CLASS);
clz.setModifier(Modifier.PUBLIC | Modifier.ABSTRACT);
return clz;
}
protected static void addConstructor(JDiffClassDescription clz, String... paramTypes) {
JDiffClassDescription.JDiffConstructor constructor =
new JDiffClassDescription.JDiffConstructor(clz.getShortClassName(), Modifier.PUBLIC);
if (paramTypes != null) {
for (String type : paramTypes) {
constructor.addParam(type);
}
}
clz.addConstructor(constructor);
}
protected static void addPublicVoidMethod(JDiffClassDescription clz, String name) {
clz.addMethod(method(name, Modifier.PUBLIC, "void"));
}
protected static void addPublicBooleanField(JDiffClassDescription clz, String name) {
JDiffClassDescription.JDiffField field = new JDiffClassDescription.JDiffField(
name, "boolean", Modifier.PUBLIC, VALUE);
clz.addField(field);
}
void checkSignatureCompliance(JDiffClassDescription classDescription,
String... excludedRuntimeClassNames) {
try (NoFailures resultObserver = new NoFailures()) {
checkSignatureCompliance(classDescription, resultObserver,
excludedRuntimeClassNames);
}
}
void checkSignatureCompliance(JDiffClassDescription classDescription,
ResultObserver resultObserver, String... excludedRuntimeClasses) {
runWithApiChecker(resultObserver,
checker -> checker.checkSignatureCompliance(classDescription),
excludedRuntimeClasses);
}
void runWithApiChecker(
ResultObserver resultObserver, Consumer<T> consumer, String... excludedRuntimeClasses) {
ClassProvider provider = createClassProvider(excludedRuntimeClasses);
T checker = createChecker(resultObserver, provider);
consumer.accept(checker);
}
protected abstract T createChecker(ResultObserver resultObserver, ClassProvider provider);
protected JDiffClassDescription createInterface(String name) {
JDiffClassDescription clz = new JDiffClassDescription(
"android.signature.cts.tests.data", name);
clz.setType(JDiffClassDescription.JDiffType.INTERFACE);
clz.setModifier(Modifier.PUBLIC | Modifier.ABSTRACT);
return clz;
}
protected static JDiffClassDescription.JDiffConstructor ctor(String name, int modifiers) {
return new JDiffClassDescription.JDiffConstructor(name, modifiers);
}
protected static JDiffClassDescription.JDiffMethod method(
String name, int modifiers, String returnType) {
return new JDiffClassDescription.JDiffMethod(name, modifiers, returnType);
}
protected static class Failure {
private final FailureType type;
private final String name;
private final String errorMessage;
private final Throwable throwable;
public Failure(FailureType type, String name, String errorMessage, Throwable throwable) {
this.type = type;
this.name = name;
this.errorMessage = errorMessage;
this.throwable = throwable;
}
@Override
public String toString() {
String exception = "<none>";
if (throwable != null) {
StringWriter out = new StringWriter();
throwable.printStackTrace(new PrintWriter(out));
exception = out.toString();
}
return "Failure{" +
"type=" + type +
", name='" + name + '\'' +
", errorMessage='" + errorMessage + '\'' +
", throwable=" + exception +
'}';
}
}
/**
* Collect failures and check them after the test has run.
*
* <p>Throwing an exception in the {@link #notifyFailure} method causes that exception to be
* reported as another failure which then fails again failing the test and losing information
* about the original exception.</p>
*/
protected static abstract class FailureGathererObserver
implements ResultObserver, AutoCloseable {
private final List<Failure> failures;
public FailureGathererObserver() {
failures = new ArrayList<>();
}
@Override
public final void notifyFailure(FailureType type, String name, String errorMessage,
Throwable throwable) {
failures.add(new Failure(type, name, errorMessage, throwable));
}
@Override
public void close() {
checkFailures(failures);
}
public abstract void checkFailures(List<Failure> failures);
}
protected static class NoFailures extends FailureGathererObserver {
@Override
public void checkFailures(List<Failure> failures) {
Assert.assertEquals("Unexpected test failure", Collections.emptyList(), failures);
}
}
protected static class ExpectFailure extends FailureGathererObserver {
private final FailureType expectedType;
ExpectFailure(FailureType expectedType) {
this.expectedType = expectedType;
}
@Override
public void checkFailures(List<Failure> failures) {
int count = failures.size();
boolean ok = count == 1;
if (ok) {
Failure failure = failures.get(0);
if (failure.type != expectedType) {
ok = false;
}
}
if (!ok) {
Assert.fail("Expect one failure of type " + expectedType + " but found " + count
+ " failures: " + failures);
}
}
}
}