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 main(String[] args)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 TestType(int type, String jarPath)287 private TestType (int type, String jarPath) { 288 this.type = type; 289 this.jarPath = jarPath; 290 } 291 getTestType(String makeFileName)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 getElement(Element element, String tagName)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 getElementAttribute(Element element, String elementName, String attributeName)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 getKnownFailure(final Class<?> testClass, final String testName)340 private static String getKnownFailure(final Class<?> testClass, 341 final String testName) { 342 return getAnnotation(testClass, testName, KNOWN_FAILURE); 343 } 344 isKnownFailure(final Class<?> testClass, final String testName)345 private static boolean isKnownFailure(final Class<?> testClass, 346 final String testName) { 347 return getAnnotation(testClass, testName, KNOWN_FAILURE) != null; 348 } 349 isSuppressed(final Class<?> testClass, final String testName)350 private static boolean isSuppressed(final Class<?> testClass, 351 final String testName) { 352 return getAnnotation(testClass, testName, SUPPRESSED_TEST) != null; 353 } 354 getAnnotation(final Class<?> testClass, final String testName, final String annotationName)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 addToTests(ExpectationStore[] expectations, String architecture, Map<String,TestClass> testCases, Class<?> testClass)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 addToTests(ExpectationStore[] expectations, String architecture, Map<String,TestClass> testCases, Class<?> test, String testName)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 isJunit3Test(Class<?> klass)465 private static boolean isJunit3Test(Class<?> klass) { 466 return TestCase.class.isAssignableFrom(klass); 467 } 468 isJunit4Class(Class<?> klass)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 isJunit4TestMethod(Method method)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 */ isValidJavaPackage(String javaPackageName)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 */ isValidJavaIdentifier(String javaIdentifier)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