You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
539 lines
20 KiB
539 lines
20 KiB
/* |
|
* Copyright (C) 2008 The Android Open Source Project |
|
* |
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
|
* you may not use this file except in compliance with the License. |
|
* You may obtain a copy of the License at |
|
* |
|
* http://www.apache.org/licenses/LICENSE-2.0 |
|
* |
|
* Unless required by applicable law or agreed to in writing, software |
|
* distributed under the License is distributed on an "AS IS" BASIS, |
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
* See the License for the specific language governing permissions and |
|
* limitations under the License. |
|
*/ |
|
|
|
import com.android.tradefed.util.AbiUtils; |
|
|
|
import org.junit.runner.RunWith; |
|
import org.w3c.dom.Document; |
|
import org.w3c.dom.Element; |
|
import org.w3c.dom.Node; |
|
import org.w3c.dom.NodeList; |
|
|
|
import vogar.Expectation; |
|
import vogar.ExpectationStore; |
|
|
|
import java.io.BufferedReader; |
|
import java.io.File; |
|
import java.io.FileInputStream; |
|
import java.io.FileReader; |
|
import java.io.IOException; |
|
import java.lang.annotation.Annotation; |
|
import java.lang.reflect.Method; |
|
import java.lang.reflect.Modifier; |
|
import java.util.ArrayList; |
|
import java.util.Enumeration; |
|
import java.util.HashSet; |
|
import java.util.Iterator; |
|
import java.util.LinkedHashMap; |
|
import java.util.Map; |
|
import java.util.Set; |
|
import java.util.jar.JarEntry; |
|
import java.util.jar.JarFile; |
|
|
|
import javax.xml.parsers.DocumentBuilderFactory; |
|
import javax.xml.parsers.ParserConfigurationException; |
|
|
|
import junit.framework.TestCase; |
|
|
|
public class CollectAllTests extends DescriptionGenerator { |
|
|
|
private static final String ATTRIBUTE_RUNNER = "runner"; |
|
private static final String ATTRIBUTE_PACKAGE = "appPackageName"; |
|
private static final String ATTRIBUTE_NS = "appNameSpace"; |
|
private static final String ATTRIBUTE_TARGET = "targetNameSpace"; |
|
private static final String ATTRIBUTE_TARGET_BINARY = "targetBinaryName"; |
|
private static final String ATTRIBUTE_HOST_SIDE_ONLY = "hostSideOnly"; |
|
private static final String ATTRIBUTE_VM_HOST_TEST = "vmHostTest"; |
|
private static final String ATTRIBUTE_JAR_PATH = "jarPath"; |
|
private static final String ATTRIBUTE_JAVA_PACKAGE_FILTER = "javaPackageFilter"; |
|
|
|
private static final String JAR_PATH = "LOCAL_JAR_PATH :="; |
|
private static final String TEST_TYPE = "LOCAL_TEST_TYPE :"; |
|
|
|
public static void main(String[] args) { |
|
if (args.length < 5 || args.length > 7) { |
|
System.err.println("usage: CollectAllTests <output-file> <manifest-file> <jar-file> " |
|
+ "<java-package> <architecture> [expectation-dir [makefile-file]]"); |
|
if (args.length != 0) { |
|
System.err.println("received:"); |
|
for (String arg : args) { |
|
System.err.println(" " + arg); |
|
} |
|
} |
|
System.exit(1); |
|
} |
|
|
|
final String outputPathPrefix = args[0]; |
|
File manifestFile = new File(args[1]); |
|
String jarFileName = args[2]; |
|
final String javaPackageFilterArg = args[3] != null ? args[3].replaceAll("\\s+", "") : ""; |
|
final String[] javaPackagePrefixes; |
|
// Validate the javaPackageFilter value if non-empty. |
|
if (!javaPackageFilterArg.isEmpty()) { |
|
javaPackagePrefixes = javaPackageFilterArg.split(":"); |
|
for (int i = 0; i < javaPackagePrefixes.length; ++i) { |
|
final String javaPackageFilter = javaPackagePrefixes[i]; |
|
if (!isValidJavaPackage(javaPackageFilter)) { |
|
System.err.println("Invalid " + ATTRIBUTE_JAVA_PACKAGE_FILTER + ": " + |
|
javaPackageFilter); |
|
System.exit(1); |
|
return; |
|
} else { |
|
javaPackagePrefixes[i] = javaPackageFilter.trim() + "."; |
|
} |
|
} |
|
} else { |
|
javaPackagePrefixes = new String[0]; |
|
} |
|
|
|
String architecture = args[4]; |
|
if (architecture == null || architecture.equals("")) { |
|
System.err.println("Invalid architecture"); |
|
System.exit(1); |
|
return; |
|
} |
|
String libcoreExpectationDir = (args.length > 5) ? args[5] : null; |
|
String androidMakeFile = (args.length > 6) ? args[6] : null; |
|
|
|
final TestType testType = TestType.getTestType(androidMakeFile); |
|
|
|
Document manifest; |
|
try { |
|
manifest = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse( |
|
new FileInputStream(manifestFile)); |
|
} catch (Exception e) { |
|
System.err.println("cannot open manifest " + manifestFile); |
|
e.printStackTrace(); |
|
System.exit(1); |
|
return; |
|
} |
|
|
|
Element documentElement = manifest.getDocumentElement(); |
|
documentElement.getAttribute("package"); |
|
final String runner = getElementAttribute(documentElement, |
|
"instrumentation", |
|
"android:name"); |
|
final String packageName = documentElement.getAttribute("package"); |
|
final String target = getElementAttribute(documentElement, |
|
"instrumentation", |
|
"android:targetPackage"); |
|
|
|
String outputXmlFile = outputPathPrefix + ".xml"; |
|
final String xmlName = new File(outputPathPrefix).getName(); |
|
XMLGenerator xmlGenerator; |
|
try { |
|
xmlGenerator = new XMLGenerator(outputXmlFile) { |
|
{ |
|
Node testPackageElem = mDoc.getDocumentElement(); |
|
|
|
setAttribute(testPackageElem, ATTRIBUTE_NAME, xmlName); |
|
setAttribute(testPackageElem, ATTRIBUTE_RUNNER, runner); |
|
setAttribute(testPackageElem, ATTRIBUTE_PACKAGE, packageName); |
|
setAttribute(testPackageElem, ATTRIBUTE_NS, packageName); |
|
setAttribute(testPackageElem, ATTRIBUTE_JAVA_PACKAGE_FILTER, javaPackageFilterArg); |
|
|
|
if (testType.type == TestType.HOST_SIDE_ONLY) { |
|
setAttribute(testPackageElem, ATTRIBUTE_HOST_SIDE_ONLY, "true"); |
|
setAttribute(testPackageElem, ATTRIBUTE_JAR_PATH, testType.jarPath); |
|
} |
|
|
|
if (testType.type == TestType.VM_HOST_TEST) { |
|
setAttribute(testPackageElem, ATTRIBUTE_VM_HOST_TEST, "true"); |
|
setAttribute(testPackageElem, ATTRIBUTE_JAR_PATH, testType.jarPath); |
|
} |
|
|
|
if (!packageName.equals(target)) { |
|
setAttribute(testPackageElem, ATTRIBUTE_TARGET, target); |
|
setAttribute(testPackageElem, ATTRIBUTE_TARGET_BINARY, target); |
|
} |
|
} |
|
}; |
|
} catch (ParserConfigurationException e) { |
|
System.err.println("Can't initialize XML Generator " + outputXmlFile); |
|
System.exit(1); |
|
return; |
|
} |
|
|
|
ExpectationStore libcoreVogarExpectationStore; |
|
ExpectationStore ctsVogarExpectationStore; |
|
|
|
try { |
|
libcoreVogarExpectationStore |
|
= VogarUtils.provideExpectationStore(libcoreExpectationDir); |
|
ctsVogarExpectationStore = VogarUtils.provideExpectationStore(CTS_EXPECTATION_DIR); |
|
} catch (IOException e) { |
|
System.err.println("Can't initialize vogar expectation store from " |
|
+ libcoreExpectationDir); |
|
e.printStackTrace(System.err); |
|
System.exit(1); |
|
return; |
|
} |
|
ExpectationStore[] expectations = new ExpectationStore[] { |
|
libcoreVogarExpectationStore, ctsVogarExpectationStore |
|
}; |
|
|
|
JarFile jarFile = null; |
|
try { |
|
jarFile = new JarFile(jarFileName); |
|
} catch (Exception e) { |
|
System.err.println("cannot open jarfile " + jarFileName); |
|
e.printStackTrace(); |
|
System.exit(1); |
|
} |
|
|
|
Map<String,TestClass> testCases = new LinkedHashMap<String, TestClass>(); |
|
|
|
Enumeration<JarEntry> jarEntries = jarFile.entries(); |
|
while (jarEntries.hasMoreElements()) { |
|
JarEntry jarEntry = jarEntries.nextElement(); |
|
String name = jarEntry.getName(); |
|
if (!name.endsWith(".class")) { |
|
continue; |
|
} |
|
String className |
|
= name.substring(0, name.length() - ".class".length()).replace('/', '.'); |
|
|
|
boolean matchesPrefix = false; |
|
if (javaPackagePrefixes.length > 0) { |
|
for (String javaPackagePrefix : javaPackagePrefixes) { |
|
if (className.startsWith(javaPackagePrefix)) { |
|
matchesPrefix = true; |
|
} |
|
} |
|
} else { |
|
matchesPrefix = true; |
|
} |
|
|
|
if (!matchesPrefix) { |
|
continue; |
|
} |
|
|
|
// Avoid inner classes: they should not have tests and often they can have dependencies |
|
// on test frameworks that need to be resolved and would need to be on the classpath. |
|
// e.g. Mockito. |
|
if (className.contains("$")) { |
|
continue; |
|
} |
|
|
|
try { |
|
Class<?> klass = Class.forName(className, |
|
false, |
|
CollectAllTests.class.getClassLoader()); |
|
final int modifiers = klass.getModifiers(); |
|
if (Modifier.isAbstract(modifiers) || !Modifier.isPublic(modifiers)) { |
|
continue; |
|
} |
|
|
|
final boolean isJunit4Class = isJunit4Class(klass); |
|
if (!isJunit4Class && !isJunit3Test(klass)) { |
|
continue; |
|
} |
|
|
|
try { |
|
klass.getConstructor(new Class<?>[] { String.class } ); |
|
addToTests(expectations, architecture, testCases, klass); |
|
continue; |
|
} catch (NoSuchMethodException e) { |
|
} catch (SecurityException e) { |
|
System.out.println("Known bug (Working as intended): problem with class " |
|
+ className); |
|
e.printStackTrace(); |
|
} |
|
|
|
try { |
|
klass.getConstructor(new Class<?>[0]); |
|
addToTests(expectations, architecture, testCases, klass); |
|
continue; |
|
} catch (NoSuchMethodException e) { |
|
} catch (SecurityException e) { |
|
System.out.println("Known bug (Working as intended): problem with class " |
|
+ className); |
|
e.printStackTrace(); |
|
} |
|
} catch (ClassNotFoundException e) { |
|
System.out.println("class not found " + className); |
|
e.printStackTrace(); |
|
System.exit(1); |
|
} |
|
} |
|
|
|
for (Iterator<TestClass> iterator = testCases.values().iterator(); iterator.hasNext();) { |
|
TestClass type = iterator.next(); |
|
xmlGenerator.addTestClass(type); |
|
} |
|
|
|
try { |
|
xmlGenerator.dump(); |
|
} catch (Exception e) { |
|
System.err.println("cannot dump xml to " + outputXmlFile); |
|
e.printStackTrace(); |
|
System.exit(1); |
|
} |
|
} |
|
|
|
private static class TestType { |
|
private static final int HOST_SIDE_ONLY = 1; |
|
private static final int DEVICE_SIDE_ONLY = 2; |
|
private static final int VM_HOST_TEST = 3; |
|
|
|
private final int type; |
|
private final String jarPath; |
|
|
|
private TestType (int type, String jarPath) { |
|
this.type = type; |
|
this.jarPath = jarPath; |
|
} |
|
|
|
private static TestType getTestType(String makeFileName) { |
|
if (makeFileName == null || makeFileName.isEmpty()) { |
|
return new TestType(DEVICE_SIDE_ONLY, null); |
|
} |
|
int type = TestType.DEVICE_SIDE_ONLY; |
|
String jarPath = null; |
|
try { |
|
BufferedReader reader = new BufferedReader(new FileReader(makeFileName)); |
|
String line; |
|
|
|
while ((line = reader.readLine())!=null) { |
|
if (line.startsWith(TEST_TYPE)) { |
|
if (line.indexOf(ATTRIBUTE_VM_HOST_TEST) >= 0) { |
|
type = VM_HOST_TEST; |
|
} else { |
|
type = HOST_SIDE_ONLY; |
|
} |
|
} else if (line.startsWith(JAR_PATH)) { |
|
jarPath = line.substring(JAR_PATH.length(), line.length()).trim(); |
|
} |
|
} |
|
reader.close(); |
|
} catch (IOException e) { |
|
} |
|
return new TestType(type, jarPath); |
|
} |
|
} |
|
|
|
private static Element getElement(Element element, String tagName) { |
|
NodeList elements = element.getElementsByTagName(tagName); |
|
if (elements.getLength() > 0) { |
|
return (Element) elements.item(0); |
|
} else { |
|
return null; |
|
} |
|
} |
|
|
|
private static String getElementAttribute(Element element, |
|
String elementName, |
|
String attributeName) { |
|
Element e = getElement(element, elementName); |
|
if (e != null) { |
|
return e.getAttribute(attributeName); |
|
} else { |
|
return ""; |
|
} |
|
} |
|
|
|
private static String getKnownFailure(final Class<?> testClass, |
|
final String testName) { |
|
return getAnnotation(testClass, testName, KNOWN_FAILURE); |
|
} |
|
|
|
private static boolean isKnownFailure(final Class<?> testClass, |
|
final String testName) { |
|
return getAnnotation(testClass, testName, KNOWN_FAILURE) != null; |
|
} |
|
|
|
private static boolean isSuppressed(final Class<?> testClass, |
|
final String testName) { |
|
return getAnnotation(testClass, testName, SUPPRESSED_TEST) != null; |
|
} |
|
|
|
private static String getAnnotation(final Class<?> testClass, |
|
final String testName, final String annotationName) { |
|
try { |
|
Method testMethod = testClass.getMethod(testName, (Class[])null); |
|
Annotation[] annotations = testMethod.getAnnotations(); |
|
for (Annotation annot : annotations) { |
|
|
|
if (annot.annotationType().getName().equals(annotationName)) { |
|
String annotStr = annot.toString(); |
|
String knownFailure = null; |
|
if (annotStr.contains("(value=")) { |
|
knownFailure = |
|
annotStr.substring(annotStr.indexOf("=") + 1, |
|
annotStr.length() - 1); |
|
|
|
} |
|
|
|
if (knownFailure == null) { |
|
knownFailure = "true"; |
|
} |
|
|
|
return knownFailure; |
|
} |
|
|
|
} |
|
|
|
} catch (NoSuchMethodException e) { |
|
} |
|
|
|
return null; |
|
} |
|
|
|
private static void addToTests(ExpectationStore[] expectations, |
|
String architecture, |
|
Map<String,TestClass> testCases, |
|
Class<?> testClass) { |
|
Set<String> testNames = new HashSet<String>(); |
|
|
|
boolean isJunit3Test = isJunit3Test(testClass); |
|
|
|
Method[] testMethods = testClass.getMethods(); |
|
for (Method testMethod : testMethods) { |
|
String testName = testMethod.getName(); |
|
if (testNames.contains(testName)) { |
|
continue; |
|
} |
|
|
|
/* Make sure the method has the right signature. */ |
|
if (!Modifier.isPublic(testMethod.getModifiers())) { |
|
continue; |
|
} |
|
if (!testMethod.getReturnType().equals(Void.TYPE)) { |
|
continue; |
|
} |
|
if (testMethod.getParameterTypes().length != 0) { |
|
continue; |
|
} |
|
|
|
if ((isJunit3Test && !testName.startsWith("test")) |
|
|| (!isJunit3Test && !isJunit4TestMethod(testMethod))) { |
|
continue; |
|
} |
|
|
|
testNames.add(testName); |
|
addToTests(expectations, architecture, testCases, testClass, testName); |
|
} |
|
} |
|
|
|
private static void addToTests(ExpectationStore[] expectations, |
|
String architecture, |
|
Map<String,TestClass> testCases, |
|
Class<?> test, |
|
String testName) { |
|
|
|
String testClassName = test.getName(); |
|
String knownFailure = getKnownFailure(test, testName); |
|
|
|
if (isKnownFailure(test, testName)) { |
|
System.out.println("ignoring known failure: " + test + "#" + testName); |
|
return; |
|
} else if (isSuppressed(test, testName)) { |
|
System.out.println("ignoring suppressed test: " + test + "#" + testName); |
|
return; |
|
} else if (VogarUtils.isVogarKnownFailure(expectations, |
|
testClassName, |
|
testName)) { |
|
System.out.println("ignoring expectation known failure: " + test |
|
+ "#" + testName); |
|
return; |
|
} |
|
|
|
Set<String> supportedAbis = VogarUtils.extractSupportedAbis(architecture, |
|
expectations, |
|
testClassName, |
|
testName); |
|
int timeoutInMinutes = VogarUtils.timeoutInMinutes(expectations, |
|
testClassName, |
|
testName); |
|
TestClass testClass; |
|
if (testCases.containsKey(testClassName)) { |
|
testClass = testCases.get(testClassName); |
|
} else { |
|
testClass = new TestClass(testClassName, new ArrayList<TestMethod>()); |
|
testCases.put(testClassName, testClass); |
|
} |
|
|
|
testClass.mCases.add(new TestMethod(testName, "", "", supportedAbis, |
|
knownFailure, false, false, timeoutInMinutes)); |
|
} |
|
|
|
private static boolean isJunit3Test(Class<?> klass) { |
|
return TestCase.class.isAssignableFrom(klass); |
|
} |
|
|
|
private static boolean isJunit4Class(Class<?> klass) { |
|
for (Annotation a : klass.getAnnotations()) { |
|
if (RunWith.class.isAssignableFrom(a.annotationType())) { |
|
// @RunWith is currently not supported for CTS tests because tradefed cannot handle |
|
// a single test spawning other tests with different names. |
|
System.out.println("Skipping test class " + klass.getName() |
|
+ ": JUnit4 @RunWith is not supported"); |
|
return false; |
|
} |
|
} |
|
|
|
for (Method m : klass.getMethods()) { |
|
if (isJunit4TestMethod(m)) { |
|
return true; |
|
} |
|
} |
|
|
|
return false; |
|
} |
|
|
|
private static boolean isJunit4TestMethod(Method method) { |
|
for (Annotation a : method.getAnnotations()) { |
|
if (org.junit.Test.class.isAssignableFrom(a.annotationType())) { |
|
return true; |
|
} |
|
} |
|
|
|
return false; |
|
} |
|
|
|
/** |
|
* Determines if a given string is a valid java package name |
|
* @param javaPackageName |
|
* @return true if it is valid, false otherwise |
|
*/ |
|
private static boolean isValidJavaPackage(String javaPackageName) { |
|
String[] strSections = javaPackageName.split("."); |
|
for (String strSection : strSections) { |
|
if (!isValidJavaIdentifier(strSection)) { |
|
return false; |
|
} |
|
} |
|
return true; |
|
} |
|
|
|
/** |
|
* Determines if a given string is a valid java identifier. |
|
* @param javaIdentifier |
|
* @return true if it is a valid identifier, false otherwise |
|
*/ |
|
private static boolean isValidJavaIdentifier(String javaIdentifier) { |
|
if (javaIdentifier.length() == 0 || |
|
!Character.isJavaIdentifierStart(javaIdentifier.charAt(0))) { |
|
return false; |
|
} |
|
for (int i = 1; i < javaIdentifier.length(); i++) { |
|
if (!Character.isJavaIdentifierPart(javaIdentifier.charAt(i))) { |
|
return false; |
|
} |
|
} |
|
return true; |
|
} |
|
}
|
|
|