upload android base code part4
This commit is contained in:
parent
b9e30e05b1
commit
78ea2404cd
23455 changed files with 5250148 additions and 0 deletions
19
android/cts/tests/core/Android.mk
Normal file
19
android/cts/tests/core/Android.mk
Normal file
|
@ -0,0 +1,19 @@
|
|||
# Copyright (C) 2009 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.
|
||||
|
||||
LOCAL_PATH:= $(call my-dir)
|
||||
|
||||
BUILD_CTSCORE_PACKAGE:=$(LOCAL_PATH)/ctscore.mk
|
||||
|
||||
include $(call all-makefiles-under,$(LOCAL_PATH))
|
35
android/cts/tests/core/ctscore.mk
Normal file
35
android/cts/tests/core/ctscore.mk
Normal file
|
@ -0,0 +1,35 @@
|
|||
# Copyright (C) 2009 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.
|
||||
|
||||
LOCAL_JAVA_LIBRARIES := android.test.runner bouncycastle conscrypt
|
||||
LOCAL_PROGUARD_ENABLED := disabled
|
||||
LOCAL_DEX_PREOPT := false
|
||||
|
||||
# don't include these packages in any target
|
||||
LOCAL_MODULE_TAGS := optional
|
||||
# and when installed explicitly put them in the data partition
|
||||
LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
|
||||
# Don't delete META-INF from the core-tests jar
|
||||
LOCAL_DONT_DELETE_JAR_META_INF := true
|
||||
|
||||
# TODO: Clean up this mess. (b/26483949). libnativehelper_compat_libc++ pulls in its own
|
||||
# static copy of libc++ and the libc++ we're bundling here is the platform libc++. This is
|
||||
# bround to break but is being submitted as a workaround for failing CTS tests.
|
||||
LOCAL_JNI_SHARED_LIBRARIES := libjavacoretests libsqlite_jni libnativehelper_compat_libc++ libc++
|
||||
|
||||
# Include both the 32 and 64 bit versions of libjavacoretests,
|
||||
# where applicable.
|
||||
LOCAL_MULTILIB := both
|
||||
|
||||
include $(BUILD_PACKAGE)
|
65
android/cts/tests/core/runner/Android.mk
Normal file
65
android/cts/tests/core/runner/Android.mk
Normal file
|
@ -0,0 +1,65 @@
|
|||
# Copyright (C) 2009 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.
|
||||
|
||||
LOCAL_PATH:= $(call my-dir)
|
||||
|
||||
ifeq ($(BUILD_CTSCORE_PACKAGE),)
|
||||
$(error BUILD_CTSCORE_PACKAGE must be defined)
|
||||
endif
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
|
||||
# include this package in the tests target for continuous testing
|
||||
LOCAL_MODULE_TAGS := tests
|
||||
|
||||
LOCAL_PACKAGE_NAME := android.core.tests.runner
|
||||
|
||||
LOCAL_STATIC_JAVA_LIBRARIES := cts-test-runner
|
||||
|
||||
include $(BUILD_CTSCORE_PACKAGE)
|
||||
|
||||
#==========================================================
|
||||
# Build the core runner.
|
||||
#==========================================================
|
||||
|
||||
# Build library
|
||||
include $(CLEAR_VARS)
|
||||
|
||||
LOCAL_MODULE_TAGS := optional
|
||||
LOCAL_SRC_FILES := $(call all-java-files-under,src)
|
||||
LOCAL_MODULE := cts-core-test-runner
|
||||
LOCAL_STATIC_JAVA_LIBRARIES := \
|
||||
compatibility-device-util \
|
||||
android-support-test \
|
||||
vogarexpect \
|
||||
testng
|
||||
|
||||
LOCAL_JAVA_LIBRARIES := android.test.runner
|
||||
|
||||
include $(BUILD_STATIC_JAVA_LIBRARY)
|
||||
|
||||
#==========================================================
|
||||
# Build the run listener
|
||||
#==========================================================
|
||||
|
||||
# Build library
|
||||
include $(CLEAR_VARS)
|
||||
|
||||
LOCAL_MODULE_TAGS := optional
|
||||
LOCAL_SRC_FILES := $(call all-java-files-under,src/com/android/cts/runner)
|
||||
LOCAL_MODULE := cts-test-runner
|
||||
LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
|
||||
LOCAL_SDK_VERSION := current
|
||||
|
||||
include $(BUILD_STATIC_JAVA_LIBRARY)
|
31
android/cts/tests/core/runner/AndroidManifest.xml
Normal file
31
android/cts/tests/core/runner/AndroidManifest.xml
Normal file
|
@ -0,0 +1,31 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
* Copyright (C) 2007 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.
|
||||
-->
|
||||
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="android.core.tests.runner">
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||
|
||||
<application>
|
||||
<uses-library android:name="android.test.runner" />
|
||||
</application>
|
||||
|
||||
<instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
|
||||
android:targetPackage="android.core.tests.runner"
|
||||
android:label="cts framework tests"/>
|
||||
|
||||
</manifest>
|
|
@ -0,0 +1,258 @@
|
|||
/*
|
||||
* Copyright (C) 2016 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* This file is a copy of https://cs.corp.google.com/android/frameworks/testing/runner/src/main/java/android/support/test/internal/runner/TestLoader.java
|
||||
* The only changes that have been made starts with // Libcore-specific
|
||||
*/
|
||||
|
||||
package com.android.cts.core.internal.runner;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import org.junit.runner.Description;
|
||||
import org.junit.runner.notification.Failure;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.Collection;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* A class for loading JUnit3 and JUnit4 test classes given a set of potential class names.
|
||||
*/
|
||||
public final class TestLoader {
|
||||
|
||||
private static final String LOG_TAG = "TestLoader";
|
||||
// Libcore-specific change: Fully qualified name of TestNG annotation class.
|
||||
private static final String TESTNG_TEST = "org.testng.annotations.Test";
|
||||
|
||||
private Map<String, Class<?>> mLoadedClassesMap = new LinkedHashMap<String, Class<?>>();
|
||||
private Map<String, Failure> mLoadFailuresMap = new LinkedHashMap<String, Failure>();
|
||||
|
||||
private ClassLoader mClassLoader;
|
||||
|
||||
/**
|
||||
* Set the {@link ClassLoader} to be used to load test cases.
|
||||
*
|
||||
* @param loader {@link ClassLoader} to load test cases with.
|
||||
*/
|
||||
public void setClassLoader(ClassLoader loader) {
|
||||
mClassLoader = loader;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the test class from a given class name if its not already loaded.
|
||||
* <p/>
|
||||
* Will store the result internally. Successfully loaded classes can be retrieved via
|
||||
* {@link #getLoadedClasses()}, failures via {@link #getLoadFailures()}.
|
||||
*
|
||||
* @param className the class name to attempt to load
|
||||
* @return the loaded class or null.
|
||||
*/
|
||||
public Class<?> loadClass(String className) {
|
||||
Class<?> loadedClass = doLoadClass(className);
|
||||
if (loadedClass != null) {
|
||||
mLoadedClassesMap.put(className, loadedClass);
|
||||
}
|
||||
return loadedClass;
|
||||
}
|
||||
|
||||
protected ClassLoader getClassLoader() {
|
||||
if (mClassLoader != null) {
|
||||
return mClassLoader;
|
||||
}
|
||||
|
||||
// TODO: InstrumentationTestRunner uses
|
||||
// Class.forName(className, false, getTargetContext().getClassLoader());
|
||||
// Evaluate if that is needed. Initial testing indicates
|
||||
// getTargetContext().getClassLoader() == this.getClass().getClassLoader()
|
||||
return this.getClass().getClassLoader();
|
||||
}
|
||||
|
||||
private Class<?> doLoadClass(String className) {
|
||||
if (mLoadFailuresMap.containsKey(className)) {
|
||||
// Don't load classes that already failed to load
|
||||
return null;
|
||||
} else if (mLoadedClassesMap.containsKey(className)) {
|
||||
// Class with the same name was already loaded, return it
|
||||
return mLoadedClassesMap.get(className);
|
||||
}
|
||||
|
||||
try {
|
||||
ClassLoader myClassLoader = getClassLoader();
|
||||
return Class.forName(className, false, myClassLoader);
|
||||
} catch (ClassNotFoundException e) {
|
||||
String errMsg = String.format("Could not find class: %s", className);
|
||||
Log.e(LOG_TAG, errMsg);
|
||||
Description description = Description.createSuiteDescription(className);
|
||||
Failure failure = new Failure(description, e);
|
||||
mLoadFailuresMap.put(className, failure);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the test class from the given class name.
|
||||
* <p/>
|
||||
* Similar to {@link #loadClass(String)}, but will ignore classes that are
|
||||
* not tests.
|
||||
*
|
||||
* @param className the class name to attempt to load
|
||||
* @return the loaded class or null.
|
||||
*/
|
||||
public Class<?> loadIfTest(String className) {
|
||||
Class<?> loadedClass = doLoadClass(className);
|
||||
if (loadedClass != null && isTestClass(loadedClass)) {
|
||||
mLoadedClassesMap.put(className, loadedClass);
|
||||
return loadedClass;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return whether this {@link TestLoader} contains any loaded classes or load failures.
|
||||
*/
|
||||
public boolean isEmpty() {
|
||||
return mLoadedClassesMap.isEmpty() && mLoadFailuresMap.isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the {@link Collection) of classes successfully loaded via
|
||||
* {@link #loadIfTest(String)} calls.
|
||||
*/
|
||||
public Collection<Class<?>> getLoadedClasses() {
|
||||
return mLoadedClassesMap.values();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the {@link List) of {@link Failure} that occurred during
|
||||
* {@link #loadIfTest(String)} calls.
|
||||
*/
|
||||
public Collection<Failure> getLoadFailures() {
|
||||
return mLoadFailuresMap.values();
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if given class is a valid test class.
|
||||
*
|
||||
* @param loadedClass
|
||||
* @return <code>true</code> if loadedClass is a test
|
||||
*/
|
||||
private boolean isTestClass(Class<?> loadedClass) {
|
||||
try {
|
||||
if (Modifier.isAbstract(loadedClass.getModifiers())) {
|
||||
logDebug(String.format("Skipping abstract class %s: not a test",
|
||||
loadedClass.getName()));
|
||||
return false;
|
||||
}
|
||||
// Libcore-specific change: Also consider TestNG annotated classes.
|
||||
if (isTestNgTestClass(loadedClass)) {
|
||||
return true;
|
||||
}
|
||||
// TODO: try to find upstream junit calls to replace these checks
|
||||
if (junit.framework.Test.class.isAssignableFrom(loadedClass)) {
|
||||
// ensure that if a TestCase, it has at least one test method otherwise
|
||||
// TestSuite will throw error
|
||||
if (junit.framework.TestCase.class.isAssignableFrom(loadedClass)) {
|
||||
return hasJUnit3TestMethod(loadedClass);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
// TODO: look for a 'suite' method?
|
||||
if (loadedClass.isAnnotationPresent(org.junit.runner.RunWith.class)) {
|
||||
return true;
|
||||
}
|
||||
for (Method testMethod : loadedClass.getMethods()) {
|
||||
if (testMethod.isAnnotationPresent(org.junit.Test.class)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
logDebug(String.format("Skipping class %s: not a test", loadedClass.getName()));
|
||||
return false;
|
||||
} catch (Exception e) {
|
||||
// Defensively catch exceptions - Will throw runtime exception if it cannot load methods.
|
||||
// For earlier versions of Android (Pre-ICS), Dalvik might try to initialize a class
|
||||
// during getMethods(), fail to do so, hide the error and throw a NoSuchMethodException.
|
||||
// Since the java.lang.Class.getMethods does not declare such an exception, resort to a
|
||||
// generic catch all.
|
||||
// For ICS+, Dalvik will throw a NoClassDefFoundException.
|
||||
Log.w(LOG_TAG, String.format("%s in isTestClass for %s", e.toString(),
|
||||
loadedClass.getName()));
|
||||
return false;
|
||||
} catch (Error e) {
|
||||
// defensively catch Errors too
|
||||
Log.w(LOG_TAG, String.format("%s in isTestClass for %s", e.toString(),
|
||||
loadedClass.getName()));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean hasJUnit3TestMethod(Class<?> loadedClass) {
|
||||
for (Method testMethod : loadedClass.getMethods()) {
|
||||
if (isPublicTestMethod(testMethod)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// copied from junit.framework.TestSuite
|
||||
private boolean isPublicTestMethod(Method m) {
|
||||
return isTestMethod(m) && Modifier.isPublic(m.getModifiers());
|
||||
}
|
||||
|
||||
// copied from junit.framework.TestSuite
|
||||
private boolean isTestMethod(Method m) {
|
||||
return m.getParameterTypes().length == 0 && m.getName().startsWith("test")
|
||||
&& m.getReturnType().equals(Void.TYPE);
|
||||
}
|
||||
|
||||
// Libcore-specific change: Add method for checking TestNG-annotated classes.
|
||||
private static boolean isTestNgTestClass(Class<?> cls) {
|
||||
// TestNG test is either marked @Test at the class
|
||||
for (Annotation a : cls.getAnnotations()) {
|
||||
if (a.annotationType().getName().equals(TESTNG_TEST)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Or It's marked @Test at the method level
|
||||
for (Method m : cls.getDeclaredMethods()) {
|
||||
for (Annotation a : m.getAnnotations()) {
|
||||
if (a.annotationType().getName().equals(TESTNG_TEST)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Utility method for logging debug messages. Only actually logs a message if LOG_TAG is marked
|
||||
* as loggable to limit log spam during normal use.
|
||||
*/
|
||||
private void logDebug(String msg) {
|
||||
if (Log.isLoggable(LOG_TAG, Log.DEBUG)) {
|
||||
Log.d(LOG_TAG, msg);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,125 @@
|
|||
/*
|
||||
* Copyright (C) 2016 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.
|
||||
*/
|
||||
|
||||
package com.android.cts.core.runner;
|
||||
|
||||
import android.support.test.runner.AndroidJUnitRunner;
|
||||
|
||||
/**
|
||||
* Constants used to communicate to and from {@link AndroidJUnitRunner}.
|
||||
*/
|
||||
public interface AndroidJUnitRunnerConstants {
|
||||
|
||||
/**
|
||||
* The name of the file containing the names of the tests to run.
|
||||
*
|
||||
* <p>This is an internal constant used within
|
||||
* {@code android.support.test.internal.runner.RunnerArgs}, which is used on both the server
|
||||
* and
|
||||
* client side. The constant is used when there are too many test names to pass on the command
|
||||
* line, in which case they are stored in a file that is pushed to the device and then the
|
||||
* location of that file is passed in this argument. The {@code RunnerArgs} on the client will
|
||||
* read the contents of that file in order to retrieve the list of names and then return that
|
||||
* to
|
||||
* its client without the client being aware of how that was done.
|
||||
*/
|
||||
String ARGUMENT_TEST_FILE = "testFile";
|
||||
|
||||
/**
|
||||
* The name of the file containing the names of the tests not to run.
|
||||
*
|
||||
* <p>This is an internal constant used within
|
||||
* {@code android.support.test.internal.runner.RunnerArgs}, which is used on both the server
|
||||
* and
|
||||
* client side. The constant is used when there are too many test names to pass on the command
|
||||
* line, in which case they are stored in a file that is pushed to the device and then the
|
||||
* location of that file is passed in this argument. The {@code RunnerArgs} on the client will
|
||||
* read the contents of that file in order to retrieve the list of names and then return that
|
||||
* to
|
||||
* its client without the client being aware of how that was done.
|
||||
*/
|
||||
String ARGUMENT_NOT_TEST_FILE = "notTestFile";
|
||||
|
||||
/**
|
||||
* A comma separated list of the names of test classes to run.
|
||||
*
|
||||
* <p>The equivalent constant in {@code InstrumentationTestRunner} is hidden and so not
|
||||
* available
|
||||
* through the public API.
|
||||
*/
|
||||
String ARGUMENT_TEST_CLASS = "class";
|
||||
|
||||
/**
|
||||
* A comma separated list of the names of test classes not to run
|
||||
*/
|
||||
String ARGUMENT_NOT_TEST_CLASS = "notClass";
|
||||
|
||||
/**
|
||||
* A comma separated list of the names of test packages to run.
|
||||
*
|
||||
* <p>The equivalent constant in {@code InstrumentationTestRunner} is hidden and so not
|
||||
* available
|
||||
* through the public API.
|
||||
*/
|
||||
String ARGUMENT_TEST_PACKAGE = "package";
|
||||
|
||||
/**
|
||||
* A comma separated list of the names of test packages not to run.
|
||||
*/
|
||||
String ARGUMENT_NOT_TEST_PACKAGE = "notPackage";
|
||||
|
||||
/**
|
||||
* Log the results as if the tests were executed but don't actually run the tests.
|
||||
*
|
||||
* <p>The equivalent constant in {@code InstrumentationTestRunner} is private.
|
||||
*/
|
||||
String ARGUMENT_LOG_ONLY = "log";
|
||||
|
||||
/**
|
||||
* Wait for the debugger before starting.
|
||||
*
|
||||
* <p>There is no equivalent constant in {@code InstrumentationTestRunner} but the string is
|
||||
* used
|
||||
* within that class.
|
||||
*/
|
||||
String ARGUMENT_DEBUG = "debug";
|
||||
|
||||
/**
|
||||
* Only count the number of tests to run.
|
||||
*
|
||||
* <p>There is no equivalent constant in {@code InstrumentationTestRunner} but the string is
|
||||
* used
|
||||
* within that class.
|
||||
*/
|
||||
String ARGUMENT_COUNT = "count";
|
||||
|
||||
/**
|
||||
* The per test timeout value.
|
||||
*/
|
||||
String ARGUMENT_TIMEOUT = "timeout_msec";
|
||||
|
||||
/**
|
||||
* Token representing how long (in seconds) the current test took to execute.
|
||||
*
|
||||
* <p>The equivalent constant in {@code InstrumentationTestRunner} is private.
|
||||
*/
|
||||
String REPORT_KEY_RUNTIME = "runtime";
|
||||
|
||||
/**
|
||||
* An identifier for tests run using this class.
|
||||
*/
|
||||
String REPORT_VALUE_ID = "CoreTestRunner";
|
||||
}
|
|
@ -0,0 +1,355 @@
|
|||
/*
|
||||
* Copyright (C) 2015 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.
|
||||
*/
|
||||
|
||||
package com.android.cts.core.runner;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.Instrumentation;
|
||||
import android.os.Bundle;
|
||||
import android.os.Debug;
|
||||
import android.support.test.internal.runner.listener.InstrumentationResultPrinter;
|
||||
import android.support.test.internal.runner.listener.InstrumentationRunListener;
|
||||
import android.support.test.internal.util.AndroidRunnerParams;
|
||||
import android.util.Log;
|
||||
import com.android.cts.core.runner.support.ExtendedAndroidRunnerBuilder;
|
||||
import com.google.common.base.Splitter;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.FileReader;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import org.junit.runner.Computer;
|
||||
import org.junit.runner.JUnitCore;
|
||||
import org.junit.runner.Request;
|
||||
import org.junit.runner.Result;
|
||||
import org.junit.runner.Runner;
|
||||
import org.junit.runner.manipulation.Filter;
|
||||
import org.junit.runner.manipulation.Filterable;
|
||||
import org.junit.runner.manipulation.NoTestsRemainException;
|
||||
import org.junit.runner.notification.RunListener;
|
||||
import org.junit.runners.model.InitializationError;
|
||||
import org.junit.runners.model.RunnerBuilder;
|
||||
|
||||
import static com.android.cts.core.runner.AndroidJUnitRunnerConstants.ARGUMENT_COUNT;
|
||||
import static com.android.cts.core.runner.AndroidJUnitRunnerConstants.ARGUMENT_DEBUG;
|
||||
import static com.android.cts.core.runner.AndroidJUnitRunnerConstants.ARGUMENT_LOG_ONLY;
|
||||
import static com.android.cts.core.runner.AndroidJUnitRunnerConstants.ARGUMENT_NOT_TEST_CLASS;
|
||||
import static com.android.cts.core.runner.AndroidJUnitRunnerConstants.ARGUMENT_NOT_TEST_FILE;
|
||||
import static com.android.cts.core.runner.AndroidJUnitRunnerConstants.ARGUMENT_NOT_TEST_PACKAGE;
|
||||
import static com.android.cts.core.runner.AndroidJUnitRunnerConstants.ARGUMENT_TEST_CLASS;
|
||||
import static com.android.cts.core.runner.AndroidJUnitRunnerConstants.ARGUMENT_TEST_FILE;
|
||||
import static com.android.cts.core.runner.AndroidJUnitRunnerConstants.ARGUMENT_TEST_PACKAGE;
|
||||
import static com.android.cts.core.runner.AndroidJUnitRunnerConstants.ARGUMENT_TIMEOUT;
|
||||
|
||||
/**
|
||||
* A drop-in replacement for AndroidJUnitTestRunner, which understands the same arguments, and has
|
||||
* similar functionality, but can filter by expectations and allows a custom runner-builder to be
|
||||
* provided.
|
||||
*/
|
||||
public class CoreTestRunner extends Instrumentation {
|
||||
|
||||
static final String TAG = "LibcoreTestRunner";
|
||||
|
||||
private static final java.lang.String ARGUMENT_ROOT_CLASSES = "core-root-classes";
|
||||
|
||||
private static final String ARGUMENT_CORE_LISTENER = "core-listener";
|
||||
|
||||
private static final Splitter CLASS_LIST_SPLITTER = Splitter.on(',').trimResults();
|
||||
|
||||
/** The args for the runner. */
|
||||
private Bundle args;
|
||||
|
||||
/** Only log the number and names of tests, and not run them. */
|
||||
private boolean logOnly;
|
||||
|
||||
/** The amount of time in millis to wait for a single test to complete. */
|
||||
private long testTimeout;
|
||||
|
||||
/**
|
||||
* The list of tests to run.
|
||||
*/
|
||||
private TestList testList;
|
||||
|
||||
/**
|
||||
* The list of {@link RunListener} classes to create.
|
||||
*/
|
||||
private List<Class<? extends RunListener>> listenerClasses;
|
||||
private Filter expectationFilter;
|
||||
|
||||
@Override
|
||||
public void onCreate(final Bundle args) {
|
||||
super.onCreate(args);
|
||||
this.args = args;
|
||||
|
||||
boolean debug = "true".equalsIgnoreCase(args.getString(ARGUMENT_DEBUG));
|
||||
if (debug) {
|
||||
Log.i(TAG, "Waiting for debugger to connect...");
|
||||
Debug.waitForDebugger();
|
||||
Log.i(TAG, "Debugger connected.");
|
||||
}
|
||||
|
||||
// Log the message only after getting a value from the args so that the args are
|
||||
// unparceled.
|
||||
Log.d(TAG, "In OnCreate: " + args);
|
||||
|
||||
// Treat logOnly and count as the same. This is not quite true as count should only send
|
||||
// the host the number of tests but logOnly should send the name and number. However,
|
||||
// this is how this has always behaved and it does not appear to have caused any problems.
|
||||
// Changing it seems unnecessary given that count is CTSv1 only and CTSv1 will be removed
|
||||
// soon now that CTSv2 is ready.
|
||||
boolean testCountOnly = args.getBoolean(ARGUMENT_COUNT);
|
||||
this.logOnly = "true".equalsIgnoreCase(args.getString(ARGUMENT_LOG_ONLY)) || testCountOnly;
|
||||
this.testTimeout = parseUnsignedLong(args.getString(ARGUMENT_TIMEOUT), ARGUMENT_TIMEOUT);
|
||||
|
||||
expectationFilter = new ExpectationBasedFilter(args);
|
||||
|
||||
// The test can be run specifying a list of tests to run, or as cts-tradefed does it,
|
||||
// by passing a fileName with a test to run on each line.
|
||||
Set<String> testNameSet = new HashSet<>();
|
||||
String arg;
|
||||
if ((arg = args.getString(ARGUMENT_TEST_FILE)) != null) {
|
||||
// The tests are specified in a file.
|
||||
try {
|
||||
testNameSet.addAll(readTestsFromFile(arg));
|
||||
} catch (IOException err) {
|
||||
finish(Activity.RESULT_CANCELED, new Bundle());
|
||||
return;
|
||||
}
|
||||
} else if ((arg = args.getString(ARGUMENT_TEST_CLASS)) != null) {
|
||||
// The tests are specified in a String passed in the bundle.
|
||||
String[] tests = arg.split(",");
|
||||
testNameSet.addAll(Arrays.asList(tests));
|
||||
}
|
||||
|
||||
// Tests may be excluded from the run by passing a list of tests not to run,
|
||||
// or by passing a fileName with a test not to run on each line.
|
||||
Set<String> notTestNameSet = new HashSet<>();
|
||||
if ((arg = args.getString(ARGUMENT_NOT_TEST_FILE)) != null) {
|
||||
// The tests are specified in a file.
|
||||
try {
|
||||
notTestNameSet.addAll(readTestsFromFile(arg));
|
||||
} catch (IOException err) {
|
||||
finish(Activity.RESULT_CANCELED, new Bundle());
|
||||
return;
|
||||
}
|
||||
} else if ((arg = args.getString(ARGUMENT_NOT_TEST_CLASS)) != null) {
|
||||
// The classes are specified in a String passed in the bundle
|
||||
String[] tests = arg.split(",");
|
||||
notTestNameSet.addAll(Arrays.asList(tests));
|
||||
}
|
||||
|
||||
Set<String> packageNameSet = new HashSet<>();
|
||||
if ((arg = args.getString(ARGUMENT_TEST_PACKAGE)) != null) {
|
||||
// The packages are specified in a String passed in the bundle
|
||||
String[] packages = arg.split(",");
|
||||
packageNameSet.addAll(Arrays.asList(packages));
|
||||
}
|
||||
|
||||
Set<String> notPackageNameSet = new HashSet<>();
|
||||
if ((arg = args.getString(ARGUMENT_NOT_TEST_PACKAGE)) != null) {
|
||||
// The packages are specified in a String passed in the bundle
|
||||
String[] packages = arg.split(",");
|
||||
notPackageNameSet.addAll(Arrays.asList(packages));
|
||||
}
|
||||
|
||||
List<String> roots = getRootClassNames(args);
|
||||
if (roots == null) {
|
||||
// Find all test classes
|
||||
Collection<Class<?>> classes = TestClassFinder.getClasses(
|
||||
Collections.singletonList(getContext().getPackageCodePath()),
|
||||
getClass().getClassLoader());
|
||||
testList = new TestList(classes);
|
||||
} else {
|
||||
testList = TestList.rootList(roots);
|
||||
}
|
||||
|
||||
testList.addIncludeTestPackages(packageNameSet);
|
||||
testList.addExcludeTestPackages(notPackageNameSet);
|
||||
testList.addIncludeTests(testNameSet);
|
||||
testList.addExcludeTests(notTestNameSet);
|
||||
|
||||
listenerClasses = new ArrayList<>();
|
||||
String listenerArg = args.getString(ARGUMENT_CORE_LISTENER);
|
||||
if (listenerArg != null) {
|
||||
List<String> listenerClassNames = CLASS_LIST_SPLITTER.splitToList(listenerArg);
|
||||
for (String listenerClassName : listenerClassNames) {
|
||||
try {
|
||||
Class<? extends RunListener> listenerClass = Class.forName(listenerClassName)
|
||||
.asSubclass(RunListener.class);
|
||||
listenerClasses.add(listenerClass);
|
||||
} catch (ClassNotFoundException e) {
|
||||
Log.e(TAG, "Could not load listener class: " + listenerClassName, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
start();
|
||||
}
|
||||
|
||||
private List<String> getRootClassNames(Bundle args) {
|
||||
String rootClasses = args.getString(ARGUMENT_ROOT_CLASSES);
|
||||
List<String> roots;
|
||||
if (rootClasses == null) {
|
||||
roots = null;
|
||||
} else {
|
||||
roots = CLASS_LIST_SPLITTER.splitToList(rootClasses);
|
||||
}
|
||||
return roots;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStart() {
|
||||
if (logOnly) {
|
||||
Log.d(TAG, "Counting/logging tests only");
|
||||
} else {
|
||||
Log.d(TAG, "Running tests");
|
||||
}
|
||||
|
||||
AndroidRunnerParams runnerParams = new AndroidRunnerParams(this, args,
|
||||
false, testTimeout, false /*ignoreSuiteMethods*/);
|
||||
|
||||
Runner runner;
|
||||
try {
|
||||
RunnerBuilder runnerBuilder = new ExtendedAndroidRunnerBuilder(runnerParams);
|
||||
Class[] classes = testList.getClassesToRun();
|
||||
for (Class cls : classes) {
|
||||
Log.d(TAG, "Found class to run: " + cls.getName());
|
||||
}
|
||||
runner = new Computer().getSuite(runnerBuilder, classes);
|
||||
|
||||
if (runner instanceof Filterable) {
|
||||
Log.d(TAG, "Applying filters");
|
||||
Filterable filterable = (Filterable) runner;
|
||||
|
||||
// Filter out all the tests that are expected to fail.
|
||||
try {
|
||||
filterable.filter(expectationFilter);
|
||||
} catch (NoTestsRemainException e) {
|
||||
// Sometimes filtering will remove all tests but we do not care about that.
|
||||
}
|
||||
Log.d(TAG, "Applied filters");
|
||||
}
|
||||
|
||||
// If the tests are only supposed to be logged and not actually run then replace the
|
||||
// runner with a runner that will fire notifications for all the tests that would have
|
||||
// been run. This is needed because CTSv2 does a log only run through a CTS module in
|
||||
// order to generate a list of tests that will be run so that it can monitor them.
|
||||
// Encapsulating that in a Runner implementation makes it easier to leverage the
|
||||
// existing code for running tests.
|
||||
if (logOnly) {
|
||||
runner = new DescriptionHierarchyNotifier(runner.getDescription());
|
||||
}
|
||||
|
||||
} catch (InitializationError e) {
|
||||
throw new RuntimeException("Could not create a suite", e);
|
||||
}
|
||||
|
||||
InstrumentationResultPrinter instrumentationResultPrinter =
|
||||
new InstrumentationResultPrinter();
|
||||
instrumentationResultPrinter.setInstrumentation(this);
|
||||
|
||||
JUnitCore core = new JUnitCore();
|
||||
core.addListener(instrumentationResultPrinter);
|
||||
|
||||
// If not logging the list of tests then add any additional configured listeners. These
|
||||
// must be added before firing any events.
|
||||
if (!logOnly) {
|
||||
// Add additional configured listeners.
|
||||
for (Class<? extends RunListener> listenerClass : listenerClasses) {
|
||||
try {
|
||||
RunListener runListener = listenerClass.newInstance();
|
||||
if (runListener instanceof InstrumentationRunListener) {
|
||||
((InstrumentationRunListener) runListener).setInstrumentation(this);
|
||||
}
|
||||
core.addListener(runListener);
|
||||
} catch (InstantiationException | IllegalAccessException e) {
|
||||
Log.e(TAG,
|
||||
"Could not create instance of listener: " + listenerClass, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Log.d(TAG, "Finished preparations, running/listing tests");
|
||||
|
||||
Bundle results = new Bundle();
|
||||
Result junitResults = new Result();
|
||||
try {
|
||||
junitResults = core.run(Request.runner(runner));
|
||||
} catch (RuntimeException e) {
|
||||
final String msg = "Fatal exception when running tests";
|
||||
Log.e(TAG, msg, e);
|
||||
// report the exception to instrumentation out
|
||||
results.putString(Instrumentation.REPORT_KEY_STREAMRESULT,
|
||||
msg + "\n" + Log.getStackTraceString(e));
|
||||
} finally {
|
||||
ByteArrayOutputStream summaryStream = new ByteArrayOutputStream();
|
||||
// create the stream used to output summary data to the user
|
||||
PrintStream summaryWriter = new PrintStream(summaryStream);
|
||||
instrumentationResultPrinter.instrumentationRunFinished(summaryWriter,
|
||||
results, junitResults);
|
||||
summaryWriter.close();
|
||||
results.putString(Instrumentation.REPORT_KEY_STREAMRESULT,
|
||||
String.format("\n%s", summaryStream.toString()));
|
||||
}
|
||||
|
||||
|
||||
Log.d(TAG, "Finished");
|
||||
finish(Activity.RESULT_OK, results);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read tests from a specified file.
|
||||
*
|
||||
* @return class names of tests. If there was an error reading the file, null is returned.
|
||||
*/
|
||||
private static List<String> readTestsFromFile(String fileName) throws IOException {
|
||||
try (BufferedReader br = new BufferedReader(new FileReader(fileName))) {
|
||||
List<String> tests = new ArrayList<>();
|
||||
String line;
|
||||
while ((line = br.readLine()) != null) {
|
||||
tests.add(line);
|
||||
}
|
||||
return tests;
|
||||
} catch (IOException err) {
|
||||
Log.e(TAG, "There was an error reading the test class list: " + err.getMessage());
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse long from given value - except either Long or String.
|
||||
*
|
||||
* @return the value, -1 if not found
|
||||
* @throws NumberFormatException if value is negative or not a number
|
||||
*/
|
||||
private static long parseUnsignedLong(Object value, String name) {
|
||||
if (value != null) {
|
||||
long longValue = Long.parseLong(value.toString());
|
||||
if (longValue < 0) {
|
||||
throw new NumberFormatException(name + " can not be negative");
|
||||
}
|
||||
return longValue;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,62 @@
|
|||
/*
|
||||
* Copyright (C) 2016 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.
|
||||
*/
|
||||
package com.android.cts.core.runner;
|
||||
|
||||
import java.util.List;
|
||||
import org.junit.runner.Description;
|
||||
import org.junit.runner.Runner;
|
||||
import org.junit.runner.notification.RunNotifier;
|
||||
|
||||
/**
|
||||
* A {@link Runner} that does not actually run any tests but simply fires events for all leaf
|
||||
* {@link Description} instances in the supplied {@link Description} hierarchy.
|
||||
*/
|
||||
class DescriptionHierarchyNotifier extends Runner {
|
||||
|
||||
private final Description description;
|
||||
|
||||
DescriptionHierarchyNotifier(Description description) {
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Description getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run(RunNotifier notifier) {
|
||||
generateListOfTests(notifier, description);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a list of tests to run by recursing over the {@link Description} hierarchy and
|
||||
* firing events to simulate the tests being run successfully.
|
||||
* @param runNotifier the notifier to which the events are sent.
|
||||
* @param description the description to traverse.
|
||||
*/
|
||||
private void generateListOfTests(RunNotifier runNotifier, Description description) {
|
||||
List<Description> children = description.getChildren();
|
||||
if (children.isEmpty()) {
|
||||
runNotifier.fireTestStarted(description);
|
||||
runNotifier.fireTestFinished(description);
|
||||
} else {
|
||||
for (Description child : children) {
|
||||
generateListOfTests(runNotifier, child);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,145 @@
|
|||
/*
|
||||
* Copyright (C) 2016 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.
|
||||
*/
|
||||
package com.android.cts.core.runner;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
import com.google.common.base.Splitter;
|
||||
import java.io.IOException;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import javax.annotation.Nullable;
|
||||
import org.junit.runner.Description;
|
||||
import org.junit.runner.manipulation.Filter;
|
||||
import org.junit.runners.ParentRunner;
|
||||
import org.junit.runners.Suite;
|
||||
import vogar.Expectation;
|
||||
import vogar.ExpectationStore;
|
||||
import vogar.ModeId;
|
||||
import vogar.Result;
|
||||
|
||||
/**
|
||||
* Filter out tests/classes that are not requested or which are expected to fail.
|
||||
*
|
||||
* <p>This filter has to handle both a hierarchy of {@code Description descriptions} that looks
|
||||
* something like this:
|
||||
* <pre>
|
||||
* Suite
|
||||
* Suite
|
||||
* Suite
|
||||
* ParentRunner
|
||||
* Test
|
||||
* ...
|
||||
* ...
|
||||
* ParentRunner
|
||||
* Test
|
||||
* ...
|
||||
* ...
|
||||
* Suite
|
||||
* ParentRunner
|
||||
* Test
|
||||
* ...
|
||||
* ...
|
||||
* ...
|
||||
* </pre>
|
||||
*
|
||||
* <p>It cannot filter out the non-leaf nodes in the hierarchy, i.e. {@link Suite} and
|
||||
* {@link ParentRunner}, as that would prevent it from traversing the hierarchy and finding
|
||||
* the leaf nodes.
|
||||
*/
|
||||
class ExpectationBasedFilter extends Filter {
|
||||
|
||||
static final String TAG = "ExpectationBasedFilter";
|
||||
|
||||
private static final String ARGUMENT_EXPECTATIONS = "core-expectations";
|
||||
|
||||
private static final Splitter CLASS_LIST_SPLITTER = Splitter.on(',').trimResults();
|
||||
|
||||
private final ExpectationStore expectationStore;
|
||||
|
||||
private static List<String> getExpectationResourcePaths(Bundle args) {
|
||||
return CLASS_LIST_SPLITTER.splitToList(args.getString(ARGUMENT_EXPECTATIONS));
|
||||
}
|
||||
|
||||
public ExpectationBasedFilter(Bundle args) {
|
||||
ExpectationStore expectationStore = null;
|
||||
try {
|
||||
// Get the set of resource names containing the expectations.
|
||||
Set<String> expectationResources = new LinkedHashSet<>(
|
||||
getExpectationResourcePaths(args));
|
||||
Log.i(TAG, "Loading expectations from: " + expectationResources);
|
||||
expectationStore = ExpectationStore.parseResources(
|
||||
getClass(), expectationResources, ModeId.DEVICE);
|
||||
} catch (IOException e) {
|
||||
Log.e(TAG, "Could not initialize ExpectationStore: ", e);
|
||||
}
|
||||
|
||||
this.expectationStore = expectationStore;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldRun(Description description) {
|
||||
// Only filter leaf nodes. The description is for a test if and only if it is a leaf node.
|
||||
// Non-leaf nodes must not be filtered out as that would prevent leaf nodes from being
|
||||
// visited in the case when we are traversing the hierarchy of classes.
|
||||
Description testDescription = getTestDescription(description);
|
||||
if (testDescription != null) {
|
||||
String className = testDescription.getClassName();
|
||||
String methodName = testDescription.getMethodName();
|
||||
String testName = className + "#" + methodName;
|
||||
|
||||
if (expectationStore != null) {
|
||||
Expectation expectation = expectationStore.get(testName);
|
||||
if (expectation.getResult() != Result.SUCCESS) {
|
||||
Log.d(CoreTestRunner.TAG, "Excluding test " + testDescription
|
||||
+ " as it matches expectation: " + expectation);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private Description getTestDescription(Description description) {
|
||||
List<Description> children = description.getChildren();
|
||||
// An empty description is by definition a test.
|
||||
if (children.isEmpty()) {
|
||||
return description;
|
||||
}
|
||||
|
||||
// Handle initialization errors that were wrapped in an ErrorReportingRunner as a special
|
||||
// case. This is needed because ErrorReportingRunner is treated as a suite of Throwables,
|
||||
// (where each Throwable corresponds to a test called initializationError) and so its
|
||||
// description contains children, one for each Throwable, and so is not treated as a test
|
||||
// to filter. Unfortunately, it does not support Filterable so this filter is never applied
|
||||
// to its children.
|
||||
// See https://github.com/junit-team/junit/issues/1253
|
||||
Description child = children.get(0);
|
||||
String methodName = child.getMethodName();
|
||||
if ("initializationError".equals(methodName)) {
|
||||
return child;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String describe() {
|
||||
return "TestFilter";
|
||||
}
|
||||
}
|
|
@ -0,0 +1,151 @@
|
|||
/*
|
||||
* Copyright (C) 2016 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.
|
||||
*/
|
||||
|
||||
package com.android.cts.core.runner;
|
||||
|
||||
import android.support.test.internal.runner.ClassPathScanner;
|
||||
import android.support.test.internal.runner.ClassPathScanner.ClassNameFilter;
|
||||
import android.util.Log;
|
||||
|
||||
import com.android.cts.core.internal.runner.TestLoader;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Enumeration;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import dalvik.system.DexFile;
|
||||
|
||||
/**
|
||||
* Find tests in the current APK.
|
||||
*/
|
||||
public class TestClassFinder {
|
||||
|
||||
private static final String TAG = "TestClassFinder";
|
||||
private static final boolean DEBUG = false;
|
||||
|
||||
// Excluded test packages
|
||||
private static final String[] DEFAULT_EXCLUDED_PACKAGES = {
|
||||
"junit",
|
||||
"org.junit",
|
||||
"org.hamcrest",
|
||||
"org.mockito",// exclude Mockito for performance and to prevent JVM related errors
|
||||
"android.support.test.internal.runner.junit3",// always skip AndroidTestSuite
|
||||
};
|
||||
|
||||
static Collection<Class<?>> getClasses(List<String> apks, ClassLoader loader) {
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "getClasses: =======================================");
|
||||
|
||||
for (String apkPath : apks) {
|
||||
Log.d(TAG, "getClasses: -------------------------------");
|
||||
Log.d(TAG, "getClasses: APK " + apkPath);
|
||||
|
||||
DexFile dexFile = null;
|
||||
try {
|
||||
dexFile = new DexFile(apkPath);
|
||||
Enumeration<String> apkClassNames = dexFile.entries();
|
||||
while (apkClassNames.hasMoreElements()) {
|
||||
String apkClassName = apkClassNames.nextElement();
|
||||
Log.d(TAG, "getClasses: DexClass element " + apkClassName);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new AssertionError(e);
|
||||
} finally {
|
||||
if (dexFile != null) {
|
||||
try {
|
||||
dexFile.close();
|
||||
} catch (IOException e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
Log.d(TAG, "getClasses: -------------------------------");
|
||||
}
|
||||
} // if DEBUG
|
||||
|
||||
List<Class<?>> classes = new ArrayList<>();
|
||||
ClassPathScanner scanner = new ClassPathScanner(apks);
|
||||
|
||||
ClassPathScanner.ChainedClassNameFilter filter =
|
||||
new ClassPathScanner.ChainedClassNameFilter();
|
||||
// exclude inner classes
|
||||
filter.add(new ClassPathScanner.ExternalClassNameFilter());
|
||||
|
||||
// exclude default classes
|
||||
for (String defaultExcludedPackage : DEFAULT_EXCLUDED_PACKAGES) {
|
||||
filter.add(new ExcludePackageNameFilter(defaultExcludedPackage));
|
||||
}
|
||||
|
||||
// exclude any classes that aren't a "test class" (see #loadIfTest)
|
||||
TestLoader testLoader = new TestLoader();
|
||||
testLoader.setClassLoader(loader);
|
||||
|
||||
try {
|
||||
Set<String> classNames = scanner.getClassPathEntries(filter);
|
||||
for (String className : classNames) {
|
||||
// Important: This further acts as an additional filter;
|
||||
// classes that aren't a "test class" are never loaded.
|
||||
Class<?> cls = testLoader.loadIfTest(className);
|
||||
if (cls != null) {
|
||||
classes.add(cls);
|
||||
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "getClasses: Loaded " + className);
|
||||
}
|
||||
} else if (DEBUG) {
|
||||
Log.d(TAG, "getClasses: Failed to load class " + className);
|
||||
}
|
||||
}
|
||||
return classes;
|
||||
} catch (IOException e) {
|
||||
Log.e(CoreTestRunner.TAG, "Failed to scan classes", e);
|
||||
}
|
||||
|
||||
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "getClasses: =======================================");
|
||||
}
|
||||
|
||||
return testLoader.getLoadedClasses();
|
||||
}
|
||||
|
||||
/**
|
||||
* A {@link ClassNameFilter} that only rejects a given package names within the given namespace.
|
||||
*/
|
||||
public static class ExcludePackageNameFilter implements ClassNameFilter {
|
||||
|
||||
private final String mPkgName;
|
||||
|
||||
ExcludePackageNameFilter(String pkgName) {
|
||||
if (!pkgName.endsWith(".")) {
|
||||
mPkgName = String.format("%s.", pkgName);
|
||||
} else {
|
||||
mPkgName = pkgName;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public boolean accept(String pathName) {
|
||||
return !pathName.startsWith(mPkgName);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,137 @@
|
|||
/*
|
||||
* Copyright (C) 2016 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.
|
||||
*/
|
||||
package com.android.cts.core.runner;
|
||||
|
||||
import android.util.Log;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* A list of the tests to run.
|
||||
*/
|
||||
class TestList {
|
||||
|
||||
/** The set of test pacakges to run */
|
||||
private final Set<String> mIncludedPackages = new HashSet<>();
|
||||
|
||||
/** The set of test packages not to run */
|
||||
private final Set<String> mExcludedPackages = new HashSet<>();
|
||||
|
||||
/** The set of tests (classes or methods) to run */
|
||||
private final Set<String> mIncludedTests = new HashSet<>();
|
||||
|
||||
/** The set of tests (classes or methods) not to run */
|
||||
private final Set<String> mExcludedTests = new HashSet<>();
|
||||
|
||||
/** The list of all test classes to run (without filtering applied)*/
|
||||
private final Collection<Class<?>> classesToRun;
|
||||
|
||||
public static TestList rootList(List<String> rootList) {
|
||||
|
||||
// Run from the root test class.
|
||||
Set<String> classNamesToRun = new LinkedHashSet<>(rootList);
|
||||
Log.d(CoreTestRunner.TAG, "Running all tests rooted at " + classNamesToRun);
|
||||
|
||||
List<Class<?>> classesToRun1 = getClasses(classNamesToRun);
|
||||
|
||||
return new TestList(classesToRun1);
|
||||
}
|
||||
|
||||
private static List<Class<?>> getClasses(Set<String> classNames) {
|
||||
// Populate the list of classes to run.
|
||||
List<Class<?>> classesToRun = new ArrayList<>();
|
||||
for (String className : classNames) {
|
||||
try {
|
||||
classesToRun.add(Class.forName(className));
|
||||
} catch (ClassNotFoundException e) {
|
||||
throw new IllegalStateException("Could not load class '" + className, e);
|
||||
}
|
||||
}
|
||||
return classesToRun;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param classes The list of classes to run.
|
||||
*/
|
||||
public TestList(Collection<Class<?>> classes) {
|
||||
this.classesToRun = classes;
|
||||
}
|
||||
|
||||
public void addIncludeTestPackages(Set<String> packageNameSet) {
|
||||
mIncludedPackages.addAll(packageNameSet);
|
||||
}
|
||||
|
||||
public void addExcludeTestPackages(Set<String> packageNameSet) {
|
||||
mExcludedPackages.addAll(packageNameSet);
|
||||
}
|
||||
|
||||
public void addIncludeTests(Set<String> testNameSet) {
|
||||
mIncludedTests.addAll(testNameSet);
|
||||
}
|
||||
|
||||
public void addExcludeTests(Set<String> testNameSet) {
|
||||
mExcludedTests.addAll(testNameSet);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return all the classes to run.
|
||||
*/
|
||||
public Class[] getClassesToRun() {
|
||||
return classesToRun.toArray(new Class[classesToRun.size()]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the test with the specified name should be run, false otherwise.
|
||||
*/
|
||||
public boolean shouldRunTest(String testName) {
|
||||
|
||||
int index = testName.indexOf('#');
|
||||
String className;
|
||||
if (index == -1) {
|
||||
className = testName;
|
||||
} else {
|
||||
className = testName.substring(0, index);
|
||||
}
|
||||
try {
|
||||
Class<?> testClass = Class.forName(className);
|
||||
Package testPackage = testClass.getPackage();
|
||||
String testPackageName = "";
|
||||
if (testPackage != null) {
|
||||
testPackageName = testPackage.getName();
|
||||
}
|
||||
|
||||
boolean include =
|
||||
(mIncludedPackages.isEmpty() || mIncludedPackages.contains(testPackageName)) &&
|
||||
(mIncludedTests.isEmpty() || mIncludedTests.contains(className) ||
|
||||
mIncludedTests.contains(testName));
|
||||
|
||||
boolean exclude =
|
||||
mExcludedPackages.contains(testPackageName) ||
|
||||
mExcludedTests.contains(className) ||
|
||||
mExcludedTests.contains(testName);
|
||||
|
||||
return include && !exclude;
|
||||
} catch (ClassNotFoundException e) {
|
||||
Log.w("Could not load class '" + className, e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,82 @@
|
|||
/*
|
||||
* Copyright (C) 2016 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.
|
||||
*/
|
||||
package com.android.cts.core.runner.support;
|
||||
|
||||
import android.support.test.internal.runner.junit3.AndroidJUnit3Builder;
|
||||
import android.support.test.internal.runner.junit3.AndroidSuiteBuilder;
|
||||
import android.support.test.internal.runner.junit4.AndroidAnnotatedBuilder;
|
||||
import android.support.test.internal.runner.junit4.AndroidJUnit4Builder;
|
||||
import android.support.test.internal.util.AndroidRunnerParams;
|
||||
|
||||
import org.junit.internal.builders.AllDefaultPossibilitiesBuilder;
|
||||
import org.junit.internal.builders.AnnotatedBuilder;
|
||||
import org.junit.internal.builders.IgnoredBuilder;
|
||||
import org.junit.internal.builders.JUnit3Builder;
|
||||
import org.junit.internal.builders.JUnit4Builder;
|
||||
import org.junit.runners.model.RunnerBuilder;
|
||||
|
||||
/**
|
||||
* A {@link RunnerBuilder} that can handle all types of tests.
|
||||
*/
|
||||
// A copy of package private class android.support.test.internal.runner.AndroidRunnerBuilder.
|
||||
// Copied here so that it can be extended.
|
||||
class AndroidRunnerBuilder extends AllDefaultPossibilitiesBuilder {
|
||||
|
||||
private final AndroidJUnit3Builder mAndroidJUnit3Builder;
|
||||
private final AndroidJUnit4Builder mAndroidJUnit4Builder;
|
||||
private final AndroidSuiteBuilder mAndroidSuiteBuilder;
|
||||
private final AndroidAnnotatedBuilder mAndroidAnnotatedBuilder;
|
||||
// TODO: customize for Android ?
|
||||
private final IgnoredBuilder mIgnoredBuilder;
|
||||
|
||||
/**
|
||||
* @param runnerParams {@link AndroidRunnerParams} that stores common runner parameters
|
||||
*/
|
||||
// Added canUseSuiteMethod parameter.
|
||||
AndroidRunnerBuilder(AndroidRunnerParams runnerParams, boolean canUseSuiteMethod) {
|
||||
super(canUseSuiteMethod);
|
||||
mAndroidJUnit3Builder = new AndroidJUnit3Builder(runnerParams);
|
||||
mAndroidJUnit4Builder = new AndroidJUnit4Builder(runnerParams);
|
||||
mAndroidSuiteBuilder = new AndroidSuiteBuilder(runnerParams);
|
||||
mAndroidAnnotatedBuilder = new AndroidAnnotatedBuilder(this, runnerParams);
|
||||
mIgnoredBuilder = new IgnoredBuilder();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected JUnit4Builder junit4Builder() {
|
||||
return mAndroidJUnit4Builder;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected JUnit3Builder junit3Builder() {
|
||||
return mAndroidJUnit3Builder;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AnnotatedBuilder annotatedBuilder() {
|
||||
return mAndroidAnnotatedBuilder;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected IgnoredBuilder ignoredBuilder() {
|
||||
return mIgnoredBuilder;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected RunnerBuilder suiteMethodBuilder() {
|
||||
return mAndroidSuiteBuilder;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,66 @@
|
|||
/*
|
||||
* Copyright (C) 2016 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.
|
||||
*/
|
||||
|
||||
package com.android.cts.core.runner.support;
|
||||
|
||||
import android.support.test.internal.util.AndroidRunnerParams;
|
||||
import android.util.Log;
|
||||
import org.junit.runners.model.RunnerBuilder;
|
||||
import org.junit.runner.Runner;
|
||||
|
||||
/**
|
||||
* Extends {@link AndroidRunnerBuilder} in order to provide alternate {@link RunnerBuilder}
|
||||
* implementations.
|
||||
*/
|
||||
public class ExtendedAndroidRunnerBuilder extends AndroidRunnerBuilder {
|
||||
|
||||
private static final boolean DEBUG = false;
|
||||
|
||||
private final TestNgRunnerBuilder mTestNgBuilder;
|
||||
|
||||
/**
|
||||
* @param runnerParams {@link AndroidRunnerParams} that stores common runner parameters
|
||||
*/
|
||||
public ExtendedAndroidRunnerBuilder(AndroidRunnerParams runnerParams) {
|
||||
super(runnerParams, false /* CTSv1 filtered out Test suite() classes. */);
|
||||
mTestNgBuilder = new TestNgRunnerBuilder();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Runner runnerForClass(Class<?> testClass) throws Throwable {
|
||||
if (DEBUG) {
|
||||
Log.d("ExAndRunBuild", "runnerForClass: Searching runner for class " + testClass.getName());
|
||||
}
|
||||
|
||||
// Give TestNG tests a chance to participate in the Runner search first.
|
||||
// (Note that the TestNG runner handles log-only runs by itself)
|
||||
Runner runner = mTestNgBuilder.runnerForClass(testClass);
|
||||
if (runner == null) {
|
||||
// Use the normal Runner search mechanism (for Junit tests).
|
||||
runner = super.runnerForClass(testClass);
|
||||
}
|
||||
|
||||
logFoundRunner(runner);
|
||||
return runner;
|
||||
}
|
||||
|
||||
private static void logFoundRunner(Runner runner) {
|
||||
if (DEBUG) {
|
||||
Log.d("ExAndRunBuild", "runnerForClass: Found runner of type " +
|
||||
((runner == null) ? "<null>" : runner.getClass().getName()));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,124 @@
|
|||
/*
|
||||
* Copyright (C) 2016 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.
|
||||
*/
|
||||
|
||||
package com.android.cts.core.runner.support;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Listener for TestNG runs that provides gtest-like console output.
|
||||
*
|
||||
* Prints a message like [RUN], [OK], [ERROR], [SKIP] to stdout
|
||||
* as tests are being executed with their status.
|
||||
*
|
||||
* This output is also saved as the device logs (logcat) when the test is run through
|
||||
* cts-tradefed.
|
||||
*/
|
||||
public class SingleTestNGTestRunListener implements org.testng.ITestListener {
|
||||
private int mTestStarted = 0;
|
||||
|
||||
private Map<String, Throwable> failures = new LinkedHashMap<>();
|
||||
|
||||
private static class Prefixes {
|
||||
@SuppressWarnings("unused")
|
||||
private static final String INFORMATIONAL_MARKER = "[----------]";
|
||||
private static final String START_TEST_MARKER = "[ RUN ]";
|
||||
private static final String OK_TEST_MARKER = "[ OK ]";
|
||||
private static final String ERROR_TEST_RUN_MARKER = "[ ERROR ]";
|
||||
private static final String SKIPPED_TEST_MARKER = "[ SKIP ]";
|
||||
private static final String TEST_RUN_MARKER = "[==========]";
|
||||
}
|
||||
|
||||
// How many tests did TestNG *actually* try to run?
|
||||
public int getNumTestStarted() {
|
||||
return mTestStarted;
|
||||
}
|
||||
|
||||
public Map<String, Throwable> getFailures() {
|
||||
return Collections.unmodifiableMap(failures);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFinish(org.testng.ITestContext context) {
|
||||
System.out.println(String.format("%s", Prefixes.TEST_RUN_MARKER));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStart(org.testng.ITestContext context) {
|
||||
System.out.println(String.format("%s", Prefixes.INFORMATIONAL_MARKER));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTestFailedButWithinSuccessPercentage(org.testng.ITestResult result) {
|
||||
onTestFailure(result);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTestFailure(org.testng.ITestResult result) {
|
||||
// All failures are coalesced into one '[ FAILED ]' message at the end
|
||||
// This is because a single test method can run multiple times with different parameters.
|
||||
// Since we only test a single method, it's safe to combine all failures into one
|
||||
// failure at the end.
|
||||
//
|
||||
// The big pass/fail is printed from SingleTestNGTestRunner, not from the listener.
|
||||
String id = getId(result);
|
||||
Throwable throwable = result.getThrowable();
|
||||
System.out.println(String.format("%s %s ::: %s", Prefixes.ERROR_TEST_RUN_MARKER,
|
||||
id, stringify(throwable)));
|
||||
failures.put(id, throwable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTestSkipped(org.testng.ITestResult result) {
|
||||
System.out.println(String.format("%s %s", Prefixes.SKIPPED_TEST_MARKER,
|
||||
getId(result)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTestStart(org.testng.ITestResult result) {
|
||||
mTestStarted++;
|
||||
System.out.println(String.format("%s %s", Prefixes.START_TEST_MARKER,
|
||||
getId(result)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTestSuccess(org.testng.ITestResult result) {
|
||||
System.out.println(String.format("%s", Prefixes.OK_TEST_MARKER));
|
||||
}
|
||||
|
||||
private String getId(org.testng.ITestResult test) {
|
||||
// TestNG is quite complicated since tests can have arbitrary parameters.
|
||||
// Use its code to stringify a result name instead of doing it ourselves.
|
||||
|
||||
org.testng.remote.strprotocol.TestResultMessage msg =
|
||||
new org.testng.remote.strprotocol.TestResultMessage(
|
||||
null, /*suite name*/
|
||||
null, /*test name -- display the test method name instead */
|
||||
test);
|
||||
|
||||
String className = test.getTestClass().getName();
|
||||
//String name = test.getMethod().getMethodName();
|
||||
return String.format("%s#%s", className, msg.toDisplayString());
|
||||
|
||||
}
|
||||
|
||||
private String stringify(Throwable error) {
|
||||
return Arrays.toString(error.getStackTrace()).replaceAll("\n", " ");
|
||||
}
|
||||
}
|
|
@ -0,0 +1,132 @@
|
|||
/*
|
||||
* Copyright (C) 2016 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.
|
||||
*/
|
||||
|
||||
package com.android.cts.core.runner.support;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import org.testng.TestNG;
|
||||
import org.testng.xml.XmlClass;
|
||||
import org.testng.xml.XmlInclude;
|
||||
import org.testng.xml.XmlSuite;
|
||||
import org.testng.xml.XmlTest;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Test executor to run a single TestNG test method.
|
||||
*/
|
||||
public class SingleTestNgTestExecutor {
|
||||
// Execute any method which is in the class klass.
|
||||
// The klass is passed in separately to handle inherited methods only.
|
||||
// Returns true if all tests pass, false otherwise.
|
||||
public static Result execute(Class<?> klass, String methodName) {
|
||||
if (klass == null) {
|
||||
throw new NullPointerException("klass must not be null");
|
||||
}
|
||||
|
||||
if (methodName == null) {
|
||||
throw new NullPointerException("methodName must not be null");
|
||||
}
|
||||
|
||||
//if (!method.getDeclaringClass().isAssignableFrom(klass)) {
|
||||
// throw new IllegalArgumentException("klass must match method's declaring class");
|
||||
//}
|
||||
|
||||
SingleTestNGTestRunListener listener = new SingleTestNGTestRunListener();
|
||||
|
||||
// Although creating a new testng "core" every time might seem heavyweight, in practice
|
||||
// it seems to take a mere few milliseconds at most.
|
||||
// Since we're running all the parameteric combinations of a test,
|
||||
// this ends up being neglible relative to that.
|
||||
TestNG testng = createTestNG(klass.getName(), methodName, listener);
|
||||
testng.run();
|
||||
|
||||
if (listener.getNumTestStarted() <= 0) {
|
||||
// It's possible to be invoked here with an arbitrary method name
|
||||
// so print out a warning incase TestNG actually had a no-op.
|
||||
Log.w("TestNgExec", "execute class " + klass.getName() + ", method " + methodName +
|
||||
" had 0 tests executed. Not a test method?");
|
||||
}
|
||||
|
||||
return new Result(testng.hasFailure(), listener.getFailures());
|
||||
}
|
||||
|
||||
private static org.testng.TestNG createTestNG(String klass, String method,
|
||||
SingleTestNGTestRunListener listener) {
|
||||
org.testng.TestNG testng = new org.testng.TestNG();
|
||||
testng.setUseDefaultListeners(false); // Don't create the testng-specific HTML/XML reports.
|
||||
// It still prints the X/Y tests succeeded/failed summary to stdout.
|
||||
|
||||
// We don't strictly need this listener for CTS, but having it print SUCCESS/FAIL
|
||||
// makes it easier to diagnose which particular combination of a test method had failed
|
||||
// from looking at device logcat.
|
||||
testng.addListener(listener);
|
||||
|
||||
/* Construct the following equivalent XML configuration:
|
||||
*
|
||||
* <!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd" >
|
||||
* <suite>
|
||||
* <test>
|
||||
* <classes>
|
||||
* <class name="$klass">
|
||||
* <include name="$method" />
|
||||
* </class>
|
||||
* </classes>
|
||||
* </test>
|
||||
* </suite>
|
||||
*
|
||||
* This will ensure that only a single klass/method is being run by testng.
|
||||
* (It can still be run multiple times due to @DataProvider, with different parameters
|
||||
* each time)
|
||||
*/
|
||||
List<XmlSuite> suites = new ArrayList<>();
|
||||
XmlSuite the_suite = new XmlSuite();
|
||||
XmlTest the_test = new XmlTest(the_suite);
|
||||
XmlClass the_class = new XmlClass(klass);
|
||||
XmlInclude the_include = new XmlInclude(method);
|
||||
|
||||
the_class.getIncludedMethods().add(the_include);
|
||||
the_test.getXmlClasses().add(the_class);
|
||||
suites.add(the_suite);
|
||||
testng.setXmlSuites(suites);
|
||||
|
||||
return testng;
|
||||
}
|
||||
|
||||
public static class Result {
|
||||
private final boolean hasFailure;
|
||||
private final Map<String,Throwable> failures;
|
||||
|
||||
|
||||
Result(boolean hasFailure, Map<String, Throwable> failures) {
|
||||
this.hasFailure = hasFailure;
|
||||
this.failures = Collections.unmodifiableMap(new LinkedHashMap<>(failures));
|
||||
}
|
||||
|
||||
public boolean hasFailure() {
|
||||
return hasFailure;
|
||||
}
|
||||
|
||||
public Map<String, Throwable> getFailures() {
|
||||
return failures;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,235 @@
|
|||
/*
|
||||
* Copyright (C) 2016 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.
|
||||
*/
|
||||
|
||||
package com.android.cts.core.runner.support;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import org.junit.runner.Description;
|
||||
import org.junit.runner.Runner;
|
||||
import org.junit.runner.manipulation.Filter;
|
||||
import org.junit.runner.manipulation.Filterable;
|
||||
import org.junit.runner.manipulation.NoTestsRemainException;
|
||||
import org.junit.runner.notification.Failure;
|
||||
import org.junit.runner.notification.RunNotifier;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* A {@link Runner} that can TestNG tests.
|
||||
*
|
||||
* <p>Implementation note: Avoid extending ParentRunner since that also has
|
||||
* logic to handle BeforeClass/AfterClass and other junit-specific functionality
|
||||
* that would be invalid for TestNG.</p>
|
||||
*/
|
||||
class TestNgRunner extends Runner implements Filterable {
|
||||
|
||||
private static final boolean DEBUG = false;
|
||||
|
||||
private Description mDescription;
|
||||
/** Class name for debugging. */
|
||||
private String mClassName;
|
||||
/** Don't include the same method names twice. */
|
||||
private HashSet<String> mMethodSet = new HashSet<>();
|
||||
|
||||
/**
|
||||
* @param testClass the test class to run
|
||||
*/
|
||||
TestNgRunner(Class<?> testClass) {
|
||||
mDescription = generateTestNgDescription(testClass);
|
||||
mClassName = testClass.getName();
|
||||
}
|
||||
|
||||
// Runner implementation
|
||||
@Override
|
||||
public Description getDescription() {
|
||||
return mDescription;
|
||||
}
|
||||
|
||||
// Runner implementation
|
||||
@Override
|
||||
public int testCount() {
|
||||
if (!descriptionHasChildren(getDescription())) { // Avoid NPE when description is null.
|
||||
return 0;
|
||||
}
|
||||
|
||||
// We always follow a flat Parent->Leaf hierarchy, so no recursion necessary.
|
||||
return getDescription().testCount();
|
||||
}
|
||||
|
||||
// Filterable implementation
|
||||
@Override
|
||||
public void filter(Filter filter) throws NoTestsRemainException {
|
||||
mDescription = filterDescription(mDescription, filter);
|
||||
|
||||
if (!descriptionHasChildren(getDescription())) { // Avoid NPE when description is null.
|
||||
if (DEBUG) {
|
||||
Log.d("TestNgRunner",
|
||||
"Filtering has removed all tests :( for class " + mClassName);
|
||||
}
|
||||
throw new NoTestsRemainException();
|
||||
}
|
||||
|
||||
if (DEBUG) {
|
||||
Log.d("TestNgRunner",
|
||||
"Filtering has retained " + testCount() + " tests for class " + mClassName);
|
||||
}
|
||||
}
|
||||
|
||||
// Filterable implementation
|
||||
@Override
|
||||
public void run(RunNotifier notifier) {
|
||||
if (!descriptionHasChildren(getDescription())) { // Avoid NPE when description is null.
|
||||
// Nothing to do.
|
||||
return;
|
||||
}
|
||||
|
||||
for (Description child : getDescription().getChildren()) {
|
||||
String className = child.getClassName();
|
||||
String methodName = child.getMethodName();
|
||||
|
||||
Class<?> klass;
|
||||
try {
|
||||
klass = Class.forName(className, false, Thread.currentThread().getContextClassLoader());
|
||||
} catch (ClassNotFoundException e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
|
||||
notifier.fireTestStarted(child);
|
||||
|
||||
// Avoid looking at all the methods by just using the string method name.
|
||||
SingleTestNgTestExecutor.Result result = SingleTestNgTestExecutor.execute(klass, methodName);
|
||||
if (result.hasFailure()) {
|
||||
// TODO: get the error messages from testng somehow.
|
||||
notifier.fireTestFailure(new Failure(child, extractException(result.getFailures())));
|
||||
}
|
||||
|
||||
notifier.fireTestFinished(child);
|
||||
// TODO: Check @Test(enabled=false) and invoke #fireTestIgnored instead.
|
||||
}
|
||||
}
|
||||
|
||||
private Throwable extractException(Map<String, Throwable> failures) {
|
||||
if (failures.isEmpty()) {
|
||||
return new AssertionError();
|
||||
}
|
||||
if (failures.size() == 1) {
|
||||
return failures.values().iterator().next();
|
||||
}
|
||||
|
||||
StringBuilder errorMessage = new StringBuilder("========== Multiple Failures ==========");
|
||||
for (Map.Entry<String, Throwable> failureEntry : failures.entrySet()) {
|
||||
errorMessage.append("\n\n=== "). append(failureEntry.getKey()).append(" ===\n");
|
||||
Throwable throwable = failureEntry.getValue();
|
||||
errorMessage
|
||||
.append(throwable.getClass()).append(": ")
|
||||
.append(throwable.getMessage());
|
||||
for (StackTraceElement e : throwable.getStackTrace()) {
|
||||
if (e.getClassName().equals(getClass().getName())) {
|
||||
break;
|
||||
}
|
||||
errorMessage.append("\n at ").append(e);
|
||||
}
|
||||
}
|
||||
errorMessage.append("\n=======================================\n\n");
|
||||
return new AssertionError(errorMessage.toString());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Recursively (preorder traversal) apply the filter to all the descriptions.
|
||||
*
|
||||
* @return null if the filter rejects the whole tree.
|
||||
*/
|
||||
private static Description filterDescription(Description desc, Filter filter) {
|
||||
if (!filter.shouldRun(desc)) { // XX: Does the filter itself do the recursion?
|
||||
return null;
|
||||
}
|
||||
|
||||
Description newDesc = desc.childlessCopy();
|
||||
|
||||
// Return leafs.
|
||||
if (!descriptionHasChildren(desc)) {
|
||||
return newDesc;
|
||||
}
|
||||
|
||||
// Filter all subtrees, only copying them if the filter accepts them.
|
||||
for (Description child : desc.getChildren()) {
|
||||
Description filteredChild = filterDescription(child, filter);
|
||||
|
||||
if (filteredChild != null) {
|
||||
newDesc.addChild(filteredChild);
|
||||
}
|
||||
}
|
||||
|
||||
return newDesc;
|
||||
}
|
||||
|
||||
private Description generateTestNgDescription(Class<?> cls) {
|
||||
// Add the overall class description as the parent.
|
||||
Description parent = Description.createSuiteDescription(cls);
|
||||
|
||||
if (DEBUG) {
|
||||
Log.d("TestNgRunner", "Generating TestNg Description for class " + cls.getName());
|
||||
}
|
||||
|
||||
// Add each test method as a child.
|
||||
for (Method m : cls.getDeclaredMethods()) {
|
||||
|
||||
// Filter to only 'public void' signatures.
|
||||
if ((m.getModifiers() & Modifier.PUBLIC) == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!m.getReturnType().equals(Void.TYPE)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Note that TestNG methods may actually have parameters
|
||||
// (e.g. with @DataProvider) which TestNG will populate itself.
|
||||
|
||||
// Add [Class, MethodName] as a Description leaf node.
|
||||
String name = m.getName();
|
||||
|
||||
if (!mMethodSet.add(name)) {
|
||||
// Overloaded methods have the same name, don't add them twice.
|
||||
if (DEBUG) {
|
||||
Log.d("TestNgRunner", "Already added child " + cls.getName() + "#" + name);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
Description child = Description.createTestDescription(cls, name);
|
||||
|
||||
parent.addChild(child);
|
||||
|
||||
if (DEBUG) {
|
||||
Log.d("TestNgRunner", "Add child " + cls.getName() + "#" + name);
|
||||
}
|
||||
}
|
||||
|
||||
return parent;
|
||||
}
|
||||
|
||||
private static boolean descriptionHasChildren(Description desc) {
|
||||
// Note: Although "desc.isTest()" is equivalent to "!desc.getChildren().isEmpty()"
|
||||
// we add the pre-requisite 2 extra null checks to avoid throwing NPEs.
|
||||
return desc != null && desc.getChildren() != null && !desc.getChildren().isEmpty();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,56 @@
|
|||
/*
|
||||
* Copyright (C) 2016 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.
|
||||
*/
|
||||
|
||||
package com.android.cts.core.runner.support;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import org.junit.runner.Runner;
|
||||
import org.junit.runners.model.RunnerBuilder;
|
||||
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
/**
|
||||
* A {@link RunnerBuilder} that can handle TestNG tests.
|
||||
*/
|
||||
public class TestNgRunnerBuilder extends RunnerBuilder {
|
||||
// Returns a TestNG runner for this class, only if it is a class
|
||||
// annotated with testng's @Test or has any methods with @Test in it.
|
||||
@Override
|
||||
public Runner runnerForClass(Class<?> testClass) {
|
||||
if (isTestNgTestClass(testClass)) {
|
||||
return new TestNgRunner(testClass);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static boolean isTestNgTestClass(Class<?> cls) {
|
||||
// TestNG test is either marked @Test at the class
|
||||
if (cls.getAnnotation(Test.class) != null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Or It's marked @Test at the method level
|
||||
for (Method m : cls.getDeclaredMethods()) {
|
||||
if (m.getAnnotation(Test.class) != null) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
* Copyright (C) 2016 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Contains all the changes needed to the {@code android.support.test.internal.runner} classes.
|
||||
*
|
||||
* <p>As its name suggests {@code android.support.test.internal.runner} are internal classes that
|
||||
* are not designed to be extended from outside those packages. This package encapsulates all the
|
||||
* workarounds needed to overcome that limitation, from duplicating classes to using reflection.
|
||||
* The intention is that these changes are temporary and they (or a better equivalent) will be
|
||||
* quickly integrated into the internal classes.
|
||||
*/
|
||||
package com.android.cts.core.runner.support;
|
|
@ -0,0 +1,262 @@
|
|||
/*
|
||||
* Copyright (C) 2014 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.
|
||||
*/
|
||||
|
||||
package com.android.cts.runner;
|
||||
|
||||
import android.app.ActivityManager;
|
||||
import android.app.Instrumentation;
|
||||
import android.app.KeyguardManager;
|
||||
import android.content.Context;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.support.test.internal.runner.listener.InstrumentationRunListener;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import org.junit.runner.Description;
|
||||
import org.junit.runner.notification.RunListener;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.lang.Class;
|
||||
import java.lang.ReflectiveOperationException;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.net.Authenticator;
|
||||
import java.net.CookieHandler;
|
||||
import java.net.ResponseCache;
|
||||
import java.text.DateFormat;
|
||||
import java.util.Locale;
|
||||
import java.util.Properties;
|
||||
import java.util.TimeZone;
|
||||
|
||||
import javax.net.ssl.HostnameVerifier;
|
||||
import javax.net.ssl.HttpsURLConnection;
|
||||
import javax.net.ssl.SSLSocketFactory;
|
||||
|
||||
/**
|
||||
* A {@link RunListener} for CTS. Sets the system properties necessary for many
|
||||
* core tests to run. This is needed because there are some core tests that need
|
||||
* writing access to the file system.
|
||||
* Finally, we add a means to free memory allocated by a TestCase after its
|
||||
* execution.
|
||||
*/
|
||||
public class CtsTestRunListener extends InstrumentationRunListener {
|
||||
|
||||
private static final String TAG = "CtsTestRunListener";
|
||||
|
||||
private TestEnvironment mEnvironment;
|
||||
private Class<?> lastClass;
|
||||
|
||||
@Override
|
||||
public void testRunStarted(Description description) throws Exception {
|
||||
mEnvironment = new TestEnvironment(getInstrumentation().getTargetContext());
|
||||
|
||||
// We might want to move this to /sdcard, if is is mounted/writable.
|
||||
File cacheDir = getInstrumentation().getTargetContext().getCacheDir();
|
||||
System.setProperty("java.io.tmpdir", cacheDir.getAbsolutePath());
|
||||
|
||||
// attempt to disable keyguard, if current test has permission to do so
|
||||
// TODO: move this to a better place, such as InstrumentationTestRunner
|
||||
// ?
|
||||
if (getInstrumentation().getContext().checkCallingOrSelfPermission(
|
||||
android.Manifest.permission.DISABLE_KEYGUARD)
|
||||
== PackageManager.PERMISSION_GRANTED) {
|
||||
Log.i(TAG, "Disabling keyguard");
|
||||
KeyguardManager keyguardManager =
|
||||
(KeyguardManager) getInstrumentation().getContext().getSystemService(
|
||||
Context.KEYGUARD_SERVICE);
|
||||
keyguardManager.newKeyguardLock("cts").disableKeyguard();
|
||||
} else {
|
||||
Log.i(TAG, "Test lacks permission to disable keyguard. " +
|
||||
"UI based tests may fail if keyguard is up");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void testStarted(Description description) throws Exception {
|
||||
if (description.getTestClass() != lastClass) {
|
||||
lastClass = description.getTestClass();
|
||||
printMemory(description.getTestClass());
|
||||
}
|
||||
|
||||
mEnvironment.reset();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void testFinished(Description description) {
|
||||
// no way to implement this in JUnit4...
|
||||
// offending test cases that need this logic should probably be cleaned
|
||||
// up individually
|
||||
// if (test instanceof TestCase) {
|
||||
// cleanup((TestCase) test);
|
||||
// }
|
||||
}
|
||||
|
||||
/**
|
||||
* Dumps some memory info.
|
||||
*/
|
||||
private void printMemory(Class<?> testClass) {
|
||||
Runtime runtime = Runtime.getRuntime();
|
||||
|
||||
long total = runtime.totalMemory();
|
||||
long free = runtime.freeMemory();
|
||||
long used = total - free;
|
||||
|
||||
Log.d(TAG, "Total memory : " + total);
|
||||
Log.d(TAG, "Used memory : " + used);
|
||||
Log.d(TAG, "Free memory : " + free);
|
||||
|
||||
String tempdir = System.getProperty("java.io.tmpdir", "");
|
||||
// TODO: Remove these extra Logs added to debug a specific timeout problem.
|
||||
Log.d(TAG, "java.io.tmpdir is:" + tempdir);
|
||||
|
||||
if (!TextUtils.isEmpty(tempdir)) {
|
||||
String[] commands = {"df", tempdir};
|
||||
BufferedReader in = null;
|
||||
try {
|
||||
Log.d(TAG, "About to .exec df");
|
||||
Process proc = runtime.exec(commands);
|
||||
Log.d(TAG, ".exec returned");
|
||||
in = new BufferedReader(new InputStreamReader(proc.getInputStream()));
|
||||
Log.d(TAG, "Stream reader created");
|
||||
String line;
|
||||
while ((line = in.readLine()) != null) {
|
||||
Log.d(TAG, line);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
Log.d(TAG, "Exception: " + e.toString());
|
||||
// Well, we tried
|
||||
} finally {
|
||||
Log.d(TAG, "In finally");
|
||||
if (in != null) {
|
||||
try {
|
||||
in.close();
|
||||
} catch (IOException e) {
|
||||
// Meh
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Log.d(TAG, "Now executing : " + testClass.getName());
|
||||
}
|
||||
|
||||
/**
|
||||
* Nulls all non-static reference fields in the given test class. This
|
||||
* method helps us with those test classes that don't have an explicit
|
||||
* tearDown() method. Normally the garbage collector should take care of
|
||||
* everything, but since JUnit keeps references to all test cases, a little
|
||||
* help might be a good idea.
|
||||
*/
|
||||
private void cleanup(TestCase test) {
|
||||
Class<?> clazz = test.getClass();
|
||||
|
||||
while (clazz != TestCase.class) {
|
||||
Field[] fields = clazz.getDeclaredFields();
|
||||
for (int i = 0; i < fields.length; i++) {
|
||||
Field f = fields[i];
|
||||
if (!f.getType().isPrimitive() &&
|
||||
!Modifier.isStatic(f.getModifiers())) {
|
||||
try {
|
||||
f.setAccessible(true);
|
||||
f.set(test, null);
|
||||
} catch (Exception ignored) {
|
||||
// Nothing we can do about it.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
clazz = clazz.getSuperclass();
|
||||
}
|
||||
}
|
||||
|
||||
// http://code.google.com/p/vogar/source/browse/trunk/src/vogar/target/TestEnvironment.java
|
||||
static class TestEnvironment {
|
||||
private static final Field sDateFormatIs24HourField;
|
||||
static {
|
||||
try {
|
||||
Class<?> dateFormatClass = Class.forName("java.text.DateFormat");
|
||||
sDateFormatIs24HourField = dateFormatClass.getDeclaredField("is24Hour");
|
||||
} catch (ReflectiveOperationException e) {
|
||||
throw new AssertionError("Missing DateFormat.is24Hour", e);
|
||||
}
|
||||
}
|
||||
|
||||
private final Locale mDefaultLocale;
|
||||
private final TimeZone mDefaultTimeZone;
|
||||
private final HostnameVerifier mHostnameVerifier;
|
||||
private final SSLSocketFactory mSslSocketFactory;
|
||||
private final Properties mProperties = new Properties();
|
||||
private final Boolean mDefaultIs24Hour;
|
||||
|
||||
TestEnvironment(Context context) {
|
||||
mDefaultLocale = Locale.getDefault();
|
||||
mDefaultTimeZone = TimeZone.getDefault();
|
||||
mHostnameVerifier = HttpsURLConnection.getDefaultHostnameVerifier();
|
||||
mSslSocketFactory = HttpsURLConnection.getDefaultSSLSocketFactory();
|
||||
|
||||
mProperties.setProperty("user.home", "");
|
||||
mProperties.setProperty("java.io.tmpdir", context.getCacheDir().getAbsolutePath());
|
||||
// The CDD mandates that devices that support WiFi are the only ones that will have
|
||||
// multicast.
|
||||
PackageManager pm = context.getPackageManager();
|
||||
mProperties.setProperty("android.cts.device.multicast",
|
||||
Boolean.toString(pm.hasSystemFeature(PackageManager.FEATURE_WIFI)));
|
||||
mDefaultIs24Hour = getDateFormatIs24Hour();
|
||||
|
||||
// There are tests in libcore that should be disabled for low ram devices. They can't
|
||||
// access ActivityManager to call isLowRamDevice, but can read system properties.
|
||||
ActivityManager activityManager =
|
||||
(ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
|
||||
mProperties.setProperty("android.cts.device.lowram",
|
||||
Boolean.toString(activityManager.isLowRamDevice()));
|
||||
}
|
||||
|
||||
void reset() {
|
||||
System.setProperties(null);
|
||||
System.setProperties(mProperties);
|
||||
Locale.setDefault(mDefaultLocale);
|
||||
TimeZone.setDefault(mDefaultTimeZone);
|
||||
Authenticator.setDefault(null);
|
||||
CookieHandler.setDefault(null);
|
||||
ResponseCache.setDefault(null);
|
||||
HttpsURLConnection.setDefaultHostnameVerifier(mHostnameVerifier);
|
||||
HttpsURLConnection.setDefaultSSLSocketFactory(mSslSocketFactory);
|
||||
setDateFormatIs24Hour(mDefaultIs24Hour);
|
||||
}
|
||||
|
||||
private static Boolean getDateFormatIs24Hour() {
|
||||
try {
|
||||
return (Boolean) sDateFormatIs24HourField.get(null);
|
||||
} catch (ReflectiveOperationException e) {
|
||||
throw new AssertionError("Unable to get java.text.DateFormat.is24Hour", e);
|
||||
}
|
||||
}
|
||||
|
||||
private static void setDateFormatIs24Hour(Boolean value) {
|
||||
try {
|
||||
sDateFormatIs24HourField.set(null, value);
|
||||
} catch (ReflectiveOperationException e) {
|
||||
throw new AssertionError("Unable to set java.text.DateFormat.is24Hour", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue