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 javaPackageFilter = args[3];
     82         // Validate the javaPackageFilter value if non null.
     83         if (javaPackageFilter.length() != 0) {
     84             if (!isValidJavaPackage(javaPackageFilter)) {
     85                 System.err.println("Invalid " + ATTRIBUTE_JAVA_PACKAGE_FILTER + ": " +
     86                         javaPackageFilter);
     87                 System.exit(1);
     88                 return;
     89             }
     90         }
     91         String architecture = args[4];
     92         if (architecture == null || architecture.equals("")) {
     93             System.err.println("Invalid architecture");
     94             System.exit(1);
     95             return;
     96         }
     97         String libcoreExpectationDir = (args.length > 5) ? args[5] : null;
     98         String androidMakeFile = (args.length > 6) ? args[6] : null;
     99 
    100         final TestType testType = TestType.getTestType(androidMakeFile);
    101 
    102         Document manifest;
    103         try {
    104             manifest = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(
    105                   new FileInputStream(manifestFile));
    106         } catch (Exception e) {
    107             System.err.println("cannot open manifest " + manifestFile);
    108             e.printStackTrace();
    109             System.exit(1);
    110             return;
    111         }
    112 
    113         Element documentElement = manifest.getDocumentElement();
    114         documentElement.getAttribute("package");
    115         final String runner = getElementAttribute(documentElement,
    116                                                   "instrumentation",
    117                                                   "android:name");
    118         final String packageName = documentElement.getAttribute("package");
    119         final String target = getElementAttribute(documentElement,
    120                                                   "instrumentation",
    121                                                   "android:targetPackage");
    122 
    123         String outputXmlFile = outputPathPrefix + ".xml";
    124         final String xmlName = new File(outputPathPrefix).getName();
    125         XMLGenerator xmlGenerator;
    126         try {
    127             xmlGenerator = new XMLGenerator(outputXmlFile) {
    128                 {
    129                     Node testPackageElem = mDoc.getDocumentElement();
    130 
    131                     setAttribute(testPackageElem, ATTRIBUTE_NAME, xmlName);
    132                     setAttribute(testPackageElem, ATTRIBUTE_RUNNER, runner);
    133                     setAttribute(testPackageElem, ATTRIBUTE_PACKAGE, packageName);
    134                     setAttribute(testPackageElem, ATTRIBUTE_NS, packageName);
    135                     setAttribute(testPackageElem, ATTRIBUTE_JAVA_PACKAGE_FILTER, javaPackageFilter);
    136 
    137                     if (testType.type == TestType.HOST_SIDE_ONLY) {
    138                         setAttribute(testPackageElem, ATTRIBUTE_HOST_SIDE_ONLY, "true");
    139                         setAttribute(testPackageElem, ATTRIBUTE_JAR_PATH, testType.jarPath);
    140                     }
    141 
    142                     if (testType.type == TestType.VM_HOST_TEST) {
    143                         setAttribute(testPackageElem, ATTRIBUTE_VM_HOST_TEST, "true");
    144                         setAttribute(testPackageElem, ATTRIBUTE_JAR_PATH, testType.jarPath);
    145                     }
    146 
    147                     if (!packageName.equals(target)) {
    148                         setAttribute(testPackageElem, ATTRIBUTE_TARGET, target);
    149                         setAttribute(testPackageElem, ATTRIBUTE_TARGET_BINARY, target);
    150                     }
    151                 }
    152             };
    153         } catch (ParserConfigurationException e) {
    154             System.err.println("Can't initialize XML Generator " + outputXmlFile);
    155             System.exit(1);
    156             return;
    157         }
    158 
    159         ExpectationStore libcoreVogarExpectationStore;
    160         ExpectationStore ctsVogarExpectationStore;
    161 
    162         try {
    163             libcoreVogarExpectationStore
    164                     = VogarUtils.provideExpectationStore(libcoreExpectationDir);
    165             ctsVogarExpectationStore = VogarUtils.provideExpectationStore(CTS_EXPECTATION_DIR);
    166         } catch (IOException e) {
    167             System.err.println("Can't initialize vogar expectation store from "
    168                                + libcoreExpectationDir);
    169             e.printStackTrace(System.err);
    170             System.exit(1);
    171             return;
    172         }
    173         ExpectationStore[] expectations = new ExpectationStore[] {
    174             libcoreVogarExpectationStore, ctsVogarExpectationStore
    175         };
    176 
    177         JarFile jarFile = null;
    178         try {
    179             jarFile = new JarFile(jarFileName);
    180         } catch (Exception e) {
    181             System.err.println("cannot open jarfile " + jarFileName);
    182             e.printStackTrace();
    183             System.exit(1);
    184         }
    185 
    186         Map<String,TestClass> testCases = new LinkedHashMap<String, TestClass>();
    187 
    188         String javaPackagePrefix = javaPackageFilter.isEmpty() ? "" : (javaPackageFilter + ".");
    189 
    190         Enumeration<JarEntry> jarEntries = jarFile.entries();
    191         while (jarEntries.hasMoreElements()) {
    192             JarEntry jarEntry = jarEntries.nextElement();
    193             String name = jarEntry.getName();
    194             if (!name.endsWith(".class")) {
    195                 continue;
    196             }
    197             String className
    198                     = name.substring(0, name.length() - ".class".length()).replace('/', '.');
    199             if (!className.startsWith(javaPackagePrefix)) {
    200                 continue;
    201             }
    202             try {
    203                 Class<?> klass = Class.forName(className,
    204                                                false,
    205                                                CollectAllTests.class.getClassLoader());
    206                 final int modifiers = klass.getModifiers();
    207                 if (Modifier.isAbstract(modifiers) || !Modifier.isPublic(modifiers)) {
    208                     continue;
    209                 }
    210 
    211                 final boolean isJunit4Class = isJunit4Class(klass);
    212                 if (!isJunit4Class && !isJunit3Test(klass)) {
    213                     continue;
    214                 }
    215 
    216                 try {
    217                     klass.getConstructor(new Class<?>[] { String.class } );
    218                     addToTests(expectations, architecture, testCases, klass);
    219                     continue;
    220                 } catch (NoSuchMethodException e) {
    221                 } catch (SecurityException e) {
    222                     System.out.println("Known bug (Working as intended): problem with class "
    223                             + className);
    224                     e.printStackTrace();
    225                 }
    226 
    227                 try {
    228                     klass.getConstructor(new Class<?>[0]);
    229                     addToTests(expectations, architecture, testCases, klass);
    230                     continue;
    231                 } catch (NoSuchMethodException e) {
    232                 } catch (SecurityException e) {
    233                     System.out.println("Known bug (Working as intended): problem with class "
    234                             + className);
    235                     e.printStackTrace();
    236                 }
    237             } catch (ClassNotFoundException e) {
    238                 System.out.println("class not found " + className);
    239                 e.printStackTrace();
    240                 System.exit(1);
    241             }
    242         }
    243 
    244         for (Iterator<TestClass> iterator = testCases.values().iterator(); iterator.hasNext();) {
    245             TestClass type = iterator.next();
    246             xmlGenerator.addTestClass(type);
    247         }
    248 
    249         try {
    250             xmlGenerator.dump();
    251         } catch (Exception e) {
    252             System.err.println("cannot dump xml to " + outputXmlFile);
    253             e.printStackTrace();
    254             System.exit(1);
    255         }
    256     }
    257 
    258     private static class TestType {
    259         private static final int HOST_SIDE_ONLY = 1;
    260         private static final int DEVICE_SIDE_ONLY = 2;
    261         private static final int VM_HOST_TEST = 3;
    262 
    263         private final int type;
    264         private final String jarPath;
    265 
    266         private TestType (int type, String jarPath) {
    267             this.type = type;
    268             this.jarPath = jarPath;
    269         }
    270 
    271         private static TestType getTestType(String makeFileName) {
    272             if (makeFileName == null || makeFileName.isEmpty()) {
    273                 return new TestType(DEVICE_SIDE_ONLY, null);
    274             }
    275             int type = TestType.DEVICE_SIDE_ONLY;
    276             String jarPath = null;
    277             try {
    278                 BufferedReader reader = new BufferedReader(new FileReader(makeFileName));
    279                 String line;
    280 
    281                 while ((line = reader.readLine())!=null) {
    282                     if (line.startsWith(TEST_TYPE)) {
    283                         if (line.indexOf(ATTRIBUTE_VM_HOST_TEST) >= 0) {
    284                             type = VM_HOST_TEST;
    285                         } else {
    286                             type = HOST_SIDE_ONLY;
    287                         }
    288                     } else if (line.startsWith(JAR_PATH)) {
    289                         jarPath = line.substring(JAR_PATH.length(), line.length()).trim();
    290                     }
    291                 }
    292                 reader.close();
    293             } catch (IOException e) {
    294             }
    295             return new TestType(type, jarPath);
    296         }
    297     }
    298 
    299     private static Element getElement(Element element, String tagName) {
    300         NodeList elements = element.getElementsByTagName(tagName);
    301         if (elements.getLength() > 0) {
    302             return (Element) elements.item(0);
    303         } else {
    304             return null;
    305         }
    306     }
    307 
    308     private static String getElementAttribute(Element element,
    309                                               String elementName,
    310                                               String attributeName) {
    311         Element e = getElement(element, elementName);
    312         if (e != null) {
    313             return e.getAttribute(attributeName);
    314         } else {
    315             return "";
    316         }
    317     }
    318 
    319     private static String getKnownFailure(final Class<?> testClass,
    320             final String testName) {
    321         return getAnnotation(testClass, testName, KNOWN_FAILURE);
    322     }
    323 
    324     private static boolean isKnownFailure(final Class<?> testClass,
    325             final String testName) {
    326         return getAnnotation(testClass, testName, KNOWN_FAILURE) != null;
    327     }
    328 
    329     private static boolean isSuppressed(final Class<?> testClass,
    330             final String testName)  {
    331         return getAnnotation(testClass, testName, SUPPRESSED_TEST) != null;
    332     }
    333 
    334     private static String getAnnotation(final Class<?> testClass,
    335             final String testName, final String annotationName) {
    336         try {
    337             Method testMethod = testClass.getMethod(testName, (Class[])null);
    338             Annotation[] annotations = testMethod.getAnnotations();
    339             for (Annotation annot : annotations) {
    340 
    341                 if (annot.annotationType().getName().equals(annotationName)) {
    342                     String annotStr = annot.toString();
    343                     String knownFailure = null;
    344                     if (annotStr.contains("(value=")) {
    345                         knownFailure =
    346                             annotStr.substring(annotStr.indexOf("=") + 1,
    347                                     annotStr.length() - 1);
    348 
    349                     }
    350 
    351                     if (knownFailure == null) {
    352                         knownFailure = "true";
    353                     }
    354 
    355                     return knownFailure;
    356                 }
    357 
    358             }
    359 
    360         } catch (NoSuchMethodException e) {
    361         }
    362 
    363         return null;
    364     }
    365 
    366     private static void addToTests(ExpectationStore[] expectations,
    367                                    String architecture,
    368                                    Map<String,TestClass> testCases,
    369                                    Class<?> testClass) {
    370         Set<String> testNames = new HashSet<String>();
    371 
    372         boolean isJunit3Test = isJunit3Test(testClass);
    373 
    374         Method[] testMethods = testClass.getMethods();
    375         for (Method testMethod : testMethods) {
    376             String testName = testMethod.getName();
    377             if (testNames.contains(testName)) {
    378                 continue;
    379             }
    380 
    381             /* Make sure the method has the right signature. */
    382             if (!Modifier.isPublic(testMethod.getModifiers())) {
    383                 continue;
    384             }
    385             if (!testMethod.getReturnType().equals(Void.TYPE)) {
    386                 continue;
    387             }
    388             if (testMethod.getParameterTypes().length != 0) {
    389                 continue;
    390             }
    391 
    392             if ((isJunit3Test && !testName.startsWith("test"))
    393                     || (!isJunit3Test && !isJunit4TestMethod(testMethod))) {
    394                 continue;
    395             }
    396 
    397             testNames.add(testName);
    398             addToTests(expectations, architecture, testCases, testClass, testName);
    399         }
    400     }
    401 
    402     private static void addToTests(ExpectationStore[] expectations,
    403                                    String architecture,
    404                                    Map<String,TestClass> testCases,
    405                                    Class<?> test,
    406                                    String testName) {
    407 
    408         String testClassName = test.getName();
    409         String knownFailure = getKnownFailure(test, testName);
    410 
    411         if (isKnownFailure(test, testName)) {
    412             System.out.println("ignoring known failure: " + test + "#" + testName);
    413             return;
    414         } else if (isSuppressed(test, testName)) {
    415             System.out.println("ignoring suppressed test: " + test + "#" + testName);
    416             return;
    417         } else if (VogarUtils.isVogarKnownFailure(expectations,
    418                                                   testClassName,
    419                                                   testName)) {
    420             System.out.println("ignoring expectation known failure: " + test
    421                                + "#" + testName);
    422             return;
    423         }
    424 
    425         Set<String> supportedAbis = VogarUtils.extractSupportedAbis(architecture,
    426                                                                     expectations,
    427                                                                     testClassName,
    428                                                                     testName);
    429         TestClass testClass;
    430         if (testCases.containsKey(testClassName)) {
    431             testClass = testCases.get(testClassName);
    432         } else {
    433             testClass = new TestClass(testClassName, new ArrayList<TestMethod>());
    434             testCases.put(testClassName, testClass);
    435         }
    436 
    437         testClass.mCases.add(new TestMethod(testName, "", "", supportedAbis,
    438               knownFailure, false, false));
    439     }
    440 
    441     private static boolean isJunit3Test(Class<?> klass) {
    442         return TestCase.class.isAssignableFrom(klass);
    443     }
    444 
    445     private static boolean isJunit4Class(Class<?> klass) {
    446         for (Annotation a : klass.getAnnotations()) {
    447             if (RunWith.class.isAssignableFrom(a.annotationType())) {
    448                 // @RunWith is currently not supported for CTS tests because tradefed cannot handle
    449                 // a single test spawning other tests with different names.
    450                 System.out.println("Skipping test class " + klass.getName()
    451                         + ": JUnit4 @RunWith is not supported");
    452                 return false;
    453             }
    454         }
    455 
    456         for (Method m : klass.getMethods()) {
    457             if (isJunit4TestMethod(m)) {
    458                 return true;
    459             }
    460         }
    461 
    462         return false;
    463     }
    464 
    465     private static boolean isJunit4TestMethod(Method method) {
    466         for (Annotation a : method.getAnnotations()) {
    467             if (org.junit.Test.class.isAssignableFrom(a.annotationType())) {
    468                 return true;
    469             }
    470         }
    471 
    472         return false;
    473     }
    474 
    475     /**
    476      * Determines if a given string is a valid java package name
    477      * @param javaPackageName
    478      * @return true if it is valid, false otherwise
    479      */
    480     private static boolean isValidJavaPackage(String javaPackageName) {
    481         String[] strSections = javaPackageName.split(".");
    482         for (String strSection : strSections) {
    483           if (!isValidJavaIdentifier(strSection)) {
    484               return false;
    485           }
    486         }
    487         return true;
    488     }
    489 
    490     /**
    491      * Determines if a given string is a valid java identifier.
    492      * @param javaIdentifier
    493      * @return true if it is a valid identifier, false otherwise
    494      */
    495     private static boolean isValidJavaIdentifier(String javaIdentifier) {
    496         if (javaIdentifier.length() == 0 ||
    497                 !Character.isJavaIdentifierStart(javaIdentifier.charAt(0))) {
    498             return false;
    499         }
    500         for (int i = 1; i < javaIdentifier.length(); i++) {
    501             if (!Character.isJavaIdentifierPart(javaIdentifier.charAt(i))) {
    502                 return false;
    503             }
    504         }
    505         return true;
    506     }
    507 }
    508