Merge "Fix MultiAppStartupTest" into froyo
diff --git a/Android.mk b/Android.mk
index 29e7cff..e3f9ba8 100644
--- a/Android.mk
+++ b/Android.mk
@@ -14,4 +14,6 @@
# limitations under the License.
#
+include cts/CtsTestCoverage.mk
+
include $(call all-subdir-makefiles)
diff --git a/CtsTestCaseList.mk b/CtsTestCaseList.mk
index 46db63c..16b81c9 100644
--- a/CtsTestCaseList.mk
+++ b/CtsTestCaseList.mk
@@ -27,16 +27,14 @@
CtsTargetInstrumentationApp \
CtsUsePermissionDiffCert
-CTS_TEST_CASE_LIST := \
- TestDeviceSetup \
- CtsTestStubs \
+# These test cases will be analyzed by the CTS API coverage tools.
+CTS_COVERAGE_TEST_CASE_LIST := \
CtsAccessibilityServiceTestCases \
CtsAccountManagerTestCases \
CtsAppTestCases \
CtsBluetoothTestCases \
CtsContentTestCases \
CtsDatabaseTestCases \
- CtsDelegatingAccessibilityService \
CtsDpiTestCases \
CtsDpiTestCases2 \
CtsExampleTestCases \
@@ -53,19 +51,25 @@
CtsSaxTestCases \
CtsSpeechTestCases \
CtsTelephonyTestCases \
+ CtsTestStubs \
CtsTextTestCases \
CtsUtilTestCases \
CtsViewTestCases \
CtsWebkitTestCases \
CtsWidgetTestCases \
CtsNetTestCases \
- SignatureTest \
CtsPerformanceTestCases \
CtsPerformance2TestCases \
CtsPerformance3TestCases \
CtsPerformance4TestCases \
- CtsPerformance5TestCases \
+ CtsPerformance5TestCases
+
+CTS_TEST_CASE_LIST := \
+ TestDeviceSetup \
+ CtsDelegatingAccessibilityService \
+ SignatureTest \
ApiDemos \
ApiDemosReferenceTest \
$(CTS_APPS_LIST) \
+ $(CTS_COVERAGE_TEST_CASE_LIST) \
$(CTS_SECURITY_APPS_LIST)
diff --git a/CtsTestCoverage.mk b/CtsTestCoverage.mk
new file mode 100644
index 0000000..44a7b1a
--- /dev/null
+++ b/CtsTestCoverage.mk
@@ -0,0 +1,53 @@
+#
+# Copyright (C) 2010 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.
+#
+
+# Makefile for producing CTS coverage reports.
+# Run "make cts-test-coverage" in the $ANDROID_BUILD_TOP directory.
+
+include cts/CtsTestCaseList.mk
+
+CTS_API_COVERAGE_DEPENDENCIES := cts-api-coverage dexdeps $(ACP)
+
+.PHONY: cts-test-coverage
+cts-test-coverage: $(CTS_COVERAGE_TEST_CASE_LIST) $(CTS_API_COVERAGE_DEPENDENCIES)
+ $(call generate-coverage-report,"CTS Tests API Coverage Report",\
+ $(CTS_COVERAGE_TEST_CASE_LIST),xml,$(HOST_OUT)/cts/test-coverage,api-coverage.xml)
+
+.PHONY: cts-verifier-coverage
+cts-verifier-coverage: CtsVerifier $(CTS_API_COVERAGE_DEPENDENCIES)
+ $(call generate-coverage-report,"CTS Verifier API Coverage Report",\
+ CtsVerifier,xml,$(HOST_OUT)/cts/verifier-coverage,api-coverage.xml)
+
+# Arguments;
+# 1 - Name of the report printed out on the screen
+# 2 - Name of APK packages that will be scanned to generate the report
+# 3 - Format of the report
+# 4 - Output directory to put the report
+# 5 - Output file name of the report
+define generate-coverage-report
+ $(hide) rm -rf $(4)
+ $(hide) mkdir -p $(4)
+ $(hide) $(ACP) cts/tools/cts-api-coverage/res/* $(4)
+
+ $(foreach testcase,$(2),$(eval $(call add-testcase-apk,$(testcase))))
+ $(hide) cts-api-coverage -f $(3) -o $(4)/$(5) $(TEST_APKS)
+
+ @echo $(1): file://$(ANDROID_BUILD_TOP)/$(4)/$(5)
+endef
+
+define add-testcase-apk
+ TEST_APKS += $(call intermediates-dir-for,APPS,$(1))/package.apk
+endef
diff --git a/development/ide/eclipse/.classpath b/development/ide/eclipse/.classpath
index 59c1521..f3b4137 100644
--- a/development/ide/eclipse/.classpath
+++ b/development/ide/eclipse/.classpath
@@ -46,6 +46,7 @@
<classpathentry kind="src" path="cts/tests/tests/webkit/src"/>
<classpathentry kind="src" path="cts/tests/tests/widget/src"/>
<classpathentry kind="src" path="cts/tools/annotation-helper/src"/>
+ <classpathentry kind="src" path="cts/tools/cts-api-coverage/src"/>
<classpathentry kind="src" path="cts/tools/cts-reference-app-lib/src"/>
<classpathentry kind="src" path="cts/tools/dasm/src"/>
<classpathentry kind="src" path="cts/tools/device-setup/TestDeviceSetup/src"/>
diff --git a/tests/tests/hardware/src/android/hardware/cts/SensorManagerTest.java b/tests/tests/hardware/src/android/hardware/cts/SensorManagerTest.java
deleted file mode 100644
index 136d077..0000000
--- a/tests/tests/hardware/src/android/hardware/cts/SensorManagerTest.java
+++ /dev/null
@@ -1,307 +0,0 @@
-/*
- * Copyright (C) 2008 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.hardware.cts;
-
-import dalvik.annotation.BrokenTest;
-import dalvik.annotation.TestLevel;
-import dalvik.annotation.TestTargetClass;
-import dalvik.annotation.TestTargetNew;
-import dalvik.annotation.TestTargets;
-import dalvik.annotation.ToBeFixed;
-
-import android.content.Context;
-import android.hardware.Sensor;
-import android.hardware.SensorEvent;
-import android.hardware.SensorEventListener;
-import android.hardware.SensorListener;
-import android.hardware.SensorManager;
-import android.test.AndroidTestCase;
-import android.util.Log;
-
-import java.util.List;
-
-@SuppressWarnings("deprecation")
-@TestTargetClass(SensorManager.class)
-public class SensorManagerTest extends AndroidTestCase {
-
- private static final String TAG = "SensorManagerTest";
- private static final float[] GRAVITY = { 0, 0, SensorManager.GRAVITY_EARTH };
- private static final double TWO_PI = 2 * Math.PI;
- private static final int MATRIX = 9;
- private static final int VECTOR = 3;
- private static final int MAX_COUNT = 10;
- private static final long TIME_OUT = 10000;
- private static Object mSync;
- private static int mCounter;
- private static boolean mHasNotified;
- private static float[] mR;
- private static float[] mI;
- private static float[] mGeomagnetic;
- private static SensorManager mSensorManager;
-
- private static int[] mSensorListenerOnChangedSensor;
- private static float[][] mSensorListenerOnChangedValus;
- private static SensorEvent[] mSensorEvents;
-
- @SuppressWarnings("serial")
- private static class SensorTestTimeOutException extends Exception {
- public SensorTestTimeOutException() {
- super("Time out while waiting for sensor events. "
- + "Please move the device to generate events.");
- }
- }
-
- private static void waitForSensorEvent() throws InterruptedException,
- SensorTestTimeOutException {
- mCounter = 0;
- mHasNotified = false;
- synchronized (mSync) {
- if (!mHasNotified) {
- mSync.wait(TIME_OUT);
- }
- if (!mHasNotified) {
- throw new SensorTestTimeOutException();
- }
- }
- }
-
- private static void addSyncCount() {
- synchronized (mSync) {
- if (++mCounter >= MAX_COUNT) {
- mCounter = MAX_COUNT -1;
- mHasNotified = true;
- mSync.notify();
- }
- }
- }
-
- @Override
- protected void setUp() throws Exception {
- super.setUp();
- mSensorManager = (SensorManager) getContext().getSystemService(Context.SENSOR_SERVICE);
- mR = new float[MATRIX];
- mI = new float[MATRIX];
- mGeomagnetic = new float[VECTOR];
- mSync = new Object();
- mSensorListenerOnChangedSensor = new int[MAX_COUNT];
- mSensorListenerOnChangedValus = new float[MAX_COUNT][];
- mSensorEvents = new SensorEvent[MAX_COUNT];
- }
-
- @TestTargets({
- @TestTargetNew(
- level = TestLevel.COMPLETE,
- method = "getSensors",
- args = {}
- ),
- @TestTargetNew(
- level = TestLevel.COMPLETE,
- method = "registerListener",
- args = {SensorListener.class, int.class}
- ),
- @TestTargetNew(
- level = TestLevel.COMPLETE,
- method = "registerListener",
- args = {SensorListener.class, int.class, int.class}
- ),
- @TestTargetNew(
- level = TestLevel.COMPLETE,
- method = "unregisterListener",
- args = {SensorListener.class}
- ),
- @TestTargetNew(
- level = TestLevel.COMPLETE,
- method = "unregisterListener",
- args = {SensorListener.class, int.class}
- )
- })
- @ToBeFixed(bug="1852042", explanation="Error values in onSensorChanged() from system")
- @BrokenTest("Relies on earthquakes or user to generate sensor events")
- public void testSensorManagerOldAPIs() throws InterruptedException, SensorTestTimeOutException {
- assertEquals(SensorManager.SENSOR_ACCELEROMETER
- | SensorManager.SENSOR_MAGNETIC_FIELD
- | SensorManager.SENSOR_ORIENTATION
- | SensorManager.SENSOR_ORIENTATION_RAW,
- mSensorManager.getSensors());
-
- final SensorListener sensorListener = new SensorListener() {
-
- public void onAccuracyChanged(int sensor, int accuracy) {
- }
-
- public void onSensorChanged(int sensor, float[] values) {
- mSensorListenerOnChangedSensor[mCounter] = sensor;
- mSensorListenerOnChangedValus[mCounter] = values;
- if(sensor == SensorManager.SENSOR_ACCELEROMETER) {
- Log.d(TAG, "Deprecated Accelerometer value X="
- + values[0] + " Y=" + values[1] + " Z=" + values[2]);
- } else {
- Log.d(TAG, "Deprecated Orientation value X="
- + values[0] + " Y=" + values[1] + " Z=" + values[2]);
- }
- addSyncCount();
- }
- };
- mSensorManager.registerListener(sensorListener,
- SensorManager.SENSOR_ACCELEROMETER
- | SensorManager.SENSOR_ORIENTATION
- | SensorManager.SENSOR_ORIENTATION_RAW,
- SensorManager.SENSOR_DELAY_NORMAL);
- waitForSensorEvent();
-
- mSensorManager.unregisterListener(sensorListener, SensorManager.SENSOR_ACCELEROMETER);
- // Following should unregister sensorListener for all sensor type but it didn't.
- // So we unregisterListener by the other unregister method.
- // mSensorManager.unregisterListener(sensorListener);
- mSensorManager.unregisterListener(sensorListener, SensorManager.SENSOR_ORIENTATION |
- SensorManager.SENSOR_ORIENTATION_RAW);
- try {
- waitForSensorEvent();
- fail("SensorListener have been unregistered, shouldn't get any more sensor events.");
- } catch (SensorTestTimeOutException e) {
- // expected
- }
- }
-
- @TestTargets({
- @TestTargetNew(
- level = TestLevel.COMPLETE,
- method = "getDefaultSensor",
- args = {int.class}
- ),
- @TestTargetNew(
- level = TestLevel.COMPLETE,
- method = "getInclination",
- args = {float[].class}
- ),
- @TestTargetNew(
- level = TestLevel.COMPLETE,
- method = "getOrientation",
- args = {float[].class, float[].class}
- ),
- @TestTargetNew(
- level = TestLevel.COMPLETE,
- method = "getRotationMatrix",
- args = {float[].class, float[].class, float[].class, float[].class}
- ),
- @TestTargetNew(
- level = TestLevel.COMPLETE,
- method = "getSensorList",
- args = {int.class}
- ),
- @TestTargetNew(
- level = TestLevel.COMPLETE,
- method = "registerListener",
- args = {SensorEventListener.class, Sensor.class, int.class}
- ),
- @TestTargetNew(
- level = TestLevel.COMPLETE,
- method = "registerListener",
- args = {SensorEventListener.class, Sensor.class, int.class, android.os.Handler.class}
- ),
- @TestTargetNew(
- level = TestLevel.COMPLETE,
- method = "remapCoordinateSystem",
- args = {float[].class, int.class, int.class, float[].class}
- ),
- @TestTargetNew(
- level = TestLevel.COMPLETE,
- method = "unregisterListener",
- args = {SensorEventListener.class}
- ),
- @TestTargetNew(
- level = TestLevel.COMPLETE,
- method = "unregisterListener",
- args = {SensorEventListener.class, Sensor.class}
- )
- })
- @ToBeFixed(bug="1852042", explanation="Error values in onSensorChanged() from system")
- @BrokenTest("Relies on earthquakes or user to generate sensor events")
- public void testSensorManager() throws InterruptedException, SensorTestTimeOutException {
- final List<Sensor> sensorList = mSensorManager.getSensorList(Sensor.TYPE_ALL);
- assertNotNull(sensorList);
- Sensor sensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
- assertEquals(Sensor.TYPE_ACCELEROMETER, sensor.getType());
- final SensorEventListener sensorAccListener = new SensorEventListener() {
- public void onAccuracyChanged(Sensor sensor, int accuracy) {
- }
-
- public void onSensorChanged(SensorEvent event) {
- assertEquals(Sensor.TYPE_ACCELEROMETER, event.sensor.getType());
- Log.d(TAG, "Accelerometer value X="
- + event.values[0] + " Y=" + event.values[1] + " Z=" + event.values[2]);
- addSyncCount();
- }
- };
-
- mSensorManager.registerListener(sensorAccListener,
- sensor, SensorManager.SENSOR_DELAY_NORMAL);
- waitForSensorEvent();
-
- mSensorManager.unregisterListener(sensorAccListener);
- try {
- waitForSensorEvent();
- fail("SensorListener have been unregistered, shouldn't get any more sensor events.");
- } catch (SensorTestTimeOutException e) {
- // expected
- }
-
- sensor = mSensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD);
- assertEquals(Sensor.TYPE_MAGNETIC_FIELD, sensor.getType());
- final SensorEventListener sensorMagListener = new SensorEventListener() {
- public void onAccuracyChanged(Sensor sensor, int accuracy) {
- }
-
- public void onSensorChanged(SensorEvent event) {
- mSensorEvents[mCounter] = event;
- Log.d(TAG, "Magnetic field value X="
- + event.values[0] + " Y=" + event.values[1] + " Z=" + event.values[2]);
- addSyncCount();
- }
- };
-
- mSensorManager.registerListener(sensorMagListener, sensor,
- SensorManager.SENSOR_DELAY_FASTEST, null);
- waitForSensorEvent();
- mSensorManager.unregisterListener(sensorMagListener, sensor);
-
- assertSensorEvents();
-
- assertTrue(SensorManager.remapCoordinateSystem(mR, SensorManager.AXIS_Y,
- SensorManager.AXIS_MINUS_X, mR));
- final float[] outR = new float[VECTOR];
- assertFalse(SensorManager.remapCoordinateSystem(mR, SensorManager.AXIS_Y,
- SensorManager.AXIS_MINUS_X, outR));
- }
-
- private static void assertSensorEvents() {
- for (int i = 0; i < MAX_COUNT; i++) {
- assertEquals(Sensor.TYPE_MAGNETIC_FIELD, mSensorEvents[i].sensor.getType());
- mGeomagnetic[0] = mSensorEvents[i].values[0];
- mGeomagnetic[1] = mSensorEvents[i].values[1];
- mGeomagnetic[2] = mSensorEvents[i].values[2];
-
- assertTrue(SensorManager.getRotationMatrix(mR, mI, GRAVITY, mGeomagnetic));
- float inclination = SensorManager.getInclination(mI);
- assertTrue(Math.abs(inclination) <= TWO_PI);
- float[] values = new float[3];
- float[] orientation = SensorManager.getOrientation(mR, values);
- assertTrue(Math.abs(orientation[0]) <= TWO_PI);
- assertTrue(Math.abs(orientation[1]) <= TWO_PI);
- assertTrue(Math.abs(orientation[2]) <= TWO_PI);
- }
- }
-}
diff --git a/tools/cts-api-coverage/Android.mk b/tools/cts-api-coverage/Android.mk
new file mode 100644
index 0000000..2382d61
--- /dev/null
+++ b/tools/cts-api-coverage/Android.mk
@@ -0,0 +1,43 @@
+# Copyright (C) 2010 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.
+
+LOCAL_PATH := $(call my-dir)
+
+# We use copy-file-to-new-target so that the installed
+# script file's timestamp is at least as new as the
+# .jar file it wraps.
+
+# the hat script
+# ============================================================
+include $(CLEAR_VARS)
+LOCAL_IS_HOST_MODULE := true
+LOCAL_MODULE_CLASS := EXECUTABLES
+LOCAL_MODULE := cts-api-coverage
+LOCAL_MODULE_TAGS := optional
+
+include $(BUILD_SYSTEM)/base_rules.mk
+
+$(LOCAL_BUILT_MODULE): $(HOST_OUT_JAVA_LIBRARIES)/$(LOCAL_MODULE)$(COMMON_JAVA_PACKAGE_SUFFIX)
+$(LOCAL_BUILT_MODULE): $(LOCAL_PATH)/etc/$(LOCAL_MODULE) | $(ACP)
+ @echo "Copy: $(PRIVATE_MODULE) ($@)"
+ $(copy-file-to-new-target)
+ $(hide) chmod 755 $@
+
+# the other stuff
+# ============================================================
+subdirs := $(addprefix $(LOCAL_PATH)/,$(addsuffix /Android.mk, \
+ src \
+ ))
+
+include $(subdirs)
diff --git a/tools/cts-api-coverage/etc/cts-api-coverage b/tools/cts-api-coverage/etc/cts-api-coverage
new file mode 100644
index 0000000..fe7278a
--- /dev/null
+++ b/tools/cts-api-coverage/etc/cts-api-coverage
@@ -0,0 +1,46 @@
+#!/bin/bash
+#
+# Copyright (C) 2010, 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.
+
+# Set up prog to be the path of this script, including following symlinks,
+# and set up progdir to be the fully-qualified pathname of its directory.
+prog="$0"
+while [ -h "${prog}" ]; do
+ newProg=`/bin/ls -ld "${prog}"`
+ newProg=`expr "${newProg}" : ".* -> \(.*\)$"`
+ if expr "x${newProg}" : 'x/' >/dev/null; then
+ prog="${newProg}"
+ else
+ progdir=`dirname "${prog}"`
+ prog="${progdir}/${newProg}"
+ fi
+done
+oldwd=`pwd`
+progdir=`dirname "${prog}"`
+cd "${progdir}"
+progdir=`pwd`
+prog="${progdir}"/`basename "${prog}"`
+cd "${oldwd}"
+
+libdir=`dirname $progdir`/framework
+
+javaOpts=""
+while expr "x$1" : 'x-J' >/dev/null; do
+ opt=`expr "$1" : '-J\(.*\)'`
+ javaOpts="${javaOpts} -${opt}"
+ shift
+done
+
+exec java $javaOpts -jar $libdir/cts-api-coverage.jar "$@"
diff --git a/tools/cts-api-coverage/res/api-coverage.css b/tools/cts-api-coverage/res/api-coverage.css
new file mode 100644
index 0000000..b956e16
--- /dev/null
+++ b/tools/cts-api-coverage/res/api-coverage.css
@@ -0,0 +1,54 @@
+/* Copyright (C) 2010 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.
+*/
+
+body {
+ background-color: #CCCCCC;
+ font-family: sans-serif;
+ margin: 10px;
+}
+
+.info {
+ margin-bottom: 10px;
+}
+
+.apks, .package, .class {
+ cursor: pointer;
+ text-decoration: underline;
+}
+
+.packageDetails {
+ padding-left: 20px;
+}
+
+.classDetails {
+ padding-left: 40px;
+}
+
+.method {
+ font-family: courier;
+ white-space: nowrap;
+}
+
+.red {
+ background-color: #FF0000;
+}
+
+.yellow {
+ background-color: #FFFF00;
+}
+
+.green {
+ background-color: #00FF00;
+}
diff --git a/tools/cts-api-coverage/res/api-coverage.xsl b/tools/cts-api-coverage/res/api-coverage.xsl
new file mode 100644
index 0000000..5d45014
--- /dev/null
+++ b/tools/cts-api-coverage/res/api-coverage.xsl
@@ -0,0 +1,120 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2010 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.
+ -->
+
+<!DOCTYPE xsl:stylesheet [ <!ENTITY nbsp " "> ]>
+<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
+ <xsl:output method="html" version="1.0" encoding="UTF-8" indent="yes" />
+ <xsl:template match="/">
+ <html>
+ <head>
+ <script type="text/javascript">
+ function toggleVisibility(id) {
+ element = document.getElementById(id);
+ if (element.style.display == "none") {
+ element.style.display = "";
+ } else {
+ element.style.display = "none";
+ }
+ }
+ </script>
+ <style type="text/css">
+ @import "api-coverage.css";
+ </style>
+ </head>
+ <body>
+ <h1>CTS API Coverage</h1>
+ <div class="info">
+ Generated: <xsl:value-of select="api-coverage/@generatedTime" />
+ </div>
+ <div class="apks" onclick="toggleVisibility('sourceApks')">
+ Source APKs (<xsl:value-of select="count(api-coverage/debug/sources/apk)" />)
+ </div>
+ <div id="sourceApks" style="display: none">
+ <ul>
+ <xsl:for-each select="api-coverage/debug/sources/apk">
+ <li><xsl:value-of select="@path" /></li>
+ </xsl:for-each>
+ </ul>
+ </div>
+ <ul>
+ <xsl:for-each select="api-coverage/api/package">
+ <xsl:call-template name="packageOrClassListItem">
+ <xsl:with-param name="bulletClass" select="'package'" />
+ </xsl:call-template>
+ <div class="packageDetails" id="{@name}" style="display: none">
+ <ul>
+ <xsl:for-each select="class">
+ <xsl:call-template name="packageOrClassListItem">
+ <xsl:with-param name="bulletClass" select="'class'" />
+ </xsl:call-template>
+ <div class="classDetails" id="{@name}" style="display: none">
+ <xsl:for-each select="constructor">
+ <xsl:call-template name="methodListItem" />
+ </xsl:for-each>
+ <xsl:for-each select="method">
+ <xsl:call-template name="methodListItem" />
+ </xsl:for-each>
+ </div>
+ </xsl:for-each>
+ </ul>
+ </div>
+ </xsl:for-each>
+ </ul>
+ </body>
+ </html>
+ </xsl:template>
+
+ <xsl:template name="packageOrClassListItem">
+ <xsl:param name="bulletClass" />
+
+ <xsl:variable name="colorClass">
+ <xsl:choose>
+ <xsl:when test="@coveragePercentage <= 50">red</xsl:when>
+ <xsl:when test="@coveragePercentage <= 80">yellow</xsl:when>
+ <xsl:otherwise>green</xsl:otherwise>
+ </xsl:choose>
+ </xsl:variable>
+
+ <li class="{$bulletClass}" onclick="toggleVisibility('{@name}')">
+ <span class="{$colorClass}">
+ <b><xsl:value-of select="@name" /></b>
+ <xsl:value-of select="@coveragePercentage" />%
+ (<xsl:value-of select="@numCovered" />/<xsl:value-of select="@numTotal" />)
+ </span>
+ </li>
+ </xsl:template>
+
+ <xsl:template name="methodListItem">
+ <span class="method">
+ <xsl:choose>
+ <xsl:when test="@covered = 'true'">[X]</xsl:when>
+ <xsl:otherwise>[ ]</xsl:otherwise>
+ </xsl:choose>
+ <xsl:if test="@returnType != ''"> <xsl:value-of select="@returnType" /></xsl:if>
+ <b> <xsl:value-of select="@name" /></b><xsl:call-template name="formatParameters" />
+ </span>
+ <br />
+ </xsl:template>
+
+ <xsl:template name="formatParameters">(<xsl:for-each select="parameter">
+ <xsl:value-of select="@type" />
+ <xsl:if test="not(position() = last())">, </xsl:if>
+ </xsl:for-each>)
+ </xsl:template>
+
+</xsl:stylesheet>
+
diff --git a/tools/cts-api-coverage/src/Android.mk b/tools/cts-api-coverage/src/Android.mk
new file mode 100644
index 0000000..67ce8b6
--- /dev/null
+++ b/tools/cts-api-coverage/src/Android.mk
@@ -0,0 +1,28 @@
+# Copyright (C) 2010 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.
+
+LOCAL_PATH := $(call my-dir)
+
+
+# cts-api-coverage java library
+# ============================================================
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := $(call all-subdir-java-files)
+LOCAL_JAR_MANIFEST := MANIFEST.mf
+
+LOCAL_MODULE := cts-api-coverage
+LOCAL_MODULE_TAGS := optional
+
+include $(BUILD_HOST_JAVA_LIBRARY)
diff --git a/tools/cts-api-coverage/src/MANIFEST.mf b/tools/cts-api-coverage/src/MANIFEST.mf
new file mode 100644
index 0000000..b6aa831
--- /dev/null
+++ b/tools/cts-api-coverage/src/MANIFEST.mf
@@ -0,0 +1,2 @@
+Manifest-Version: 1.0
+Main-Class: com.android.cts.apicoverage.CtsApiCoverage
diff --git a/tools/cts-api-coverage/src/com/android/cts/apicoverage/ApiClass.java b/tools/cts-api-coverage/src/com/android/cts/apicoverage/ApiClass.java
new file mode 100644
index 0000000..de2e3eb
--- /dev/null
+++ b/tools/cts-api-coverage/src/com/android/cts/apicoverage/ApiClass.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2010 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 com.android.cts.apicoverage;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+
+/** Representation of a class in the API with constructors and methods. */
+class ApiClass implements Comparable<ApiClass>, HasCoverage {
+
+ private final String mName;
+
+ private final List<ApiConstructor> mApiConstructors = new ArrayList<ApiConstructor>();
+
+ private final List<ApiMethod> mApiMethods = new ArrayList<ApiMethod>();
+
+ ApiClass(String name) {
+ this.mName = name;
+ }
+
+ public int compareTo(ApiClass another) {
+ return mName.compareTo(another.mName);
+ }
+
+ public String getName() {
+ return mName;
+ }
+
+ public void addConstructor(ApiConstructor constructor) {
+ mApiConstructors.add(constructor);
+ }
+
+ public ApiConstructor getConstructor(List<String> parameterTypes) {
+ for (ApiConstructor constructor : mApiConstructors) {
+ if (parameterTypes.equals(constructor.getParameterTypes())) {
+ return constructor;
+ }
+ }
+ return null;
+ }
+
+ public Collection<ApiConstructor> getConstructors() {
+ return Collections.unmodifiableList(mApiConstructors);
+ }
+
+ public void addMethod(ApiMethod method) {
+ mApiMethods.add(method);
+ }
+
+ public ApiMethod getMethod(String name, List<String> parameterTypes, String returnType) {
+ for (ApiMethod method : mApiMethods) {
+ if (name.equals(method.getName())
+ && parameterTypes.equals(method.getParameterTypes())
+ && returnType.equals(method.getReturnType())) {
+ return method;
+ }
+ }
+ return null;
+ }
+
+ public Collection<ApiMethod> getMethods() {
+ return Collections.unmodifiableList(mApiMethods);
+ }
+
+ public int getNumCoveredMethods() {
+ int numCovered = 0;
+ for (ApiConstructor constructor : mApiConstructors) {
+ if (constructor.isCovered()) {
+ numCovered++;
+ }
+ }
+ for (ApiMethod method : mApiMethods) {
+ if (method.isCovered()) {
+ numCovered++;
+ }
+ }
+ return numCovered;
+ }
+
+ public int getTotalMethods() {
+ return mApiConstructors.size() + mApiMethods.size();
+ }
+
+ public float getCoveragePercentage() {
+ return (float) getNumCoveredMethods() / getTotalMethods() * 100;
+ }
+}
\ No newline at end of file
diff --git a/tools/cts-api-coverage/src/com/android/cts/apicoverage/ApiConstructor.java b/tools/cts-api-coverage/src/com/android/cts/apicoverage/ApiConstructor.java
new file mode 100644
index 0000000..b38bd34
--- /dev/null
+++ b/tools/cts-api-coverage/src/com/android/cts/apicoverage/ApiConstructor.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2010 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 com.android.cts.apicoverage;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/** Representation of a constructor in the API with parameters (arguments). */
+class ApiConstructor implements Comparable<ApiConstructor> {
+
+ private final String mName;
+
+ private final List<String> mParameterTypes;
+
+ private boolean mIsCovered;
+
+ ApiConstructor(String name, List<String> parameterTypes) {
+ this.mName = name;
+ this.mParameterTypes = new ArrayList<String>(parameterTypes);
+ }
+
+ public int compareTo(ApiConstructor another) {
+ return mParameterTypes.size() - another.mParameterTypes.size();
+ }
+
+ public String getName() {
+ return mName;
+ }
+
+ public List<String> getParameterTypes() {
+ return Collections.unmodifiableList(mParameterTypes);
+ }
+
+ public boolean isCovered() {
+ return mIsCovered;
+ }
+
+ public void setCovered(boolean covered) {
+ mIsCovered = covered;
+ }
+}
\ No newline at end of file
diff --git a/tools/cts-api-coverage/src/com/android/cts/apicoverage/ApiCoverage.java b/tools/cts-api-coverage/src/com/android/cts/apicoverage/ApiCoverage.java
new file mode 100644
index 0000000..dc40062
--- /dev/null
+++ b/tools/cts-api-coverage/src/com/android/cts/apicoverage/ApiCoverage.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2010 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 com.android.cts.apicoverage;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+/** Representation of the entire API containing packages. */
+class ApiCoverage {
+
+ private final Map<String, ApiPackage> mPackages = new HashMap<String, ApiPackage>();
+
+ public void addPackage(ApiPackage pkg) {
+ mPackages.put(pkg.getName(), pkg);
+ }
+
+ public ApiPackage getPackage(String name) {
+ return mPackages.get(name);
+ }
+
+ public Collection<ApiPackage> getPackages() {
+ return Collections.unmodifiableCollection(mPackages.values());
+ }
+}
diff --git a/tools/cts-api-coverage/src/com/android/cts/apicoverage/ApiMethod.java b/tools/cts-api-coverage/src/com/android/cts/apicoverage/ApiMethod.java
new file mode 100644
index 0000000..eb67b8e
--- /dev/null
+++ b/tools/cts-api-coverage/src/com/android/cts/apicoverage/ApiMethod.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2010 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 com.android.cts.apicoverage;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/** Representation of a method in the API with parameters (arguments) and a return value. */
+class ApiMethod implements Comparable<ApiMethod> {
+
+ private final String mName;
+
+ private final List<String> mParameterTypes;
+
+ private final String mReturnType;
+
+ private boolean mIsCovered;
+
+ ApiMethod(String name, List<String> parameterTypes, String returnType) {
+ this.mName = name;
+ this.mParameterTypes = new ArrayList<String>(parameterTypes);
+ this.mReturnType = returnType;
+ }
+
+ public int compareTo(ApiMethod another) {
+ return mName.compareTo(another.mName);
+ }
+
+ public String getName() {
+ return mName;
+ }
+
+ public List<String> getParameterTypes() {
+ return Collections.unmodifiableList(mParameterTypes);
+ }
+
+ public String getReturnType() {
+ return mReturnType;
+ }
+
+ public boolean isCovered() {
+ return mIsCovered;
+ }
+
+ public void setCovered(boolean covered) {
+ mIsCovered = covered;
+ }
+}
\ No newline at end of file
diff --git a/tools/cts-api-coverage/src/com/android/cts/apicoverage/ApiPackage.java b/tools/cts-api-coverage/src/com/android/cts/apicoverage/ApiPackage.java
new file mode 100644
index 0000000..f0ca889
--- /dev/null
+++ b/tools/cts-api-coverage/src/com/android/cts/apicoverage/ApiPackage.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2010 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 com.android.cts.apicoverage;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+/** Representation of a package in the API containing classes. */
+class ApiPackage implements HasCoverage {
+
+ private final String mName;
+
+ private final Map<String, ApiClass> mApiClassMap = new HashMap<String, ApiClass>();
+
+ ApiPackage(String name) {
+ this.mName = name;
+ }
+
+ public String getName() {
+ return mName;
+ }
+
+ public void addClass(ApiClass apiClass) {
+ mApiClassMap.put(apiClass.getName(), apiClass);
+ }
+
+ public ApiClass getClass(String name) {
+ return mApiClassMap.get(name);
+ }
+
+ public Collection<ApiClass> getClasses() {
+ return Collections.unmodifiableCollection(mApiClassMap.values());
+ }
+
+ public int getNumCoveredMethods() {
+ int covered = 0;
+ for (ApiClass apiClass : mApiClassMap.values()) {
+ covered += apiClass.getNumCoveredMethods();
+ }
+ return covered;
+ }
+
+ public int getTotalMethods() {
+ int total = 0;
+ for (ApiClass apiClass : mApiClassMap.values()) {
+ total += apiClass.getTotalMethods();
+ }
+ return total;
+ }
+
+ public float getCoveragePercentage() {
+ return (float) getNumCoveredMethods() / getTotalMethods() * 100;
+ }
+}
\ No newline at end of file
diff --git a/tools/cts-api-coverage/src/com/android/cts/apicoverage/CtsApiCoverage.java b/tools/cts-api-coverage/src/com/android/cts/apicoverage/CtsApiCoverage.java
new file mode 100644
index 0000000..112ba39
--- /dev/null
+++ b/tools/cts-api-coverage/src/com/android/cts/apicoverage/CtsApiCoverage.java
@@ -0,0 +1,176 @@
+/*
+ * Copyright (C) 2010 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 com.android.cts.apicoverage;
+
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+import org.xml.sax.XMLReader;
+import org.xml.sax.helpers.XMLReaderFactory;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Tool that generates a report of what Android framework methods are being called from a given
+ * set of APKS. See the {@link #printUsage()} method for more details.
+ */
+public class CtsApiCoverage {
+
+ private static final int FORMAT_TXT = 0;
+
+ private static final int FORMAT_XML = 1;
+
+ private static void printUsage() {
+ System.out.println("Usage: cts-api-coverage [OPTION]... [APK]...");
+ System.out.println();
+ System.out.println("Generates a report about what Android framework methods are called ");
+ System.out.println("from the given APKs.");
+ System.out.println();
+ System.out.println("Use the Makefiles rules in CtsTestCoverage.mk to generate the report ");
+ System.out.println("rather than executing this directly. If you still want to run this ");
+ System.out.println("directly, then this must be used from the $ANDROID_BUILD_TOP ");
+ System.out.println("directory and dexdeps must be built via \"make dexdeps\".");
+ System.out.println();
+ System.out.println("Options:");
+ System.out.println(" -o FILE output file or standard out if not given");
+ System.out.println(" -f [txt|xml] format of output either text or xml");
+ System.out.println();
+ System.exit(1);
+ }
+
+ public static void main(String[] args) throws Exception {
+ List<File> testApks = new ArrayList<File>();
+ File outputFile = null;
+ int format = FORMAT_TXT;
+
+ for (int i = 0; i < args.length; i++) {
+ if (args[i].startsWith("-")) {
+ if ("-o".equals(args[i])) {
+ if (i + 1 < args.length) {
+ outputFile = new File(args[++i]);;
+ } else {
+ printUsage();
+ }
+ } else if ("-f".equals(args[i])) {
+ if (i + 1 < args.length) {
+ String formatArg = args[++i];
+ if ("xml".equalsIgnoreCase(formatArg)) {
+ format = FORMAT_XML;
+ } else if ("txt".equalsIgnoreCase(formatArg)) {
+ format = FORMAT_TXT;
+ } else {
+ printUsage();
+ }
+ } else {
+ printUsage();
+ }
+ } else {
+ printUsage();
+ }
+ } else {
+ testApks.add(new File(args[i]));
+ }
+ }
+
+ /*
+ * 1. Create an ApiCoverage object that is a tree of Java objects representing the API
+ * in current.xml. The object will have no information about the coverage for each
+ * constructor or method yet.
+ *
+ * 2. For each provided APK, scan it using dexdeps, parse the output of dexdeps, and
+ * call methods on the ApiCoverage object to cumulatively add coverage stats.
+ *
+ * 3. Output a report based on the coverage stats in the ApiCoverage object.
+ */
+
+ ApiCoverage apiCoverage = getEmptyApiCoverage();
+ for (File testApk : testApks) {
+ addApiCoverage(apiCoverage, testApk);
+ }
+ outputCoverageReport(apiCoverage, testApks, outputFile, format);
+ }
+
+ /**
+ * Creates an object representing the API that will be used later to collect coverage
+ * statistics as we iterate over the test APKs.
+ *
+ * @return an {@link ApiCoverage} object representing the API in current.xml without any
+ * coverage statistics yet
+ */
+ private static ApiCoverage getEmptyApiCoverage()
+ throws SAXException, IOException {
+ XMLReader xmlReader = XMLReaderFactory.createXMLReader();
+ CurrentXmlHandler currentXmlHandler = new CurrentXmlHandler();
+ xmlReader.setContentHandler(currentXmlHandler);
+
+ File currentXml = new File("frameworks/base/api/current.xml");
+ FileReader fileReader = null;
+ try {
+ fileReader = new FileReader(currentXml);
+ xmlReader.parse(new InputSource(fileReader));
+ } finally {
+ if (fileReader != null) {
+ fileReader.close();
+ }
+ }
+
+ return currentXmlHandler.getApi();
+ }
+
+ /**
+ * Adds coverage information gleamed from running dexdeps on the APK to the
+ * {@link ApiCoverage} object.
+ *
+ * @param apiCoverage object to which the coverage statistics will be added to
+ * @param testApk containing the tests that will be scanned by dexdeps
+ */
+ private static void addApiCoverage(ApiCoverage apiCoverage, File testApk)
+ throws SAXException, IOException {
+ XMLReader xmlReader = XMLReaderFactory.createXMLReader();
+ DexDepsXmlHandler dexDepsXmlHandler = new DexDepsXmlHandler(apiCoverage);
+ xmlReader.setContentHandler(dexDepsXmlHandler);
+
+ Process process = new ProcessBuilder("dexdeps", "--format=xml", testApk.getPath()).start();
+ xmlReader.parse(new InputSource(process.getInputStream()));
+ }
+
+ private static void outputCoverageReport(ApiCoverage apiCoverage, List<File> testApks,
+ File outputFile, int format) throws IOException {
+ OutputStream out = outputFile != null
+ ? new FileOutputStream(outputFile)
+ : System.out;
+
+ try {
+ switch (format) {
+ case FORMAT_TXT:
+ TextReport.printTextReport(apiCoverage, out);
+ break;
+
+ case FORMAT_XML:
+ XmlReport.printXmlReport(testApks, apiCoverage, out);
+ break;
+ }
+ } finally {
+ out.close();
+ }
+ }
+}
diff --git a/tools/cts-api-coverage/src/com/android/cts/apicoverage/CurrentXmlHandler.java b/tools/cts-api-coverage/src/com/android/cts/apicoverage/CurrentXmlHandler.java
new file mode 100644
index 0000000..41ee0dc
--- /dev/null
+++ b/tools/cts-api-coverage/src/com/android/cts/apicoverage/CurrentXmlHandler.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2010 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 com.android.cts.apicoverage;
+
+import org.xml.sax.Attributes;
+import org.xml.sax.SAXException;
+import org.xml.sax.helpers.DefaultHandler;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * {@link DefaultHandler} that builds an empty {@link ApiCoverage} object from scanning current.xml.
+ */
+class CurrentXmlHandler extends DefaultHandler {
+
+ private String mCurrentPackageName;
+
+ private String mCurrentClassName;
+
+ private String mCurrentMethodName;
+
+ private String mCurrentMethodReturnType;
+
+ private List<String> mCurrentParameterTypes = new ArrayList<String>();
+
+ private ApiCoverage mApiCoverage = new ApiCoverage();
+
+ public ApiCoverage getApi() {
+ return mApiCoverage;
+ }
+
+ @Override
+ public void startElement(String uri, String localName, String name, Attributes attributes)
+ throws SAXException {
+ super.startElement(uri, localName, name, attributes);
+ if ("package".equalsIgnoreCase(localName)) {
+ mCurrentPackageName = CurrentXmlHandler.getValue(attributes, "name");
+
+ ApiPackage apiPackage = new ApiPackage(mCurrentPackageName);
+ mApiCoverage.addPackage(apiPackage);
+
+ } else if ("class".equalsIgnoreCase(localName)
+ || "interface".equalsIgnoreCase(localName)) {
+ mCurrentClassName = CurrentXmlHandler.getValue(attributes, "name");
+
+ ApiClass apiClass = new ApiClass(mCurrentClassName);
+ ApiPackage apiPackage = mApiCoverage.getPackage(mCurrentPackageName);
+ apiPackage.addClass(apiClass);
+
+ } else if ("constructor".equalsIgnoreCase(localName)) {
+ mCurrentParameterTypes.clear();
+ } else if ("method".equalsIgnoreCase(localName)) {
+ mCurrentMethodName = CurrentXmlHandler.getValue(attributes, "name");
+ mCurrentMethodReturnType = CurrentXmlHandler.getValue(attributes, "return");
+ mCurrentParameterTypes.clear();
+ } else if ("parameter".equalsIgnoreCase(localName)) {
+ mCurrentParameterTypes.add(CurrentXmlHandler.getValue(attributes, "type"));
+ }
+ }
+
+ @Override
+ public void endElement(String uri, String localName, String name) throws SAXException {
+ super.endElement(uri, localName, name);
+ if ("constructor".equalsIgnoreCase(localName)) {
+ ApiConstructor apiConstructor = new ApiConstructor(mCurrentClassName,
+ mCurrentParameterTypes);
+ ApiPackage apiPackage = mApiCoverage.getPackage(mCurrentPackageName);
+ ApiClass apiClass = apiPackage.getClass(mCurrentClassName);
+ apiClass.addConstructor(apiConstructor);
+ } else if ("method".equalsIgnoreCase(localName)) {
+ ApiMethod apiMethod = new ApiMethod(mCurrentMethodName, mCurrentParameterTypes,
+ mCurrentMethodReturnType);
+ ApiPackage apiPackage = mApiCoverage.getPackage(mCurrentPackageName);
+ ApiClass apiClass = apiPackage.getClass(mCurrentClassName);
+ apiClass.addMethod(apiMethod);
+ }
+ }
+
+ static String getValue(Attributes attributes, String key) {
+ // Strip away generics <...> and make inner classes always use a "." rather than "$".
+ return attributes.getValue(key)
+ .replaceAll("<.+>", "")
+ .replace("$", ".");
+ }
+}
\ No newline at end of file
diff --git a/tools/cts-api-coverage/src/com/android/cts/apicoverage/DexDepsXmlHandler.java b/tools/cts-api-coverage/src/com/android/cts/apicoverage/DexDepsXmlHandler.java
new file mode 100644
index 0000000..0a90bdd
--- /dev/null
+++ b/tools/cts-api-coverage/src/com/android/cts/apicoverage/DexDepsXmlHandler.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2010 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 com.android.cts.apicoverage;
+
+import org.xml.sax.Attributes;
+import org.xml.sax.SAXException;
+import org.xml.sax.helpers.DefaultHandler;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * {@link DefaultHander} that parses the output of dexdeps and adds the coverage information to
+ * an {@link ApiCoverage} object.
+ */
+class DexDepsXmlHandler extends DefaultHandler {
+
+ private final ApiCoverage mPackageMap;
+
+ private String mCurrentPackageName;
+
+ private String mCurrentClassName;
+
+ private String mCurrentMethodName;
+
+ private String mCurrentMethodReturnType;
+
+ private List<String> mCurrentParameterTypes = new ArrayList<String>();
+
+ DexDepsXmlHandler(ApiCoverage packageMap) {
+ this.mPackageMap = packageMap;
+ }
+
+ @Override
+ public void startElement(String uri, String localName, String name, Attributes attributes)
+ throws SAXException {
+ super.startElement(uri, localName, name, attributes);
+ if ("package".equalsIgnoreCase(localName)) {
+ mCurrentPackageName = CurrentXmlHandler.getValue(attributes, "name");
+ } else if ("class".equalsIgnoreCase(localName)
+ || "interface".equalsIgnoreCase(localName)) {
+ mCurrentClassName = CurrentXmlHandler.getValue(attributes, "name");
+ } else if ("constructor".equalsIgnoreCase(localName)) {
+ mCurrentParameterTypes.clear();
+ } else if ("method".equalsIgnoreCase(localName)) {
+ mCurrentMethodName = CurrentXmlHandler.getValue(attributes, "name");
+ mCurrentMethodReturnType = CurrentXmlHandler.getValue(attributes, "return");
+ mCurrentParameterTypes.clear();
+ } else if ("parameter".equalsIgnoreCase(localName)) {
+ mCurrentParameterTypes.add(CurrentXmlHandler.getValue(attributes, "type"));
+ }
+ }
+
+ @Override
+ public void endElement(String uri, String localName, String name) throws SAXException {
+ super.endElement(uri, localName, name);
+ if ("constructor".equalsIgnoreCase(localName)) {
+ ApiPackage apiPackage = mPackageMap.getPackage(mCurrentPackageName);
+ if (apiPackage != null) {
+ ApiClass apiClass = apiPackage.getClass(mCurrentClassName);
+ if (apiClass != null) {
+ ApiConstructor apiConstructor = apiClass.getConstructor(mCurrentParameterTypes);
+ if (apiConstructor != null) {
+ apiConstructor.setCovered(true);
+ }
+ }
+ }
+ } else if ("method".equalsIgnoreCase(localName)) {
+ ApiPackage apiPackage = mPackageMap.getPackage(mCurrentPackageName);
+ if (apiPackage != null) {
+ ApiClass apiClass = apiPackage.getClass(mCurrentClassName);
+ if (apiClass != null) {
+ ApiMethod apiMethod = apiClass.getMethod(mCurrentMethodName,
+ mCurrentParameterTypes, mCurrentMethodReturnType);
+ if (apiMethod != null) {
+ apiMethod.setCovered(true);
+ }
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/tools/cts-api-coverage/src/com/android/cts/apicoverage/HasCoverage.java b/tools/cts-api-coverage/src/com/android/cts/apicoverage/HasCoverage.java
new file mode 100644
index 0000000..3b369bb
--- /dev/null
+++ b/tools/cts-api-coverage/src/com/android/cts/apicoverage/HasCoverage.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2010 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 com.android.cts.apicoverage;
+
+import java.util.Comparator;
+
+interface HasCoverage {
+ float getCoveragePercentage();
+ String getName();
+}
+
+class CoverageComparator implements Comparator<HasCoverage> {
+ public int compare(HasCoverage entity, HasCoverage otherEntity) {
+ int diff = Math.round(entity.getCoveragePercentage())
+ - Math.round(otherEntity.getCoveragePercentage());
+ return diff != 0 ? diff : entity.getName().compareTo(otherEntity.getName());
+ }
+}
diff --git a/tools/cts-api-coverage/src/com/android/cts/apicoverage/TextReport.java b/tools/cts-api-coverage/src/com/android/cts/apicoverage/TextReport.java
new file mode 100644
index 0000000..ebcefe5
--- /dev/null
+++ b/tools/cts-api-coverage/src/com/android/cts/apicoverage/TextReport.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2010 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 com.android.cts.apicoverage;
+
+import java.io.OutputStream;
+import java.io.PrintStream;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Class that outputs a text report of {@link ApiCoverage}.
+ */
+class TextReport {
+
+ public static void printTextReport(ApiCoverage api, OutputStream outputStream) {
+ PrintStream out = new PrintStream(outputStream);
+
+ CoverageComparator comparator = new CoverageComparator();
+ List<ApiPackage> packages = new ArrayList<ApiPackage>(api.getPackages());
+ Collections.sort(packages, comparator);
+
+ for (ApiPackage apiPackage : packages) {
+ if (apiPackage.getName().startsWith("android")
+ && apiPackage.getTotalMethods() > 0) {
+ printPackage(apiPackage, out);
+ }
+ }
+
+ out.println();
+ out.println();
+
+ for (ApiPackage apiPackage : packages) {
+ if (apiPackage.getName().startsWith("android")) {
+ printPackage(apiPackage, out);
+
+ List<ApiClass> classes = new ArrayList<ApiClass>(apiPackage.getClasses());
+ Collections.sort(classes, comparator);
+ for (ApiClass apiClass : classes) {
+ if (apiClass.getTotalMethods() > 0) {
+ printClass(apiClass, out);
+
+ List<ApiConstructor> constructors =
+ new ArrayList<ApiConstructor>(apiClass.getConstructors());
+ Collections.sort(constructors);
+ for (ApiConstructor constructor : constructors) {
+ printConstructor(constructor, out);
+ }
+
+ List<ApiMethod> methods = new ArrayList<ApiMethod>(apiClass.getMethods());
+ Collections.sort(methods);
+ for (ApiMethod method : methods) {
+ printMethod(method, out);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ private static void printPackage(ApiPackage apiPackage, PrintStream out) {
+ out.println(apiPackage.getName() + " "
+ + Math.round(apiPackage.getCoveragePercentage()) + "% ("
+ + apiPackage.getNumCoveredMethods() + "/" + apiPackage.getTotalMethods() + ")");
+ }
+
+ private static void printClass(ApiClass apiClass, PrintStream out) {
+ out.println(" " + apiClass.getName() + " "
+ + Math.round(apiClass.getCoveragePercentage()) + "% ("
+ + apiClass.getNumCoveredMethods() + "/" + apiClass.getTotalMethods() + ") ");
+ }
+
+ private static void printConstructor(ApiConstructor constructor, PrintStream out) {
+ StringBuilder builder = new StringBuilder(" [")
+ .append(constructor.isCovered() ? "X" : " ")
+ .append("] ").append(constructor.getName()).append("(");
+
+ List<String> parameterTypes = constructor.getParameterTypes();
+ int numParameterTypes = parameterTypes.size();
+ for (int i = 0; i < numParameterTypes; i++) {
+ builder.append(parameterTypes.get(i));
+ if (i + 1 < numParameterTypes) {
+ builder.append(", ");
+ }
+ }
+ out.println(builder.append(")"));
+ }
+
+ private static void printMethod(ApiMethod method, PrintStream out) {
+ StringBuilder builder = new StringBuilder(" [")
+ .append(method.isCovered() ? "X" : " ")
+ .append("] ").append(method.getReturnType()).append(" ")
+ .append(method.getName()).append("(");
+ List<String> parameterTypes = method.getParameterTypes();
+ int numParameterTypes = parameterTypes.size();
+ for (int i = 0; i < numParameterTypes; i++) {
+ builder.append(parameterTypes.get(i));
+ if (i + 1 < numParameterTypes) {
+ builder.append(", ");
+ }
+ }
+ out.println(builder.append(")"));
+ }
+}
diff --git a/tools/cts-api-coverage/src/com/android/cts/apicoverage/XmlReport.java b/tools/cts-api-coverage/src/com/android/cts/apicoverage/XmlReport.java
new file mode 100644
index 0000000..68acf06
--- /dev/null
+++ b/tools/cts-api-coverage/src/com/android/cts/apicoverage/XmlReport.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2010 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 com.android.cts.apicoverage;
+
+import java.io.File;
+import java.io.OutputStream;
+import java.io.PrintStream;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Date;
+import java.util.List;
+
+/**
+ * Class that outputs an XML report of the {@link ApiCoverage} collected. It can be viewed in
+ * a browser when used with the api-coverage.css and api-coverage.xsl files.
+ */
+class XmlReport {
+
+ public static void printXmlReport(List<File> testApks, ApiCoverage apiCoverage,
+ OutputStream outputStream) {
+ PrintStream out = new PrintStream(outputStream);
+ out.println("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
+ out.println("<?xml-stylesheet type=\"text/xsl\" href=\"api-coverage.xsl\"?>");
+
+ SimpleDateFormat format = new SimpleDateFormat("EEE, MMM d, yyyy h:mm a z");
+ String date = format.format(new Date(System.currentTimeMillis()));
+ out.println("<api-coverage generatedTime=\"" + date + "\">");
+
+ out.println("<debug>");
+ out.println("<sources>");
+ for (File testApk : testApks) {
+ out.println("<apk path=\"" + testApk.getPath() + "\" />");
+ }
+ out.println("</sources>");
+ out.println("</debug>");
+
+ out.println("<api>");
+
+ CoverageComparator comparator = new CoverageComparator();
+ List<ApiPackage> packages = new ArrayList<ApiPackage>(apiCoverage.getPackages());
+ Collections.sort(packages, comparator);
+ for (ApiPackage pkg : packages) {
+ if (pkg.getName().startsWith("android")
+ && pkg.getTotalMethods() > 0) {
+ out.println("<package name=\"" + pkg.getName()
+ + "\" numCovered=\"" + pkg.getNumCoveredMethods()
+ + "\" numTotal=\"" + pkg.getTotalMethods()
+ + "\" coveragePercentage=\""
+ + Math.round(pkg.getCoveragePercentage())
+ + "\">");
+
+ List<ApiClass> classes = new ArrayList<ApiClass>(pkg.getClasses());
+ Collections.sort(classes, comparator);
+
+ for (ApiClass apiClass : classes) {
+ if (apiClass.getTotalMethods() > 0) {
+ out.println("<class name=\"" + apiClass.getName()
+ + "\" numCovered=\"" + apiClass.getNumCoveredMethods()
+ + "\" numTotal=\"" + apiClass.getTotalMethods()
+ + "\" coveragePercentage=\""
+ + Math.round(apiClass.getCoveragePercentage())
+ + "\">");
+
+ for (ApiConstructor constructor : apiClass.getConstructors()) {
+ out.println("<constructor name=\"" + constructor.getName()
+ + "\" covered=\"" + constructor.isCovered() + "\">");
+
+ for (String parameterType : constructor.getParameterTypes()) {
+ out.println("<parameter type=\"" + parameterType + "\" />");
+ }
+
+ out.println("</constructor>");
+ }
+
+ for (ApiMethod method : apiClass.getMethods()) {
+ out.println("<method name=\"" + method.getName()
+ + "\" returnType=\"" + method.getReturnType()
+ + "\" covered=\"" + method.isCovered() + "\">");
+
+ for (String parameterType : method.getParameterTypes()) {
+ out.println("<parameter type=\"" + parameterType + "\" />");
+ }
+
+ out.println("</method>");
+ }
+ out.println("</class>");
+ }
+ }
+ out.println("</package>");
+ }
+ }
+
+ out.println("</api>");
+ out.println("</api-coverage>");
+ }
+}