1 /* 2 * Copyright (C) 2018 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 package android.signature.cts.tests; 17 18 import android.signature.cts.ApiPresenceChecker; 19 import android.signature.cts.ClassProvider; 20 import android.signature.cts.ExcludingClassProvider; 21 import android.signature.cts.FailureType; 22 import android.signature.cts.JDiffClassDescription; 23 import android.signature.cts.ResultObserver; 24 import java.io.PrintWriter; 25 import java.io.StringWriter; 26 import java.lang.reflect.Modifier; 27 import java.util.ArrayList; 28 import java.util.Arrays; 29 import java.util.Collections; 30 import java.util.List; 31 import java.util.function.Consumer; 32 33 import org.junit.Assert; 34 35 /** 36 * Base class for tests of implementations of {@link ApiPresenceChecker}. 37 */ 38 public abstract class ApiPresenceCheckerTest<T extends ApiPresenceChecker> { 39 40 static final String VALUE = "VALUE"; 41 createClassProvider(String[] excludedRuntimeClasses)42 private static ClassProvider createClassProvider(String[] excludedRuntimeClasses) { 43 ClassProvider provider = new TestClassesProvider(); 44 if (excludedRuntimeClasses.length != 0) { 45 provider = new ExcludingClassProvider(provider, 46 name -> Arrays.stream(excludedRuntimeClasses) 47 .anyMatch(myname -> myname.equals(name))); 48 } 49 return provider; 50 } 51 createClass(String name)52 protected static JDiffClassDescription createClass(String name) { 53 JDiffClassDescription clz = new JDiffClassDescription( 54 "android.signature.cts.tests.data", name); 55 clz.setType(JDiffClassDescription.JDiffType.CLASS); 56 clz.setModifier(Modifier.PUBLIC); 57 return clz; 58 } 59 createAbstractClass(String name)60 protected static JDiffClassDescription createAbstractClass(String name) { 61 JDiffClassDescription clz = new JDiffClassDescription( 62 "android.signature.cts.tests.data", name); 63 clz.setType(JDiffClassDescription.JDiffType.CLASS); 64 clz.setModifier(Modifier.PUBLIC | Modifier.ABSTRACT); 65 return clz; 66 } 67 addConstructor(JDiffClassDescription clz, String... paramTypes)68 protected static void addConstructor(JDiffClassDescription clz, String... paramTypes) { 69 JDiffClassDescription.JDiffConstructor constructor = 70 new JDiffClassDescription.JDiffConstructor(clz.getShortClassName(), Modifier.PUBLIC); 71 if (paramTypes != null) { 72 for (String type : paramTypes) { 73 constructor.addParam(type); 74 } 75 } 76 clz.addConstructor(constructor); 77 } 78 addPublicVoidMethod(JDiffClassDescription clz, String name)79 protected static void addPublicVoidMethod(JDiffClassDescription clz, String name) { 80 clz.addMethod(method(name, Modifier.PUBLIC, "void")); 81 } 82 addPublicBooleanField(JDiffClassDescription clz, String name)83 protected static void addPublicBooleanField(JDiffClassDescription clz, String name) { 84 JDiffClassDescription.JDiffField field = new JDiffClassDescription.JDiffField( 85 name, "boolean", Modifier.PUBLIC, VALUE); 86 clz.addField(field); 87 } 88 checkSignatureCompliance(JDiffClassDescription classDescription, String... excludedRuntimeClassNames)89 void checkSignatureCompliance(JDiffClassDescription classDescription, 90 String... excludedRuntimeClassNames) { 91 try (NoFailures resultObserver = new NoFailures()) { 92 checkSignatureCompliance(classDescription, resultObserver, 93 excludedRuntimeClassNames); 94 } 95 } 96 checkSignatureCompliance(JDiffClassDescription classDescription, ResultObserver resultObserver, String... excludedRuntimeClasses)97 void checkSignatureCompliance(JDiffClassDescription classDescription, 98 ResultObserver resultObserver, String... excludedRuntimeClasses) { 99 runWithApiChecker(resultObserver, 100 checker -> checker.checkSignatureCompliance(classDescription), 101 excludedRuntimeClasses); 102 } 103 runWithApiChecker( ResultObserver resultObserver, Consumer<T> consumer, String... excludedRuntimeClasses)104 void runWithApiChecker( 105 ResultObserver resultObserver, Consumer<T> consumer, String... excludedRuntimeClasses) { 106 ClassProvider provider = createClassProvider(excludedRuntimeClasses); 107 T checker = createChecker(resultObserver, provider); 108 consumer.accept(checker); 109 } 110 createChecker(ResultObserver resultObserver, ClassProvider provider)111 protected abstract T createChecker(ResultObserver resultObserver, ClassProvider provider); 112 createInterface(String name)113 protected JDiffClassDescription createInterface(String name) { 114 JDiffClassDescription clz = new JDiffClassDescription( 115 "android.signature.cts.tests.data", name); 116 clz.setType(JDiffClassDescription.JDiffType.INTERFACE); 117 clz.setModifier(Modifier.PUBLIC | Modifier.ABSTRACT); 118 return clz; 119 } 120 ctor(String name, int modifiers)121 protected static JDiffClassDescription.JDiffConstructor ctor(String name, int modifiers) { 122 return new JDiffClassDescription.JDiffConstructor(name, modifiers); 123 } 124 method( String name, int modifiers, String returnType)125 protected static JDiffClassDescription.JDiffMethod method( 126 String name, int modifiers, String returnType) { 127 return new JDiffClassDescription.JDiffMethod(name, modifiers, returnType); 128 } 129 130 protected static class Failure { 131 private final FailureType type; 132 private final String name; 133 private final String errorMessage; 134 private final Throwable throwable; 135 Failure(FailureType type, String name, String errorMessage, Throwable throwable)136 public Failure(FailureType type, String name, String errorMessage, Throwable throwable) { 137 this.type = type; 138 this.name = name; 139 this.errorMessage = errorMessage; 140 this.throwable = throwable; 141 } 142 143 @Override toString()144 public String toString() { 145 String exception = "<none>"; 146 if (throwable != null) { 147 StringWriter out = new StringWriter(); 148 throwable.printStackTrace(new PrintWriter(out)); 149 exception = out.toString(); 150 } 151 return "Failure{" + 152 "type=" + type + 153 ", name='" + name + '\'' + 154 ", errorMessage='" + errorMessage + '\'' + 155 ", throwable=" + exception + 156 '}'; 157 } 158 } 159 160 /** 161 * Collect failures and check them after the test has run. 162 * 163 * <p>Throwing an exception in the {@link #notifyFailure} method causes that exception to be 164 * reported as another failure which then fails again failing the test and losing information 165 * about the original exception.</p> 166 */ 167 protected static abstract class FailureGathererObserver 168 implements ResultObserver, AutoCloseable { 169 170 private final List<Failure> failures; 171 FailureGathererObserver()172 public FailureGathererObserver() { 173 failures = new ArrayList<>(); 174 } 175 176 @Override notifyFailure(FailureType type, String name, String errorMessage, Throwable throwable)177 public final void notifyFailure(FailureType type, String name, String errorMessage, 178 Throwable throwable) { 179 failures.add(new Failure(type, name, errorMessage, throwable)); 180 } 181 182 @Override close()183 public void close() { 184 checkFailures(failures); 185 } 186 checkFailures(List<Failure> failures)187 public abstract void checkFailures(List<Failure> failures); 188 } 189 190 protected static class NoFailures extends FailureGathererObserver { 191 192 @Override checkFailures(List<Failure> failures)193 public void checkFailures(List<Failure> failures) { 194 Assert.assertEquals("Unexpected test failure", Collections.emptyList(), failures); 195 } 196 } 197 198 protected static class ExpectFailure extends FailureGathererObserver { 199 200 private final FailureType expectedType; 201 ExpectFailure(FailureType expectedType)202 ExpectFailure(FailureType expectedType) { 203 this.expectedType = expectedType; 204 } 205 206 @Override checkFailures(List<Failure> failures)207 public void checkFailures(List<Failure> failures) { 208 int count = failures.size(); 209 boolean ok = count == 1; 210 if (ok) { 211 Failure failure = failures.get(0); 212 if (failure.type != expectedType) { 213 ok = false; 214 } 215 } 216 217 if (!ok) { 218 Assert.fail("Expect one failure of type " + expectedType + " but found " + count 219 + " failures: " + failures); 220 } 221 } 222 } 223 } 224