Home | History | Annotate | Download | only in utils
      1 /*
      2  * Copyright (C) 2008 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 import com.android.cts.util.AbiUtils;
     17 
     18 import org.junit.runner.RunWith;
     19 import org.w3c.dom.Document;
     20 import org.w3c.dom.Element;
     21 import org.w3c.dom.Node;
     22 import org.w3c.dom.NodeList;
     23 
     24 import vogar.Expectation;
     25 import vogar.ExpectationStore;
     26 
     27 import java.io.BufferedReader;
     28 import java.io.File;
     29 import java.io.FileInputStream;
     30 import java.io.FileReader;
     31 import java.io.IOException;
     32 import java.lang.annotation.Annotation;
     33 import java.lang.reflect.Method;
     34 import java.lang.reflect.Modifier;
     35 import java.util.ArrayList;
     36 import java.util.Enumeration;
     37 import java.util.HashSet;
     38 import java.util.Iterator;
     39 import java.util.LinkedHashMap;
     40 import java.util.Map;
     41 import java.util.Set;
     42 import java.util.jar.JarEntry;
     43 import java.util.jar.JarFile;
     44 
     45 import javax.xml.parsers.DocumentBuilderFactory;
     46 import javax.xml.parsers.ParserConfigurationException;
     47 
     48 import junit.framework.TestCase;
     49 
     50 public class CollectAllTests extends DescriptionGenerator {
     51 
     52     private static final String ATTRIBUTE_RUNNER = "runner";
     53     private static final String ATTRIBUTE_PACKAGE = "appPackageName";
     54     private static final String ATTRIBUTE_NS = "appNameSpace";
     55     private static final String ATTRIBUTE_TARGET = "targetNameSpace";
     56     private static final String ATTRIBUTE_TARGET_BINARY = "targetBinaryName";
     57     private static final String ATTRIBUTE_HOST_SIDE_ONLY = "hostSideOnly";
     58     private static final String ATTRIBUTE_VM_HOST_TEST = "vmHostTest";
     59     private static final String ATTRIBUTE_JAR_PATH = "jarPath";
     60     private static final String ATTRIBUTE_JAVA_PACKAGE_FILTER = "javaPackageFilter";
     61 
     62     private static final String JAR_PATH = "LOCAL_JAR_PATH :=";
     63     private static final String TEST_TYPE = "LOCAL_TEST_TYPE :";
     64 
     65     public static void main(String[] args) {
     66         if (args.length < 5 || args.length > 7) {
     67             System.err.println("usage: CollectAllTests <output-file> <manifest-file> <jar-file> "
     68                                + "<java-package> <architecture> [expectation-dir [makefile-file]]");
     69             if (args.length != 0) {
     70                 System.err.println("received:");
     71                 for (String arg : args) {
     72                     System.err.println("  " + arg);
     73                 }
     74             }
     75             System.exit(1);
     76         }
     77 
     78         final String outputPathPrefix = args[0];
     79         File manifestFile = new File(args[1]);
     80         String jarFileName = args[2];
     81         final String javaPackageFilterArg = args[3] != null ? args[3].replaceAll("\\s+", "") : "";
     82         final String[] javaPackagePrefixes;
     83         // Validate the javaPackageFilter value if non-empty.
     84         if (!javaPackageFilterArg.isEmpty()) {
     85             javaPackagePrefixes = javaPackageFilterArg.split(":");
     86             for (int i = 0; i < javaPackagePrefixes.length; ++i) {
     87                 final String javaPackageFilter = javaPackagePrefixes[i];
     88                 if (!isValidJavaPackage(javaPackageFilter)) {
     89                     System.err.println("Invalid " + ATTRIBUTE_JAVA_PACKAGE_FILTER + ": " +
     90                            javaPackageFilter);
     91                     System.exit(1);
     92                     return;
     93                 } else {
     94                     javaPackagePrefixes[i] = javaPackageFilter.trim() + ".";
     95                 }
     96             }
     97         } else {
     98             javaPackagePrefixes = new String[0];
     99         }
    100 
    101         String architecture = args[4];
    102         if (architecture == null || architecture.equals("")) {
    103             System.err.println("Invalid architecture");
    104             System.exit(1);
    105             return;
    106         }
    107         String libcoreExpectationDir = (args.length > 5) ? args[5] : null;
    108         String androidMakeFile = (args.length > 6) ? args[6] : null;
    109 
    110         final TestType testType = TestType.getTestType(androidMakeFile);
    111 
    112         Document manifest;
    113         try {
    114             manifest = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(
    115                   new FileInputStream(manifestFile));
    116         } catch (Exception e) {
    117             System.err.println("cannot open manifest " + manifestFile);
    118             e.printStackTrace();
    119             System.exit(1);
    120             return;
    121         }
    122 
    123         Element documentElement = manifest.getDocumentElement();
    124         documentElement.getAttribute("package");
    125         final String runner = getElementAttribute(documentElement,
    126                                                   "instrumentation",
    127                                                   "android:name");
    128         final String packageName = documentElement.getAttribute("package");
    129         final String target = getElementAttribute(documentElement,
    130                                                   "instrumentation",
    131                                                   "android:targetPackage");
    132 
    133         String outputXmlFile = outputPathPrefix + ".xml";
    134         final String xmlName = new File(outputPathPrefix).getName();
    135         XMLGenerator xmlGenerator;
    136         try {
    137             xmlGenerator = new XMLGenerator(outputXmlFile) {
    138                 {
    139                     Node testPackageElem = mDoc.getDocumentElement();
    140 
    141                     setAttribute(testPackageElem, ATTRIBUTE_NAME, xmlName);
    142                     setAttribute(testPackageElem, ATTRIBUTE_RUNNER, runner);
    143                     setAttribute(testPackageElem, ATTRIBUTE_PACKAGE, packageName);
    144                     setAttribute(testPackageElem, ATTRIBUTE_NS, packageName);
    145                     setAttribute(testPackageElem, ATTRIBUTE_JAVA_PACKAGE_FILTER, javaPackageFilterArg);
    146 
    147                     if (testType.type == TestType.HOST_SIDE_ONLY) {
    148                         setAttribute(testPackageElem, ATTRIBUTE_HOST_SIDE_ONLY, "true");
    149                         setAttribute(testPackageElem, ATTRIBUTE_JAR_PATH, testType.jarPath);
    150                     }
    151 
    152                     if (testType.type == TestType.VM_HOST_TEST) {
    153                         setAttribute(testPackageElem, ATTRIBUTE_VM_HOST_TEST, "true");
    154                         setAttribute(testPackageElem, ATTRIBUTE_JAR_PATH, testType.jarPath);
    155                     }
    156 
    157                     if (!packageName.equals(target)) {
    158                         setAttribute(testPackageElem, ATTRIBUTE_TARGET, target);
    159                         setAttribute(testPackageElem, ATTRIBUTE_TARGET_BINARY, target);
    160                     }
    161                 }
    162             };
    163         } catch (ParserConfigurationException e) {
    164             System.err.println("Can't initialize XML Generator " + outputXmlFile);
    165             System.exit(1);
    166             return;
    167         }
    168 
    169         ExpectationStore libcoreVogarExpectationStore;
    170         ExpectationStore ctsVogarExpectationStore;
    171 
    172         try {
    173             libcoreVogarExpectationStore
    174                     = VogarUtils.provideExpectationStore(libcoreExpectationDir);
    175             ctsVogarExpectationStore = VogarUtils.provideExpectationStore(CTS_EXPECTATION_DIR);
    176         } catch (IOException e) {
    177             System.err.println("Can't initialize vogar expectation store from "
    178                                + libcoreExpectationDir);
    179             e.printStackTrace(System.err);
    180             System.exit(1);
    181             return;
    182         }
    183         ExpectationStore[] expectations = new ExpectationStore[] {
    184             libcoreVogarExpectationStore, ctsVogarExpectationStore
    185         };
    186 
    187         JarFile jarFile = null;
    188         try {
    189             jarFile = new JarFile(jarFileName);
    190         } catch (Exception e) {
    191             System.err.println("cannot open jarfile " + jarFileName);
    192             e.printStackTrace();
    193             System.exit(1);
    194         }
    195 
    196         Map<String,TestClass> testCases = new LinkedHashMap<String, TestClass>();
    197 
    198         Enumeration<JarEntry> jarEntries = jarFile.entries();
    199         while (jarEntries.hasMoreElements()) {
    200             JarEntry jarEntry = jarEntries.nextElement();
    201             String name = jarEntry.getName();
    202             if (!name.endsWith(".class")) {
    203                 continue;
    204             }
    205             String className
    206                     = name.substring(0, name.length() - ".class".length()).replace('/', '.');
    207 
    208             boolean matchesPrefix = false;
    209             if (javaPackagePrefixes.length > 0) {
    210                 for (String javaPackagePrefix : javaPackagePrefixes) {
    211                     if (className.startsWith(javaPackagePrefix)) {
    212                         matchesPrefix = true;
    213                     }
    214                 }
    215             } else {
    216                 matchesPrefix = true;
    217             }
    218 
    219             if (!matchesPrefix) {
    220                 continue;
    221             }
    222 
    223             try {
    224                 Class<?> klass = Class.forName(className,
    225                                                false,
    226                                                CollectAllTests.class.getClassLoader());
    227                 final int modifiers = klass.getModifiers();
    228                 if (Modifier.isAbstract(modifiers) || !Modifier.isPublic(modifiers)) {
    229                     continue;
    230                 }
    231 
    232                 final boolean isJunit4Class = isJunit4Class(klass);
    233                 if (!isJunit4Class && !isJunit3Test(klass)) {
    234                     continue;
    235                 }
    236 
    237                 try {
    238                     klass.getConstructor(new Class<?>[] { String.class } );
    239                     addToTests(expectations, architecture, testCases, klass);
    240                     continue;
    241                 } catch (NoSuchMethodException e) {
    242                 } catch (SecurityException e) {
    243                     System.out.println("Known bug (Working as intended): problem with class "
    244                             + className);
    245                     e.printStackTrace();
    246                 }
    247 
    248                 try {
    249                     klass.getConstructor(new Class<?>[0]);
    250                     addToTests(expectations, architecture, testCases, klass);
    251                     continue;
    252                 } catch (NoSuchMethodException e) {
    253                 } catch (SecurityException e) {
    254                     System.out.println("Known bug (Working as intended): problem with class "
    255                             + className);
    256                     e.printStackTrace();
    257                 }
    258             } catch (ClassNotFoundException e) {
    259                 System.out.println("class not found " + className);
    260                 e.printStackTrace();
    261                 System.exit(1);
    262             }
    263         }
    264 
    265         for (Iterator<TestClass> iterator = testCases.values().iterator(); iterator.hasNext();) {
    266             TestClass type = iterator.next();
    267             xmlGenerator.addTestClass(type);
    268         }
    269 
    270         try {
    271             xmlGenerator.dump();
    272         } catch (Exception e) {
    273             System.err.println("cannot dump xml to " + outputXmlFile);
    274             e.printStackTrace();
    275             System.exit(1);
    276         }
    277     }
    278 
    279     private static class TestType {
    280         private static final int HOST_SIDE_ONLY = 1;
    281         private static final int DEVICE_SIDE_ONLY = 2;
    282         private static final int VM_HOST_TEST = 3;
    283 
    284         private final int type;
    285         private final String jarPath;
    286 
    287         private TestType (int type, String jarPath) {
    288             this.type = type;
    289             this.jarPath = jarPath;
    290         }
    291 
    292         private static TestType getTestType(String makeFileName) {
    293             if (makeFileName == null || makeFileName.isEmpty()) {
    294                 return new TestType(DEVICE_SIDE_ONLY, null);
    295             }
    296             int type = TestType.DEVICE_SIDE_ONLY;
    297             String jarPath = null;
    298             try {
    299                 BufferedReader reader = new BufferedReader(new FileReader(makeFileName));
    300                 String line;
    301 
    302                 while ((line = reader.readLine())!=null) {
    303                     if (line.startsWith(TEST_TYPE)) {
    304                         if (line.indexOf(ATTRIBUTE_VM_HOST_TEST) >= 0) {
    305                             type = VM_HOST_TEST;
    306                         } else {
    307                             type = HOST_SIDE_ONLY;
    308                         }
    309                     } else if (line.startsWith(JAR_PATH)) {
    310                         jarPath = line.substring(JAR_PATH.length(), line.length()).trim();
    311                     }
    312                 }
    313                 reader.close();
    314             } catch (IOException e) {
    315             }
    316             return new TestType(type, jarPath);
    317         }
    318     }
    319 
    320     private static Element getElement(Element element, String tagName) {
    321         NodeList elements = element.getElementsByTagName(tagName);
    322         if (elements.getLength() > 0) {
    323             return (Element) elements.item(0);
    324         } else {
    325             return null;
    326         }
    327     }
    328 
    329     private static String getElementAttribute(Element element,
    330                                               String elementName,
    331                                               String attributeName) {
    332         Element e = getElement(element, elementName);
    333         if (e != null) {
    334             return e.getAttribute(attributeName);
    335         } else {
    336             return "";
    337         }
    338     }
    339 
    340     private static String getKnownFailure(final Class<?> testClass,
    341             final String testName) {
    342         return getAnnotation(testClass, testName, KNOWN_FAILURE);
    343     }
    344 
    345     private static boolean isKnownFailure(final Class<?> testClass,
    346             final String testName) {
    347         return getAnnotation(testClass, testName, KNOWN_FAILURE) != null;
    348     }
    349 
    350     private static boolean isSuppressed(final Class<?> testClass,
    351             final String testName)  {
    352         return getAnnotation(testClass, testName, SUPPRESSED_TEST) != null;
    353     }
    354 
    355     private static String getAnnotation(final Class<?> testClass,
    356             final String testName, final String annotationName) {
    357         try {
    358             Method testMethod = testClass.getMethod(testName, (Class[])null);
    359             Annotation[] annotations = testMethod.getAnnotations();
    360             for (Annotation annot : annotations) {
    361 
    362                 if (annot.annotationType().getName().equals(annotationName)) {
    363                     String annotStr = annot.toString();
    364                     String knownFailure = null;
    365                     if (annotStr.contains("(value=")) {
    366                         knownFailure =
    367                             annotStr.substring(annotStr.indexOf("=") + 1,
    368                                     annotStr.length() - 1);
    369 
    370                     }
    371 
    372                     if (knownFailure == null) {
    373                         knownFailure = "true";
    374                     }
    375 
    376                     return knownFailure;
    377                 }
    378 
    379             }
    380 
    381         } catch (NoSuchMethodException e) {
    382         }
    383 
    384         return null;
    385     }
    386 
    387     private static void addToTests(ExpectationStore[] expectations,
    388                                    String architecture,
    389                                    Map<String,TestClass> testCases,
    390                                    Class<?> testClass) {
    391         Set<String> testNames = new HashSet<String>();
    392 
    393         boolean isJunit3Test = isJunit3Test(testClass);
    394 
    395         Method[] testMethods = testClass.getMethods();
    396         for (Method testMethod : testMethods) {
    397             String testName = testMethod.getName();
    398             if (testNames.contains(testName)) {
    399                 continue;
    400             }
    401 
    402             /* Make sure the method has the right signature. */
    403             if (!Modifier.isPublic(testMethod.getModifiers())) {
    404                 continue;
    405             }
    406             if (!testMethod.getReturnType().equals(Void.TYPE)) {
    407                 continue;
    408             }
    409             if (testMethod.getParameterTypes().length != 0) {
    410                 continue;
    411             }
    412 
    413             if ((isJunit3Test && !testName.startsWith("test"))
    414                     || (!isJunit3Test && !isJunit4TestMethod(testMethod))) {
    415                 continue;
    416             }
    417 
    418             testNames.add(testName);
    419             addToTests(expectations, architecture, testCases, testClass, testName);
    420         }
    421     }
    422 
    423     private static void addToTests(ExpectationStore[] expectations,
    424                                    String architecture,
    425                                    Map<String,TestClass> testCases,
    426                                    Class<?> test,
    427                                    String testName) {
    428 
    429         String testClassName = test.getName();
    430         String knownFailure = getKnownFailure(test, testName);
    431 
    432         if (isKnownFailure(test, testName)) {
    433             System.out.println("ignoring known failure: " + test + "#" + testName);
    434             return;
    435         } else if (isSuppressed(test, testName)) {
    436             System.out.println("ignoring suppressed test: " + test + "#" + testName);
    437             return;
    438         } else if (VogarUtils.isVogarKnownFailure(expectations,
    439                                                   testClassName,
    440                                                   testName)) {
    441             System.out.println("ignoring expectation known failure: " + test
    442                                + "#" + testName);
    443             return;
    444         }
    445 
    446         Set<String> supportedAbis = VogarUtils.extractSupportedAbis(architecture,
    447                                                                     expectations,
    448                                                                     testClassName,
    449                                                                     testName);
    450         int timeoutInMinutes = VogarUtils.timeoutInMinutes(expectations,
    451                                                            testClassName,
    452                                                            testName);
    453         TestClass testClass;
    454         if (testCases.containsKey(testClassName)) {
    455             testClass = testCases.get(testClassName);
    456         } else {
    457             testClass = new TestClass(testClassName, new ArrayList<TestMethod>());
    458             testCases.put(testClassName, testClass);
    459         }
    460 
    461         testClass.mCases.add(new TestMethod(testName, "", "", supportedAbis,
    462               knownFailure, false, false, timeoutInMinutes));
    463     }
    464 
    465     private static boolean isJunit3Test(Class<?> klass) {
    466         return TestCase.class.isAssignableFrom(klass);
    467     }
    468 
    469     private static boolean isJunit4Class(Class<?> klass) {
    470         for (Annotation a : klass.getAnnotations()) {
    471             if (RunWith.class.isAssignableFrom(a.annotationType())) {
    472                 // @RunWith is currently not supported for CTS tests because tradefed cannot handle
    473                 // a single test spawning other tests with different names.
    474                 System.out.println("Skipping test class " + klass.getName()
    475                         + ": JUnit4 @RunWith is not supported");
    476                 return false;
    477             }
    478         }
    479 
    480         for (Method m : klass.getMethods()) {
    481             if (isJunit4TestMethod(m)) {
    482                 return true;
    483             }
    484         }
    485 
    486         return false;
    487     }
    488 
    489     private static boolean isJunit4TestMethod(Method method) {
    490         for (Annotation a : method.getAnnotations()) {
    491             if (org.junit.Test.class.isAssignableFrom(a.annotationType())) {
    492                 return true;
    493             }
    494         }
    495 
    496         return false;
    497     }
    498 
    499     /**
    500      * Determines if a given string is a valid java package name
    501      * @param javaPackageName
    502      * @return true if it is valid, false otherwise
    503      */
    504     private static boolean isValidJavaPackage(String javaPackageName) {
    505         String[] strSections = javaPackageName.split(".");
    506         for (String strSection : strSections) {
    507           if (!isValidJavaIdentifier(strSection)) {
    508               return false;
    509           }
    510         }
    511         return true;
    512     }
    513 
    514     /**
    515      * Determines if a given string is a valid java identifier.
    516      * @param javaIdentifier
    517      * @return true if it is a valid identifier, false otherwise
    518      */
    519     private static boolean isValidJavaIdentifier(String javaIdentifier) {
    520         if (javaIdentifier.length() == 0 ||
    521                 !Character.isJavaIdentifierStart(javaIdentifier.charAt(0))) {
    522             return false;
    523         }
    524         for (int i = 1; i < javaIdentifier.length(); i++) {
    525             if (!Character.isJavaIdentifierPart(javaIdentifier.charAt(i))) {
    526                 return false;
    527             }
    528         }
    529         return true;
    530     }
    531 }
    532