blob: af82a4693ad7e67f9f4ec97687307162e9cbe1a1 [file] [log] [blame]
/*
* Copyright (C) 2011 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;
import static android.signature.cts.CurrentApi.CURRENT_API_FILE;
import static android.signature.cts.CurrentApi.SYSTEM_CURRENT_API_FILE;
import static android.signature.cts.CurrentApi.TAG_ROOT;
import static android.signature.cts.CurrentApi.TAG_PACKAGE;
import static android.signature.cts.CurrentApi.TAG_CLASS;
import static android.signature.cts.CurrentApi.TAG_INTERFACE;
import static android.signature.cts.CurrentApi.TAG_IMPLEMENTS;
import static android.signature.cts.CurrentApi.TAG_CONSTRUCTOR;
import static android.signature.cts.CurrentApi.TAG_METHOD;
import static android.signature.cts.CurrentApi.TAG_PARAM;
import static android.signature.cts.CurrentApi.TAG_EXCEPTION;
import static android.signature.cts.CurrentApi.TAG_FIELD;
import static android.signature.cts.CurrentApi.MODIFIER_ABSTRACT;
import static android.signature.cts.CurrentApi.MODIFIER_FINAL;
import static android.signature.cts.CurrentApi.MODIFIER_NATIVE;
import static android.signature.cts.CurrentApi.MODIFIER_PRIVATE;
import static android.signature.cts.CurrentApi.MODIFIER_PROTECTED;
import static android.signature.cts.CurrentApi.MODIFIER_PUBLIC;
import static android.signature.cts.CurrentApi.MODIFIER_STATIC;
import static android.signature.cts.CurrentApi.MODIFIER_SYNCHRONIZED;
import static android.signature.cts.CurrentApi.MODIFIER_TRANSIENT;
import static android.signature.cts.CurrentApi.MODIFIER_VOLATILE;
import static android.signature.cts.CurrentApi.MODIFIER_VISIBILITY;
import static android.signature.cts.CurrentApi.ATTRIBUTE_NAME;
import static android.signature.cts.CurrentApi.ATTRIBUTE_EXTENDS;
import static android.signature.cts.CurrentApi.ATTRIBUTE_TYPE;
import static android.signature.cts.CurrentApi.ATTRIBUTE_RETURN;
import android.content.res.Resources;
import android.signature.cts.JDiffClassDescription.JDiffConstructor;
import android.signature.cts.JDiffClassDescription.JDiffField;
import android.signature.cts.JDiffClassDescription.JDiffMethod;
import android.test.AndroidTestCase;
import android.util.Log;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlPullParserFactory;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Scanner;
/**
* Performs the signature check via a JUnit test.
*/
public class SignatureTest extends AndroidTestCase {
private static final String TAG = SignatureTest.class.getSimpleName();
private HashSet<String> mKeyTagSet;
private TestResultObserver mResultObserver;
private class TestResultObserver implements ResultObserver {
boolean mDidFail = false;
StringBuilder mErrorString = new StringBuilder();
@Override
public void notifyFailure(FailureType type, String name, String errorMessage) {
mDidFail = true;
mErrorString.append("\n");
mErrorString.append(type.toString().toLowerCase());
mErrorString.append(":\t");
mErrorString.append(name);
mErrorString.append("\tError: ");
mErrorString.append(errorMessage);
}
}
@Override
protected void setUp() throws Exception {
super.setUp();
mKeyTagSet = new HashSet<String>();
mKeyTagSet.addAll(Arrays.asList(new String[] {
TAG_PACKAGE, TAG_CLASS, TAG_INTERFACE, TAG_IMPLEMENTS, TAG_CONSTRUCTOR,
TAG_METHOD, TAG_PARAM, TAG_EXCEPTION, TAG_FIELD }));
mResultObserver = new TestResultObserver();
}
/**
* Tests that the device's API matches the expected set defined in xml.
* <p/>
* Will check the entire API, and then report the complete list of failures
*/
public void testSignature() {
try {
XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
XmlPullParser parser = factory.newPullParser();
parser.setInput(new FileInputStream(new File(CURRENT_API_FILE)), null);
start(parser);
} catch (Exception e) {
mResultObserver.notifyFailure(FailureType.CAUGHT_EXCEPTION, e.getMessage(),
e.getMessage());
}
if (mResultObserver.mDidFail) {
fail(mResultObserver.mErrorString.toString());
}
}
private void beginDocument(XmlPullParser parser, String firstElementName)
throws XmlPullParserException, IOException {
int type;
while ((type=parser.next()) != XmlPullParser.START_TAG
&& type != XmlPullParser.END_DOCUMENT) { }
if (type != XmlPullParser.START_TAG) {
throw new XmlPullParserException("No start tag found");
}
if (!parser.getName().equals(firstElementName)) {
throw new XmlPullParserException("Unexpected start tag: found " + parser.getName() +
", expected " + firstElementName);
}
}
/**
* Signature test entry point.
*/
private void start(XmlPullParser parser) throws XmlPullParserException, IOException {
logd(String.format("Name: %s", parser.getName()));
logd(String.format("Text: %s", parser.getText()));
logd(String.format("Namespace: %s", parser.getNamespace()));
logd(String.format("Line Number: %s", parser.getLineNumber()));
logd(String.format("Column Number: %s", parser.getColumnNumber()));
logd(String.format("Position Description: %s", parser.getPositionDescription()));
JDiffClassDescription currentClass = null;
String currentPackage = "";
JDiffMethod currentMethod = null;
beginDocument(parser, TAG_ROOT);
int type;
while (true) {
type = XmlPullParser.START_DOCUMENT;
while ((type=parser.next()) != XmlPullParser.START_TAG
&& type != XmlPullParser.END_DOCUMENT
&& type != XmlPullParser.END_TAG) {
}
if (type == XmlPullParser.END_TAG) {
if (TAG_CLASS.equals(parser.getName())
|| TAG_INTERFACE.equals(parser.getName())) {
currentClass.checkSignatureCompliance();
} else if (TAG_PACKAGE.equals(parser.getName())) {
currentPackage = "";
}
continue;
}
if (type == XmlPullParser.END_DOCUMENT) {
break;
}
String tagname = parser.getName();
if (!mKeyTagSet.contains(tagname)) {
continue;
}
if (type == XmlPullParser.START_TAG && tagname.equals(TAG_PACKAGE)) {
currentPackage = parser.getAttributeValue(null, ATTRIBUTE_NAME);
} else if (tagname.equals(TAG_CLASS)) {
currentClass = CurrentApi.loadClassInfo(
parser, false, currentPackage, mResultObserver);
} else if (tagname.equals(TAG_INTERFACE)) {
currentClass = CurrentApi.loadClassInfo(
parser, true, currentPackage, mResultObserver);
} else if (tagname.equals(TAG_IMPLEMENTS)) {
currentClass.addImplInterface(parser.getAttributeValue(null, ATTRIBUTE_NAME));
} else if (tagname.equals(TAG_CONSTRUCTOR)) {
JDiffConstructor constructor =
CurrentApi.loadConstructorInfo(parser, currentClass);
currentClass.addConstructor(constructor);
currentMethod = constructor;
} else if (tagname.equals(TAG_METHOD)) {
currentMethod = CurrentApi.loadMethodInfo(currentClass.getClassName(), parser);
currentClass.addMethod(currentMethod);
} else if (tagname.equals(TAG_PARAM)) {
currentMethod.addParam(parser.getAttributeValue(null, ATTRIBUTE_TYPE));
} else if (tagname.equals(TAG_EXCEPTION)) {
currentMethod.addException(parser.getAttributeValue(null, ATTRIBUTE_TYPE));
} else if (tagname.equals(TAG_FIELD)) {
JDiffField field = CurrentApi.loadFieldInfo(currentClass.getClassName(), parser);
currentClass.addField(field);
} else {
throw new RuntimeException(
"unknown tag exception:" + tagname);
}
if (currentPackage != null) {
logd(String.format("currentPackage: %s", currentPackage));
}
if (currentClass != null) {
logd(String.format("currentClass: %s", currentClass.toSignatureString()));
}
if (currentMethod != null) {
logd(String.format("currentMethod: %s", currentMethod.toSignatureString()));
}
}
}
public static void loge(String msg, Exception e) {
Log.e(TAG, msg, e);
}
public static void logd(String msg) {
Log.d(TAG, msg);
}
}