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