blob: 19f19c0b69c75c2d3b4332c603c5fe686853c557 [file] [log] [blame]
/*
* 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.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", "");
if (!TextUtils.isEmpty(tempdir)) {
String[] commands = {"df", tempdir};
BufferedReader in = null;
try {
Process proc = runtime.exec(commands);
in = new BufferedReader(new InputStreamReader(proc.getInputStream()));
String line;
while ((line = in.readLine()) != null) {
Log.d(TAG, line);
}
} catch (IOException e) {
// Well, we tried
} 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();
}
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);
}
}
}
}