keep previous history
diff --git a/development/ide/eclipse/.classpath b/development/ide/eclipse/.classpath
index edb6b1b..8cba973 100644
--- a/development/ide/eclipse/.classpath
+++ b/development/ide/eclipse/.classpath
@@ -47,6 +47,7 @@
<classpathentry kind="src" path="cts/tests/tests/view/src"/>
<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"/>
@@ -58,6 +59,7 @@
<classpathentry kind="src" path="cts/tools/signature-tools/src"/>
<classpathentry kind="src" path="cts/tools/signature-tools/test"/>
<classpathentry kind="src" path="cts/tools/spec-progress/src"/>
+ <classpathentry kind="src" path="cts/tools/test-progress-new/src"/>
<classpathentry kind="src" path="cts/tools/utils"/>
<classpathentry kind="src" path="cts/tools/vm-tests/src"/>
<classpathentry kind="src" path="out/target/common/obj/APPS/CtsAccessibilityServiceTestCases_intermediates/src/src"/>
diff --git a/tests/expectations/knownfailures.txt b/tests/expectations/knownfailures.txt
index 6292d7a..c7b25b2 100644
--- a/tests/expectations/knownfailures.txt
+++ b/tests/expectations/knownfailures.txt
@@ -2,15 +2,24 @@
/* These tests consistently fail on GRH78. */
{ name: "android.location.cts.GeocoderTest#testGetFromLocation" },
{ name: "android.location.cts.GeocoderTest#testGetFromLocationName" },
+{ name: "android.net.cts.ListeningPortsTest#testNoListeningUdp6Ports" },
{ name: "android.webkit.cts.WebSettingsTest#testSetAppCacheEnabled" },
{ name: "android.net.cts.SSLCertificateSocketFactoryTest" },
{
+ description: "testGetDuration causes testOnKeyDown to fail for some reason",
+ name: "android.widget.cts.VideoViewTest#testOnKeyDown"
+},
+
+{
description: "Flaky tests that need to be rewritten or deleted.",
names: [
"android.media.cts.MediaRecorderTest#testSetCamera",
"android.webkit.cts.CacheManagerTest#testCacheFile",
- "android.widget.cts.AutoCompleteTextViewTest#testOnFilterComplete"
+ "android.widget.cts.AutoCompleteTextViewTest#testOnFilterComplete",
+ "android.util.cts.EventLogTest#testReadEvents",
+ "android.util.cts.EventLogTest#testWriteEvent",
+ "android.util.cts.EventLogTest#testWriteEventWithOversizeValue"
]
},
@@ -19,17 +28,29 @@
name: "android.provider.cts.MediaStore_Audio_Playlists_MembersTest"
},
+/* These tests pass when executed individually but fail when running CTS as a whole on GRH78. */
{
- description: "These tests pass when executed individually but fail when running CTS as a whole on GRH78.",
- bug: 3184701,
- names: [
- "org.apache.harmony.luni.tests.internal.net.www.protocol.https.HttpsURLConnectionTest#testProxyConnection",
- "org.apache.harmony.luni.tests.internal.net.www.protocol.https.HttpsURLConnectionTest#testProxyAuthConnection",
- "org.apache.harmony.luni.tests.internal.net.www.protocol.https.HttpsURLConnectionTest#testConsequentProxyConnection",
- "org.apache.harmony.luni.tests.internal.net.www.protocol.https.HttpsURLConnectionTest#testProxyAuthConnection_doOutput",
- "org.apache.harmony.luni.tests.internal.net.www.protocol.https.HttpsURLConnectionTest#testProxyAuthConnectionFailed",
- "org.apache.harmony.luni.tests.internal.net.www.protocol.https.HttpsURLConnectionTest#testProxyConnection_Not_Found_Response"
- ]
+ name: "org.apache.harmony.luni.tests.internal.net.www.protocol.https.HttpsURLConnectionTest#testProxyConnection",
+ bug: 3184701
+},
+{
+ name: "org.apache.harmony.luni.tests.internal.net.www.protocol.https.HttpsURLConnectionTest#testProxyAuthConnection",
+ bug: 3184701
+},
+{
+ name: "org.apache.harmony.luni.tests.internal.net.www.protocol.https.HttpsURLConnectionTest#testConsequentProxyConnection",
+ bug: 3184701
+},
+{
+ name: "org.apache.harmony.luni.tests.internal.net.www.protocol.https.HttpsURLConnectionTest#testProxyAuthConnection_doOutput",
+ bug: 3184701
+},
+{
+ name: "org.apache.harmony.luni.tests.internal.net.www.protocol.https.HttpsURLConnectionTest#testProxyAuthConnectionFailed",
+ bug: 3184701
+},
+{
+ name: "org.apache.harmony.luni.tests.internal.net.www.protocol.https.HttpsURLConnectionTest#testProxyConnection_Not_Found_Response",
+ bug: 3184701
}
-
]
diff --git a/tests/tests/telephony/src/android/telephony/cts/SmsManagerTest.java b/tests/tests/telephony/src/android/telephony/cts/SmsManagerTest.java
index 8483fbb..57ce365 100755
--- a/tests/tests/telephony/src/android/telephony/cts/SmsManagerTest.java
+++ b/tests/tests/telephony/src/android/telephony/cts/SmsManagerTest.java
@@ -62,8 +62,7 @@
"44010", // NTT DOCOMO
"45005", // SKT Mobility
"45002", // SKT Mobility
- "45008", // KT Mobility
- "45006" // LGT
+ "45008" // KT Mobility
);
// List of network operators that doesn't support Data(binary) SMS message
diff --git a/tests/tests/widget/src/android/widget/cts/VideoViewTest.java b/tests/tests/widget/src/android/widget/cts/VideoViewTest.java
index 44d48a5..6b9aa84 100644
--- a/tests/tests/widget/src/android/widget/cts/VideoViewTest.java
+++ b/tests/tests/widget/src/android/widget/cts/VideoViewTest.java
@@ -31,6 +31,7 @@
import android.media.MediaPlayer.OnErrorListener;
import android.media.MediaPlayer.OnPreparedListener;
import android.test.ActivityInstrumentationTestCase2;
+import android.view.KeyEvent;
import android.view.View.MeasureSpec;
import android.view.animation.cts.DelayedCheck;
import android.widget.MediaController;
@@ -309,6 +310,89 @@
}
@TestTargetNew(
+ level = TestLevel.NOT_NECESSARY,
+ method = "onTouchEvent",
+ args = {android.view.MotionEvent.class}
+ )
+ public void testOnTouchEvent() {
+ // onTouchEvent() is implementation details, do NOT test
+ }
+
+ @TestTargetNew(
+ level = TestLevel.SUFFICIENT,
+ method = "onKeyDown",
+ args = {int.class, android.view.KeyEvent.class}
+ )
+ public void testOnKeyDown() throws Throwable {
+ runTestOnUiThread(new Runnable() {
+ public void run() {
+ mVideoView.setVideoPath(mVideoPath);
+ mVideoView.requestFocus();
+ }
+ });
+ mInstrumentation.waitForIdleSync();
+
+ assertFalse(mVideoView.isPlaying());
+ sendKeys(KeyEvent.KEYCODE_HEADSETHOOK);
+ // video should be played.
+ new DelayedCheck(TIME_OUT) {
+ @Override
+ protected boolean check() {
+ return mVideoView.isPlaying();
+ }
+ }.run();
+ assertFalse(mMediaController.isShowing());
+
+ sendKeys(KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE);
+ new DelayedCheck(TIME_OUT) {
+ @Override
+ protected boolean check() {
+ return !mVideoView.isPlaying();
+ }
+ }.run();
+ // MediaController should show
+ assertTrue(mMediaController.isShowing());
+
+ runTestOnUiThread(new Runnable() {
+ public void run() {
+ mVideoView.start();
+ }
+ });
+ new DelayedCheck(TIME_OUT) {
+ @Override
+ protected boolean check() {
+ return mVideoView.isPlaying();
+ }
+ }.run();
+
+ sendKeys(KeyEvent.KEYCODE_MEDIA_STOP);
+ new DelayedCheck(TIME_OUT) {
+ @Override
+ protected boolean check() {
+ return !mVideoView.isPlaying();
+ }
+ }.run();
+ }
+
+ @TestTargetNew(
+ level = TestLevel.NOT_NECESSARY,
+ method = "onMeasure",
+ args = {int.class, int.class}
+ )
+ public void testOnMeasure() {
+ // Do not test onMeasure(), implementation details
+ }
+
+ @TestTargetNew(
+ level = TestLevel.NOT_NECESSARY,
+ method = "onTrackballEvent",
+ args = {android.view.MotionEvent.class}
+ )
+ public void testOnTrackballEvent() {
+ // Do not test onTrackballEvent(), implementation details
+ }
+
+ @TestTargetNew(
level = TestLevel.COMPLETE,
method = "getDuration",
args = {}
diff --git a/tools/annotation-helper/.classpath b/tools/annotation-helper/.classpath
new file mode 100644
index 0000000..bece0f3
--- /dev/null
+++ b/tools/annotation-helper/.classpath
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+ <classpathentry kind="src" path="src"/>
+ <classpathentry exported="true" kind="lib" path="bin/"/>
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
+ <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
+ <classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/tools/annotation-helper/.project b/tools/annotation-helper/.project
new file mode 100644
index 0000000..ea4be4f
--- /dev/null
+++ b/tools/annotation-helper/.project
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>spechelper</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>org.eclipse.jdt.core.javabuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.pde.ManifestBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.pde.SchemaBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>org.eclipse.pde.PluginNature</nature>
+ <nature>org.eclipse.jdt.core.javanature</nature>
+ </natures>
+</projectDescription>
diff --git a/tools/annotation-helper/.settings/org.eclipse.jdt.core.prefs b/tools/annotation-helper/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 0000000..c4095e1
--- /dev/null
+++ b/tools/annotation-helper/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,57 @@
+#Wed Jun 04 14:49:33 CEST 2008
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.problem.annotationSuperInterface=ignore
+org.eclipse.jdt.core.compiler.problem.autoboxing=ignore
+org.eclipse.jdt.core.compiler.problem.deprecation=ignore
+org.eclipse.jdt.core.compiler.problem.deprecationInDeprecatedCode=disabled
+org.eclipse.jdt.core.compiler.problem.deprecationWhenOverridingDeprecatedMethod=enabled
+org.eclipse.jdt.core.compiler.problem.discouragedReference=ignore
+org.eclipse.jdt.core.compiler.problem.emptyStatement=warning
+org.eclipse.jdt.core.compiler.problem.fallthroughCase=ignore
+org.eclipse.jdt.core.compiler.problem.fatalOptionalError=enabled
+org.eclipse.jdt.core.compiler.problem.fieldHiding=warning
+org.eclipse.jdt.core.compiler.problem.finalParameterBound=ignore
+org.eclipse.jdt.core.compiler.problem.finallyBlockNotCompletingNormally=warning
+org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning
+org.eclipse.jdt.core.compiler.problem.hiddenCatchBlock=warning
+org.eclipse.jdt.core.compiler.problem.incompatibleNonInheritedInterfaceMethod=warning
+org.eclipse.jdt.core.compiler.problem.incompleteEnumSwitch=ignore
+org.eclipse.jdt.core.compiler.problem.indirectStaticAccess=ignore
+org.eclipse.jdt.core.compiler.problem.localVariableHiding=ignore
+org.eclipse.jdt.core.compiler.problem.methodWithConstructorName=warning
+org.eclipse.jdt.core.compiler.problem.missingDeprecatedAnnotation=ignore
+org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotation=ignore
+org.eclipse.jdt.core.compiler.problem.missingSerialVersion=ignore
+org.eclipse.jdt.core.compiler.problem.noEffectAssignment=warning
+org.eclipse.jdt.core.compiler.problem.noImplicitStringConversion=warning
+org.eclipse.jdt.core.compiler.problem.nonExternalizedStringLiteral=ignore
+org.eclipse.jdt.core.compiler.problem.nullReference=ignore
+org.eclipse.jdt.core.compiler.problem.overridingPackageDefaultMethod=warning
+org.eclipse.jdt.core.compiler.problem.parameterAssignment=ignore
+org.eclipse.jdt.core.compiler.problem.possibleAccidentalBooleanAssignment=warning
+org.eclipse.jdt.core.compiler.problem.potentialNullReference=ignore
+org.eclipse.jdt.core.compiler.problem.rawTypeReference=warning
+org.eclipse.jdt.core.compiler.problem.redundantNullCheck=ignore
+org.eclipse.jdt.core.compiler.problem.specialParameterHidingField=disabled
+org.eclipse.jdt.core.compiler.problem.staticAccessReceiver=ignore
+org.eclipse.jdt.core.compiler.problem.suppressWarnings=enabled
+org.eclipse.jdt.core.compiler.problem.syntheticAccessEmulation=ignore
+org.eclipse.jdt.core.compiler.problem.typeParameterHiding=ignore
+org.eclipse.jdt.core.compiler.problem.uncheckedTypeOperation=warning
+org.eclipse.jdt.core.compiler.problem.undocumentedEmptyBlock=ignore
+org.eclipse.jdt.core.compiler.problem.unhandledWarningToken=ignore
+org.eclipse.jdt.core.compiler.problem.unnecessaryElse=ignore
+org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=warning
+org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=ignore
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownException=warning
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionWhenOverriding=enabled
+org.eclipse.jdt.core.compiler.problem.unusedImport=warning
+org.eclipse.jdt.core.compiler.problem.unusedLabel=warning
+org.eclipse.jdt.core.compiler.problem.unusedLocal=warning
+org.eclipse.jdt.core.compiler.problem.unusedParameter=warning
+org.eclipse.jdt.core.compiler.problem.unusedParameterIncludeDocCommentReference=enabled
+org.eclipse.jdt.core.compiler.problem.unusedParameterWhenImplementingAbstract=disabled
+org.eclipse.jdt.core.compiler.problem.unusedParameterWhenOverridingConcrete=disabled
+org.eclipse.jdt.core.compiler.problem.unusedPrivateMember=warning
+org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=warning
+org.eclipse.jdt.core.compiler.processAnnotations=disabled
diff --git a/tools/annotation-helper/META-INF/MANIFEST.MF b/tools/annotation-helper/META-INF/MANIFEST.MF
new file mode 100644
index 0000000..1f39a53
--- /dev/null
+++ b/tools/annotation-helper/META-INF/MANIFEST.MF
@@ -0,0 +1,21 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: Spechelper Plug-in
+Bundle-SymbolicName: spechelper; singleton:=true
+Bundle-Version: 1.0.0
+Bundle-Activator: spechelper.Activator
+Require-Bundle: org.eclipse.ui,
+ org.eclipse.core.runtime
+Eclipse-LazyStart: false
+Import-Package: org.eclipse.jdt.core,
+ org.eclipse.jdt.core.compiler,
+ org.eclipse.jdt.core.dom,
+ org.eclipse.jdt.core.util,
+ org.eclipse.jdt.internal.ui,
+ org.eclipse.jdt.internal.ui.javaeditor,
+ org.eclipse.jdt.ui,
+ org.eclipse.jdt.ui.text.java,
+ org.eclipse.jface.text,
+ org.eclipse.jface.text.contentassist,
+ org.eclipse.ui.texteditor
+Bundle-ClassPath: bin/
diff --git a/tools/annotation-helper/README.txt b/tools/annotation-helper/README.txt
new file mode 100644
index 0000000..8ff7eec
--- /dev/null
+++ b/tools/annotation-helper/README.txt
@@ -0,0 +1 @@
+for information, read the javadoc under src/spechelper/SimpleComputer.java
diff --git a/tools/annotation-helper/build.properties b/tools/annotation-helper/build.properties
new file mode 100644
index 0000000..e5dc709
--- /dev/null
+++ b/tools/annotation-helper/build.properties
@@ -0,0 +1,9 @@
+output.. = bin/
+bin.includes = plugin.xml,\
+ META-INF/,\
+ bin/,\
+ build.properties,\
+ feature.xml
+src.includes = src/,\
+ plugin.xml,\
+ feature.xml
diff --git a/tools/annotation-helper/plugin.xml b/tools/annotation-helper/plugin.xml
new file mode 100644
index 0000000..6cc641d
--- /dev/null
+++ b/tools/annotation-helper/plugin.xml
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<?eclipse version="3.2"?>
+<plugin>
+ <extension
+ point="org.eclipse.jdt.ui.javaCompletionProposalComputer"
+ id="WordCompletionProposalComputer"
+ name="Noser Annotation Completion Proposal Computer">
+
+ <javaCompletionProposalComputer
+ activate="true"
+ class="spechelper.SimpleComputer"
+ categoryId="org.eclipse.ui.texteditor.textual_proposals">
+ </javaCompletionProposalComputer>
+ </extension>
+</plugin>
diff --git a/tools/annotation-helper/src/spechelper/MethodSelector.java b/tools/annotation-helper/src/spechelper/MethodSelector.java
new file mode 100644
index 0000000..a0b4bba
--- /dev/null
+++ b/tools/annotation-helper/src/spechelper/MethodSelector.java
@@ -0,0 +1,228 @@
+/*
+ * 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 spechelper;
+
+import org.eclipse.jdt.core.Flags;
+import org.eclipse.jdt.core.ICompilationUnit;
+import org.eclipse.jdt.core.IJavaProject;
+import org.eclipse.jdt.core.IMethod;
+import org.eclipse.jdt.core.IType;
+import org.eclipse.jdt.core.ITypeParameter;
+import org.eclipse.jdt.core.JavaModelException;
+import org.eclipse.jdt.core.Signature;
+import org.eclipse.jdt.core.dom.AST;
+import org.eclipse.jdt.core.dom.ASTParser;
+import org.eclipse.jdt.core.dom.ASTVisitor;
+import org.eclipse.jdt.core.dom.CompilationUnit;
+import org.eclipse.jdt.core.dom.Expression;
+import org.eclipse.jdt.core.dom.ITypeBinding;
+import org.eclipse.jdt.core.dom.SingleMemberAnnotation;
+import org.eclipse.jdt.internal.ui.JavaPlugin;
+import org.eclipse.jdt.ui.JavaElementLabelProvider;
+import org.eclipse.jface.dialogs.IDialogConstants;
+import org.eclipse.jface.dialogs.MessageDialog;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.ui.IEditorInput;
+import org.eclipse.ui.IEditorPart;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.dialogs.ElementListSelectionDialog;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ *
+ */
+public class MethodSelector {
+
+ public String obtainReplacement(String buffer) {
+ IMethod method = selectMethod();
+ // if user did cancel the selection
+ if (method == null) {
+ return null;
+ }
+
+ // see if we are already in a annotation:
+ // if yes -> only dump the testtarget annotation, not the complete
+ // TestInfo
+ // (could not easily find this out with CompilationUnit, since inserting
+ // a :
+ // broke the AST - maybe use WorkingCopy and so on,
+ // but for now: do it with simple String analysis
+ boolean shortOnly = false;
+ int annotPos = buffer.lastIndexOf("@TestInfo");
+ // the latest annotation - count "(" ")" pairs - if not the same count
+ // we assume to be in the annotation (H: code compiles fine)
+ if (annotPos != -1) {
+ String sub = buffer.substring(annotPos);
+ // only consider the latest 6 lines for the annotation to occur
+ // (6 = range within which the annotation @TestTarget
+ // must occur, but out of range to reach the annotation from the
+ // previous method - ah i'd prefer working with compilationUnit...
+ String[] lines = sub.split("\n");
+ for (int i = lines.length - 6; i < lines.length; i++) {
+ String line = lines[i];
+ if (line.contains("@TestTarget")) {
+ shortOnly = true;
+ }
+ }
+ }
+
+ return generateAnnotation(shortOnly, method);
+ }
+
+
+ private String generateAnnotation(boolean shortOnly, IMethod method) {
+ String[] ptypes = method.getParameterTypes();
+ String param = "";
+ for (int i = 0; i < ptypes.length; i++) {
+ String ptype = ptypes[i];
+ String sig = Signature.toString(ptype);
+ // kind of a hack: convert all Generic Type args to Object, or to
+ // its bound Type
+ if (sig.length() == 1) {
+ ITypeParameter tps = method.getTypeParameter(sig);
+ sig = "Object";
+
+ if (tps != null && tps.exists()) {
+ try {
+ String[] bounds = tps.getBounds();
+ if (bounds.length > 0) {
+ sig = bounds[0];
+ }
+ } catch (JavaModelException e) {
+ e.printStackTrace();
+ }
+
+ }
+ }
+ // omit type signature
+ sig = sig.replaceAll("<.*>", "");
+ param += (i > 0 ? ", " : "") + sig + ".class";
+ }
+ String IND = " ";
+
+ String targ = "@TestTarget(\n" + IND + " methodName = \""
+ + method.getElementName() + "\",\n" + IND
+ + " methodArgs = {" + param + "}\n" + IND + " )\n";
+
+ String s;
+ if (shortOnly) {
+ s = targ;
+ } else {
+
+ s = "@TestInfo(\n" + IND + " status = TestStatus.TBR,\n" + IND
+ + " notes = \"\",\n" + IND + " targets = {\n" + IND
+ + " " + targ + IND + "})";
+ }
+ return s;
+ }
+
+ private IMethod selectMethod() {
+ IEditorPart part = PlatformUI.getWorkbench().getActiveWorkbenchWindow()
+ .getActivePage().getActiveEditor();
+ Shell shell = PlatformUI.getWorkbench().getActiveWorkbenchWindow()
+ .getShell();
+ IEditorInput ei = part.getEditorInput();
+ final ICompilationUnit cu = JavaPlugin.getDefault()
+ .getWorkingCopyManager().getWorkingCopy(ei);
+ // cu != null since we register only for java/javadoc completion
+ // proposals
+ ASTParser parser = ASTParser.newParser(AST.JLS3);
+ parser.setSource(cu);
+ parser.setResolveBindings(true);
+ CompilationUnit unit = (CompilationUnit) parser.createAST(null);
+
+ class MHolder {
+ IMethod method;
+ }
+ final MHolder mholder = new MHolder();
+
+ class FHolder {
+ boolean foundClassAnnotation;
+ }
+ final FHolder fholder = new FHolder();
+
+ unit.accept(new ASTVisitor() {
+ public boolean visit(SingleMemberAnnotation node) {
+ String name = node.getTypeName().getFullyQualifiedName();
+ if (!name.equals("TestTargetClass")) {
+ return false;
+ }
+ fholder.foundClassAnnotation = true;
+ Expression targetClassE = node.getValue();
+ ITypeBinding ty = targetClassE.resolveTypeBinding();
+ if (ty == null) {
+ return false;
+ }
+ ITypeBinding[] classTypes = ty.getTypeArguments();
+ if (classTypes.length > 0) {
+ ITypeBinding tp = classTypes[0];
+ String qname = tp.getQualifiedName();
+ System.out.println("qname:" + qname);
+ IJavaProject myProject = cu.getJavaProject();
+ try {
+ IType myType = myProject.findType(qname);
+ if (myType != null) {
+ Shell parent = PlatformUI.getWorkbench()
+ .getActiveWorkbenchWindow().getShell();
+ ElementListSelectionDialog dialog = new ElementListSelectionDialog(
+ parent,
+ new JavaElementLabelProvider(
+ JavaElementLabelProvider.SHOW_PARAMETERS
+ | JavaElementLabelProvider.SHOW_OVERLAY_ICONS
+ | JavaElementLabelProvider.SHOW_RETURN_TYPE));
+ // restrict to public/protected methods only
+ IMethod[] allMeth = myType.getMethods();
+ List<IMethod> pubproMethods = new ArrayList<IMethod>();
+ for (int i = 0; i < allMeth.length; i++) {
+ IMethod method = allMeth[i];
+ if ((method.getFlags() & (Flags.AccPublic | Flags.AccProtected)) != 0) {
+ pubproMethods.add(method);
+ }
+ }
+ IMethod[] res = pubproMethods
+ .toArray(new IMethod[pubproMethods.size()]);
+ dialog.setIgnoreCase(true);
+ dialog.setBlockOnOpen(true);
+ dialog.setElements(res);//
+ dialog.setFilter("");
+ dialog.setTitle(qname);
+ if (dialog.open() != IDialogConstants.CANCEL_ID) {
+ Object[] types = dialog.getResult();
+ System.out.println("selected:" + types[0]);
+ IMethod method = (IMethod) types[0];
+ mholder.method = method;
+
+ } else {
+ // System.out.println("cancelled!!");
+ }
+ }
+ } catch (JavaModelException e) {
+ e.printStackTrace();
+ }
+ }
+ return true;
+ }
+ });
+ if (!fholder.foundClassAnnotation) {
+ MessageDialog.openInformation(shell, "Class Annotation missing",
+ "@TestTargetClass(...) is missing");
+ return null;
+ }
+ return mholder.method;
+ }
+}
diff --git a/tools/annotation-helper/src/spechelper/MyCompletion.java b/tools/annotation-helper/src/spechelper/MyCompletion.java
new file mode 100644
index 0000000..7a5c3c7
--- /dev/null
+++ b/tools/annotation-helper/src/spechelper/MyCompletion.java
@@ -0,0 +1,88 @@
+/*
+ * 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 spechelper;
+
+import org.eclipse.jface.text.BadLocationException;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.contentassist.ICompletionProposal;
+import org.eclipse.jface.text.contentassist.IContextInformation;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.Point;
+
+public class MyCompletion implements ICompletionProposal {
+
+ private String m_displ;
+ private String m_replace;
+ private int m_offset;
+ private int m_replacelen;
+ private int m_cursorpos;
+ private Image m_img;
+ private IContextInformation m_context;
+ private String m_addinfo;
+ private String fBuffer;
+
+ public MyCompletion(String buffer, String replacementString,
+ int replacementOffset, int replacementLength, int cursorPosition,
+ Image image, String displayString,
+ IContextInformation contextInformation,
+ String additionalProposalInfo) {
+ m_replace = replacementString;
+ m_offset = replacementOffset;
+ m_replacelen = replacementLength;
+ m_cursorpos = cursorPosition;
+ m_img = image;
+ fBuffer = buffer;
+ m_displ = displayString;
+ m_context = contextInformation;
+ m_addinfo = additionalProposalInfo;
+ }
+
+ public void apply(IDocument document) {
+ try {
+ MethodSelector ms = new MethodSelector();
+ String replace = ms.obtainReplacement(fBuffer);
+ if (replace == null) {
+ m_cursorpos = 0;
+ return;
+ }
+ m_cursorpos = replace.length();
+ document.replace(m_offset, m_replacelen, replace);
+ } catch (BadLocationException x) {
+ // ignore
+ }
+ }
+
+ public Point getSelection(IDocument document) {
+ return new Point(m_offset + m_cursorpos, 0);
+ }
+
+ public IContextInformation getContextInformation() {
+ return m_context;
+ }
+
+ public Image getImage() {
+ return m_img;
+ }
+
+ public String getDisplayString() {
+ if (m_displ != null) return m_displ;
+ return m_replace;
+ }
+
+ public String getAdditionalProposalInfo() {
+ return m_addinfo;
+ }
+}
diff --git a/tools/annotation-helper/src/spechelper/SimpleComputer.java b/tools/annotation-helper/src/spechelper/SimpleComputer.java
new file mode 100644
index 0000000..757b1a8
--- /dev/null
+++ b/tools/annotation-helper/src/spechelper/SimpleComputer.java
@@ -0,0 +1,178 @@
+/*
+ * 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 spechelper;
+
+import dalvik.annotation.TestTargetClass;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.jdt.ui.text.java.ContentAssistInvocationContext;
+import org.eclipse.jdt.ui.text.java.IJavaCompletionProposalComputer;
+import org.eclipse.jface.text.BadLocationException;
+import org.eclipse.jface.text.contentassist.ICompletionProposal;
+
+import java.util.List;
+import java.util.Vector;
+import java.util.regex.Pattern;
+
+/**
+ * <p>
+ * a plugin to auto-insert the following annotation constructs:
+ * TestInfo.java, TestStatus.java, TestTarget.java, and TestTargetClass.java
+ * under
+ * /android/device/dalvik/libcore/dalvik/src/main/java/dalvik/annotation/
+ * <p>
+ * usage:<br>
+ * - install export/plugins/spechelper_1.0.0.jar into your eclipse/plugin folder.<br>
+ * - restart eclipse<br>
+ * - open a java file<br>
+ * - insert the TestTargetClass annotation above the class declaration, e.g.
+ * <code>@TestTargetClass(Pattern.class)</code><br>
+ * - insert a ":" one line above the signature of a method to be annotated,
+ * and press ctrl-space for eclipse autocompletion. a popup appears which
+ * lists all target methods. choose one, and the annotation will be filled in
+ * at the cursor position.<br>
+ * <p>
+ * to annotate more than one target method, simply add a comma after the
+ * first TestTarget, press enter and insert a ":", press ctrl-space again.
+ *
+ * <p>
+ * a sample:
+ *
+<pre>
+package org.apache.harmony.tests.java.util.regex;
+
+import dalvik.annotation.TestTargetClass;
+import dalvik.annotation.TestInfo;
+import dalvik.annotation.TestTarget;
+import dalvik.annotation.TestStatus;
+
+import junit.framework.TestCase;
+
+import java.util.regex.Pattern;
+
+@TestTargetClass(Pattern.class)
+
+public class PatternTest extends TestCase {
+
+ // add ":", press ctrl-space here to let the eclipse plugin generate
+ // the next few lines
+ @TestInfo(
+ status = TestStatus.TBR,
+ notes = "",
+ targets = {
+ @TestTarget(
+ methodName = "compile",
+ methodArgs = {String.class}
+ )
+ })
+ public void foo() {
+ //
+ }
+
+ @TestInfo(
+ status = TestStatus.TBR,
+ notes = "",
+ targets = {
+ @TestTarget(
+ methodName = "compile",
+ methodArgs = {String.class}
+ ),
+ // add ":", press ctrl-space here to insert another TestTarget
+ })
+ public void bar() {
+ //
+ }
+
+ @TestInfo(
+ status = TestStatus.TBR,
+ notes = "",
+ targets = {
+ @TestTarget(
+ methodName = "compile",
+ methodArgs = {String.class}
+ ),
+ @TestTarget(
+ methodName = "split",
+ methodArgs = {CharSequence.class, int.class}
+ )
+
+ })
+ public void foobarsample() {
+ //
+ }
+
+}
+</pre>
+ *
+ *
+ *
+ */
+public class SimpleComputer implements IJavaCompletionProposalComputer {
+
+ public List<ICompletionProposal> computeCompletionProposals(
+ ContentAssistInvocationContext context, IProgressMonitor monitor) {
+ List<ICompletionProposal> ret = new Vector<ICompletionProposal>();
+ try {
+ int offs = context.getInvocationOffset();
+ String buffer = context.getDocument().get(0, offs);
+ //System.out.println("buffer:'"+buffer+"'");
+ //System.out.println("offset:"+offs);
+ String keyWord = ":";
+ String keyWordInfo = "':': noser: autofills the annotation";
+
+ int idx = 0;
+ // find the replacement position
+ int klen = keyWord.length();
+ for (int i = 0; i < klen; i++) {
+ String test = keyWord.substring(0, klen - i);
+ if (buffer.endsWith(test)) {
+ idx = klen - i;
+ break;
+ }
+ }
+ if (idx != 0) {
+ System.out.println("idx:"+idx);
+ String replace ="hi there! a longer sample text\nnew line";
+ ICompletionProposal ci = new MyCompletion(buffer, replace,
+ context.getInvocationOffset() - idx, idx, replace
+ .length(), null, keyWordInfo, null, null);
+ ret.add(ci);
+ }
+ } catch (BadLocationException e) {
+ e.printStackTrace();
+ }
+ return ret;
+ }
+
+
+ public List<ICompletionProposal> computeContextInformation(
+ ContentAssistInvocationContext context, IProgressMonitor monitor) {
+ return new Vector<ICompletionProposal>();
+ }
+
+ public String getErrorMessage() {
+ return "Error from SimpleComputer";
+ }
+
+ public void sessionEnded() {
+ //System.out.println("session ended");
+ }
+
+ public void sessionStarted() {
+ //System.out.println("session started");
+ }
+
+}
diff --git a/tools/annotation-helper/src/spechelper/Test.java b/tools/annotation-helper/src/spechelper/Test.java
new file mode 100644
index 0000000..01b11df
--- /dev/null
+++ b/tools/annotation-helper/src/spechelper/Test.java
@@ -0,0 +1,41 @@
+/*
+ * 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 spechelper;
+
+import java.util.Comparator;
+
+public class Test {
+
+ public Test() {}
+
+ public <E> void bla(Comparable<Long> comp1, Comparator<Comparable<String>> comp2, E arg, int a, byte[] b, char[] c, double d, float f, boolean bo, int[][] ar, String[][] arr,
+ long l, String... strs) {
+ }
+
+ public String fooaaa(int aasdfsdfsd) {
+ return null;
+ }
+
+ private void priv() {}
+
+ protected void prot() {}
+
+ void packloc() {}
+
+ public void testInputfoo() {
+ Math.sin(0d);
+ }
+}
diff --git a/tools/test-progress-new/Android.mk b/tools/test-progress-new/Android.mk
new file mode 100644
index 0000000..2ee261d
--- /dev/null
+++ b/tools/test-progress-new/Android.mk
@@ -0,0 +1,29 @@
+# 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.
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+LOCAL_IS_HOST_MODULE := true
+LOCAL_MODULE_CLASS := EXECUTABLES
+LOCAL_MODULE := test-progress-new
+
+include $(BUILD_SYSTEM)/base_rules.mk
+
+$(LOCAL_BUILT_MODULE): $(LOCAL_PATH)/etc/test-progress | $(ACP)
+ @echo "Copy: $(PRIVATE_MODULE) ($@)"
+ $(copy-file-to-new-target)
+ $(hide) chmod 755 $@
+
+include $(LOCAL_PATH)/src/Android.mk
diff --git a/tools/test-progress-new/etc/test-progress b/tools/test-progress-new/etc/test-progress
new file mode 100644
index 0000000..af7eec4
--- /dev/null
+++ b/tools/test-progress-new/etc/test-progress
@@ -0,0 +1,393 @@
+#!/bin/bash
+#
+# 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.
+
+# 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
+
+# Parse the test type, by default it will run against all tests
+# FIXME:
+# This should be removed after vm-tests and core-tests have been integrated
+# in CTS.
+testType="all"
+if [ ! -z $1 ]; then
+ if [ $1 == "all" -o $1 == "dalvik" -o $1 == "android" ]; then
+ testType=$1
+ shift
+ fi
+fi
+
+javaOpts=""
+while expr "x$1" : 'x-J' >/dev/null; do
+ opt=`expr "$1" : '-J\(.*\)'`
+ javaOpts="${javaOpts} -${opt}"
+ shift
+done
+
+#exec java $javaOpts -jar $libdir/hat.jar "$@"
+
+#######################################################################
+# Original content of invocation script follows. Uses values cleverly
+# deduced by the above code. If you want to use this for a different
+# set of packages, adjust both the list of source directories and the
+# list of packages.
+#######################################################################
+export CLASSES=$progdir/../framework/test-progress-new.jar
+export INPUT=$ANDROID_BUILD_TOP
+export OUTPUT=$ANDROID_BUILD_TOP/out/target/common/cts/test-progress-new
+
+TESTCOVERAGE_FLAGS="countdisabled,acceptcandx"
+
+if [[ ("$1" == "-f") && ("$2" != "") ]]; then
+ TESTCOVERAGE_FLAGS=$2
+ echo "running with flag '$TESTCOVERAGE_FLAGS'"
+else
+ if [ "$1" != "" ]; then
+ export OUTPUT=$1
+ fi
+fi
+
+DALVIK_SOURCE_PATH="\
+$INPUT/dalvik/libcore/annotation/src/main/java:\
+$INPUT/dalvik/libcore/archive/src/main/java:\
+$INPUT/dalvik/libcore/auth/src/main/java:\
+$INPUT/dalvik/libcore/awt-kernel/src/main/java:\
+$INPUT/dalvik/libcore/concurrent/src/main/java:\
+$INPUT/dalvik/libcore/crypto/src/main/java:\
+$INPUT/dalvik/libcore/dalvik/src/main/java:\
+$INPUT/dalvik/libcore/icu/src/main/java:\
+$INPUT/dalvik/libcore/json/src/main/java:\
+$INPUT/dalvik/libcore/junit/src/main/java:\
+$INPUT/dalvik/libcore/logging/src/main/java:\
+$INPUT/dalvik/libcore/luni-kernel/src/main/java:\
+$INPUT/dalvik/libcore/luni/src/main/java:\
+$INPUT/dalvik/libcore/math/src/main/java:\
+$INPUT/dalvik/libcore/nio_char/src/main/java:\
+$INPUT/dalvik/libcore/nio/src/main/java:\
+$INPUT/dalvik/libcore/openssl/src/main/java:\
+$INPUT/dalvik/libcore/prefs/src/main/java:\
+$INPUT/dalvik/libcore/regex/src/main/java:\
+$INPUT/dalvik/libcore/security-kernel/src/main/java:\
+$INPUT/dalvik/libcore/security/src/main/java:\
+$INPUT/dalvik/libcore/sql/src/main/java:\
+$INPUT/dalvik/libcore/suncompat/src/main/java:\
+$INPUT/dalvik/libcore/text/src/main/java:\
+$INPUT/dalvik/libcore/xml/src/main/java:\
+$INPUT/dalvik/libcore/x-net/src/main/java:\
+$INPUT/tests/HttpClient:\
+$INPUT/dalvik/libcore/annotation/src/test/java:\
+$INPUT/dalvik/libcore/archive/src/test/java:\
+$INPUT/dalvik/libcore/crypto/src/test/java:\
+$INPUT/dalvik/libcore/crypto/src/test/java/support/common/java:\
+$INPUT/dalvik/libcore/dalvik/src/test/java:\
+$INPUT/dalvik/libcore/logging/src/test/java:\
+$INPUT/dalvik/libcore/luni-kernel/src/test/java:\
+$INPUT/dalvik/libcore/luni/src/test/java:\
+$INPUT/dalvik/libcore/math/src/test/java:\
+$INPUT/dalvik/libcore/nio_char/src/test/java:\
+$INPUT/dalvik/libcore/nio/src/test/java:\
+$INPUT/dalvik/libcore/prefs/src/test/java:\
+$INPUT/dalvik/libcore/regex/src/test/java:\
+$INPUT/dalvik/libcore/security/src/test/java:\
+$INPUT/dalvik/libcore/sql/src/test/java:\
+$INPUT/dalvik/libcore/support/src/test/java:\
+$INPUT/dalvik/libcore/text/src/test/java:\
+$INPUT/dalvik/libcore/xml/src/test/java:\
+$INPUT/dalvik/libcore/x-net/src/test/java"
+
+DALVIK_PACKAGE="\
+junit.framework \
+org.apache.harmony.annotation.tests.java.lang.annotation \
+org.apache.harmony.archive.tests.java.util.jar \
+org.apache.harmony.archive.tests.java.util.zip \
+org.apache.harmony.crypto.tests.javax.crypto \
+org.apache.harmony.crypto.tests.javax.crypto.spec \
+org.apache.harmony.crypto.tests.javax.crypto.interfaces \
+org.apache.harmony.logging.tests.java.util.logging \
+org.apache.harmony.luni.tests.internal.net.www.protocol.http \
+org.apache.harmony.luni.tests.internal.net.www.protocol.https \
+org.apache.harmony.luni.tests.java.io \
+org.apache.harmony.luni.tests.java.lang \
+org.apache.harmony.luni.tests.java.net \
+org.apache.harmony.luni.tests.java.util \
+org.apache.harmony.luni.tests.util \
+org.apache.harmony.math.tests.java.math \
+org.apache.harmony.nio_char.tests.java.nio.charset \
+org.apache.harmony.nio_char.tests.java.nio.charset.spi \
+org.apache.harmony.nio.tests.java.nio \
+org.apache.harmony.nio.tests.java.nio.channels \
+org.apache.harmony.nio.tests.java.nio.channels.spi \
+org.apache.harmony.prefs.tests.java.util.prefs \
+org.apache.harmony.regex.tests.java.util.regex \
+org.apache.harmony.security.tests.java.security \
+org.apache.harmony.sql.tests.java.sql \
+org.apache.harmony.sql.tests.javax.sql \
+tests.SQLite \
+org.apache.harmony.testframework.serialization \
+org.apache.harmony.text.tests.java.text \
+targets \
+tests.api.java.io \
+tests.api.java.lang \
+tests.api.java.lang.ref \
+tests.api.java.lang.reflect \
+tests.api.java.math \
+tests.api.java.net \
+tests.api.java.nio.charset \
+tests.api.java.security \
+tests.api.java.util \
+tests.api.javax.net \
+tests.api.javax.net.ssl \
+tests.api.javax.security.auth \
+tests.api.javax.security.cert \
+tests.api.javax.xml.parsers \
+tests.api.org.xml.sax \
+tests.api.org.xml.sax.ext \
+tests.api.org.xml.sax.helpers \
+tests.api.org.apache.harmony.kernel.dalvik \
+tests.java.security \
+tests.java.sql \
+tests.javax.sql \
+tests.org.w3c.dom \
+tests.security \
+tests.security.acl \
+tests.security.cert \
+tests.security.interfaces \
+tests.security.permissions \
+tests.security.spec \
+tests.sql \
+tests.support \
+tests.targets.security \
+tests.targets.security.cert \
+tests.xml \
+java.io \
+java.lang \
+java.lang.annotation \
+java.lang.ref \
+java.lang.reflect \
+java.math \
+java.net \
+java.nio \
+java.nio.channels \
+java.nio.channels.spi \
+java.nio.charset \
+java.nio.charset.spi \
+java.security \
+java.security.acl \
+java.security.cert \
+java.security.interfaces \
+java.security.spec \
+java.sql \
+java.text \
+java.util \
+java.util.jar \
+java.util.logging \
+java.util.prefs \
+java.util.regex \
+java.util.zip \
+javax.crypto \
+javax.crypto.interfaces \
+javax.crypto.spec \
+javax.net \
+javax.net.ssl \
+javax.security.auth \
+javax.security.auth.callback \
+javax.security.auth.login \
+javax.security.auth.x500 \
+javax.security.cert \
+javax.sql \
+javax.xml.parsers \
+org.xml.sax \
+org.xml.sax.ext \
+org.xml.sax.helpers"
+
+ANDROID_SOURCE_PATH="\
+${INPUT}/frameworks/base/core/java:\
+${INPUT}/frameworks/base/graphics/java:\
+${INPUT}/frameworks/base/location/java:\
+${INPUT}/frameworks/base/media/java:\
+${INPUT}/frameworks/base/opengl/java:\
+${INPUT}/frameworks/base/sax/java:\
+${INPUT}/frameworks/base/telephony/java:\
+${INPUT}/frameworks/base/wifi/java:\
+${INPUT}/cts/tests/tests/app/src:\
+${INPUT}/cts/tests/tests/content/src:\
+${INPUT}/cts/tests/tests/database/src:\
+${INPUT}/cts/tests/tests/graphics/src:\
+${INPUT}/cts/tests/tests/hardware/src:\
+${INPUT}/cts/tests/tests/location/src:\
+${INPUT}/cts/tests/tests/media/src:\
+${INPUT}/cts/tests/tests/net/src:\
+${INPUT}/cts/tests/tests/opengl/src:\
+${INPUT}/cts/tests/tests/os/src:\
+${INPUT}/cts/tests/tests/preference/src:\
+${INPUT}/cts/tests/tests/provider/src:\
+${INPUT}/cts/tests/tests/sax/src:\
+${INPUT}/cts/tests/tests/security/src:\
+${INPUT}/cts/tests/tests/speech/src:\
+${INPUT}/cts/tests/tests/telephony/src:\
+${INPUT}/cts/tests/tests/text/src:\
+${INPUT}/cts/tests/tests/util/src:\
+${INPUT}/cts/tests/tests/view/src:\
+${INPUT}/cts/tests/tests/webkit/src:\
+${INPUT}/cts/tests/tests/widget/src:\
+${INPUT}/cts/tests/src:\
+${INPUT}/frameworks/base/test-runner:\
+${INPUT}/dalvik/libcore/dalvik/src/main/java:\
+${INPUT}/dalvik/libcore/xml/src/main/java:\
+${INPUT}/dalvik/libcore/junit/src/main/java:"
+
+ANDROID_PACKAGE="\
+android.accessibilityservice \
+android.accessibilityservice.cts \
+android.accounts \
+android.accounts.cts \
+android.app \
+android.app.cts \
+android.bluetooth \
+android.content \
+android.content.cts \
+android.content.pm \
+android.content.pm.cts \
+android.content.res \
+android.content.res.cts \
+android.database \
+android.database.cts \
+android.database.sqlite \
+android.database.sqlite.cts \
+android.gesture \
+android.gesture.cts \
+android.graphics \
+android.graphics.cts \
+android.graphics.drawable \
+android.graphics.drawable.cts \
+android.graphics.drawable.shapes \
+android.graphics.drawable.shapes.cts \
+android.hardware \
+android.hardware.cts \
+android.location \
+android.location.cts \
+android.media \
+android.media.cts \
+android.net \
+android.net.cts \
+android.net.http \
+android.net.http.cts \
+android.net.wifi \
+android.net.wifi.cts \
+android.opengl \
+android.opengl.cts \
+android.os \
+android.os.cts \
+android.preference \
+android.preference.cts \
+android.provider \
+android.provider.cts \
+android.sax \
+android.sax.cts \
+android.security \
+android.security.cts \
+android.speech.srec \
+android.speech.srec.cts \
+android.speech.tts \
+android.speech.tts.cts \
+android.telephony \
+android.telephony.cdma \
+android.telephony.cts \
+android.telephony.gsm \
+android.telephony.gsm.cts \
+android.text \
+android.text.cts \
+android.text.format \
+android.text.format.cts \
+android.text.method \
+android.text.method.cts \
+android.text.style \
+android.text.style.cts \
+android.text.util \
+android.text.util.cts \
+android.util \
+android.util.cts \
+android.view \
+android.view.cts \
+android.view.accessibility \
+android.view.accessibility.cts \
+android.view.animation \
+android.view.animation.cts \
+android.view.inputmethod \
+android.view.inputmethod.cts \
+android.webkit \
+android.webkit.cts \
+android.widget \
+android.widget.cts"
+
+ALL_SOURCE_PATH="${DALVIK_SOURCE_PATH}:${ANDROID_SOURCE_PATH}"
+ALL_PACKAGE="${DALVIK_PACKAGE} ${ANDROID_PACKAGE}"
+
+SOURCE_PATH=""
+if [ ${testType} == "all" ]; then
+ SOURCE_PATH="${ALL_SOURCE_PATH} ${ALL_PACKAGE}"
+elif [ ${testType} == "dalvik" ]; then
+ SOURCE_PATH="${DALVIK_SOURCE_PATH} ${DALVIK_PACKAGE}"
+elif [ ${testType} == "android" ]; then
+ SOURCE_PATH="${ANDROID_SOURCE_PATH} ${ANDROID_PACKAGE}"
+fi
+
+javadoc -J-Xmx1024M -docletpath $CLASSES -doclet testprogress2.TestCoverageDoclet -f $TESTCOVERAGE_FLAGS -d $OUTPUT -sourcepath $SOURCE_PATH
+
+# left out on purpose:
+# org.w3c.dom packages: the tests in the xml module are superseded by the
+# official w3c test suite located in the dom module.
+#
+
+# left out on purpose:
+# concurrent packages: a full test suite exists (JCP JSR166 test suite) and it located at
+# device/dalvik/libcore/concurrent/src/test/java/tests/api/java/util/concurrent
+#
+#java.util.concurrent \
+#java.util.concurrent.atomic \
+#java.util.concurrent.locks \
+#
+
+# left out on purpose: (100% coverage/jsr)
+# tests/api/java/util/concurrent
+# tests.api.java.util.concurrent
+
+
+# all test source path found by:
+# find . -type d | sed -e '/.svn/d' | grep "src/test/java" | sed -e 's/test\/java.*/test\/java/g' | sort -u
+
+# the list of packages (test only) was taken from:
+# egrep -R "[ \.]TestCase" dalvik/* | sed -e '/.svn/d' | cut -d':' -f 1 | cut -d'.' -f1 | sed -e's/.*src\/test\/java\///g;s/\/[^\/]*$//g' | sort -u
+
+
diff --git a/tools/test-progress-new/src/Android.mk b/tools/test-progress-new/src/Android.mk
new file mode 100644
index 0000000..00d5904
--- /dev/null
+++ b/tools/test-progress-new/src/Android.mk
@@ -0,0 +1,27 @@
+# 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.
+
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+#LOCAL_MODULE_TAGS := cts
+
+
+LOCAL_SRC_FILES := $(call all-subdir-java-files)
+LOCAL_CLASSPATH := \
+ $(HOST_JDK_TOOLS_JAR)
+
+LOCAL_MODULE:= test-progress-new
+
+include $(BUILD_HOST_JAVA_LIBRARY)
diff --git a/tools/test-progress-new/src/testprogress2/AnnotationPointer.java b/tools/test-progress-new/src/testprogress2/AnnotationPointer.java
new file mode 100644
index 0000000..8f466de
--- /dev/null
+++ b/tools/test-progress-new/src/testprogress2/AnnotationPointer.java
@@ -0,0 +1,59 @@
+/*
+ * 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 testprogress2;
+
+import com.sun.javadoc.ExecutableMemberDoc;
+
+import java.util.ArrayList;
+import java.util.List;
+
+// points from one targetMethod to 0..n testMethods which test the target method
+public class AnnotationPointer {
+ final ExecutableMemberDoc targetMethod;
+
+ private List<TestTargetNew> targets = new ArrayList<TestTargetNew>();
+
+ AnnotationPointer(ExecutableMemberDoc targetMethod) {
+ this.targetMethod = targetMethod;
+ }
+
+ public void addTestTargetNew(TestTargetNew testMethodInfo) {
+ /*
+ * if (testMethods.contains(testMethodInfo)) { throw new
+ * RuntimeException("warn: testMethod refers more than once to the
+ * targetMethod, testMethod="+testMethodInfo.getMethodDoc());
+ * //System.out.println("warn: testMethod refers more than once to the
+ * targetMethod, testMethod="+testMethod); } else {
+ */
+ targets.add(testMethodInfo);
+ // }
+ }
+
+ public List<TestTargetNew> getTargets() {
+ return targets;
+ }
+
+ public void addProxiesFrom(AnnotationPointer ap) {
+ List<TestTargetNew> t = ap.targets;
+ // clone the TestTargetNew and add to it a note from which
+ // target method it orignally stems
+ for (TestTargetNew ttn : t) {
+ TestTargetNew tnew = ttn.cloneMe("<b>(INDIRECTLY tested)</b>");
+ targets.add(tnew);
+ }
+ }
+}
diff --git a/tools/test-progress-new/src/testprogress2/ClassOriginator.java b/tools/test-progress-new/src/testprogress2/ClassOriginator.java
new file mode 100644
index 0000000..378cec7
--- /dev/null
+++ b/tools/test-progress-new/src/testprogress2/ClassOriginator.java
@@ -0,0 +1,68 @@
+/*
+ * 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 testprogress2;
+
+import com.sun.javadoc.ClassDoc;
+
+/**
+ *
+ */
+public class ClassOriginator implements Originator {
+ private final ClassDoc mClassDoc;
+
+ private final String mComment;
+
+ ClassOriginator(ClassDoc classDoc, String comment) {
+ mClassDoc = classDoc;
+ mComment = comment;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see testprogress.Originator#asString()
+ */
+ public String asString() {
+ return (mComment != null ? mComment + " - " : "") + " -class- "
+ + mClassDoc.qualifiedName();
+ }
+
+ public boolean isDisabled() {
+ return false;
+ }
+
+ /**
+ * a reference from a class is never a to be fixed
+ */
+ public String getToBeFixed() {
+ return null;
+ }
+
+ /**
+ * a reference from a class is never a broken tests
+ */
+ public String getBrokenTest() {
+ return null;
+ }
+
+ /**
+ * a reference from a class is never a failure
+ */
+ public String getKnownFailure() {
+ return null;
+ }
+
+}
diff --git a/tools/test-progress-new/src/testprogress2/MethodOriginator.java b/tools/test-progress-new/src/testprogress2/MethodOriginator.java
new file mode 100644
index 0000000..e46b436
--- /dev/null
+++ b/tools/test-progress-new/src/testprogress2/MethodOriginator.java
@@ -0,0 +1,104 @@
+/*
+ * 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 testprogress2;
+
+import com.sun.javadoc.AnnotationDesc;
+import com.sun.javadoc.ClassDoc;
+import com.sun.javadoc.MethodDoc;
+
+/**
+ *
+ */
+public class MethodOriginator implements Originator {
+ private final MethodDoc mMethod;
+
+ private final String mComment;
+
+ private final ClassDoc mClass;
+
+ private String knownFailure = null;
+
+ private String brokenTest = null;
+
+ private String toBeFixed = null;
+
+ /**
+ * @param testMethod
+ * @param clazz we need to provide the clazz since junit collects the
+ * testMethods from all super classes - and thus MethodDoc's
+ * class can point to a superclass. However, we want to know the
+ * class where the TestTargetClass is pointing to the API class.
+ * @param comment
+ */
+ MethodOriginator(MethodDoc testMethod, ClassDoc clazz, String comment) {
+ mMethod = testMethod;
+ mComment = comment;
+ mClass = clazz;
+
+ AnnotationDesc[] annots = testMethod.annotations();
+ for (AnnotationDesc annot : annots) {
+ if (annot.annotationType().qualifiedName().equals(
+ "dalvik.annotation.KnownFailure")) {
+ knownFailure = "<b>@KnownFailure:</b>"
+ + (String)annot.elementValues()[0].value().value();
+ } else if (annot.annotationType().qualifiedName().equals(
+ "dalvik.annotation.BrokenTest")) {
+ brokenTest = "<b>@BrokenTest:</b>"
+ + (String)annot.elementValues()[0].value().value();
+ } else if (annot.annotationType().qualifiedName().equals(
+ "dalvik.annotation.ToBeFixed")) {
+ String info = "N/A";
+ if (annot.elementValues().length > 0) {
+ info = (String)annot.elementValues()[0].value().value();
+ }
+
+ toBeFixed = "<b>@ToBeFixed:</b>" + info;
+ }
+ // else some other annotation - ignore
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see testprogress.Originator#asString()
+ */
+ public String asString() {
+ return (mComment != null ? "comment:" + mComment + " - " : "")
+ + mClass.qualifiedName() + ": <b>" + mMethod.name() + "</b>"
+ + mMethod.signature()
+ + (brokenTest != null ? " [" + brokenTest + "]" : "")
+ + (toBeFixed != null ? " [" + toBeFixed + "]" : "")
+ + (knownFailure != null ? " [" + knownFailure + "]" : "");
+ }
+
+ public boolean isDisabled() {
+ return mMethod.name().startsWith("_test");
+ }
+
+ public String getBrokenTest() {
+ return brokenTest;
+ }
+
+ public String getToBeFixed() {
+ return toBeFixed;
+ }
+
+ public String getKnownFailure() {
+ return knownFailure;
+ }
+
+}
diff --git a/tools/test-progress-new/src/testprogress2/Originator.java b/tools/test-progress-new/src/testprogress2/Originator.java
new file mode 100644
index 0000000..a6e593c
--- /dev/null
+++ b/tools/test-progress-new/src/testprogress2/Originator.java
@@ -0,0 +1,55 @@
+/*
+ * 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 testprogress2;
+
+public interface Originator {
+
+ /**
+ * @return
+ */
+ String asString();
+
+ /**
+ * whether the originating test is disabled
+ *
+ * @return true if the test is disabled (starts with _test...)
+ */
+ boolean isDisabled();
+
+ /**
+ * indicates if the test is to be fixed (annotated with @ToBeFixed
+ *
+ * @return a string containing the annotation, null otherwise
+ */
+ String getToBeFixed();
+
+ /**
+ * indicates if the test is broken (annotated with @BrokenTest
+ *
+ * @return a string containing the annotation, null otherwise
+ */
+ String getBrokenTest();
+
+ /**
+ * indicates if the test is a known failure (runs fine on a RI, annotated
+ * with @KnownFailure)
+ *
+ * @return a string containing the annotation, null otherwise
+ */
+ String getKnownFailure();
+
+}
diff --git a/tools/test-progress-new/src/testprogress2/TestCoverageDoclet.java b/tools/test-progress-new/src/testprogress2/TestCoverageDoclet.java
new file mode 100644
index 0000000..a08a09b
--- /dev/null
+++ b/tools/test-progress-new/src/testprogress2/TestCoverageDoclet.java
@@ -0,0 +1,1365 @@
+/*
+ * 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 testprogress2;
+
+import com.sun.javadoc.AnnotationDesc;
+import com.sun.javadoc.AnnotationTypeDoc;
+import com.sun.javadoc.AnnotationValue;
+import com.sun.javadoc.ClassDoc;
+import com.sun.javadoc.ConstructorDoc;
+import com.sun.javadoc.Doc;
+import com.sun.javadoc.ExecutableMemberDoc;
+import com.sun.javadoc.LanguageVersion;
+import com.sun.javadoc.MethodDoc;
+import com.sun.javadoc.PackageDoc;
+import com.sun.javadoc.ParameterizedType;
+import com.sun.javadoc.RootDoc;
+import com.sun.javadoc.Tag;
+import com.sun.javadoc.AnnotationDesc.ElementValuePair;
+
+import testprogress2.TestMethodInformation.Color;
+import testprogress2.TestMethodInformation.Level;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * this doclet generates a .html report about the test coverage of the core api
+ * reading test method's annotations.
+ */
+public class TestCoverageDoclet {
+
+ /**
+ * color for the stats: green, yellow, red
+ */
+ public static final String[] COLORS = {
+ "#a0ffa0", "#ffffa0", "#ff8080"
+ };
+
+ // debugging output
+ private static final boolean DEBUG = false;
+
+ /**
+ * Holds our basic output directory.
+ */
+ private File directory;
+
+ private boolean _ignoreInterfacesAndAbstractMethods = false;
+
+ private boolean _doIncludeDisabledTests = false;
+
+ private boolean _acceptCompleteWithOtherStatus = false;
+
+ private Map<ExecutableMemberDoc, AnnotationPointer> resolved =
+ new HashMap<ExecutableMemberDoc, AnnotationPointer>(8192);
+
+ /**
+ * Helper class for comparing element with each other, in oder to determine
+ * an order. Uses lexicographic order of names.
+ */
+ private class DocComparator implements Comparator<Doc> {
+ public int compare(Doc elem1, Doc elem2) {
+ return elem1.name().compareTo(elem2.name());
+ }
+
+ public boolean equals(Doc elem) {
+ return this == elem;
+ }
+ }
+
+ private class MemberComparator implements Comparator<ExecutableMemberDoc> {
+ public int compare(ExecutableMemberDoc mem1, ExecutableMemberDoc mem2) {
+ return mem1.toString().compareTo(mem2.toString());
+ }
+ }
+
+ /**
+ * Holds our comparator instance for everything.
+ */
+ private DocComparator classComparator = new DocComparator();
+
+ private MemberComparator memberComparator = new MemberComparator();
+
+ private Map<ClassDoc, List<TestTargetNew>> classToSpecialTargets =
+ new HashMap<ClassDoc, List<TestTargetNew>>();
+
+ /**
+ * Creates a new instance of the TestProgressDoclet for a given target
+ * directory.
+ *
+ * @param directory
+ */
+ public TestCoverageDoclet(String directory) {
+ this.directory = new File(directory);
+ }
+
+ /**
+ * Opens a new output file and writes the usual HTML header. Directories are
+ * created on demand.
+ */
+ private PrintWriter openFile(String filename, String title) {
+ File file = new File(directory, filename);
+ File parent = file.getParentFile();
+ parent.mkdirs();
+
+ PrintWriter printer;
+ try {
+ printer = new PrintWriter(new FileOutputStream(file));
+ } catch (FileNotFoundException e) {
+ throw new RuntimeException("file not found:" + e.getMessage());
+ }
+
+ printer.println("<html>");
+ printer.println(" <head>");
+ printer.println(" <title>" + title + "</title>");
+ printer.println("<style type=\"text/css\">\n"
+ + "body, body table tr td { font-size:10pt;font-family: "
+ + " sans-serif; }\n" + "li { padding-bottom:2px }\n"
+ + "table { width:100%; border-width: 0px; border: solid; "
+ + "border-collapse: collapse;}\n"
+ + "table tr td { vertical-align:top; padding:3px; border: "
+ + "1px solid black;}\n" + "</style>");
+ printer.println(" </head>");
+ printer.println(" <body>");
+ printer.println(" <h1>" + title + "</h1>");
+
+ return printer;
+ }
+
+ /**
+ * Closes the given output file, writing the usual HTML footer before.
+ */
+ private void closeFile(PrintWriter printer) {
+ printer.println(" </body>");
+ printer.println("</html>");
+ printer.flush();
+ printer.close();
+ }
+
+ private class TablePrinter {
+ private PrintWriter pr;
+
+ public TablePrinter(PrintWriter pr) {
+ this.pr = pr;
+ }
+
+ public void printRow(String... columns) {
+ pr.print("<tr style=\"background-color:white\">");
+ for (String col : columns) {
+ pr.print("<td>" + col + "</td>");
+ }
+ pr.print("</tr>");
+ }
+
+ public void printPlain(String val) {
+ pr.print(val);
+ }
+
+ public void printTableStart() {
+ pr.print("<table>");
+ }
+
+ public void printTableEnd() {
+ pr.print("</table>");
+ }
+
+ public void printPlainLn(String val) {
+ printPlain(val);
+ pr.print("<br>");
+ }
+ }
+
+ /**
+ * Processes the whole list of classes that JavaDoc knows about.
+ */
+ private void process(RootDoc root) {
+ System.out.println("V 0.9a");
+ String mode = getOption(root, "-f", 1, "dummy-see bash script");
+ System.out.println("mode: " + mode);
+
+ // standard mode is to include disabled tests
+ _doIncludeDisabledTests = mode.contains("countdisabled");
+ _acceptCompleteWithOtherStatus = mode.contains("acceptcandx");
+ if (_doIncludeDisabledTests) {
+ System.out.println("- including disabled tests");
+ } else {
+ System.out.println("- excluding disabled tests");
+ }
+ if (_acceptCompleteWithOtherStatus) {
+ System.out.println("- accepting complete tests with partial tests");
+ } else {
+ System.out.println("- not accepting complete tests with partial "
+ + "tests");
+ }
+
+ // 1. traverse all test-classes (those extending JUnit's TestCase)
+ // and collect the annotation info.
+ // ===================================================================
+ System.out.println("stage 1 - get targets from all junit test methods");
+ PrintWriter pr = openFile("testcoverage/test-annotation.html",
+ "test class annotation coverage");
+ TablePrinter printer = new TablePrinter(pr);
+ printer.printTableStart();
+ printer.printRow("Test package name", "JUnit classes", "Meth", "Unt",
+ "Part", "Compl", "Disab", "Broken", "ToBeFixed", "KnownFail");
+
+ ColorStat totalStats = new ColorStat("All test packages", null);
+ PackageDoc[] packages = root.specifiedPackages();
+ Arrays.sort(packages, classComparator);
+ for (PackageDoc pack : packages) {
+ if (pack.allClasses().length != 0) {
+ ColorStat packageStat = processTestPackage(pack);
+ if (!packageStat.ignored) {
+ // ignore those packages which have 0 tests in it
+ printTestStats(printer, packageStat, true);
+ totalStats.add(packageStat);
+ }
+ }
+ }
+ printer.printTableEnd();
+
+ printer.printPlainLn("<h2>Summary of all test packages</h2>");
+ printer.printTableStart();
+ printTestStats(printer, totalStats, false);
+ printer.printTableEnd();
+ closeFile(pr);
+
+ System.out.println("stage 2 - proxy test targets to abstract classes"
+ + "and interfaces");
+ // 1/2 - traverse all normal (non-junit) classes for interface
+ // and abstract methods implementation tests.
+ ClassDoc[] classes = root.classes();
+ for (ClassDoc classDoc : classes) {
+ if (!extendsJUnitTestCase(classDoc)) {
+ MethodDoc[] methods = classDoc.methods();
+ for (MethodDoc methodDoc : methods) {
+ AnnotationPointer ap = getAnnotationPointer(methodDoc,
+ false);
+ if (ap != null) {
+ // if there are already tests targeting this method,
+ // search for abstract/interface methods which this
+ // method
+ // implements, and mark them as tested by this method,
+ // too.
+ List<MethodDoc> impls = implementingMethods(methodDoc);
+ for (MethodDoc impl : impls) {
+ AnnotationPointer apImpl = getAnnotationPointer(
+ impl, true);
+ // add all tests which pointed to the original
+ // method.
+ apImpl.addProxiesFrom(ap);
+ }
+ }
+ }
+ }
+ }
+
+ // 2. traverse all "normal" (non-junit) source files
+ // ===================================================================
+ System.out.println("stage 3 - generating report for target api");
+ pr = openFile("index.html", "All target api packages");
+ printer = new TablePrinter(pr);
+ printer.printPlainLn("Generated " + new Date().toString()
+ + " - V0.9a<br>");
+ printer.printPlainLn("<a href=\"testcoverage/test-annotation.html\">"
+ + "annotation progress of test classes</a><br><br>");
+
+ printer.printTableStart();
+ printer.printRow("Package", "Classes", "Methods", "Untested",
+ "Partial", "Complete", "Disabled", "Broken", "ToBeFixed", "KnownFail");
+
+ totalStats = new ColorStat("All target packages", null);
+ packages = root.specifiedPackages();
+ Arrays.sort(packages, classComparator);
+
+ int classCount = 0;
+ for (PackageDoc pack : packages) {
+ if (pack.allClasses().length != 0) {
+ ColorStat packageStat = processPackage(pack);
+
+ if (!packageStat.ignored) {
+ printStats(printer, packageStat, true);
+ totalStats.add(packageStat);
+
+ classCount += Integer.parseInt(packageStat.getExtra());
+ }
+ }
+ }
+ printer.printTableEnd();
+
+ totalStats.setExtra("" + classCount);
+ printer.printPlainLn("<h2>Summary of all target packages</h2>");
+ printer.printTableStart();
+ printStats(printer, totalStats, false);
+ printer.printTableEnd();
+ closeFile(pr);
+ }
+
+ /**
+ * Returns the interface(s) method(s) and the abstract method(s) that a
+ * given method implements, or an empty list if it does not implement
+ * anything.
+ */
+ private List<MethodDoc> implementingMethods(MethodDoc doc) {
+ List<MethodDoc> resultmethods = new ArrayList<MethodDoc>();
+ ClassDoc clazz = doc.containingClass();
+ implementedMethod0(resultmethods, doc, clazz, false);
+ return resultmethods;
+ }
+
+ /**
+ * Recursive helper method for finding out which interface methods or
+ * abstract methods a given method implements.
+ */
+ private void implementedMethod0(List<MethodDoc> resultmethods,
+ MethodDoc doc, ClassDoc testClass, boolean testMethods) {
+
+ // test all methods of this class
+ if (testMethods) {
+ MethodDoc[] methods = testClass.methods();
+ for (int j = 0; j < methods.length; j++) {
+ MethodDoc methodDoc = methods[j];
+ if ((methodDoc.isAbstract() || testClass.isInterface())
+ && doc.overrides(methodDoc)) {
+ resultmethods.add(methodDoc);
+ }
+ }
+ }
+
+ // test all implementing interfaces
+ ClassDoc[] ifs = testClass.interfaces();
+ for (int i = 0; i < ifs.length; i++) {
+ ClassDoc iface = ifs[i];
+ implementedMethod0(resultmethods, doc, iface, true);
+ }
+
+ // test the superclass
+ ClassDoc superclass = testClass.superclass();
+ if (superclass != null) {
+ implementedMethod0(resultmethods, doc, superclass, true);
+ }
+ }
+
+ private ColorStat processTestPackage(PackageDoc packageDoc) {
+ ColorStat stats = new ColorStat(packageDoc.name(),
+ getPackageBaseLink(packageDoc) + "/package.html");
+ if (hasHideFlag(packageDoc)) {
+ stats.ignored = true;
+ return stats;
+ }
+ String file = getPackageDir("testcoverage", packageDoc)
+ + "/package.html";
+ PrintWriter pr = openFile(file, "Test package " + packageDoc.name());
+ TablePrinter printer = new TablePrinter(pr);
+ printer.printTableStart();
+ printer.printRow("Class", "Extra", "Meth", "Unt", "Part", "Compl",
+ "Disab", "Broken", "ToBeFixed", "KnownFail");
+
+ ClassDoc[] classes = packageDoc.allClasses();
+ Arrays.sort(classes, classComparator);
+ int junitCnt = 0;
+ for (ClassDoc clazz : classes) {
+ if (extendsJUnitTestCase(clazz)) {
+ junitCnt++;
+ ColorStat subStats = processTestClass(clazz);
+ printTestStats(printer, subStats, true);
+ stats.add(subStats);
+ } else {
+ printer.printRow(clazz.name() + " ignored (no junit class): ",
+ "", "", "", "", "", "", "", "");
+ }
+ }
+ printer.printTableEnd();
+
+ printer.printPlainLn("<h2>Test package summary</h2>");
+ printer.printTableStart();
+ printStats(printer, stats, false);
+ printer.printTableEnd();
+
+ closeFile(pr);
+ if (junitCnt == 0) {
+ if ((packageDoc.name().contains("tests.")
+ || packageDoc.name().contains("junit.") || packageDoc
+ .name().contains(".testframework"))
+ && !(packageDoc.name().equals("junit.framework"))) {
+ System.err.println("warning!: no junit classes in package '"
+ + packageDoc.name() + "' even though package name "
+ + "contains tests.,junit. or .testframework");
+ }
+ stats = new ColorStat(packageDoc.name(),
+ getPackageBaseLink(packageDoc) + "/package.html");
+ stats.incColor(TestMethodInformation.Color.GREEN);
+ stats.setExtra("Ignored since no Junit test and suites");
+ stats.ignored = true;
+ } else {
+ stats.setExtra("" + junitCnt);
+ }
+ return stats;
+ }
+
+ private ColorStat processPackage(PackageDoc packageDoc) {
+ ColorStat stats = new ColorStat(packageDoc.name(),
+ getPackageBaseLink(packageDoc) + "/package.html");
+ if (hasHideFlag(packageDoc)) {
+ stats.ignored = true;
+ return stats;
+ }
+ String file = getPackageDir("", packageDoc) + "/package.html";
+ PrintWriter pr = openFile(file, "Package " + packageDoc.name());
+ TablePrinter printer = new TablePrinter(pr);
+ printer.printTableStart();
+ printer.printRow("Class", "Extra", "Meth", "Unt", "Part", "Compl",
+ "Disab", "Broken", "ToBeFixed", "KnownFail");
+
+ ClassDoc[] classes = packageDoc.allClasses();
+ Arrays.sort(classes, classComparator);
+ int cnt = 0;
+ int junitCnt = 0;
+ for (ClassDoc clazz : classes) {
+ cnt++;
+ if (hasHideFlag(clazz)) {
+ // ignored since it has a @hide in the javadoc on the class
+ // level
+ } else if (extendsJUnitTestCase(clazz)) {
+ printer.printRow("ignored (junit class): " + clazz.name());
+ junitCnt++;
+ } else if (clazz.name().equals("AllTests")) {
+ printer.printRow("ignored (junit test suite class): "
+ + clazz.name());
+ junitCnt++;
+ } else {
+ ColorStat subStats = processClass(clazz);
+ printStats(printer, subStats, true);
+ stats.add(subStats);
+ }
+ }
+ printer.printTableEnd();
+
+ printer.printPlainLn("<h2>Target package summary</h2>");
+ printer.printTableStart();
+ printStats(printer, stats, false);
+ printer.printTableEnd();
+
+ closeFile(pr);
+ if (junitCnt == cnt || packageDoc.name().contains("tests.")
+ || packageDoc.name().contains("junit.")
+ || packageDoc.name().contains(".testframework")
+ || packageDoc.name().endsWith(".cts")) {
+ // we only have junit classes -> mark green
+ stats = new ColorStat(packageDoc.name(),
+ getPackageBaseLink(packageDoc) + "/package.html");
+ stats.incColor(TestMethodInformation.Color.GREEN);
+ stats
+ .setExtra(junitCnt == cnt ? "Ignored since only Junit test and "
+ + "suites"
+ : "Ignored since \"tests.\" in name - recheck");
+ stats.ignored = true;
+ } else {
+ stats.setExtra("" + cnt);
+ }
+ return stats;
+ }
+
+ private boolean hasHideFlag(Doc doc) {
+ // workaround for the non-recognized @hide tag in package.html
+ if (doc instanceof PackageDoc) {
+ String comment = doc.getRawCommentText();
+ return comment != null && comment.contains("@hide");
+ } else {
+ Tag[] hideTags = doc.tags("hide");
+ return hideTags.length > 0;
+ }
+ }
+
+ private ColorStat processTestClass(ClassDoc clazz) {
+ String file = getPackageDir("testcoverage", clazz.containingPackage())
+ + "/" + clazz.name() + ".html";
+ PrintWriter pr = openFile(file, "Test class " + clazz.qualifiedName());
+ TablePrinter printer = new TablePrinter(pr);
+ ColorStat classStat = new ColorStat(clazz.name(), clazz.name()
+ + ".html");
+
+ TestTargetClass testTargetClass = getTargetClass(clazz);
+ ClassDoc targetClass = testTargetClass.targetClass;
+
+ String note = "Note:";
+ if (targetClass == null) {
+ note += "<br>targetClass annotation missing!<br>";
+ } else {
+ // add untested[] annotations to statistics
+ ClassOriginator co = new ClassOriginator(clazz, null);
+
+ AnnotationDesc[] annotsC = testTargetClass.untestedMethods
+ .toArray(new AnnotationDesc[] {});
+ if (annotsC.length > 0) {
+ // we have "untested" refs
+ ColorStat classLevelStat = new ColorStat(clazz.name(), null);
+ TestMethodInformation tminfo = new TestMethodInformation(co,
+ annotsC, targetClass);
+ if (tminfo.getError() != null) {
+ printer.printPlainLn("<b>Error:</b>" + tminfo.getError());
+ classLevelStat.incColor(Color.RED);
+ } else {
+ linkTargets(tminfo.getTargets());
+ classLevelStat.incColor(Color.GREEN);
+ }
+ classStat.add(classLevelStat);
+ }
+ }
+
+ printer.printPlainLn(note);
+
+ printer.printTableStart();
+ printer.printRow("Method", "Note", "Meth", "Unt", "Part", "Compl",
+ "Disab", "Broken", "ToBeFixed", "KnownFail");
+
+ int methodCnt = 0;
+ // also collects test... methods from all superclasses below TestCase,
+ // since those are called as well
+ List<MethodDoc> testMethods = collectAllTestMethods(clazz);
+ Collections.sort(testMethods, memberComparator);
+
+ for (MethodDoc testMethod : testMethods) {
+ methodCnt++;
+
+ // Make disabled tests visible in the report so we don't forget
+ // them.
+ boolean disTest = testMethod.name().startsWith("_test");
+
+ ColorStat methodStat = new ColorStat(testMethod.name(), null);
+ if (disTest) {
+ methodStat.incDisabledTestsCnt();
+ }
+ String comments = disTest ? "<b><span style=\"background:red\">"
+ + "DISABLED</span></b>" : null;
+
+ MethodOriginator mo = new MethodOriginator(testMethod, clazz,
+ comments);
+ AnnotationDesc[] annots = testMethod.annotations();
+ TestMethodInformation minfo = new TestMethodInformation(mo, annots,
+ targetClass);
+ linkTargets(minfo.getTargets());
+
+ String extra = null;
+ if (comments != null) {
+ if (extra == null)
+ extra = "";
+ extra += comments;
+ }
+
+ if (minfo.getError() != null) { // error case
+ if (extra == null)
+ extra = "";
+ extra += "<b>Error:</b> " + minfo.getError() + "<br>";
+ methodStat.addMethodInfo(minfo);
+ } else {
+ if (mo.getKnownFailure() != null) {
+ methodStat.incKnownFailureCnt();
+ if (extra == null)
+ extra = "";
+ extra += mo.getKnownFailure();
+ }
+
+ // check for @BrokenTest
+ if (mo.getBrokenTest() != null) {
+ // override with warning
+ methodStat.incBrokenTestCnt();
+ methodStat.incColor(Color.YELLOW);
+ if (extra == null)
+ extra = "";
+ extra += mo.getBrokenTest();
+ }
+
+ // check for @ToBeFixed
+ if (mo.getToBeFixed() != null) {
+ // override with warning
+ methodStat.incToBeFixedCnt();
+ methodStat.incColor(Color.YELLOW);
+ if (extra == null) {
+ extra = "";
+ }
+
+ extra += mo.getToBeFixed();
+ } else { // add regular stats
+ methodStat.addMethodInfo(minfo);
+ }
+ }
+ if (extra != null) {
+ methodStat.setExtra(extra);
+ }
+
+ printTestStats(printer, methodStat, false);
+ classStat.add(methodStat);
+ }
+ printer.printTableEnd();
+
+ printer.printPlainLn("<h2>Test class summary</h2>");
+ printer.printTableStart();
+ printStats(printer, classStat, false);
+ printer.printTableEnd();
+
+ closeFile(pr);
+ classStat.setExtra("#methods: " + testMethods.size());
+ return classStat;
+ }
+
+ private void linkTargets(List<TestTargetNew> targets) {
+ for (TestTargetNew ttn : targets) {
+ if (ttn.getTargetMethod() != null) {
+ AnnotationPointer tar = getAnnotationPointer(ttn
+ .getTargetMethod(), true);
+ tar.addTestTargetNew(ttn);
+ } else if (ttn.getTargetClass() != null) {
+ // some special target only pointing to a class, not a method.
+ addToClassTargets(ttn.getTargetClass(), ttn);
+ }
+ }
+ }
+
+ private boolean isGreen(TestMethodInformation.Level level) {
+ boolean lComplete = level == TestMethodInformation.Level.COMPLETE;
+ boolean lSufficient = level == TestMethodInformation.Level.SUFFICIENT;
+ boolean lPartialOk = level == TestMethodInformation.Level.PARTIAL_COMPLETE;
+ boolean lPartial = level == TestMethodInformation.Level.PARTIAL;
+ boolean lTodo = level == TestMethodInformation.Level.TODO;
+ boolean lNotFeasible = level == TestMethodInformation.Level.NOT_FEASIBLE;
+ boolean lNotNecessary = level == TestMethodInformation.Level.NOT_NECESSARY;
+
+ return lComplete || lPartialOk || lSufficient || lNotFeasible
+ || lNotNecessary;
+ }
+
+ private ColorStat processClass(ClassDoc clazz) {
+ String file = getPackageDir("", clazz.containingPackage()) + "/"
+ + clazz.name() + ".html";
+ String classDesc = getClassString(clazz);
+ PrintWriter pr = openFile(file, classDesc);
+ TablePrinter printer = new TablePrinter(pr);
+ printer.printPlain("<b>package " + clazz.containingPackage() + "</b>");
+ ColorStat classStats = new ColorStat(classDesc, clazz.name() + ".html");
+
+ // list special targets
+ List<TestTargetNew> classTargets = getTargetsFor(clazz);
+ if (classTargets != null) {
+ printer.printPlain("<h3>Class level tests</h3>");
+ printer.printPlain("<ul>");
+ for (TestTargetNew ttn : classTargets) {
+ String line = "<li>" + ttn.getOriginator().asString();
+ Level lev = ttn.getLevel();
+ line += " <font color=\""
+ + (isGreen(lev) ? "green" : "red")
+ + "\"><b>"
+ + lev.name()
+ + "</b></font>"
+ + (ttn.getNotes() != null ? "<br>Notes: "
+ + ttn.getNotes() : "") + "</li>";
+ printer.printPlain(line);
+ }
+ printer.printPlainLn("</ul>");
+ }
+
+ printer.printPlain("<h3>Method level tests</h3>");
+ printer.printTableStart();
+ printer.printRow("Method", "Tested by", "Meth", "Unt", "Part", "Compl",
+ "Disab", "Broken", "ToBeFixed", "KnownFail");
+ ConstructorDoc[] constructors = clazz.constructors();
+ Arrays.sort(constructors, classComparator);
+ int cnt = 0;
+ for (ConstructorDoc constructor : constructors) {
+ if (!hasHideFlag(constructor) && !hasHideFlag(clazz)) {
+ cnt++;
+ ColorStat memberStat = processElement(constructor);
+ printStats(printer, memberStat, false);
+ classStats.add(memberStat);
+ }
+ }
+
+ MethodDoc[] methods = clazz.methods();
+ Arrays.sort(methods, classComparator);
+ for (MethodDoc method : methods) {
+ if (!hasHideFlag(method) && !hasHideFlag(clazz)) {
+ cnt++;
+ ColorStat subStat = processElement(method);
+ printStats(printer, subStat, false);
+ classStats.add(subStat);
+ }
+ }
+ printer.printTableEnd();
+
+ printer.printPlainLn("<h2>Target class summary</h2>");
+ printer.printTableStart();
+ printStats(printer, classStats, false);
+ printer.printTableEnd();
+
+ closeFile(pr);
+ classStats.setExtra("#methods: " + cnt);
+
+ // mark as green
+ if (_ignoreInterfacesAndAbstractMethods && clazz.isInterface()) {
+ classStats = new ColorStat(clazz.name()
+ + (clazz.isInterface() ? " (Interface)" : ""), clazz.name()
+ + ".html");
+ int mcnt = clazz.methods().length;
+ // fake all methods to green
+ for (int i = 0; i < mcnt; i++) {
+ classStats.incColor(TestMethodInformation.Color.GREEN);
+ }
+ classStats.setExtra("Ignored since interface");
+ }
+ return classStats;
+ }
+
+ private class TestTargetClass {
+ ClassDoc targetClass;
+
+ List<AnnotationDesc> untestedMethods = new ArrayList<AnnotationDesc>();
+ // a List of @TestTargetNew annotations
+ }
+
+ private TestTargetClass getTargetClass(ClassDoc classDoc) {
+ // get the class annotation which names the default test target class
+ TestTargetClass ttc = new TestTargetClass();
+ ClassDoc targetClass = null;
+ AnnotationDesc[] cAnnots = classDoc.annotations();
+ for (AnnotationDesc cAnnot : cAnnots) {
+ AnnotationTypeDoc atype = cAnnot.annotationType();
+ if (atype.toString().equals("dalvik.annotation.TestTargetClass")) {
+ ElementValuePair[] cpairs = cAnnot.elementValues();
+ for (int i = 0; i < cpairs.length; i++) {
+ ElementValuePair ev = cpairs[i];
+ String elName = ev.element().name();
+ if (elName.equals("value")) {
+ // the target class
+ AnnotationValue av = ev.value();
+ Object obj = av.value();
+ // value must be a class doc
+ if (obj instanceof ClassDoc) {
+ targetClass = (ClassDoc)obj;
+ } else if (obj instanceof ParameterizedType) {
+ targetClass = ((ParameterizedType)obj).asClassDoc();
+ } else
+ throw new RuntimeException(
+ "annotation elem value is of type "
+ + obj.getClass().getName());
+ } else if (elName.equals("untestedMethods")) {
+ // TestTargetNew[] untestedMethods() default {};
+ AnnotationValue[] targets = (AnnotationValue[])ev
+ .value().value();
+ for (AnnotationValue ttn : targets) {
+ // each untested method must be a TestTargetNew
+ AnnotationDesc ttnd = (AnnotationDesc)ttn.value();
+ ttc.untestedMethods.add(ttnd);
+ }
+ }
+ }
+ }
+ }
+ ttc.targetClass = targetClass;
+ return ttc;
+ }
+
+ private List<MethodDoc> collectAllTestMethods(ClassDoc classDoc) {
+ List<MethodDoc> m = new ArrayList<MethodDoc>();
+
+ ClassDoc curCl = classDoc;
+ do {
+ m.addAll(getJunitTestMethods(curCl));
+ } while ((curCl = curCl.superclass()) != null
+ && !curCl.qualifiedName().equals("junit.framework.TestCase"));
+ return m;
+ }
+
+ private List<MethodDoc> getJunitTestMethods(ClassDoc classDoc) {
+ List<MethodDoc> cl = new ArrayList<MethodDoc>();
+ for (MethodDoc methodDoc : classDoc.methods()) {
+ if (methodDoc.isPublic()
+ && (methodDoc.name().startsWith("test") || methodDoc.name()
+ .startsWith("_test"))) {
+ cl.add(methodDoc);
+ }
+ }
+ return cl;
+ }
+
+ private class ColorStat {
+ private String name;
+
+ private String link;
+
+ private String extra;
+
+ public boolean ignored;
+
+ private int[] cntCol = new int[4];
+
+ private int disabledTestsCnt = 0;
+
+ private int brokenTestCnt = 0;
+
+ private int toBeFixedCnt = 0;
+
+ private int knownFailureCnt = 0;
+
+ public String getName() {
+ return name;
+ }
+
+ public String getLink() {
+ return link;
+ }
+
+ public ColorStat(String name, String link) {
+ this.name = name;
+ this.link = link;
+ }
+
+ public void add(ColorStat subStat) {
+ for (int i = 0; i < cntCol.length; i++) {
+ cntCol[i] += subStat.cntCol[i];
+ }
+ disabledTestsCnt += subStat.disabledTestsCnt;
+ brokenTestCnt += subStat.brokenTestCnt;
+ toBeFixedCnt += subStat.toBeFixedCnt;
+ knownFailureCnt += subStat.knownFailureCnt;
+ }
+
+ public void incDisabledTestsCnt() {
+ disabledTestsCnt++;
+ }
+
+ public void incBrokenTestCnt() {
+ brokenTestCnt++;
+ }
+
+ public void incToBeFixedCnt() {
+ toBeFixedCnt++;
+ }
+
+ public void incKnownFailureCnt() {
+ knownFailureCnt++;
+ }
+
+ public void incColor(TestMethodInformation.Color color) {
+ cntCol[color.ordinal()]++;
+ }
+
+ public int getColorCnt(TestMethodInformation.Color color) {
+ return cntCol[color.ordinal()];
+ }
+
+ public void addMethodInfo(TestMethodInformation minfo) {
+ TestMethodInformation.Color c = minfo.getColor();
+ int ord = c.ordinal();
+ cntCol[ord]++;
+ }
+
+ public void setExtra(String extra) {
+ this.extra = extra;
+ }
+
+ public String getExtra() {
+ return extra;
+ }
+
+ public int getDisabledTestsCnt() {
+ return disabledTestsCnt;
+ }
+
+ public int getBrokenTestCnt() {
+ return brokenTestCnt;
+ }
+
+ public int getToBeFixedCnt() {
+ return toBeFixedCnt;
+ }
+
+ public int getKnownFailureCnt() {
+ return knownFailureCnt;
+ }
+ }
+
+ private AnnotationPointer getAnnotationPointer(
+ ExecutableMemberDoc targetMethod, boolean create) {
+ AnnotationPointer ap = resolved.get(targetMethod);
+ if (create && ap == null) {
+ ap = new AnnotationPointer(targetMethod);
+ resolved.put(targetMethod, ap);
+ }
+ return ap;
+ }
+
+ private void addToClassTargets(ClassDoc targetClass, TestTargetNew ttn) {
+ List<TestTargetNew> targets = classToSpecialTargets.get(targetClass);
+ if (targets == null) {
+ targets = new ArrayList<TestTargetNew>();
+ classToSpecialTargets.put(targetClass, targets);
+ }
+ targets.add(ttn);
+ }
+
+ private List<TestTargetNew> getTargetsFor(ClassDoc targetClass) {
+ return classToSpecialTargets.get(targetClass);
+ }
+
+ private boolean extendsJUnitTestCase(ClassDoc classDoc) {
+ // junit.framework.TestCase.java
+ ClassDoc curClass = classDoc;
+ while ((curClass = curClass.superclass()) != null) {
+ if (curClass.toString().equals("junit.framework.TestCase")) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Processes a single method/constructor.
+ */
+ private ColorStat processElement(ExecutableMemberDoc method) {
+ if (DEBUG) System.out.println("Processing " + method);
+ ColorStat memberStats = new ColorStat(getMethodString(method), null);
+ TestMethodInformation.Color c = TestMethodInformation.Color.RED;
+ // if flagged, consider abstract method ok also without tests
+ if (_ignoreInterfacesAndAbstractMethods && method instanceof MethodDoc
+ && ((MethodDoc)method).isAbstract()) {
+ c = TestMethodInformation.Color.GREEN;
+ memberStats.setExtra("ignored since abstract");
+ } else {
+ AnnotationPointer ap = getAnnotationPointer(method, false);
+ int testedByCnt = 0;
+ if (ap != null) {
+ List<TestTargetNew> targets = ap.getTargets();
+ testedByCnt = targets.size();
+ if (testedByCnt == 0) {
+ throw new RuntimeException(
+ "existing annotation pointer with no entries!, "
+ + "method:" + method);
+ }
+ // at least tested by one method
+ String by = "<ul>";
+ int completeTestCnt = 0;
+ int partialOkTestCnt = 0;
+ int partialTestCnt = 0;
+ int todoTestCnt = 0;
+ int notFeasableTestCnt = 0;
+ int notNecessaryTestCnt = 0;
+ int sufficientTestCnt = 0;
+ // int totalCnt = targets.size();
+
+ for (TestTargetNew target : targets) {
+ Originator originator = target.getOriginator();
+ boolean disabledTest = originator.isDisabled();
+ boolean brokenTest = originator.getBrokenTest() != null;
+ boolean toBeFixed = originator.getToBeFixed() != null;
+ boolean knownFailure = originator.getKnownFailure() != null;
+ by += "<li>" + originator.asString();
+ TestMethodInformation.Level lev;
+ if (target.isHavingProblems()) {
+ lev = TestMethodInformation.Level.TODO;
+ } else {
+ lev = target.getLevel();
+ }
+ // TODO: improve: avoid c&p by adding ColorStat.addInfo
+ // (originator) or similar
+ if (disabledTest) {
+ memberStats.incDisabledTestsCnt();
+ }
+ if (brokenTest) {
+ memberStats.incBrokenTestCnt();
+ }
+ if (toBeFixed) {
+ memberStats.incToBeFixedCnt();
+ }
+ if (knownFailure) {
+ memberStats.incKnownFailureCnt();
+ }
+
+ boolean lComplete = lev == TestMethodInformation.Level.COMPLETE;
+ boolean lSufficient = lev == TestMethodInformation.Level.SUFFICIENT;
+ boolean lPartialOk = lev == TestMethodInformation.Level.PARTIAL_COMPLETE;
+ boolean lPartial = lev == TestMethodInformation.Level.PARTIAL;
+ boolean lTodo = lev == TestMethodInformation.Level.TODO;
+ boolean lNotFeasible = lev == TestMethodInformation.Level.NOT_FEASIBLE;
+ boolean lNotNecessary = lev == TestMethodInformation.Level.NOT_NECESSARY;
+
+ by += " <font color=\""
+ + (lComplete || lPartialOk || lSufficient
+ || lNotFeasible || lNotNecessary ? "green"
+ : "red")
+ + "\"><b>"
+ + lev.name()
+ + "</b></font>"
+ + (target.getNotes() != null ? "<br>Notes: "
+ + target.getNotes() : "");
+
+ // only count tests when they are either ok (not disabled)
+ // or if the doIncludeDisabledTests flag is set
+ if ((_doIncludeDisabledTests || !disabledTest)
+ && (!brokenTest) && (!toBeFixed)) {
+ if (lComplete) {
+ completeTestCnt++;
+ } else if (lPartialOk) {
+ partialOkTestCnt++;
+ } else if (lPartial) {
+ partialTestCnt++;
+ } else if (lTodo) {
+ todoTestCnt++;
+ } else if (lSufficient) {
+ sufficientTestCnt++;
+ } else if (lNotFeasible) {
+ notFeasableTestCnt++;
+ } else if (lNotNecessary) {
+ notNecessaryTestCnt++;
+ }
+ }
+
+ if (toBeFixed) {
+ partialTestCnt++;
+ }
+
+ if (DEBUG) {
+ System.out.println("completeTestCnt: " + completeTestCnt
+ + ", partialOkTestCnt: " + partialOkTestCnt
+ + ", partialTestCnt: " + partialTestCnt
+ + ", todoTestCnt: " + todoTestCnt
+ + ", sufficientTestCnt: " + sufficientTestCnt
+ + ", notFeasableTestCnt: " + notFeasableTestCnt
+ + ", notNecessaryTestCnt: " + notNecessaryTestCnt);
+ }
+ by += "</li>";
+ }
+
+ String warnings = "";
+ // calculate the color
+ int singularTestCnt = notFeasableTestCnt + notNecessaryTestCnt;
+ boolean isAbstract = (method.containingClass().isInterface() ||
+ (method instanceof MethodDoc) && ((MethodDoc)method).isAbstract());
+
+ if (_acceptCompleteWithOtherStatus
+ && (completeTestCnt > 0 || sufficientTestCnt > 0)) {
+ c = TestMethodInformation.Color.GREEN;
+ } else if (_acceptCompleteWithOtherStatus
+ && (partialOkTestCnt > 1)) {
+ c = TestMethodInformation.Color.GREEN;
+ } else {
+ if (singularTestCnt > 0) {
+ // we have tests which claim not_neccessary or
+ // not_feasible
+ if (targets.size() > singularTestCnt) {
+ // other tests exist
+ c = TestMethodInformation.Color.RED;
+ warnings += "<b>WARNING:</b>NOT_FEASIBLE or "
+ + "NOT_NECESSARY together with other "
+ + "status!<br>";
+ } else {
+ // a collection of not_necessary and/or not_feasible
+ if (notNecessaryTestCnt > 0
+ && notFeasableTestCnt > 0) {
+ // a blend of both -> error
+ warnings += "<b>WARNING:</b>both NOT_FEASIBLE "
+ + "and NOT_NECESSARY together!<br>";
+ c = TestMethodInformation.Color.RED;
+ } else { // just one sort of tests -> ok and
+ // sufficent
+ c = TestMethodInformation.Color.GREEN;
+ }
+ }
+ } else if (todoTestCnt > 0) {
+ c = TestMethodInformation.Color.RED;
+ } else if (partialTestCnt > 0) {
+ // at least one partial test
+ c = TestMethodInformation.Color.YELLOW;
+ if (completeTestCnt > 0 || sufficientTestCnt > 0) {
+ if (_acceptCompleteWithOtherStatus) {
+ // accept complete even if there are partials
+ c = TestMethodInformation.Color.GREEN;
+ } else if (completeTestCnt > 0) {
+ // yellow+warning: mixed PARTIAL_COMPLETE and
+ // COMPLETE status
+ warnings += "<b>WARNING</b>: mixed PARTIAL "
+ + "and COMPLETE status<br>";
+ }
+ }
+ } else if (partialOkTestCnt > 0 || sufficientTestCnt > 0) {
+ // at least one partialOk test and no partial tests
+ c = TestMethodInformation.Color.GREEN;
+ if (partialOkTestCnt == 1) {
+ // yellow: 1 PARTIAL_COMPLETE (we need either zero
+ // or >=2 PARTIAL_OK tests)
+ warnings += "<b>WARNING</b>: only one "
+ + "PARTIAL_COMPLETE status<br>";
+ c = TestMethodInformation.Color.YELLOW;
+ }
+ } else if (completeTestCnt > 0 || singularTestCnt == 1) {
+ // only complete tests
+ c = TestMethodInformation.Color.GREEN;
+ }
+
+ if (completeTestCnt > 1 && !isAbstract
+ && !_acceptCompleteWithOtherStatus) {
+ // green+warning: >1 COMPLETE (we need either 0 or 1
+ // COMPLETE, more would be strange, but possible)
+ warnings += "<b>WARNING</b>: more than one "
+ + "COMPLETE status<br>";
+ if (c != TestMethodInformation.Color.RED) {
+ c = TestMethodInformation.Color.YELLOW;
+ }
+ }
+ }
+ by = warnings + by;
+ memberStats.setExtra(by);
+ } else { // else this class has no single test that targets the
+ // current method
+ // handle special cases:
+
+ // can be ok if the current method is a default constructor
+ // which does not occur in the source code.
+ if (method.isConstructor() && method.signature().equals("()")) {
+ if (method.position() != null) {
+ // hacky stuff - the doclet should return null for a
+ // default constructor,
+ // but instead returns a source position pointing to the
+ // same location as
+ // the class.
+ String constPos = method.position().toString();
+ String classPos = method.containingClass().position()
+ .toString();
+ if (constPos.equals(classPos)) {
+ // we have a default constructor not visible in
+ // source code -> mark green, no testing needed
+ c = TestMethodInformation.Color.GREEN;
+ memberStats
+ .setExtra("automatically marked green "
+ + "since implicit default "
+ + "constructor");
+ }
+ } else {
+ // should be called for default constructors according
+ // to the doclet spec, but is never called.
+ // spec:
+ // "A default constructor returns null because it has no
+ // location in the source file"
+ // ??
+ // what about default constructors being in the source
+ // code?
+ System.err.println("warning: doclet returned null for "
+ + "source position: method:" + method);
+ }
+ } else if (method.containingClass().superclass() != null
+ && method.containingClass().superclass()
+ .qualifiedName().equals("java.lang.Enum")) {
+ // check enum types
+ // <anyreturnclass> valueOf (java.lang.String) and
+ // <anyreturnclass[]> values()
+ // do not need to be tested, since they are autogenerated
+ // by the compiler
+ String sig = method.name() + method.signature();
+ if (sig.equals("valueOf(java.lang.String)")
+ || sig.equals("values()")) {
+ c = TestMethodInformation.Color.GREEN;
+ memberStats
+ .setExtra("automatically marked green since "
+ + "generated by compiler for enums");
+ }
+ }
+ }
+ }
+ memberStats.incColor(c);
+ return memberStats;
+ }
+
+ private String getMethodString(ExecutableMemberDoc method) {
+ String methodDesc = (method.isPublic() ? "public " : method
+ .isProtected() ? "protected " : method.isPrivate() ? "private "
+ : "");
+
+ return methodDesc + "<b>" + method.name() + "</b> "
+ + method.signature();
+ }
+
+ private String getClassString(ClassDoc clazz) {
+ return (clazz.isPublic() ? "public "
+ : clazz.isProtected() ? "protected "
+ : clazz.isPrivate() ? "private " : "")
+ + (clazz.isInterface() ? "interface" : "class")
+ + " "
+ + clazz.name();
+ }
+
+ private void printTestStats(TablePrinter printer, ColorStat stat,
+ boolean wantLink) {
+ printStats(printer, stat, wantLink);
+ }
+
+ private void printStats(TablePrinter printer, ColorStat stat,
+ boolean wantLink) {
+ int redCnt = stat.getColorCnt(TestMethodInformation.Color.RED);
+ int yellowCnt = stat.getColorCnt(TestMethodInformation.Color.YELLOW);
+ int greenCnt = stat.getColorCnt(TestMethodInformation.Color.GREEN);
+ int disabledCnt = stat.getDisabledTestsCnt();
+ int brokenTestCnt = stat.getBrokenTestCnt();
+ int toBeFixedCnt = stat.getToBeFixedCnt();
+ int knownFailureCnt = stat.getKnownFailureCnt();
+
+ int total = redCnt + yellowCnt + greenCnt;
+
+ String link = stat.getLink();
+ String namePart;
+ if (wantLink && link != null) {
+ namePart = "<a href=\"" + link + "\">" + stat.getName() + "</a>";
+ } else {
+ namePart = stat.getName();
+ }
+
+ String extra = stat.getExtra() == null ? "" : stat.getExtra();
+
+ int totalDots = 120;
+
+ float toP = total == 0 ? 0 : (((float)totalDots) / total);
+
+ int redD = (int)(toP * redCnt);
+ if (redD == 0 && redCnt > 0) {
+ // never let red cut to zero;
+ redD = 1;
+ }
+ int yellowD = (int)(toP * yellowCnt);
+ if (yellowD == 0 && yellowCnt > 0) {
+ yellowD = 1;
+ }
+ int greenD = totalDots - redD - yellowD; // (int)(toP*greenCnt);
+
+ printer.printRow(namePart, extra, "" + total, "" + redCnt, ""
+ + yellowCnt, "" + greenCnt, "" + disabledCnt, ""
+ + brokenTestCnt, "" + toBeFixedCnt, "" + knownFailureCnt, ""
+ + (redCnt == 0 ? "" : "<span style=\"background:"
+ + COLORS[TestMethodInformation.Color.RED.ordinal()]
+ + "\">" + getDots(redD) + "</span>")
+ + (yellowCnt == 0 ? "" : "<span style=\"background:"
+ + COLORS[TestMethodInformation.Color.YELLOW.ordinal()]
+ + "\">" + getDots(yellowD) + "</span>")
+ + (greenCnt == 0 && total > 0 ? ""
+ : "<span style=\"background:"
+ + COLORS[TestMethodInformation.Color.GREEN
+ .ordinal()] + "\">" + getDots(greenD)
+ + "</span>")
+ + " <span style=\"background:blue\">"
+ + getDots(total / 10) + "</span>");
+ }
+
+ private String getDots(int cnt) {
+ StringBuffer sb = new StringBuffer();
+ for (int i = 0; i < cnt; i++) {
+ sb.append(" ");
+ }
+ return sb.toString();
+ }
+
+ /**
+ * Returns the directory for a given package. Basically converts embedded
+ * dots in the name into slashes.
+ */
+
+ private String getPackageBaseLink(PackageDoc pack) {
+ return pack.name().replace('.', '/');
+ }
+
+ private File getPackageDir(String prefix, PackageDoc pack) {
+ if (pack == null || pack.name() == null || "".equals(pack.name())) {
+ return new File(prefix + "/" + ".");
+ } else {
+ return new File(prefix + "/" + pack.name().replace('.', '/'));
+ }
+ }
+
+ /**
+ * Called by JavaDoc to find our which command line arguments are supported
+ * and how many parameters they take. Part of the JavaDoc API.
+ *
+ * @param option the options
+ * @return an int
+ */
+ public static int optionLength(String option) {
+ if ("-d".equals(option)) {
+ return 2;
+ }
+ if ("-f".equals(option)) {
+ return 2;
+ } else {
+ return 0;
+ }
+ }
+
+ /**
+ * Called by JavaDoc to query a specific command line argument. Part of the
+ * JavaDoc API.
+ */
+ private static String getOption(RootDoc root, String option, int index,
+ String defValue) {
+ String[][] allOptions = root.options();
+ for (int i = 0; i < allOptions.length; i++) {
+ if (allOptions[i][0].equals(option)) {
+ return allOptions[i][index];
+ }
+ }
+ return defValue;
+ }
+
+ /**
+ * Called by JavaDoc to find out which Java version we claim to support.
+ * Part of the JavaDoc API.
+ *
+ * @return the version of the language
+ */
+ public static LanguageVersion languageVersion() {
+ return LanguageVersion.JAVA_1_5;
+ }
+
+ /**
+ * The main entry point called by JavaDoc after all required information has
+ * been collected. Part of the JavaDoc API.
+ *
+ * @param root
+ * @return whether an error occurred
+ */
+ public static boolean start(RootDoc root) {
+ try {
+ String target = getOption(root, "-d", 1, ".");
+ TestCoverageDoclet doclet = new TestCoverageDoclet(target);
+ doclet.process(root);
+
+ File file = new File(target, "index.html");
+ System.out.println("Please see complete report in " +
+ file.getAbsolutePath());
+
+ } catch (Exception ex) {
+ ex.printStackTrace();
+ return false;
+ }
+ return true;
+ }
+
+}
diff --git a/tools/test-progress-new/src/testprogress2/TestMethodInformation.java b/tools/test-progress-new/src/testprogress2/TestMethodInformation.java
new file mode 100644
index 0000000..c609423
--- /dev/null
+++ b/tools/test-progress-new/src/testprogress2/TestMethodInformation.java
@@ -0,0 +1,132 @@
+/*
+ * 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 testprogress2;
+
+import com.sun.javadoc.AnnotationDesc;
+import com.sun.javadoc.AnnotationValue;
+import com.sun.javadoc.ClassDoc;
+import com.sun.javadoc.AnnotationDesc.ElementValuePair;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * represents a list of testtargets annotations all belonging to one test method
+ * / one testclass together with its processed information.
+ */
+public class TestMethodInformation {
+ private boolean annotationExists = false;
+
+ private List<TestTargetNew> targets = new ArrayList<TestTargetNew>();
+
+ private String error = null;
+
+ private Color color = Color.RED;
+
+ public enum Level {
+ TODO, PARTIAL, PARTIAL_COMPLETE, COMPLETE, ADDITIONAL, NOT_NECESSARY, NOT_FEASIBLE, SUFFICIENT
+ }
+
+ public enum Color {
+ GREEN /* ready */, YELLOW /* work */, RED
+ /* missing essential stuff */
+ }
+
+ public TestMethodInformation(Originator originator,
+ AnnotationDesc[] annots, ClassDoc targetClass) {
+ // System.out.println("looking at "+testMethodDoc);
+ if (targetClass == null) {
+ addError("target class annotation missing!");
+ return;
+ }
+
+ for (AnnotationDesc annot : annots) {
+ if (annot.annotationType().qualifiedName().equals(
+ "dalvik.annotation.TestTargets")) {
+ // multi target case
+ annotationExists = true;
+ ElementValuePair[] pairs = annot.elementValues();
+ if (pairs.length != 1
+ && !pairs[0].element().qualifiedName().equals(
+ "dalvik.annotation.TestTargets.value")) {
+ throw new RuntimeException("TestTargets has mismatched "
+ + "attributes");
+ }
+ AnnotationValue[] targets = (AnnotationValue[])pairs[0].value()
+ .value();
+ for (AnnotationValue ttn : targets) {
+ // the test targets must be annotations themselves
+ AnnotationDesc ttnd = (AnnotationDesc)ttn.value();
+ handleTestTargetNew(originator, ttnd, targetClass);
+ }
+ } else if (annot.annotationType().qualifiedName().equals(
+ "dalvik.annotation.TestTargetNew")) {
+ // singular case
+ annotationExists = true;
+ handleTestTargetNew(originator, annot, targetClass);
+ } // else some other annotation - ignore
+ }
+
+ boolean targetsCorrect = true;
+ for (TestTargetNew ttn : targets) {
+ targetsCorrect &= (ttn.getTargetMethod() != null || ttn
+ .getTargetClass() != null);
+ }
+
+ // calculate color of test method
+ if (annotationExists) {
+ if (targetsCorrect) {
+ color = Color.GREEN;
+ } // else incorrect targets
+ } else {
+ addError("no annotation!");
+ }
+ }
+
+ private void handleTestTargetNew(Originator originator, AnnotationDesc ttn,
+ ClassDoc targetClass) {
+ TestTargetNew testTarget = new TestTargetNew(originator, ttn,
+ targetClass);
+ if (testTarget.isHavingProblems()) {
+ // add to overall message
+ addError(testTarget.getNotes());
+ }
+ targets.add(testTarget);
+ }
+
+ private void addError(String err) {
+ if (error == null)
+ error = "";
+ error += err + " ; ";
+ }
+
+ /**
+ * @return the error
+ */
+ public String getError() {
+ return error;
+ }
+
+ public List<TestTargetNew> getTargets() {
+ return targets;
+ }
+
+ public Color getColor() {
+ return color;
+ }
+
+}
diff --git a/tools/test-progress-new/src/testprogress2/TestTargetNew.java b/tools/test-progress-new/src/testprogress2/TestTargetNew.java
new file mode 100644
index 0000000..6102614
--- /dev/null
+++ b/tools/test-progress-new/src/testprogress2/TestTargetNew.java
@@ -0,0 +1,373 @@
+/*
+ * 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 testprogress2;
+
+import com.sun.javadoc.AnnotationDesc;
+import com.sun.javadoc.AnnotationValue;
+import com.sun.javadoc.ClassDoc;
+import com.sun.javadoc.ExecutableMemberDoc;
+import com.sun.javadoc.FieldDoc;
+import com.sun.javadoc.Parameter;
+import com.sun.javadoc.ParameterizedType;
+import com.sun.javadoc.Type;
+import com.sun.javadoc.TypeVariable;
+import com.sun.javadoc.AnnotationDesc.ElementValuePair;
+
+import testprogress2.TestMethodInformation.Level;
+
+/**
+ * holder for a TestTargetNew annotation
+ */
+public class TestTargetNew {
+ private final Originator originator;
+
+ private Level level = null;
+
+ private String notes = null;
+
+ /*
+ * method or constructor of target class
+ */
+ private ExecutableMemberDoc targetMethod = null;
+
+ /*
+ * only set if the target points -only- to a class, not to a method. e.g for
+ * special "!..." targets
+ */
+ private ClassDoc targetClass = null;
+
+ /*
+ * read from annotation, e.g. foobar(java.lang.String)
+ */
+ private String readMethodSignature = null;
+
+ /*
+ * e.g. foobar
+ */
+ private String readMethodName = null;
+
+ /*
+ * read from annotation
+ */
+ private ClassDoc readTargetClass = null;
+
+ private boolean havingProblems = false;
+
+ private TestTargetNew(Originator originator) {
+ this.originator = originator;
+ }
+
+ /**
+ * @param originator the origin (class or method)
+ * @param ttn the annotation (testtargetnew)
+ * @param classLevelTargetClass the default target class as given in the
+ * testtargetclass annotation
+ */
+ public TestTargetNew(Originator originator, AnnotationDesc ttn,
+ ClassDoc classLevelTargetClass) {
+ this.originator = originator;
+ parseTargetClassAndMethodSignature(ttn, classLevelTargetClass);
+ // post: readMethod, readMethodSignature and readTargetClass are now set
+
+ // test for artificial method targets
+ if (readMethodName.startsWith("!")) {
+ targetMethod = null;
+ targetClass = readTargetClass;
+ // level = Level.ADDITIONAL;
+ // notes already set
+ notes = "target: " + readMethodName
+ + (notes != null ? ", " + "notes: " + notes : "");
+
+ } else if (level == Level.TODO) {
+ notes = "TODO :" + notes;
+ havingProblems = true;
+ } else {
+ // prepare method target:
+ // if the signature contains a "." then the prefix is used as a
+ // reference
+ // to an inner class. This is an alternative to using the clazz
+ // attribute in cases where the class is an inner protected class,
+ // because then the inner class is not visible for the compiler at
+ // the
+ // place of the annotation.
+ // e.g. clazz = Certificate.CertificateRep.class does not work,
+ // so we use clazz = Certificate.class (enclosing class), and method
+ // "Certificate.CertificateRep.<methodHere>", e.g.
+ // "CertificateRep.CertificateRep"
+ // to denote the constructor of the inner protected class
+ // CertificateRep
+ // within Certificate
+ int dotPos = readMethodName.lastIndexOf('.');
+ if (dotPos != -1) {
+ String prefixClassName = readMethodName.substring(0, dotPos);
+ readMethodName = readMethodName.substring(dotPos + 1);
+ ClassDoc[] iCs = readTargetClass.innerClasses();
+ for (ClassDoc iC : iCs) {
+ if (iC.name().equals(prefixClassName)) {
+ readTargetClass = iC;
+ break;
+ }
+ }
+ }
+
+ String methodAndSig = readMethodName + readMethodSignature;
+ ExecutableMemberDoc tmeth = findMethodSignatureIn(methodAndSig,
+ readTargetClass);
+ // we need this double test for the note below
+ if (tmeth == null) {
+ // a) wrong signature or
+ // b) a testMethod in a superclass or superinterface, ok also
+ tmeth = findTargetMethodInSelfAndSupers(methodAndSig,
+ readTargetClass);
+ if (tmeth != null) {
+ if (notes == null)
+ notes = "";
+ notes += "- targetmethod (" + tmeth + ") was found in a "
+ + "superclass/superinterface of the target<br>";
+ }
+ }
+ if (tmeth != null) {
+ // found
+ targetMethod = tmeth;
+ } else {
+ havingProblems = true;
+ notes = "From " + originator.asString()
+ + " -> could not resolve " + "targetMethod for class "
+ + readTargetClass + ", " + "annotation was:" + ttn
+ + ", testMethodSig " + "= " + methodAndSig + "<br>";
+ System.err.println(">>> warning: " + notes);
+ }
+ }
+ }
+
+ private ExecutableMemberDoc findMethodSignatureIn(String sig,
+ ClassDoc targetClass) {
+ ExecutableMemberDoc targetMethod = null;
+ // find the matching method in the target class, check all methods
+ for (ExecutableMemberDoc mdoc : targetClass.methods()) {
+ if (equalsSignature(mdoc, sig)) {
+ return mdoc;
+ }
+ }
+ // check constructors, too
+ for (ExecutableMemberDoc mdoc : targetClass.constructors()) {
+ if (equalsSignature(mdoc, sig)) {
+ return mdoc;
+ }
+ }
+ return null;
+ }
+
+ private ExecutableMemberDoc findTargetMethodInSelfAndSupers(String sig,
+ ClassDoc targetClass) {
+ ExecutableMemberDoc mem = findMethodSignatureIn(sig, targetClass);
+ if (mem != null) {
+ return mem;
+ }
+
+ // else visit parent class or parent interface(s)
+ ClassDoc[] ifs = targetClass.interfaces();
+ for (int i = 0; i < ifs.length; i++) {
+ ClassDoc iface = ifs[i];
+ mem = findTargetMethodInSelfAndSupers(sig, iface);
+ if (mem != null) {
+ return mem;
+ }
+ }
+
+ ClassDoc superclass = targetClass.superclass();
+ if (superclass != null) {
+ mem = findTargetMethodInSelfAndSupers(sig, superclass);
+ if (mem != null) {
+ return mem;
+ }
+ }
+ return null;
+ }
+
+ private void parseTargetClassAndMethodSignature(AnnotationDesc targetAnnot,
+ ClassDoc targetClass) {
+ ElementValuePair[] pairs = targetAnnot.elementValues();
+ String methodName = null;
+ String args = "";
+ for (ElementValuePair kval : pairs) {
+ if (kval.element().name().equals("method")) {
+ methodName = (String)kval.value().value();
+ } else if (kval.element().name().equals("clazz")) {
+ // optional: a different target class than the test-class-level
+ // default.
+ Object obj = kval.value().value();
+ if (obj instanceof ClassDoc) {
+ targetClass = (ClassDoc)obj;
+ } else if (obj instanceof ParameterizedType) {
+ targetClass = ((ParameterizedType)obj).asClassDoc();
+ } else {
+ throw new RuntimeException("annotation elem value is of "
+ + "type " + obj.getClass().getName() + " target "
+ + "annotation = " + targetAnnot);
+ }
+ } else if (kval.element().name().equals("args")) {
+ AnnotationValue[] vals = (AnnotationValue[])kval.value()
+ .value();
+ for (int i = 0; i < vals.length; i++) {
+ AnnotationValue arg = vals[i];
+ String argV;
+ // TODO: we should be able to use Type.asClassDoc() here
+ if (arg.value() instanceof ClassDoc) {
+ ClassDoc cd = (ClassDoc)arg.value();
+ argV = cd.qualifiedName();
+ } else { // primitive type or array type
+ // is there a nicer way to do this?
+ argV = arg.toString();
+ }
+ // strip .class out of args since signature does not contain
+ // those
+ if (argV.endsWith(".class")) {
+ argV = argV.substring(0, argV.length() - 6);
+ }
+ args += (i > 0 ? "," : "") + argV;
+ }
+ } else if (kval.element().name().equals("level")) {
+ AnnotationValue lev = kval.value();
+ FieldDoc fd = (FieldDoc)lev.value();
+ String slevel = fd.name();
+
+ try {
+ level = Enum.valueOf(Level.class, slevel);
+ } catch (IllegalArgumentException iae) {
+ throw new RuntimeException("COMPILE ERROR!!! enum "
+ + slevel + " used in targetMethod for class "
+ + "\"+targetClass+\", "
+ + "annotation was:\"+targetAnnot+\", "
+ + "testMethod = \"+methodDoc.toString()");
+ }
+ } else if (kval.element().name().equals("notes")) {
+ notes = (String)kval.value().value();
+ if (notes.equals("")) {
+ notes = null;
+ }
+ }
+ }
+
+ // String refSig = methodName + "(" + args + ")";
+ // both methodName and methodArgs != null because of Annotation
+ // definition
+ this.readTargetClass = targetClass;
+ this.readMethodSignature = "(" + args + ")";
+ this.readMethodName = methodName;
+ }
+
+ private boolean equalsSignature(ExecutableMemberDoc mdoc,
+ String refSignature) {
+ Parameter[] params = mdoc.parameters();
+ String targs = "";
+ for (int i = 0; i < params.length; i++) {
+ Parameter parameter = params[i];
+ // check for generic type types
+ Type ptype = parameter.type();
+
+ TypeVariable typeVar = ptype.asTypeVariable();
+ String ptname;
+ if (typeVar != null) {
+ ptname = "java.lang.Object"; // the default fallback
+ Type[] bounds = typeVar.bounds();
+ if (bounds.length > 0) {
+ ClassDoc typeClass = bounds[0].asClassDoc();
+ ptname = typeClass.qualifiedName();
+ }
+ String dim = ptype.dimension();
+ if (dim != null && dim.length() > 0) {
+ ptname += dim;
+ }
+ } else {
+ // regular var
+ // ptname = parameter.type().qualifiedTypeName();
+ ptname = parameter.type().toString();
+
+ // System.out.println("quali:"+ptname);
+ // ptname = parameter.typeName();
+ // omit type signature
+ ptname = ptname.replaceAll("<.*>", "");
+ }
+ targs += (i > 0 ? "," : "") + ptname;
+ }
+
+ String methodName = mdoc.name();
+ int lastDot = methodName.lastIndexOf('.');
+ if (lastDot != -1) {
+ // we have a inner class constructor
+ // shrink the name to just name the constructor
+ methodName = methodName.substring(lastDot + 1);
+ }
+
+ String testSig = methodName + "(" + targs + ")";
+
+ // return testSig.equals(refSignature);
+ if (testSig.equals(refSignature)) {
+ // System.out.println("match!!!: ref = "+refSignature+",
+ // test = "+testSig);
+ return true;
+ } else {
+ // System.out.println("no match: ref = "+refSignature+",
+ // test = "+testSig);
+ return false;
+ }
+ }
+
+ public Level getLevel() {
+ return level;
+ }
+
+ public boolean isHavingProblems() {
+ return havingProblems;
+ }
+
+ public Originator getOriginator() {
+ return originator;
+ }
+
+ TestTargetNew cloneMe(String extraNote) {
+ TestTargetNew anew = new TestTargetNew(this.originator);
+ anew.level = this.level;
+ anew.notes = this.notes;
+ anew.targetMethod = this.targetMethod;
+ anew.readMethodSignature = this.readMethodSignature;
+ anew.readTargetClass = this.readTargetClass;
+
+ // mark indirectly tested method always as green, independent
+ // of the original status (to better estimate workload)
+ // anew.level = Level.COMPLETE;
+ anew.notes = extraNote + (notes != null ? ", " + notes : "");
+ return anew;
+ }
+
+ public ExecutableMemberDoc getTargetMethod() {
+ return targetMethod;
+ }
+
+ /**
+ * @return the class of the testtargetnew which method starts with "!", null
+ * otherwise
+ */
+ public ClassDoc getTargetClass() {
+ return targetClass;
+ }
+
+ public String getNotes() {
+ return notes;
+ }
+}
diff --git a/tools/test-progress/Android.mk b/tools/test-progress/Android.mk
new file mode 100644
index 0000000..0cdab2e
--- /dev/null
+++ b/tools/test-progress/Android.mk
@@ -0,0 +1,15 @@
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+LOCAL_IS_HOST_MODULE := true
+LOCAL_MODULE_CLASS := EXECUTABLES
+LOCAL_MODULE := test-progress
+
+include $(BUILD_SYSTEM)/base_rules.mk
+
+$(LOCAL_BUILT_MODULE): $(LOCAL_PATH)/etc/test-progress | $(ACP)
+ @echo "Copy: $(PRIVATE_MODULE) ($@)"
+ $(copy-file-to-new-target)
+ $(hide) chmod 755 $@
+
+include $(LOCAL_PATH)/src/Android.mk
diff --git a/tools/test-progress/etc/test-progress b/tools/test-progress/etc/test-progress
new file mode 100644
index 0000000..5e42cc5
--- /dev/null
+++ b/tools/test-progress/etc/test-progress
@@ -0,0 +1,324 @@
+#!/bin/bash
+#
+# 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.
+
+# 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/hat.jar "$@"
+
+#######################################################################
+# Original content of invocation script follows. Uses values cleverly
+# deduced by the above code. If you want to use this for a different
+# set of packages, adjust both the list of source directories and the
+# list of packages.
+#######################################################################
+export CLASSES=$progdir/../framework/test-progress.jar
+export INPUT=$ANDROID_BUILD_TOP
+export OUTPUT=$ANDROID_BUILD_TOP/out/target/common/cts/test-progress
+
+if [ "$1" != "" ]; then
+ export OUTPUT=$1
+fi
+
+# These two variables are defined for run the coverage tool for Dalvik or CTS
+DALVIK_SOURCE_PATH="\
+${INPUT}/dalvik/libcore/junit/src/main/java:\
+${INPUT}/dalvik/libcore/icu/src/main/java:\
+${INPUT}/dalvik/libcore/openssl/src/main/java:\
+${INPUT}/dalvik/libcore/android/src/main/java:\
+${INPUT}/dalvik/libcore/annotation/src/main/java:\
+${INPUT}/dalvik/libcore/archive/src/main/java:\
+${INPUT}/dalvik/libcore/auth/src/main/java:\
+${INPUT}/dalvik/libcore/awt/src/main/java:\
+${INPUT}/dalvik/libcore/beans/src/main/java:\
+${INPUT}/dalvik/libcore/concurrent/src/main/java:\
+${INPUT}/dalvik/libcore/crypto/src/main/java:\
+${INPUT}/dalvik/libcore/instrument/src/main/java:\
+${INPUT}/dalvik/libcore/logging/src/main/java:\
+${INPUT}/dalvik/libcore/luni/src/main/java:\
+${INPUT}/dalvik/libcore/luni-kernel/src/main/java:\
+${INPUT}/dalvik/libcore/math/src/main/java:\
+${INPUT}/dalvik/libcore/nio/src/main/java:\
+${INPUT}/dalvik/libcore/nio_char/src/main/java:\
+${INPUT}/dalvik/libcore/prefs/src/main/java:\
+${INPUT}/dalvik/libcore/regex/src/main/java:\
+${INPUT}/dalvik/libcore/security/src/main/java:\
+${INPUT}/dalvik/libcore/security-kernel/src/main/java:\
+${INPUT}/dalvik/libcore/sound/src/main/java:\
+${INPUT}/dalvik/libcore/sql/src/main/java:\
+${INPUT}/dalvik/libcore/text/src/main/java:\
+${INPUT}/dalvik/libcore/xml/src/main/java:\
+${INPUT}/dalvik/libcore/x-net/src/main/java:\
+${INPUT}/java/android:\
+${INPUT}/tests/framework-tests/src:\
+${INPUT}/apps/AndroidTests/src:\
+${INPUT}/apps/TestHarness/tests:\
+${INPUT}/apps/TestHarness/src:\
+${INPUT}/java/tests:\
+${INPUT}/dalvik/libcore/text/src/test/java:\
+${INPUT}/dalvik/libcore/support/src/test/java:\
+${INPUT}/dalvik/libcore/nio_char/src/test/java:\
+${INPUT}/dalvik/libcore/nio/src/test/java:\
+${INPUT}/dalvik/libcore/regex/src/test/java:\
+${INPUT}/dalvik/libcore/xml/src/test/java:\
+${INPUT}/dalvik/libcore/prefs/src/test/java:\
+${INPUT}/dalvik/libcore/x-net/src/test/java:\
+${INPUT}/dalvik/libcore/luni-kernel/src/test/java:\
+${INPUT}/dalvik/libcore/sql/src/test/java:\
+${INPUT}/dalvik/libcore/security/src/test/java:\
+${INPUT}/dalvik/libcore/math/src/test/java:\
+${INPUT}/dalvik/libcore/logging/src/test/java:\
+${INPUT}/dalvik/libcore/luni/src/test/java:\
+${INPUT}/dalvik/libcore/archive/src/test/java:\
+${INPUT}/dalvik/libcore/annotation/src/test/java:\
+${INPUT}/dalvik/libcore/dalvik/src/main/java:\
+${INPUT}/tests/HttpClient:\
+${INPUT}/apps/Mms/tests:\
+ \
+SQLite \
+junit.framework \
+org.apache.harmony.logging.tests.java.util.logging \
+org.apache.harmony.luni.tests.java.io \
+org.apache.harmony.luni.tests.java.lang \
+org.apache.harmony.luni.tests.java.lang.ref \
+org.apache.harmony.luni.tests.java.net \
+org.apache.harmony.luni.tests.java.util \
+org.apache.harmony.nio.tests.java.nio \
+org.apache.harmony.nio.tests.java.nio.channels \
+org.apache.harmony.nio.tests.java.nio.channels.spi \
+org.apache.harmony.nio_char.tests.java.nio.charset \
+org.apache.harmony.nio_char.tests.java.nio.charset.spi \
+org.apache.harmony.prefs.tests.java.util.prefs \
+org.apache.harmony.sql.tests.java.sql \
+org.apache.harmony.sql.tests.javax.sql \
+org.apache.harmony.tests.java.math \
+org.apache.harmony.tests.java.util.regex \
+org.apache.harmony.text.tests.java.text \
+tests.api.android.dalvik \
+tests.api.java.io \
+tests.api.java.lang \
+tests.api.java.lang.ref \
+tests.api.java.lang.reflect \
+tests.api.java.math \
+tests.api.java.net \
+tests.api.java.nio.charset \
+tests.api.java.util \
+tests.api.javax.net \
+tests.api.javax.net.ssl \
+tests.api.javax.xml.parsers \
+tests.java.lang.StrictMath \
+tests.java.sql \
+tests.sql \
+tests.xml \
+java.awt \
+java.awt.color \
+java.awt.event \
+java.awt.font \
+java.awt.geom \
+java.awt.im \
+java.awt.im.spi \
+java.awt.image \
+java.awt.image.renderable \
+javax.imageio \
+javax.imageio.event \
+javax.imageio.metadata \
+javax.imageio.plugins.bmp \
+javax.imageio.plugins.jpeg \
+javax.imageio.spi \
+javax.imageio.stream \
+java.io \
+java.lang \
+java.lang.annotation \
+java.lang.instrument \
+java.lang.ref \
+java.lang.reflect \
+java.math \
+java.net \
+java.nio \
+java.nio.channels \
+java.nio.channels.spi \
+java.nio.charset \
+java.nio.charset.spi \
+java.security \
+java.security.acl \
+java.security.cert \
+java.security.interfaces \
+java.security.spec \
+java.sql \
+java.text \
+java.util \
+java.util.concurrent \
+java.util.concurrent.atomic \
+java.util.concurrent.locks \
+java.util.jar \
+java.util.logging \
+java.util.prefs \
+java.util.regex \
+java.util.zip \
+javax.crypto \
+javax.crypto.interfaces \
+javax.crypto.spec \
+javax.net \
+javax.net.ssl \
+javax.security.auth \
+javax.security.auth.callback \
+javax.security.auth.login \
+javax.security.auth.x500 \
+javax.security.cert \
+javax.sound.midi \
+javax.sound.midi.spi \
+javax.sound.sampled \
+javax.sound.sampled.spi \
+javax.sql \
+javax.xml.parsers \
+org.w3c.dom \
+org.xml.sax \
+org.xml.sax.ext \
+org.xml.sax.helpers"
+
+CTS_SOURCE_PATH="\
+${INPUT}/frameworks/base/core/java:\
+${INPUT}/frameworks/base/graphics/java:\
+${INPUT}/frameworks/base/location/java:\
+${INPUT}/frameworks/base/media/java:\
+${INPUT}/frameworks/base/opengl/java:\
+${INPUT}/frameworks/base/sax/java:\
+${INPUT}/frameworks/base/telephony/java:\
+${INPUT}/frameworks/base/wifi/java:\
+${INPUT}/cts/tests/tests/app/src:\
+${INPUT}/cts/tests/tests/content/src:\
+${INPUT}/cts/tests/tests/database/src:\
+${INPUT}/cts/tests/tests/graphics/src:\
+${INPUT}/cts/tests/tests/hardware/src:\
+${INPUT}/cts/tests/tests/location/src:\
+${INPUT}/cts/tests/tests/media/src:\
+${INPUT}/cts/tests/tests/net/src:\
+${INPUT}/cts/tests/tests/os/src:\
+${INPUT}/cts/tests/tests/provider/src:\
+${INPUT}/cts/tests/tests/preference/src:\
+${INPUT}/cts/tests/tests/telephony/src:\
+${INPUT}/cts/tests/tests/text/src:\
+${INPUT}/cts/tests/tests/util/src:\
+${INPUT}/cts/tests/tests/view/src:\
+${INPUT}/cts/tests/tests/widget/src:\
+${INPUT}/cts/tests/src:\
+${INPUT}/frameworks/base/test-runner:\
+${INPUT}/dalvik/libcore/dalvik/src/main/java:\
+${INPUT}/dalvik/libcore/xml/src/main/java:\
+${INPUT}/dalvik/libcore/junit/src/main/java:\
+ \
+android.app \
+android.app.cts \
+android.content \
+android.content.cts \
+android.content.pm \
+android.content.pm.cts \
+android.content.res \
+android.content.res.cts \
+android.location \
+android.location.cts \
+android.os \
+android.os.cts \
+android.text \
+android.text.cts \
+android.text.method \
+android.text.method.cts \
+android.text.style \
+android.text.style.cts \
+android.text.util \
+android.text.util.cts \
+android.util \
+android.util.cts \
+android.view \
+android.view.cts \
+android.view.animation \
+android.view.animation.cts \
+android.view.inputmethod \
+android.view.inputmethod.cts \
+android.widget \
+android.widget.cts \
+android.hardware \
+android.hardware.cts \
+android.net \
+android.net.cts \
+android.net.http \
+android.net.http.cts \
+android.net.wifi \
+android.net.wifi.cts \
+android.provider \
+android.provider.cts \
+android.bluetooth \
+android.bluetooth.cts \
+android.database \
+android.database.cts \
+android.database.sqlite \
+android.database.sqlite.cts \
+android.graphics \
+android.graphics.cts \
+android.graphics.drawable \
+android.graphics.drawable.cts \
+android.graphics.drawable.shapes \
+android.graphics.drawable.shapes.cts \
+android.media \
+android.media.cts \
+android.telephony \
+android.telephony.cts \
+android.telephony.gsm \
+android.telephony.gsm.cts \
+android.accounts \
+android.accounts.cts \
+android.opengl \
+android.opengl.cts \
+android.preference \
+android.preference.cts \
+android.sax \
+android.sax.cts \
+android.security \
+android.security.cts \
+android.speech.srec \
+android.speech.srec.cts \
+android.webkit \
+android.webkit.cts \
+android.webkit.gears \
+android.webkit.gears.cts"
+
+javadoc -J-Xmx512m -docletpath $CLASSES -doclet TestCoverageDoclet -d $OUTPUT -sourcepath ${CTS_SOURCE_PATH}
diff --git a/tools/test-progress/src/Android.mk b/tools/test-progress/src/Android.mk
new file mode 100644
index 0000000..0428e48
--- /dev/null
+++ b/tools/test-progress/src/Android.mk
@@ -0,0 +1,14 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+#LOCAL_MODULE_TAGS := cts
+
+LOCAL_SRC_FILES := \
+ TestCoverageDoclet.java
+
+LOCAL_CLASSPATH := \
+ $(HOST_JDK_TOOLS_JAR)
+
+LOCAL_MODULE:= test-progress
+
+include $(BUILD_HOST_JAVA_LIBRARY)
diff --git a/tools/test-progress/src/TestCoverageDoclet.java b/tools/test-progress/src/TestCoverageDoclet.java
new file mode 100644
index 0000000..c6bb7d0
--- /dev/null
+++ b/tools/test-progress/src/TestCoverageDoclet.java
@@ -0,0 +1,823 @@
+/*
+ * 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.
+ */
+
+import com.sun.javadoc.AnnotationDesc;
+import com.sun.javadoc.AnnotationTypeDoc;
+import com.sun.javadoc.AnnotationValue;
+import com.sun.javadoc.ClassDoc;
+import com.sun.javadoc.ConstructorDoc;
+import com.sun.javadoc.Doc;
+import com.sun.javadoc.ExecutableMemberDoc;
+import com.sun.javadoc.LanguageVersion;
+import com.sun.javadoc.MethodDoc;
+import com.sun.javadoc.PackageDoc;
+import com.sun.javadoc.Parameter;
+import com.sun.javadoc.ParameterizedType;
+import com.sun.javadoc.RootDoc;
+import com.sun.javadoc.SourcePosition;
+import com.sun.javadoc.Tag;
+import com.sun.javadoc.Type;
+import com.sun.javadoc.TypeVariable;
+import com.sun.javadoc.AnnotationDesc.ElementValuePair;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/*
+ */
+public class TestCoverageDoclet {
+
+ public static final int TYPE_FIELD = 0;
+ public static final int TYPE_METHOD = 1;
+ public static final int TYPE_CLASS = 2;
+ public static final int TYPE_PACKAGE = 3;
+ public static final int TYPE_ROOT = 4;
+ public static final int VALUE_RED = 0;
+ public static final int VALUE_YELLOW = 1;
+ public static final int VALUE_GREEN = 2;
+ public static final String[] COLORS = { "#ffa0a0", "#ffffa0", "#a0ffa0" };
+ public static final String[] TYPES = { "Field", "Method", "Class", "Package", "All packages" };
+
+ /**
+ * Holds our basic output directory.
+ */
+ private File directory;
+
+ private Map<ExecutableMemberDoc, AnnotationPointer> resolved =
+ new HashMap<ExecutableMemberDoc, AnnotationPointer>(8192);
+
+ /**
+ * Helper class for comparing element with each other, in oder to determine
+ * an order. Uses lexicographic order of names.
+ */
+ private class DocComparator implements Comparator<Doc> {
+ public int compare(Doc elem1, Doc elem2) {
+ return elem1.name().compareTo(elem2.name());
+ }
+
+ public boolean equals(Doc elem) {
+ return this == elem;
+ }
+ }
+
+ private class MemberComparator implements Comparator<ExecutableMemberDoc> {
+ public int compare(ExecutableMemberDoc mem1, ExecutableMemberDoc mem2) {
+ return mem1.toString().compareTo(mem2.toString());
+ }
+ }
+
+ class MyStats {
+ private String name;
+ private String link;
+ private int elemCnt = 0;
+ private int[] ryg = new int[3];
+ private String extra;
+
+ public MyStats(int type, String name, String link) {
+ this.name = name;
+ this.link = link;
+ }
+
+ public void add(MyStats subStats) {
+ elemCnt++;
+ for (int i = 0; i < ryg.length; i++) {
+ ryg[i]+= subStats.ryg[i];
+ }
+ }
+
+ public int getCountFor(int color) {
+ return ryg[color];
+ }
+
+ public String getStat() {
+ float coverage = (float)(ryg[1]+ryg[2]) / (float)(ryg[0]+ryg[1]+ryg[2]);
+ return "red: "+ryg[0]+", yellow:"+ryg[1]+", green:"+ryg[2]+",coverage:"+coverage;
+ }
+
+ public void inc(int color) {
+ ryg[color]++;
+ }
+
+ public String getLink() {
+ return link;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public String getExtra() {
+ return extra;
+ }
+
+ public void setExtra(String extra) {
+ this.extra = extra;
+ }
+ }
+
+ /**
+ * Holds our comparator instance for everything.
+ */
+ private DocComparator comparator = new DocComparator();
+ private MemberComparator membercomparator = new MemberComparator();
+
+ /**
+ * Creates a new instance of the TestProgressDoclet for a given target
+ * directory.
+ */
+ public TestCoverageDoclet(String directory) {
+ this.directory = new File(directory);
+ }
+
+ /**
+ * Opens a new output file and writes the usual HTML header. Directories
+ * are created on demand.
+ */
+ private PrintWriter openFile(String name, String title) throws IOException {
+ File file = new File(directory, name);
+ File parent = file.getParentFile();
+ parent.mkdirs();
+
+ PrintWriter printer = new PrintWriter(new FileOutputStream(file));
+
+ printer.println("<html>");
+ printer.println(" <head>");
+ printer.println(" <title>" + title + "</title>");
+ printer.println("<style type=\"text/css\">\n"+
+ "body { }\n"+
+ "table {border-width: 0px; border: solid; border-collapse: collapse;}\n"+
+ "table tr td { vertical-align:top; padding:3px; border: 1px solid black;}\n"+
+ "</style>");
+ printer.println(" </head>");
+ printer.println(" <body>");
+ printer.println(" <h1>" + title + "</h1>");
+
+ return printer;
+ }
+
+ /**
+ * Closes the given output file, writing the usual HTML footer before.
+ */
+ private void closeFile(PrintWriter printer) {
+ printer.println(" </body>");
+ printer.println("</html>");
+ printer.flush();
+ printer.close();
+ }
+
+ private class TablePrinter {
+ private PrintWriter pr;
+
+ public TablePrinter(PrintWriter pr) {
+ this.pr = pr;
+ }
+
+ public void printRow(int color, String... columns) {
+ String colo = COLORS[color];
+ pr.print("<tr style=\"background-color:"+colo+"\">");
+ for (String col : columns) {
+ pr.print("<td>"+col+"</td>");
+ }
+ pr.print("</tr>");
+ }
+
+ public void printRow(String... columns) {
+ printRow(1, columns);
+ }
+
+ public void printPlain(String val) {
+ pr.print(val);
+ }
+
+ }
+
+ /**
+ * Processes the whole list of classes that JavaDoc knows about.
+ */
+ private void process(RootDoc root) throws IOException {
+
+ // 1. traverse all test-classes (those extending JUnit's TestCase)
+ // and collect the annotation info. Print which test classes
+ // need annotating
+ PrintWriter pr = openFile("test-annotation.html", "test class annotation coverage");
+ TablePrinter printer = new TablePrinter(pr);
+ printer.printPlain("<table>");
+ printer.printRow("className", "annotated methods", "total methods", "percentage");
+
+ ClassDoc[] classes = root.classes();
+ Arrays.sort(classes, new Comparator<ClassDoc>() {
+ public int compare(ClassDoc c1, ClassDoc c2) {
+ return c1.toString().compareTo(c2.toString());
+ }});
+ for (ClassDoc classDoc : classes) {
+ if (extendsJUnitTestCase(classDoc)) {
+ processTestClass(classDoc, printer);
+ }
+ }
+ printer.printPlain("</table>");
+ closeFile(pr);
+ //dumpInfo();
+
+ // 2. traverse all "normal" (non-junit) source files, for each method
+ // get its status and propagate it up the tree
+ MyStats stats = new MyStats(TYPE_ROOT, "All", "aaa.html");
+ PrintWriter aprinter = openFile("index.html", "All packages");
+ aprinter.println("Generated " + new Date().toString());
+ aprinter.println("<br/><a href=\"test-annotation.html\">annotation progress of test classes</a><br/>");
+ aprinter.println("<br/><a href=\"hidden-doc.html\">hidden classes and methods</a><br/>");
+ aprinter.println("<br/><a href=\"interfaces.html\">interfaces</a><br/>");
+ aprinter.println("<h2>Packages</h2>");
+ aprinter.println("<table>");
+
+ PrintWriter hiddenDocPr = openFile("hidden-doc.html", "hidden classes and methods list");
+ TablePrinter hiddenDocPrinter = new TablePrinter(hiddenDocPr);
+ hiddenDocPrinter.printPlain("<table>");
+ hiddenDocPrinter.printRow("Package Name", "Class Name", "Method Name");
+
+ PrintWriter interfacePr = openFile("interfaces.html", "interface list");
+ TablePrinter interfacePrinter = new TablePrinter(interfacePr);
+ interfacePrinter.printPlain("<table>");
+ interfacePrinter.printRow("packageName", "className");
+
+ PackageDoc[] packages = root.specifiedPackages();
+ Arrays.sort(packages, comparator);
+ for (PackageDoc pack : packages) {
+ if (pack.allClasses().length != 0) {
+
+ if (pack.name().endsWith(".cts")) {
+ // Skip the cts test packages
+// System.out.println(">>>>>>>>>>>Skip package: " + pack.name());
+ } else {
+ MyStats subStat = processPackage(pack, hiddenDocPrinter, interfacePrinter);
+
+ System.out.println("package " + pack.name() + " has " + subStat.getCountFor(0) + " red.");
+ printStats(aprinter, subStat, true);
+ stats.add(subStat);
+ }
+ }
+ }
+
+
+ System.out.println("Total has " + stats.getCountFor(0) + " red.");
+
+ interfacePrinter.printPlain("</table>");
+ closeFile(interfacePr);
+
+ hiddenDocPrinter.printPlain("</table>");
+ closeFile(hiddenDocPr);
+
+ aprinter.println("</table>");
+ aprinter.println("<h2>Summary</h2>");
+ aprinter.println("<table>");
+ printStats(aprinter, stats, false);
+ aprinter.println("</table>");
+
+ closeFile(aprinter);
+ }
+
+ /*private void processTargetClass(ClassDoc classDoc) {
+ System.out.println("class:"+classDoc);
+ // show all public/protected constructors
+ for (ExecutableMemberDoc constr : classDoc.constructors()) {
+ if (constr.isPublic() || constr.isProtected()) {
+ processTargetMC(constr);
+ }
+ }
+ // show all public/protected methods
+ for (ExecutableMemberDoc method : classDoc.methods()) {
+ if (method.isPublic() || method.isProtected()) {
+ processTargetMC(method);
+ }
+ }
+ }*/
+
+ /*private void dumpInfo() {
+ for (Map.Entry<ExecutableMemberDoc, AnnotationPointer> entry : resolved.entrySet()) {
+ ExecutableMemberDoc mdoc = entry.getKey();
+ AnnotationPointer ap = entry.getValue();
+ System.out.println("----- entry -----------------------");
+ System.out.println("target:"+mdoc.toString());
+ System.out.println("=");
+ for (MethodDoc meth : ap.testMethods) {
+ System.out.println("test method:"+meth);
+ }
+ }
+ }*/
+
+ private void processTestClass(ClassDoc classDoc, TablePrinter printer) {
+ // System.out.println("Processing >>> " + classDoc);
+ // collects all testinfo-annotation info of this class
+ ClassDoc targetClass = null;
+ // get the class annotation which names the default test target class
+ AnnotationDesc[] cAnnots = classDoc.annotations();
+ for (AnnotationDesc cAnnot : cAnnots) {
+
+ AnnotationTypeDoc atype = cAnnot.annotationType();
+ if (atype.toString().equals("dalvik.annotation.TestTargetClass")) {
+ // single member annot with one child 'value'
+ ElementValuePair[] cpairs = cAnnot.elementValues();
+ ElementValuePair evp = cpairs[0];
+ AnnotationValue av = evp.value();
+ Object obj = av.value();
+
+ // value must be a class doc
+ if (obj instanceof ClassDoc) {
+ targetClass = (ClassDoc) obj;
+ } else if (obj instanceof ParameterizedType) {
+ targetClass = ((ParameterizedType)obj).asClassDoc();
+ }
+ else throw new RuntimeException("annotation elem value is of type "+obj.getClass().getName());
+ }
+ }
+
+ // now visit all methods (junit test methods - therefore we need not visit the constructors
+ AnnotStat ast = new AnnotStat();
+
+ //System.out.println("checking:"+classDoc.qualifiedName());
+
+ MethodDoc[] methods = classDoc.methods();
+ String note = "";
+ if (targetClass == null) {
+ note += "<br/>targetClass annotation missing!<br/>";
+ }
+
+ for (MethodDoc methodDoc : methods) {
+ // ignore if it is not a junit test method
+ if (!methodDoc.name().startsWith("test")) continue;
+ if (classDoc.qualifiedName().equals("tests.api.java.io.BufferedInputStreamTest")) {
+ //System.out.println("method: "+methodDoc.toString());
+ }
+
+ if (targetClass == null) {
+ // if the targetClass is missing, count all methods as non-annotated
+ ast.incMethodCnt(false);
+ } else {
+ String error = processTestMethod(methodDoc, ast, targetClass);
+ if (error != null) {
+ note+="<br/><b>E:</b> "+error;
+ }
+ }
+ }
+
+ int man = ast.cntMethodWithAnnot;
+ int mto = ast.cntAllMethods;
+ float perc = mto==0? 100f : ((float)man)/mto * 100f;
+
+ printer.printRow(man==mto && note.equals("")? 2:0, classDoc.qualifiedName(), ""+ast.cntMethodWithAnnot, ""+ast.cntAllMethods,
+ ""+perc+ note);
+
+ }
+
+ private class AnnotStat {
+ int cntMethodWithAnnot = 0;
+ int cntAllMethods = 0;
+ /**
+ * @param correctAnnot
+ */
+ public void incMethodCnt(boolean correctAnnot) {
+ cntAllMethods++;
+ if (correctAnnot) {
+ cntMethodWithAnnot++;
+ }
+ }
+ }
+
+ // points from one targetMethod to 0..n testMethods which test the target method
+ private class AnnotationPointer {
+ AnnotationPointer(ExecutableMemberDoc targetMethod) {
+ this.targetMethod = targetMethod;
+ }
+
+ final ExecutableMemberDoc targetMethod;
+ List<MethodDoc> testMethods = new ArrayList<MethodDoc>();
+
+ public void addTestMethod(MethodDoc testMethod) {
+ if (testMethods.contains(testMethod)) {
+ System.out.println("warn: testMethod refers more than once to the targetMethod, testMethod="+testMethod);
+ } else {
+ testMethods.add(testMethod);
+ }
+ }
+ }
+
+ private String processTestMethod(MethodDoc methodDoc, AnnotStat ast, ClassDoc targetClass) {
+ //System.out.println("processing method: " + methodDoc);
+ // get all per-method-annotation
+ boolean correctAnnot = false;
+ AnnotationDesc[] annots = methodDoc.annotations();
+ for (AnnotationDesc annot : annots) {
+ if (annot.annotationType().toString().equals("dalvik.annotation.TestInfo")) {
+ ElementValuePair[] pairs = annot.elementValues();
+ for (ElementValuePair kv : pairs) {
+ if (kv.element().qualifiedName().equals("dalvik.annotation.TestInfo.targets")) {
+ // targets is an [] type
+ AnnotationValue[] targets = (AnnotationValue[]) kv.value().value();
+ for (AnnotationValue tval : targets) {
+ // the test targets must be annotations themselves
+ AnnotationDesc targetAnnot = (AnnotationDesc) tval.value();
+ ExecutableMemberDoc targetMethod = getTargetMethod(targetAnnot, targetClass);
+ if (targetMethod != null) {
+ AnnotationPointer tar = getAnnotationPointer(targetMethod, true);
+ tar.addTestMethod(methodDoc);
+ correctAnnot = true;
+ } else {
+ ast.incMethodCnt(false);
+ return "error: could not resolve targetMethod for class "+targetClass+", annotation was:"+targetAnnot+", testMethod = "+methodDoc.toString();
+ }
+ }
+ }
+ }
+ } // else some other annotation
+ }
+ ast.incMethodCnt(correctAnnot);
+ return null;
+ }
+
+ private AnnotationPointer getAnnotationPointer(ExecutableMemberDoc targetMethod, boolean create) {
+ AnnotationPointer ap = resolved.get(targetMethod);
+ if (create && ap == null) {
+ ap = new AnnotationPointer(targetMethod);
+ resolved.put(targetMethod, ap);
+ }
+ return ap;
+ }
+
+ private ExecutableMemberDoc getTargetMethod(AnnotationDesc targetAnnot,
+ ClassDoc targetClass) {
+ // targetAnnot like @android.annotation.TestTarget(methodName="group", methodArgs=int.class)
+ ElementValuePair[] pairs = targetAnnot.elementValues();
+ String methodName = null;
+ String args = "";
+ for (ElementValuePair kval : pairs) {
+ if (kval.element().name().equals("methodName")) {
+ methodName = (String) kval.value().value();
+ } else if (kval.element().name().equals("methodArgs")) {
+ AnnotationValue[] vals = (AnnotationValue[]) kval.value().value();
+ for (int i = 0; i < vals.length; i++) {
+ AnnotationValue arg = vals[i];
+ String argV;
+ if (arg.value() instanceof ClassDoc) {
+ ClassDoc cd = (ClassDoc)arg.value();
+ argV = cd.qualifiedName();
+ } else { // primitive type or array type
+ // is there a nicer way to do this?
+ argV = arg.toString();
+ }
+ // strip .class out of args since signature does not contain those
+ if (argV.endsWith(".class")) {
+ argV = argV.substring(0, argV.length()-6);
+ }
+ args+= (i>0? ",":"") + argV;
+ }
+ }
+ }
+ // both methodName and methodArgs != null because of Annotation definition
+
+ String refSig = methodName+"("+args+")";
+ //System.out.println("Check " + refSig);
+ // find the matching method in the target class
+ // check all methods
+ for (ExecutableMemberDoc mdoc : targetClass.methods()) {
+ if (equalsSignature(mdoc, refSig)) {
+ return mdoc;
+ }
+ }
+ // check constructors, too
+ for (ExecutableMemberDoc mdoc : targetClass.constructors()) {
+ if (equalsSignature(mdoc, refSig)) {
+ return mdoc;
+ }
+ }
+ return null;
+ }
+
+ private boolean equalsSignature(ExecutableMemberDoc mdoc, String refSignature) {
+ Parameter[] params = mdoc.parameters();
+ String targs = "";
+ for (int i = 0; i < params.length; i++) {
+ Parameter parameter = params[i];
+ // check for generic type types
+ Type ptype = parameter.type();
+ TypeVariable typeVar = ptype.asTypeVariable();
+ String ptname;
+ if (typeVar != null) {
+ ptname = "java.lang.Object"; // the default fallback
+ Type[] bounds = typeVar.bounds();
+ if (bounds.length > 0) {
+ ClassDoc typeClass = bounds[0].asClassDoc();
+ ptname = typeClass.qualifiedName();
+ }
+ } else {
+ // regular var
+ //ptname = parameter.type().qualifiedTypeName();
+ ptname = parameter.type().toString();
+
+ //System.out.println("quali:"+ptname);
+ //ptname = parameter.typeName();
+ // omit type signature
+ ptname = ptname.replaceAll("<.*>","");
+ }
+ targs+= (i>0? ",":"") + ptname;
+ }
+ String testSig = mdoc.name()+"("+targs+")";
+
+ //return testSig.equals(refSignature);
+ if (testSig.equals(refSignature)) {
+ //System.out.println("found: Sig:"+testSig);
+ return true;
+ } else {
+ //System.out.println("no match: ref = "+refSignature+", test = "+testSig);
+ return false;
+ }
+ }
+
+ private boolean extendsJUnitTestCase(ClassDoc classDoc) {
+ //junit.framework.TestCase.java
+ ClassDoc curClass = classDoc;
+ while ((curClass = curClass.superclass()) != null) {
+ if (curClass.toString().equals("junit.framework.TestCase")) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Processes the details of a single package.
+ * @param hiddenDocPrinter
+ * @param excludedClassPrinter
+ * @param interfacePrinter
+ */
+ private MyStats processPackage(PackageDoc pack, TablePrinter hiddenDocPrinter,
+ TablePrinter interfacePrinter) throws IOException {
+ String file = getPackageDir(pack) + "/package.html";
+ PrintWriter printer = openFile(file, "Package " + pack.name());
+
+ MyStats stats = new MyStats(TYPE_PACKAGE, pack.name(), file);
+ printer.println("<table>");
+
+ ClassDoc[] classes = pack.allClasses();
+ Arrays.sort(classes, comparator);
+ for (ClassDoc clazz : classes) {
+ if (extendsJUnitTestCase(clazz)) {
+ printer.println("<tr><td>ignored(junit):"+clazz.name()+"</td></tr>");
+ } else if (isHiddenClass(clazz)) {
+ hiddenDocPrinter.printRow(pack.name(), clazz.name(), "*");
+ } else if (clazz.isInterface()) {
+ interfacePrinter.printRow(pack.name(), clazz.name());
+ } else {
+ MyStats subStats = processClass(clazz, hiddenDocPrinter);
+ printStats(printer, subStats, true);
+ stats.add(subStats);
+ }
+ }
+ printer.println("</table>");
+ closeFile(printer);
+ return stats;
+ }
+
+ private boolean isHiddenClass(ClassDoc clazz) {
+ if (clazz == null) {
+ return false;
+ }
+
+ if (isHiddenDoc(clazz)) {
+ return true;
+ }
+
+ // If outter class is hidden, this class should be hidden as well
+ return isHiddenClass(clazz.containingClass());
+ }
+
+ private boolean isHiddenDoc(Doc doc) {
+ // Since currently we have two kinds of annotations to mark a class as hide:
+ // 1. @hide
+ // 2. {@hide}
+ // So we should consider both conditions.
+ for (Tag t : doc.tags()) {
+ if (t.name().equals("@hide")) {
+ return true;
+ }
+ }
+
+ for (Tag t : doc.inlineTags()) {
+ if (t.name().equals("@hide")) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ private MyStats processClass(ClassDoc clazz, TablePrinter hiddenDocPrinter) throws IOException {
+ //System.out.println("Process source class: " + clazz);
+ String file = getPackageDir(clazz.containingPackage()) + "/" + clazz.name() + ".html";
+ PrintWriter printer = openFile(file, "Class " + clazz.name());
+
+ String packageName = clazz.containingPackage().name();
+ String className = clazz.name();
+
+ MyStats stats = new MyStats(TYPE_CLASS, className, className+".html");
+ printer.println("<table><tr><td>name</td><td>tested by</td></tr>");
+ ConstructorDoc[] constructors = clazz.constructors();
+ Arrays.sort(constructors, comparator);
+ for (ConstructorDoc constructor : constructors) {
+ //System.out.println("constructor: " + constructor);
+ if (isHiddenDoc(constructor)) {
+ hiddenDocPrinter.printRow(packageName, className, constructor.name());
+ } else if (!isGeneratedConstructor(constructor)) {
+ MyStats subStat = processElement(constructor);
+ printStats(printer, subStat, false);
+ stats.add(subStat);
+ }
+ }
+
+ MethodDoc[] methods = clazz.methods();
+ Arrays.sort(methods, comparator);
+ for (MethodDoc method : methods) {
+ //System.out.println("method: " + method);
+ if ("finalize".equals(method.name())) {
+ // Skip finalize method
+ } else if (isHiddenDoc(method)) {
+ hiddenDocPrinter.printRow(packageName, className, method.name());
+ } else if (method.isAbstract()) {
+ // Skip abstract method
+ } else {
+ MyStats subStat = processElement(method);
+ printStats(printer, subStat, false);
+ stats.add(subStat);
+ }
+ }
+
+ printer.println("</table>");
+ closeFile(printer);
+ return stats;
+ }
+
+ /**
+ * Determines whether a constructor has been automatically generated and is
+ * thus not present in the original source. The only way to find out seems
+ * to compare the source position against the one of the class. If they're
+ * equal, the constructor does not exist. It's a bit hacky, but it works.
+ */
+ private boolean isGeneratedConstructor(ConstructorDoc doc) {
+ SourcePosition constPos = doc.position();
+ SourcePosition classPos = doc.containingClass().position();
+
+ return ("" + constPos).equals("" + classPos);
+ }
+
+ /**
+ * Processes a single method/constructor.
+ */
+ private MyStats processElement(ExecutableMemberDoc method) {
+ //int color = getColor(doc)
+ //derived.add(subStats)
+ AnnotationPointer ap = getAnnotationPointer(method, false);
+ MyStats stats = new MyStats(TYPE_METHOD, "<b>"+method.name() + "</b> "+method.signature(), null);
+ int refCnt = 0;
+ if (ap != null) {
+ refCnt = ap.testMethods.size();
+ String by = "";
+ List<MethodDoc> testM = ap.testMethods;
+ Collections.sort(testM, membercomparator);
+ for (MethodDoc teme : testM) {
+ by+= "<br/>"+teme.toString();
+ }
+ stats.setExtra(by);
+ } // else this class has no single test that targets one of its method
+
+ if (refCnt == 0) {
+ stats.inc(VALUE_RED);
+ } else if (refCnt == 1) {
+ stats.inc(VALUE_YELLOW);
+ } else {
+ stats.inc(VALUE_GREEN);
+ }
+ return stats;
+ }
+
+ /**
+ * Prints a single row to a stats table.
+ */
+ private void printStats(PrintWriter printer, MyStats info, boolean wantLink) {
+ int red = info.getCountFor(VALUE_RED);
+ int yellow = info.getCountFor(VALUE_YELLOW);
+
+ printer.println("<tr>");
+
+ // rule for coloring:
+ // if red > 0 -> red
+ // if yellow > 0 -> yellow
+ // else green
+ int color;
+ if (red > 0) {
+ color = VALUE_RED;
+ } else if (yellow > 0) {
+ color = VALUE_YELLOW;
+ } else {
+ color = VALUE_GREEN;
+ }
+
+ printer.println("<td bgcolor=\""+COLORS[color]+"\">");
+ String link = info.getLink();
+ if (wantLink && link != null) {
+ printer.print("<a href=\"" + link + "\">" + info.getName() + "</a>");
+ } else {
+ printer.print(info.getName());
+ }
+ printer.println(" ("+info.getStat()+") </td>");
+ if (info.getExtra()!=null) {
+ printer.println("<td>"+info.getExtra()+"</td>");
+ }
+ printer.println("</tr>");
+ }
+
+ /**
+ * Returns the directory for a given package. Basically converts embedded
+ * dots in the name into slashes.
+ */
+ private File getPackageDir(PackageDoc pack) {
+ if (pack == null || pack.name() == null || "".equals(pack.name())) {
+ return new File(".");
+ } else {
+ return new File(pack.name().replace('.', '/'));
+ }
+ }
+
+ /**
+ * Called by JavaDoc to find our which command line arguments are supported
+ * and how many parameters they take. Part of the JavaDoc API.
+ */
+ public static int optionLength(String option) {
+ if ("-d".equals(option)) {
+ return 2;
+ } else {
+ return 0;
+ }
+ }
+
+ /**
+ * Called by JavaDoc to query a specific command line argument. Part of the
+ * JavaDoc API.
+ */
+ private static String getOption(RootDoc root, String option, int index, String defValue) {
+ String[][] allOptions = root.options();
+ for (int i = 0; i < allOptions.length; i++) {
+ if (allOptions[i][0].equals(option)) {
+ return allOptions[i][index];
+ }
+ }
+ return defValue;
+ }
+
+ /**
+ * Called by JavaDoc to find out which Java version we claim to support.
+ * Part of the JavaDoc API.
+ */
+ public static LanguageVersion languageVersion() {
+ return LanguageVersion.JAVA_1_5;
+ }
+
+ /**
+ * The main entry point called by JavaDoc after all required information has
+ * been collected. Part of the JavaDoc API.
+ */
+ public static boolean start(RootDoc root) {
+ try {
+ String target = getOption(root, "-d", 1, ".");
+ TestCoverageDoclet doclet = new TestCoverageDoclet(target);
+ doclet.process(root);
+
+ } catch (Exception ex) {
+ ex.printStackTrace();
+ return false;
+ }
+ return true;
+ }
+
+}