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