1 /* 2 * Copyright (C) 2014 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.cts.runner; 18 19 import android.app.ActivityManager; 20 import android.app.Instrumentation; 21 import android.app.KeyguardManager; 22 import android.content.Context; 23 import android.content.pm.PackageManager; 24 import android.support.test.internal.runner.listener.InstrumentationRunListener; 25 import android.text.TextUtils; 26 import android.util.Log; 27 28 import junit.framework.TestCase; 29 30 import org.junit.runner.Description; 31 import org.junit.runner.notification.RunListener; 32 33 import java.io.BufferedReader; 34 import java.io.File; 35 import java.io.IOException; 36 import java.io.InputStreamReader; 37 import java.lang.Class; 38 import java.lang.ReflectiveOperationException; 39 import java.lang.reflect.Field; 40 import java.lang.reflect.Modifier; 41 import java.net.Authenticator; 42 import java.net.CookieHandler; 43 import java.net.ResponseCache; 44 import java.text.DateFormat; 45 import java.util.Locale; 46 import java.util.Properties; 47 import java.util.TimeZone; 48 49 import javax.net.ssl.HostnameVerifier; 50 import javax.net.ssl.HttpsURLConnection; 51 import javax.net.ssl.SSLSocketFactory; 52 53 /** 54 * A {@link RunListener} for CTS. Sets the system properties necessary for many 55 * core tests to run. This is needed because there are some core tests that need 56 * writing access to the file system. 57 * Finally, we add a means to free memory allocated by a TestCase after its 58 * execution. 59 */ 60 public class CtsTestRunListener extends InstrumentationRunListener { 61 62 private static final String TAG = "CtsTestRunListener"; 63 64 private TestEnvironment mEnvironment; 65 private Class<?> lastClass; 66 67 @Override testRunStarted(Description description)68 public void testRunStarted(Description description) throws Exception { 69 mEnvironment = new TestEnvironment(getInstrumentation().getTargetContext()); 70 71 // We might want to move this to /sdcard, if is is mounted/writable. 72 File cacheDir = getInstrumentation().getTargetContext().getCacheDir(); 73 System.setProperty("java.io.tmpdir", cacheDir.getAbsolutePath()); 74 75 // attempt to disable keyguard, if current test has permission to do so 76 // TODO: move this to a better place, such as InstrumentationTestRunner 77 // ? 78 if (getInstrumentation().getContext().checkCallingOrSelfPermission( 79 android.Manifest.permission.DISABLE_KEYGUARD) 80 == PackageManager.PERMISSION_GRANTED) { 81 Log.i(TAG, "Disabling keyguard"); 82 KeyguardManager keyguardManager = 83 (KeyguardManager) getInstrumentation().getContext().getSystemService( 84 Context.KEYGUARD_SERVICE); 85 keyguardManager.newKeyguardLock("cts").disableKeyguard(); 86 } else { 87 Log.i(TAG, "Test lacks permission to disable keyguard. " + 88 "UI based tests may fail if keyguard is up"); 89 } 90 } 91 92 @Override testStarted(Description description)93 public void testStarted(Description description) throws Exception { 94 if (description.getTestClass() != lastClass) { 95 lastClass = description.getTestClass(); 96 printMemory(description.getTestClass()); 97 } 98 99 mEnvironment.reset(); 100 } 101 102 @Override testFinished(Description description)103 public void testFinished(Description description) { 104 // no way to implement this in JUnit4... 105 // offending test cases that need this logic should probably be cleaned 106 // up individually 107 // if (test instanceof TestCase) { 108 // cleanup((TestCase) test); 109 // } 110 } 111 112 /** 113 * Dumps some memory info. 114 */ printMemory(Class<?> testClass)115 private void printMemory(Class<?> testClass) { 116 Runtime runtime = Runtime.getRuntime(); 117 118 long total = runtime.totalMemory(); 119 long free = runtime.freeMemory(); 120 long used = total - free; 121 122 Log.d(TAG, "Total memory : " + total); 123 Log.d(TAG, "Used memory : " + used); 124 Log.d(TAG, "Free memory : " + free); 125 126 String tempdir = System.getProperty("java.io.tmpdir", ""); 127 // TODO: Remove these extra Logs added to debug a specific timeout problem. 128 Log.d(TAG, "java.io.tmpdir is:" + tempdir); 129 130 if (!TextUtils.isEmpty(tempdir)) { 131 String[] commands = {"df", tempdir}; 132 BufferedReader in = null; 133 try { 134 Log.d(TAG, "About to .exec df"); 135 Process proc = runtime.exec(commands); 136 Log.d(TAG, ".exec returned"); 137 in = new BufferedReader(new InputStreamReader(proc.getInputStream())); 138 Log.d(TAG, "Stream reader created"); 139 String line; 140 while ((line = in.readLine()) != null) { 141 Log.d(TAG, line); 142 } 143 } catch (IOException e) { 144 Log.d(TAG, "Exception: " + e.toString()); 145 // Well, we tried 146 } finally { 147 Log.d(TAG, "In finally"); 148 if (in != null) { 149 try { 150 in.close(); 151 } catch (IOException e) { 152 // Meh 153 } 154 } 155 } 156 } 157 158 Log.d(TAG, "Now executing : " + testClass.getName()); 159 } 160 161 /** 162 * Nulls all non-static reference fields in the given test class. This 163 * method helps us with those test classes that don't have an explicit 164 * tearDown() method. Normally the garbage collector should take care of 165 * everything, but since JUnit keeps references to all test cases, a little 166 * help might be a good idea. 167 */ cleanup(TestCase test)168 private void cleanup(TestCase test) { 169 Class<?> clazz = test.getClass(); 170 171 while (clazz != TestCase.class) { 172 Field[] fields = clazz.getDeclaredFields(); 173 for (int i = 0; i < fields.length; i++) { 174 Field f = fields[i]; 175 if (!f.getType().isPrimitive() && 176 !Modifier.isStatic(f.getModifiers())) { 177 try { 178 f.setAccessible(true); 179 f.set(test, null); 180 } catch (Exception ignored) { 181 // Nothing we can do about it. 182 } 183 } 184 } 185 186 clazz = clazz.getSuperclass(); 187 } 188 } 189 190 // http://code.google.com/p/vogar/source/browse/trunk/src/vogar/target/TestEnvironment.java 191 static class TestEnvironment { 192 private static final Field sDateFormatIs24HourField; 193 static { 194 try { 195 Class<?> dateFormatClass = Class.forName("java.text.DateFormat"); 196 sDateFormatIs24HourField = dateFormatClass.getDeclaredField("is24Hour"); 197 } catch (ReflectiveOperationException e) { 198 throw new AssertionError("Missing DateFormat.is24Hour", e); 199 } 200 } 201 202 private final Locale mDefaultLocale; 203 private final TimeZone mDefaultTimeZone; 204 private final HostnameVerifier mHostnameVerifier; 205 private final SSLSocketFactory mSslSocketFactory; 206 private final Properties mProperties = new Properties(); 207 private final Boolean mDefaultIs24Hour; 208 TestEnvironment(Context context)209 TestEnvironment(Context context) { 210 mDefaultLocale = Locale.getDefault(); 211 mDefaultTimeZone = TimeZone.getDefault(); 212 mHostnameVerifier = HttpsURLConnection.getDefaultHostnameVerifier(); 213 mSslSocketFactory = HttpsURLConnection.getDefaultSSLSocketFactory(); 214 215 mProperties.setProperty("user.home", ""); 216 mProperties.setProperty("java.io.tmpdir", context.getCacheDir().getAbsolutePath()); 217 // The CDD mandates that devices that support WiFi are the only ones that will have 218 // multicast. 219 PackageManager pm = context.getPackageManager(); 220 mProperties.setProperty("android.cts.device.multicast", 221 Boolean.toString(pm.hasSystemFeature(PackageManager.FEATURE_WIFI))); 222 mDefaultIs24Hour = getDateFormatIs24Hour(); 223 224 // There are tests in libcore that should be disabled for low ram devices. They can't 225 // access ActivityManager to call isLowRamDevice, but can read system properties. 226 ActivityManager activityManager = 227 (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE); 228 mProperties.setProperty("android.cts.device.lowram", 229 Boolean.toString(activityManager.isLowRamDevice())); 230 } 231 reset()232 void reset() { 233 System.setProperties(null); 234 System.setProperties(mProperties); 235 Locale.setDefault(mDefaultLocale); 236 TimeZone.setDefault(mDefaultTimeZone); 237 Authenticator.setDefault(null); 238 CookieHandler.setDefault(null); 239 ResponseCache.setDefault(null); 240 HttpsURLConnection.setDefaultHostnameVerifier(mHostnameVerifier); 241 HttpsURLConnection.setDefaultSSLSocketFactory(mSslSocketFactory); 242 setDateFormatIs24Hour(mDefaultIs24Hour); 243 } 244 getDateFormatIs24Hour()245 private static Boolean getDateFormatIs24Hour() { 246 try { 247 return (Boolean) sDateFormatIs24HourField.get(null); 248 } catch (ReflectiveOperationException e) { 249 throw new AssertionError("Unable to get java.text.DateFormat.is24Hour", e); 250 } 251 } 252 setDateFormatIs24Hour(Boolean value)253 private static void setDateFormatIs24Hour(Boolean value) { 254 try { 255 sDateFormatIs24HourField.set(null, value); 256 } catch (ReflectiveOperationException e) { 257 throw new AssertionError("Unable to set java.text.DateFormat.is24Hour", e); 258 } 259 } 260 } 261 262 } 263