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 17 import org.w3c.dom.Document; 18 import org.w3c.dom.Element; 19 import org.w3c.dom.Node; 20 import org.w3c.dom.NodeList; 21 22 import vogar.Expectation; 23 import vogar.ExpectationStore; 24 import vogar.ModeId; 25 26 import java.io.BufferedReader; 27 import java.io.File; 28 import java.io.FileInputStream; 29 import java.io.FileReader; 30 import java.io.FilenameFilter; 31 import java.io.IOException; 32 import java.lang.annotation.Annotation; 33 import java.lang.reflect.InvocationTargetException; 34 import java.lang.reflect.Method; 35 import java.lang.reflect.Modifier; 36 import java.util.ArrayList; 37 import java.util.Arrays; 38 import java.util.Enumeration; 39 import java.util.HashSet; 40 import java.util.Iterator; 41 import java.util.LinkedHashMap; 42 import java.util.Map; 43 import java.util.Set; 44 import java.util.jar.JarEntry; 45 import java.util.jar.JarFile; 46 47 import javax.xml.parsers.DocumentBuilderFactory; 48 import javax.xml.parsers.ParserConfigurationException; 49 50 import junit.framework.Test; 51 import junit.framework.TestCase; 52 import junit.framework.TestResult; 53 import junit.textui.ResultPrinter; 54 import junit.textui.TestRunner; 55 56 public class CollectAllTests extends DescriptionGenerator { 57 58 private static final String ATTRIBUTE_RUNNER = "runner"; 59 private static final String ATTRIBUTE_PACKAGE = "appPackageName"; 60 private static final String ATTRIBUTE_NS = "appNameSpace"; 61 private static final String ATTRIBUTE_TARGET = "targetNameSpace"; 62 private static final String ATTRIBUTE_TARGET_BINARY = "targetBinaryName"; 63 private static final String ATTRIBUTE_HOST_SIDE_ONLY = "hostSideOnly"; 64 private static final String ATTRIBUTE_VM_HOST_TEST = "vmHostTest"; 65 private static final String ATTRIBUTE_JAR_PATH = "jarPath"; 66 private static final String ATTRIBUTE_JAVA_PACKAGE_FILTER = "javaPackageFilter"; 67 68 private static final String JAR_PATH = "LOCAL_JAR_PATH :="; 69 private static final String TEST_TYPE = "LOCAL_TEST_TYPE :"; 70 71 public static void main(String[] args) { 72 if (args.length < 4 || args.length > 6) { 73 System.err.println("usage: CollectAllTests <output-file> <manifest-file> <jar-file> " 74 + "<java-package> [expectation-dir [makefile-file]]"); 75 if (args.length != 0) { 76 System.err.println("received:"); 77 for (String arg : args) { 78 System.err.println(" " + arg); 79 } 80 } 81 System.exit(1); 82 } 83 84 final String outputPathPrefix = args[0]; 85 File manifestFile = new File(args[1]); 86 String jarFileName = args[2]; 87 final String javaPackageFilter = args[3]; 88 // Validate the javaPackageFilter value if non null. 89 if (javaPackageFilter.length() != 0) { 90 if (!isValidJavaPackage(javaPackageFilter)) { 91 System.err.println("Invalid " + ATTRIBUTE_JAVA_PACKAGE_FILTER + ": " + 92 javaPackageFilter); 93 System.exit(1); 94 return; 95 } 96 } 97 String libcoreExpectationDir = (args.length > 4) ? args[4] : null; 98 String androidMakeFile = (args.length > 5) ? args[5] : 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 if (!TestCase.class.isAssignableFrom(klass)) { 207 continue; 208 } 209 if (Modifier.isAbstract(klass.getModifiers())) { 210 continue; 211 } 212 if (!Modifier.isPublic(klass.getModifiers())) { 213 continue; 214 } 215 try { 216 klass.getConstructor(new Class<?>[] { String.class } ); 217 addToTests(expectations, testCases, klass.asSubclass(TestCase.class)); 218 continue; 219 } catch (NoSuchMethodException e) { 220 } catch (SecurityException e) { 221 System.out.println("Known bug (Working as intended): problem with class " 222 + className); 223 e.printStackTrace(); 224 } 225 try { 226 klass.getConstructor(new Class<?>[0]); 227 addToTests(expectations, testCases, klass.asSubclass(TestCase.class)); 228 continue; 229 } catch (NoSuchMethodException e) { 230 } catch (SecurityException e) { 231 System.out.println("Known bug (Working as intended): problem with class " 232 + className); 233 e.printStackTrace(); 234 } 235 } catch (ClassNotFoundException e) { 236 System.out.println("class not found " + className); 237 e.printStackTrace(); 238 System.exit(1); 239 } 240 } 241 242 for (Iterator<TestClass> iterator = testCases.values().iterator(); iterator.hasNext();) { 243 TestClass type = iterator.next(); 244 xmlGenerator.addTestClass(type); 245 } 246 247 try { 248 xmlGenerator.dump(); 249 } catch (Exception e) { 250 System.err.println("cannot dump xml to " + outputXmlFile); 251 e.printStackTrace(); 252 System.exit(1); 253 } 254 } 255 256 private static class TestType { 257 private static final int HOST_SIDE_ONLY = 1; 258 private static final int DEVICE_SIDE_ONLY = 2; 259 private static final int VM_HOST_TEST = 3; 260 261 private final int type; 262 private final String jarPath; 263 264 private TestType (int type, String jarPath) { 265 this.type = type; 266 this.jarPath = jarPath; 267 } 268 269 private static TestType getTestType(String makeFileName) { 270 if (makeFileName == null || makeFileName.isEmpty()) { 271 return new TestType(DEVICE_SIDE_ONLY, null); 272 } 273 int type = TestType.DEVICE_SIDE_ONLY; 274 String jarPath = null; 275 try { 276 BufferedReader reader = new BufferedReader(new FileReader(makeFileName)); 277 String line; 278 279 while ((line =reader.readLine())!=null) { 280 if (line.startsWith(TEST_TYPE)) { 281 if (line.indexOf(ATTRIBUTE_VM_HOST_TEST) >= 0) { 282 type = VM_HOST_TEST; 283 } else { 284 type = HOST_SIDE_ONLY; 285 } 286 } else if (line.startsWith(JAR_PATH)) { 287 jarPath = line.substring(JAR_PATH.length(), line.length()).trim(); 288 } 289 } 290 reader.close(); 291 } catch (IOException e) { 292 } 293 return new TestType(type, jarPath); 294 } 295 } 296 297 private static Element getElement(Element element, String tagName) { 298 NodeList elements = element.getElementsByTagName(tagName); 299 if (elements.getLength() > 0) { 300 return (Element) elements.item(0); 301 } else { 302 return null; 303 } 304 } 305 306 private static String getElementAttribute(Element element, 307 String elementName, 308 String attributeName) { 309 Element e = getElement(element, elementName); 310 if (e != null) { 311 return e.getAttribute(attributeName); 312 } else { 313 return ""; 314 } 315 } 316 317 private static String getKnownFailure(final Class<? extends TestCase> testClass, 318 final String testName) { 319 return getAnnotation(testClass, testName, KNOWN_FAILURE); 320 } 321 322 private static boolean isKnownFailure(final Class<? extends TestCase> testClass, 323 final String testName) { 324 return getAnnotation(testClass, testName, KNOWN_FAILURE) != null; 325 } 326 327 private static boolean isBrokenTest(final Class<? extends TestCase> testClass, 328 final String testName) { 329 return getAnnotation(testClass, testName, BROKEN_TEST) != null; 330 } 331 332 private static boolean isSuppressed(final Class<? extends TestCase> testClass, 333 final String testName) { 334 return getAnnotation(testClass, testName, SUPPRESSED_TEST) != null; 335 } 336 337 private static boolean hasSideEffects(final Class<? extends TestCase> testClass, 338 final String testName) { 339 return getAnnotation(testClass, testName, SIDE_EFFECT) != null; 340 } 341 342 private static String getAnnotation(final Class<? extends TestCase> testClass, 343 final String testName, final String annotationName) { 344 try { 345 Method testMethod = testClass.getMethod(testName, (Class[])null); 346 Annotation[] annotations = testMethod.getAnnotations(); 347 for (Annotation annot : annotations) { 348 349 if (annot.annotationType().getName().equals(annotationName)) { 350 String annotStr = annot.toString(); 351 String knownFailure = null; 352 if (annotStr.contains("(value=")) { 353 knownFailure = 354 annotStr.substring(annotStr.indexOf("=") + 1, 355 annotStr.length() - 1); 356 357 } 358 359 if (knownFailure == null) { 360 knownFailure = "true"; 361 } 362 363 return knownFailure; 364 } 365 366 } 367 368 } catch (NoSuchMethodException e) { 369 } 370 371 return null; 372 } 373 374 private static void addToTests(ExpectationStore[] expectations, 375 Map<String,TestClass> testCases, 376 Class<? extends TestCase> test) { 377 Class testClass = test; 378 Set<String> testNames = new HashSet<String>(); 379 while (TestCase.class.isAssignableFrom(testClass)) { 380 Method[] testMethods = testClass.getDeclaredMethods(); 381 for (Method testMethod : testMethods) { 382 String testName = testMethod.getName(); 383 if (testNames.contains(testName)) { 384 continue; 385 } 386 if (!testName.startsWith("test")) { 387 continue; 388 } 389 if (testMethod.getParameterTypes().length != 0) { 390 continue; 391 } 392 if (!testMethod.getReturnType().equals(Void.TYPE)) { 393 continue; 394 } 395 if (!Modifier.isPublic(testMethod.getModifiers())) { 396 continue; 397 } 398 testNames.add(testName); 399 addToTests(expectations, testCases, test, testName); 400 } 401 testClass = testClass.getSuperclass(); 402 } 403 } 404 405 private static void addToTests(ExpectationStore[] expectations, 406 Map<String,TestClass> testCases, 407 Class<? extends TestCase> test, 408 String testName) { 409 410 String testClassName = test.getName(); 411 String knownFailure = getKnownFailure(test, testName); 412 413 if (isKnownFailure(test, testName)) { 414 System.out.println("ignoring known failure: " + test + "#" + testName); 415 return; 416 } else if (isBrokenTest(test, testName)) { 417 System.out.println("ignoring broken test: " + test + "#" + testName); 418 return; 419 } else if (isSuppressed(test, testName)) { 420 System.out.println("ignoring suppressed test: " + test + "#" + testName); 421 return; 422 } else if (hasSideEffects(test, testName)) { 423 System.out.println("ignoring test with side effects: " + test + "#" + testName); 424 return; 425 } else if (VogarUtils.isVogarKnownFailure(expectations, 426 testClassName, 427 testName)) { 428 System.out.println("ignoring expectation known failure: " + test 429 + "#" + testName); 430 return; 431 } 432 433 TestClass testClass = null; 434 if (testCases.containsKey(testClassName)) { 435 testClass = testCases.get(testClassName); 436 } else { 437 testClass = new TestClass(testClassName, new ArrayList<TestMethod>()); 438 testCases.put(testClassName, testClass); 439 } 440 441 testClass.mCases.add(new TestMethod(testName, "", "", knownFailure, false, false)); 442 } 443 444 /** 445 * Determines if a given string is a valid java package name 446 * @param javaPackageName 447 * @return true if it is valid, false otherwise 448 */ 449 private static boolean isValidJavaPackage(String javaPackageName) { 450 String[] strSections = javaPackageName.split("."); 451 for (String strSection : strSections) { 452 if (!isValidJavaIdentifier(strSection)) { 453 return false; 454 } 455 } 456 return true; 457 } 458 459 /** 460 * Determines if a given string is a valid java identifier. 461 * @param javaIdentifier 462 * @return true if it is a valid identifier, false otherwise 463 */ 464 private static boolean isValidJavaIdentifier(String javaIdentifier) { 465 if (javaIdentifier.length() == 0 || 466 !Character.isJavaIdentifierStart(javaIdentifier.charAt(0))) { 467 return false; 468 } 469 for (int i = 1; i < javaIdentifier.length(); i++) { 470 if (!Character.isJavaIdentifierPart(javaIdentifier.charAt(i))) { 471 return false; 472 } 473 } 474 return true; 475 } 476 } 477