blob: 1e2542f84375f1dd5526f348296827202394f8df [file] [log] [blame]
The Android Open Source Projectf8057102009-03-15 16:47:16 -07001/*
2 * Copyright (C) The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16import java.io.BufferedWriter;
17import java.io.File;
18import java.io.FileNotFoundException;
19import java.io.FileOutputStream;
20import java.io.FileWriter;
21import java.io.IOException;
22import java.util.ArrayList;
23import java.util.Collection;
24import java.util.Iterator;
Stuart Scott01c2b492014-10-08 18:07:28 -070025import java.util.Set;
The Android Open Source Projectf8057102009-03-15 16:47:16 -070026
27import javax.xml.parsers.DocumentBuilderFactory;
28import javax.xml.parsers.ParserConfigurationException;
29import javax.xml.transform.Transformer;
30import javax.xml.transform.TransformerException;
31import javax.xml.transform.TransformerFactory;
32import javax.xml.transform.TransformerFactoryConfigurationError;
33import javax.xml.transform.dom.DOMSource;
34import javax.xml.transform.stream.StreamResult;
35
36import org.w3c.dom.Attr;
37import org.w3c.dom.Document;
38import org.w3c.dom.Node;
39import org.w3c.dom.NodeList;
40
Brian Muramatsu7f64e852011-02-17 16:52:16 -080041import vogar.ExpectationStore;
Stuart Scott01c2b492014-10-08 18:07:28 -070042import vogar.Expectation;
Brian Muramatsu7f64e852011-02-17 16:52:16 -080043
The Android Open Source Projectf8057102009-03-15 16:47:16 -070044import com.sun.javadoc.AnnotationDesc;
45import com.sun.javadoc.AnnotationTypeDoc;
46import com.sun.javadoc.AnnotationValue;
47import com.sun.javadoc.ClassDoc;
48import com.sun.javadoc.Doclet;
49import com.sun.javadoc.MethodDoc;
50import com.sun.javadoc.RootDoc;
51import com.sun.javadoc.AnnotationDesc.ElementValuePair;
52
53/**
54 * This is only a very simple and brief JavaDoc parser for the CTS.
55 *
56 * Input: The source files of the test cases. It will be represented
57 * as a list of ClassDoc
58 * Output: Generate file description.xml, which defines the TestPackage
59 * TestSuite and TestCases.
60 *
61 * Note:
62 * 1. Since this class has dependencies on com.sun.javadoc package which
63 * is not implemented on Android. So this class can't be compiled.
64 * 2. The TestSuite can be embedded, which means:
65 * TestPackage := TestSuite*
66 * TestSuite := TestSuite* | TestCase*
67 */
68public class DescriptionGenerator extends Doclet {
69 static final String HOST_CONTROLLER = "dalvik.annotation.HostController";
70 static final String KNOWN_FAILURE = "dalvik.annotation.KnownFailure";
Brian Muramatsu168beb02010-10-21 12:39:45 -070071 static final String SUPPRESSED_TEST = "android.test.suitebuilder.annotation.Suppress";
Brian Muramatsu7f64e852011-02-17 16:52:16 -080072 static final String CTS_EXPECTATION_DIR = "cts/tests/expectations";
The Android Open Source Projectf8057102009-03-15 16:47:16 -070073
74 static final String JUNIT_TEST_CASE_CLASS_NAME = "junit.framework.testcase";
75 static final String TAG_PACKAGE = "TestPackage";
76 static final String TAG_SUITE = "TestSuite";
77 static final String TAG_CASE = "TestCase";
78 static final String TAG_TEST = "Test";
79 static final String TAG_DESCRIPTION = "Description";
80
81 static final String ATTRIBUTE_NAME_VERSION = "version";
82 static final String ATTRIBUTE_VALUE_VERSION = "1.0";
83 static final String ATTRIBUTE_NAME_FRAMEWORK = "AndroidFramework";
84 static final String ATTRIBUTE_VALUE_FRAMEWORK = "Android 1.0";
85
86 static final String ATTRIBUTE_NAME = "name";
Stuart Scott01c2b492014-10-08 18:07:28 -070087 static final String ATTRIBUTE_ABIS = "abis";
The Android Open Source Projectf8057102009-03-15 16:47:16 -070088 static final String ATTRIBUTE_HOST_CONTROLLER = "HostController";
Brian Carlstromd16b8642015-06-18 22:21:41 -070089 static final String ATTRIBUTE_TIMEOUT = "timeout";
The Android Open Source Projectf8057102009-03-15 16:47:16 -070090
91 static final String XML_OUTPUT_PATH = "./description.xml";
92
Phil Dubach0d6ef062009-08-12 18:13:16 -070093 static final String OUTPUT_PATH_OPTION = "-o";
Stuart Scott01c2b492014-10-08 18:07:28 -070094 static final String ARCHITECTURE_OPTION = "-a";
Phil Dubach0d6ef062009-08-12 18:13:16 -070095
The Android Open Source Projectf8057102009-03-15 16:47:16 -070096 /**
97 * Start to parse the classes passed in by javadoc, and generate
98 * the xml file needed by CTS packer.
99 *
100 * @param root The root document passed in by javadoc.
101 * @return Whether the document has been processed.
102 */
103 public static boolean start(RootDoc root) {
104 ClassDoc[] classes = root.classes();
105 if (classes == null) {
106 Log.e("No class found!", null);
107 return true;
108 }
109
Phil Dubach0d6ef062009-08-12 18:13:16 -0700110 String outputPath = XML_OUTPUT_PATH;
Stuart Scott01c2b492014-10-08 18:07:28 -0700111 String architecture = null;
Phil Dubach0d6ef062009-08-12 18:13:16 -0700112 String[][] options = root.options();
113 for (String[] option : options) {
Stuart Scott01c2b492014-10-08 18:07:28 -0700114 if (option.length == 2) {
115 if (option[0].equals(OUTPUT_PATH_OPTION)) {
116 outputPath = option[1];
117 } else if (option[0].equals(ARCHITECTURE_OPTION)) {
118 architecture = option[1];
119 }
Phil Dubach0d6ef062009-08-12 18:13:16 -0700120 }
121 }
Stuart Scott01c2b492014-10-08 18:07:28 -0700122 if (architecture == null || architecture.equals("")) {
123 Log.e("Missing architecture!", null);
124 return false;
125 }
Phil Dubach0d6ef062009-08-12 18:13:16 -0700126
The Android Open Source Projectf8057102009-03-15 16:47:16 -0700127 XMLGenerator xmlGenerator = null;
128 try {
Phil Dubach0d6ef062009-08-12 18:13:16 -0700129 xmlGenerator = new XMLGenerator(outputPath);
The Android Open Source Projectf8057102009-03-15 16:47:16 -0700130 } catch (ParserConfigurationException e) {
131 Log.e("Cant initialize XML Generator!", e);
132 return true;
133 }
134
Brian Muramatsu7f64e852011-02-17 16:52:16 -0800135 ExpectationStore ctsExpectationStore = null;
136 try {
137 ctsExpectationStore = VogarUtils.provideExpectationStore("./" + CTS_EXPECTATION_DIR);
138 } catch (IOException e) {
139 Log.e("Couldn't load expectation store.", e);
140 return false;
141 }
142
The Android Open Source Projectf8057102009-03-15 16:47:16 -0700143 for (ClassDoc clazz : classes) {
144 if ((!clazz.isAbstract()) && (isValidJUnitTestCase(clazz))) {
Stuart Scott01c2b492014-10-08 18:07:28 -0700145 xmlGenerator.addTestClass(new TestClass(clazz, ctsExpectationStore, architecture));
The Android Open Source Projectf8057102009-03-15 16:47:16 -0700146 }
147 }
148
149 try {
150 xmlGenerator.dump();
151 } catch (Exception e) {
152 Log.e("Can't dump to XML file!", e);
153 }
154
155 return true;
156 }
157
158 /**
Phil Dubach0d6ef062009-08-12 18:13:16 -0700159 * Return the length of any doclet options we recognize
160 * @param option The option name
161 * @return The number of words this option takes (including the option) or 0 if the option
162 * is not recognized.
163 */
164 public static int optionLength(String option) {
165 if (option.equals(OUTPUT_PATH_OPTION)) {
166 return 2;
167 }
168 return 0;
169 }
170
171 /**
The Android Open Source Projectf8057102009-03-15 16:47:16 -0700172 * Check if the class is valid test case inherited from JUnit TestCase.
173 *
174 * @param clazz The class to be checked.
175 * @return If the class is valid test case inherited from JUnit TestCase, return true;
176 * else, return false.
177 */
178 static boolean isValidJUnitTestCase(ClassDoc clazz) {
179 while((clazz = clazz.superclass()) != null) {
180 if (JUNIT_TEST_CASE_CLASS_NAME.equals(clazz.qualifiedName().toLowerCase())) {
181 return true;
182 }
183 }
184
185 return false;
186 }
187
188 /**
189 * Log utility.
190 */
191 static class Log {
192 private static boolean TRACE = true;
193 private static BufferedWriter mTraceOutput = null;
194
195 /**
196 * Log the specified message.
197 *
198 * @param msg The message to be logged.
199 */
200 static void e(String msg, Exception e) {
201 System.out.println(msg);
202
203 if (e != null) {
204 e.printStackTrace();
205 }
206 }
207
208 /**
209 * Add the message to the trace stream.
210 *
211 * @param msg The message to be added to the trace stream.
212 */
213 public static void t(String msg) {
214 if (TRACE) {
215 try {
216 if ((mTraceOutput != null) && (msg != null)) {
217 mTraceOutput.write(msg + "\n");
218 mTraceOutput.flush();
219 }
220 } catch (IOException e) {
221 e.printStackTrace();
222 }
223 }
224 }
225
226 /**
227 * Initialize the trace stream.
228 *
229 * @param name The class name.
230 */
231 public static void initTrace(String name) {
232 if (TRACE) {
233 try {
234 if (mTraceOutput == null) {
235 String fileName = "cts_debug_dg_" + name + ".txt";
236 mTraceOutput = new BufferedWriter(new FileWriter(fileName));
237 }
238 } catch (IOException e) {
239 e.printStackTrace();
240 }
241 }
242 }
243
244 /**
245 * Close the trace stream.
246 */
247 public static void closeTrace() {
248 if (mTraceOutput != null) {
249 try {
250 mTraceOutput.close();
251 mTraceOutput = null;
252 } catch (IOException e) {
253 e.printStackTrace();
254 }
255 }
256 }
257 }
258
259 static class XMLGenerator {
260 String mOutputPath;
261
262 /**
263 * This document is used to represent the description XML file.
264 * It is construct by the classes passed in, which contains the
265 * information of all the test package, test suite and test cases.
266 */
267 Document mDoc;
268
269 XMLGenerator(String outputPath) throws ParserConfigurationException {
270 mOutputPath = outputPath;
271
272 mDoc = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument();
273
274 Node testPackageElem = mDoc.appendChild(mDoc.createElement(TAG_PACKAGE));
275
The Android Open Source Projectf8057102009-03-15 16:47:16 -0700276 setAttribute(testPackageElem, ATTRIBUTE_NAME_VERSION, ATTRIBUTE_VALUE_VERSION);
277 setAttribute(testPackageElem, ATTRIBUTE_NAME_FRAMEWORK, ATTRIBUTE_VALUE_FRAMEWORK);
278 }
279
280 void addTestClass(TestClass tc) {
281 appendSuiteToElement(mDoc.getDocumentElement(), tc);
282 }
283
284 void dump() throws TransformerFactoryConfigurationError,
285 FileNotFoundException, TransformerException {
286 //rebuildDocument();
287
288 Transformer t = TransformerFactory.newInstance().newTransformer();
289
290 // enable indent in result file
291 t.setOutputProperty("indent", "yes");
292 t.setOutputProperty("{http://xml.apache.org/xslt}indent-amount","4");
293
Jorg Pleumann86192702009-04-21 10:08:53 -0700294 File file = new File(mOutputPath);
295 file.getParentFile().mkdirs();
Scott Su259ced72009-04-30 17:05:49 -0700296
The Android Open Source Projectf8057102009-03-15 16:47:16 -0700297 t.transform(new DOMSource(mDoc),
Jorg Pleumann86192702009-04-21 10:08:53 -0700298 new StreamResult(new FileOutputStream(file)));
The Android Open Source Projectf8057102009-03-15 16:47:16 -0700299 }
300
301 /**
302 * Rebuild the document, merging empty suite nodes.
303 */
304 void rebuildDocument() {
305 // merge empty suite nodes
306 Collection<Node> suiteElems = getUnmutableChildNodes(mDoc.getDocumentElement());
307 Iterator<Node> suiteIterator = suiteElems.iterator();
308 while (suiteIterator.hasNext()) {
309 Node suiteElem = suiteIterator.next();
310
311 mergeEmptySuites(suiteElem);
312 }
313 }
314
315 /**
316 * Merge the test suite which only has one sub-suite. In this case, unify
317 * the name of the two test suites.
318 *
319 * @param suiteElem The suite element of which to be merged.
320 */
321 void mergeEmptySuites(Node suiteElem) {
322 Collection<Node> suiteChildren = getSuiteChildren(suiteElem);
323 if (suiteChildren.size() > 1) {
324 for (Node suiteChild : suiteChildren) {
325 mergeEmptySuites(suiteChild);
326 }
327 } else if (suiteChildren.size() == 1) {
328 // do merge
329 Node child = suiteChildren.iterator().next();
330
331 // update name
332 String newName = getAttribute(suiteElem, ATTRIBUTE_NAME) + "."
333 + getAttribute(child, ATTRIBUTE_NAME);
334 setAttribute(child, ATTRIBUTE_NAME, newName);
335
336 // update parent node
337 Node parentNode = suiteElem.getParentNode();
338 parentNode.removeChild(suiteElem);
339 parentNode.appendChild(child);
340
341 mergeEmptySuites(child);
342 }
343 }
344
345 /**
346 * Get the unmuatable child nodes for specified node.
347 *
348 * @param node The specified node.
349 * @return A collection of copied child node.
350 */
351 private Collection<Node> getUnmutableChildNodes(Node node) {
352 ArrayList<Node> nodes = new ArrayList<Node>();
353 NodeList nodelist = node.getChildNodes();
354
355 for (int i = 0; i < nodelist.getLength(); i++) {
356 nodes.add(nodelist.item(i));
357 }
358
359 return nodes;
360 }
361
362 /**
363 * Append a named test suite to a specified element. Including match with
364 * the existing suite nodes and do the real creation and append.
365 *
366 * @param elem The specified element.
367 * @param testSuite The test suite to be appended.
368 */
369 void appendSuiteToElement(Node elem, TestClass testSuite) {
370 String suiteName = testSuite.mName;
371 Collection<Node> children = getSuiteChildren(elem);
372 int dotIndex = suiteName.indexOf('.');
373 String name = dotIndex == -1 ? suiteName : suiteName.substring(0, dotIndex);
374
375 boolean foundMatch = false;
376 for (Node child : children) {
377 String childName = child.getAttributes().getNamedItem(ATTRIBUTE_NAME)
378 .getNodeValue();
379
380 if (childName.equals(name)) {
381 foundMatch = true;
382 if (dotIndex == -1) {
383 appendTestCases(child, testSuite.mCases);
384 } else {
385 testSuite.mName = suiteName.substring(dotIndex + 1, suiteName.length());
386 appendSuiteToElement(child, testSuite);
387 }
388 }
389
390 }
391
392 if (!foundMatch) {
393 appendSuiteToElementImpl(elem, testSuite);
394 }
395 }
396
397 /**
398 * Get the test suite child nodes of a specified element.
399 *
400 * @param elem The specified element node.
401 * @return The matched child nodes.
402 */
403 Collection<Node> getSuiteChildren(Node elem) {
404 ArrayList<Node> suites = new ArrayList<Node>();
405
406 NodeList children = elem.getChildNodes();
407 for (int i = 0; i < children.getLength(); i++) {
408 Node child = children.item(i);
409
410 if (child.getNodeName().equals(DescriptionGenerator.TAG_SUITE)) {
411 suites.add(child);
412 }
413 }
414
415 return suites;
416 }
417
418 /**
419 * Create test case node according to the given method names, and append them
420 * to the test suite element.
421 *
422 * @param elem The test suite element.
423 * @param cases A collection of test cases included by the test suite class.
424 */
425 void appendTestCases(Node elem, Collection<TestMethod> cases) {
426 if (cases.isEmpty()) {
427 // if no method, remove from parent
428 elem.getParentNode().removeChild(elem);
429 } else {
430 for (TestMethod caze : cases) {
Brian Muramatsu168beb02010-10-21 12:39:45 -0700431 if (caze.mIsBroken || caze.mIsSuppressed || caze.mKnownFailure != null) {
Scott Su259ced72009-04-30 17:05:49 -0700432 continue;
433 }
The Android Open Source Projectf8057102009-03-15 16:47:16 -0700434 Node caseNode = elem.appendChild(mDoc.createElement(TAG_TEST));
435
Phil Dubachdc4e1792009-05-05 14:01:34 -0700436 setAttribute(caseNode, ATTRIBUTE_NAME, caze.mName);
Stuart Scott01c2b492014-10-08 18:07:28 -0700437 String abis = caze.mAbis.toString();
438 setAttribute(caseNode, ATTRIBUTE_ABIS, abis.substring(1, abis.length() - 1));
The Android Open Source Projectf8057102009-03-15 16:47:16 -0700439 if ((caze.mController != null) && (caze.mController.length() != 0)) {
440 setAttribute(caseNode, ATTRIBUTE_HOST_CONTROLLER, caze.mController);
441 }
Brian Carlstromd16b8642015-06-18 22:21:41 -0700442 if (caze.mTimeoutInMinutes != 0) {
443 setAttribute(caseNode, ATTRIBUTE_TIMEOUT,
444 Integer.toString(caze.mTimeoutInMinutes));
445 }
The Android Open Source Projectf8057102009-03-15 16:47:16 -0700446
The Android Open Source Projectf8057102009-03-15 16:47:16 -0700447 if (caze.mDescription != null && !caze.mDescription.equals("")) {
448 caseNode.appendChild(mDoc.createElement(TAG_DESCRIPTION))
449 .setTextContent(caze.mDescription);
450 }
451 }
452 }
453 }
454
455 /**
456 * Set the attribute of element.
457 *
458 * @param elem The element to be set attribute.
459 * @param name The attribute name.
460 * @param value The attribute value.
461 */
462 protected void setAttribute(Node elem, String name, String value) {
463 Attr attr = mDoc.createAttribute(name);
464 attr.setNodeValue(value);
465
466 elem.getAttributes().setNamedItem(attr);
467 }
468
469 /**
470 * Get the value of a specified attribute of an element.
471 *
472 * @param elem The element node.
473 * @param name The attribute name.
474 * @return The value of the specified attribute.
475 */
476 private String getAttribute(Node elem, String name) {
477 return elem.getAttributes().getNamedItem(name).getNodeValue();
478 }
479
480 /**
481 * Do the append, including creating test suite nodes and test case nodes, and
482 * append them to the element.
483 *
484 * @param elem The specified element node.
485 * @param testSuite The test suite to be append.
486 */
487 void appendSuiteToElementImpl(Node elem, TestClass testSuite) {
488 Node parent = elem;
489 String suiteName = testSuite.mName;
490
491 int dotIndex;
492 while ((dotIndex = suiteName.indexOf('.')) != -1) {
493 String name = suiteName.substring(0, dotIndex);
494
495 Node suiteElem = parent.appendChild(mDoc.createElement(TAG_SUITE));
496 setAttribute(suiteElem, ATTRIBUTE_NAME, name);
497
498 parent = suiteElem;
499 suiteName = suiteName.substring(dotIndex + 1, suiteName.length());
500 }
501
502 Node leafSuiteElem = parent.appendChild(mDoc.createElement(TAG_CASE));
503 setAttribute(leafSuiteElem, ATTRIBUTE_NAME, suiteName);
504
505 appendTestCases(leafSuiteElem, testSuite.mCases);
506 }
507 }
508
509 /**
510 * Represent the test class.
511 */
512 static class TestClass {
513 String mName;
514 Collection<TestMethod> mCases;
515
516 /**
517 * Construct an test suite object.
518 *
519 * @param name Full name of the test suite, such as "com.google.android.Foo"
520 * @param cases The test cases included in this test suite.
521 */
522 TestClass(String name, Collection<TestMethod> cases) {
523 mName = name;
524 mCases = cases;
525 }
526
527 /**
528 * Construct a TestClass object using ClassDoc.
529 *
530 * @param clazz The specified ClassDoc.
531 */
Stuart Scott01c2b492014-10-08 18:07:28 -0700532 TestClass(ClassDoc clazz, ExpectationStore expectationStore, String architecture) {
The Android Open Source Projectf8057102009-03-15 16:47:16 -0700533 mName = clazz.toString();
Stuart Scott01c2b492014-10-08 18:07:28 -0700534 mCases = getTestMethods(expectationStore, architecture, clazz);
The Android Open Source Projectf8057102009-03-15 16:47:16 -0700535 }
536
537 /**
538 * Get all the TestMethod from a ClassDoc, including inherited methods.
539 *
540 * @param clazz The specified ClassDoc.
541 * @return A collection of TestMethod.
542 */
Stuart Scott01c2b492014-10-08 18:07:28 -0700543 Collection<TestMethod> getTestMethods(ExpectationStore expectationStore,
544 String architecture, ClassDoc clazz) {
The Android Open Source Projectf8057102009-03-15 16:47:16 -0700545 Collection<MethodDoc> methods = getAllMethods(clazz);
546
547 ArrayList<TestMethod> cases = new ArrayList<TestMethod>();
548 Iterator<MethodDoc> iterator = methods.iterator();
549
550 while (iterator.hasNext()) {
551 MethodDoc method = iterator.next();
552
553 String name = method.name();
554
555 AnnotationDesc[] annotations = method.annotations();
556 String controller = "";
557 String knownFailure = null;
Scott Su259ced72009-04-30 17:05:49 -0700558 boolean isBroken = false;
Brian Muramatsu168beb02010-10-21 12:39:45 -0700559 boolean isSuppressed = false;
The Android Open Source Projectf8057102009-03-15 16:47:16 -0700560 for (AnnotationDesc cAnnot : annotations) {
561
562 AnnotationTypeDoc atype = cAnnot.annotationType();
563 if (atype.toString().equals(HOST_CONTROLLER)) {
564 controller = getAnnotationDescription(cAnnot);
565 } else if (atype.toString().equals(KNOWN_FAILURE)) {
566 knownFailure = getAnnotationDescription(cAnnot);
Brian Muramatsu168beb02010-10-21 12:39:45 -0700567 } else if (atype.toString().equals(SUPPRESSED_TEST)) {
568 isSuppressed = true;
The Android Open Source Projectf8057102009-03-15 16:47:16 -0700569 }
570 }
571
Brian Muramatsu7f64e852011-02-17 16:52:16 -0800572 if (VogarUtils.isVogarKnownFailure(expectationStore, clazz.toString(), name)) {
573 isBroken = true;
574 }
575
The Android Open Source Projectf8057102009-03-15 16:47:16 -0700576 if (name.startsWith("test")) {
Stuart Scott01c2b492014-10-08 18:07:28 -0700577 Expectation expectation = expectationStore.get(
578 VogarUtils.buildFullTestName(clazz.toString(), name));
579 Set<String> supportedAbis =
580 VogarUtils.extractSupportedAbis(architecture, expectation);
Brian Carlstromd16b8642015-06-18 22:21:41 -0700581 int timeoutInMinutes = VogarUtils.timeoutInMinutes(expectation);
Stuart Scott01c2b492014-10-08 18:07:28 -0700582 cases.add(new TestMethod(
583 name, method.commentText(), controller, supportedAbis,
Brian Carlstromd16b8642015-06-18 22:21:41 -0700584 knownFailure, isBroken, isSuppressed, timeoutInMinutes));
The Android Open Source Projectf8057102009-03-15 16:47:16 -0700585 }
586 }
587
588 return cases;
589 }
590
591 /**
592 * Get annotation description.
593 *
594 * @param cAnnot The annotation.
595 */
596 String getAnnotationDescription(AnnotationDesc cAnnot) {
597 ElementValuePair[] cpairs = cAnnot.elementValues();
598 ElementValuePair evp = cpairs[0];
599 AnnotationValue av = evp.value();
600 String description = av.toString();
601 // FIXME: need to find out the reason why there are leading and trailing "
602 description = description.substring(1, description.length() -1);
603 return description;
604 }
605
606 /**
607 * Get all MethodDoc of a ClassDoc, including inherited methods.
608 *
609 * @param clazz The specified ClassDoc.
610 * @return A collection of MethodDoc.
611 */
612 Collection<MethodDoc> getAllMethods(ClassDoc clazz) {
613 ArrayList<MethodDoc> methods = new ArrayList<MethodDoc>();
614
615 for (MethodDoc method : clazz.methods()) {
616 methods.add(method);
617 }
618
619 ClassDoc superClass = clazz.superclass();
620 while (superClass != null) {
621 for (MethodDoc method : superClass.methods()) {
622 methods.add(method);
623 }
624
625 superClass = superClass.superclass();
626 }
627
628 return methods;
629 }
630
631 }
632
633 /**
634 * Represent the test method inside the test class.
635 */
636 static class TestMethod {
637 String mName;
638 String mDescription;
639 String mController;
Stuart Scott01c2b492014-10-08 18:07:28 -0700640 Set<String> mAbis;
The Android Open Source Projectf8057102009-03-15 16:47:16 -0700641 String mKnownFailure;
Scott Su259ced72009-04-30 17:05:49 -0700642 boolean mIsBroken;
Brian Muramatsu168beb02010-10-21 12:39:45 -0700643 boolean mIsSuppressed;
Brian Carlstromd16b8642015-06-18 22:21:41 -0700644 int mTimeoutInMinutes; // zero to use default timeout.
The Android Open Source Projectf8057102009-03-15 16:47:16 -0700645
646 /**
647 * Construct an test case object.
648 *
649 * @param name The name of the test case.
650 * @param description The description of the test case.
651 * @param knownFailure The reason of known failure.
652 */
Stuart Scott01c2b492014-10-08 18:07:28 -0700653 TestMethod(String name, String description, String controller, Set<String> abis,
Brian Carlstromd16b8642015-06-18 22:21:41 -0700654 String knownFailure, boolean isBroken, boolean isSuppressed, int timeoutInMinutes) {
655 if (timeoutInMinutes < 0) {
656 throw new IllegalArgumentException("timeoutInMinutes < 0: " + timeoutInMinutes);
657 }
The Android Open Source Projectf8057102009-03-15 16:47:16 -0700658 mName = name;
659 mDescription = description;
660 mController = controller;
Stuart Scott01c2b492014-10-08 18:07:28 -0700661 mAbis = abis;
The Android Open Source Projectf8057102009-03-15 16:47:16 -0700662 mKnownFailure = knownFailure;
Scott Su259ced72009-04-30 17:05:49 -0700663 mIsBroken = isBroken;
Brian Muramatsu168beb02010-10-21 12:39:45 -0700664 mIsSuppressed = isSuppressed;
Brian Carlstromd16b8642015-06-18 22:21:41 -0700665 mTimeoutInMinutes = timeoutInMinutes;
The Android Open Source Projectf8057102009-03-15 16:47:16 -0700666 }
667 }
668}