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 17package com.google.android.apps.common.testing.ui.espresso.base; 18 19import com.google.android.apps.common.testing.ui.espresso.InjectEventSecurityException; 20import com.google.android.apps.common.testing.ui.espresso.UiController; 21import com.google.android.apps.common.testing.ui.espresso.util.HumanReadables; 22import com.google.android.apps.common.testing.ui.testapp.R; 23import com.google.android.apps.common.testing.ui.testapp.SendActivity; 24import com.google.common.base.Optional; 25 26import android.app.Activity; 27import android.app.Instrumentation; 28import android.os.Build; 29import android.os.Looper; 30import android.os.SystemClock; 31import android.test.ActivityInstrumentationTestCase2; 32import android.test.suitebuilder.annotation.LargeTest; 33import android.util.Log; 34import android.view.KeyCharacterMap; 35import android.view.KeyEvent; 36import android.view.MotionEvent; 37import android.view.View; 38 39import java.util.concurrent.CountDownLatch; 40import java.util.concurrent.TimeUnit; 41import java.util.concurrent.atomic.AtomicBoolean; 42 43/** 44 * Test for {@link UiControllerImpl}. 45 */ 46public class UiControllerImplIntegrationTest 47 extends ActivityInstrumentationTestCase2<SendActivity> { 48 private Activity sendActivity; 49 private final AtomicBoolean injectEventWorked = new AtomicBoolean(false); 50 private final AtomicBoolean injectEventThrewSecurityException = new AtomicBoolean(false); 51 private final CountDownLatch focusLatch = new CountDownLatch(1); 52 private final CountDownLatch latch = new CountDownLatch(1); 53 private UiController uiController; 54 55 @SuppressWarnings("deprecation") 56 public UiControllerImplIntegrationTest() { 57 // Supporting froyo. 58 super("com.google.android.apps.common.testing.ui.testapp", SendActivity.class); 59 } 60 61 @Override 62 public void setUp() throws Exception { 63 super.setUp(); 64 EventInjector injector = null; 65 if (Build.VERSION.SDK_INT > 15) { 66 InputManagerEventInjectionStrategy strat = new InputManagerEventInjectionStrategy(); 67 strat.initialize(); 68 injector = new EventInjector(strat); 69 } else { 70 WindowManagerEventInjectionStrategy strat = new WindowManagerEventInjectionStrategy(); 71 strat.initialize(); 72 injector = new EventInjector(strat); 73 } 74 uiController = new UiControllerImpl( 75 injector, 76 new AsyncTaskPoolMonitor(new ThreadPoolExecutorExtractor( 77 Looper.getMainLooper()).getAsyncTaskThreadPool()), 78 Optional.<AsyncTaskPoolMonitor>absent(), 79 new IdlingResourceRegistry(Looper.getMainLooper()), 80 Looper.getMainLooper()); 81 } 82 83 84 @Override 85 public SendActivity getActivity() { 86 SendActivity a = super.getActivity(); 87 88 while (!a.hasWindowFocus()) { 89 getInstrumentation().waitForIdleSync(); 90 } 91 92 return a; 93 } 94 95 @LargeTest 96 public void testInjectKeyEvent() throws InterruptedException { 97 sendActivity = getActivity(); 98 getInstrumentation().waitForIdleSync(); 99 100 getInstrumentation().runOnMainSync(new Runnable() { 101 @Override 102 public void run() { 103 try { 104 KeyCharacterMap keyCharacterMap = UiControllerImpl.getKeyCharacterMap(); 105 KeyEvent[] events = keyCharacterMap.getEvents("a".toCharArray()); 106 injectEventWorked.set(uiController.injectKeyEvent(events[0])); 107 latch.countDown(); 108 } catch (InjectEventSecurityException e) { 109 injectEventThrewSecurityException.set(true); 110 } 111 } 112 }); 113 114 assertFalse("injectEvent threw a SecurityException", injectEventThrewSecurityException.get()); 115 assertTrue("Timed out!", latch.await(10, TimeUnit.SECONDS)); 116 assertTrue(injectEventWorked.get()); 117 } 118 119 @LargeTest 120 public void testInjectString() throws InterruptedException { 121 sendActivity = getActivity(); 122 getInstrumentation().waitForIdleSync(); 123 final AtomicBoolean requestFocusSucceded = new AtomicBoolean(false); 124 125 getInstrumentation().runOnMainSync(new Runnable() { 126 @Override 127 public void run() { 128 final View view = sendActivity.findViewById(R.id.send_data_to_call_edit_text); 129 Log.i("TEST", HumanReadables.describe(view)); 130 requestFocusSucceded.set(view.requestFocus() && view.hasWindowFocus()); 131 Log.i("TEST-post", HumanReadables.describe(view)); 132 focusLatch.countDown(); 133 } 134 }); 135 136 getInstrumentation().waitForIdleSync(); 137 assertTrue("requestFocus timed out!", focusLatch.await(2, TimeUnit.SECONDS)); 138 assertTrue("requestFocus failed.", requestFocusSucceded.get()); 139 140 getInstrumentation().runOnMainSync(new Runnable() { 141 @Override 142 public void run() { 143 try { 144 injectEventWorked.set(uiController.injectString("Hello! \n&*$$$")); 145 latch.countDown(); 146 } catch (InjectEventSecurityException e) { 147 injectEventThrewSecurityException.set(true); 148 } 149 } 150 }); 151 152 assertFalse("SecurityException exception was thrown.", injectEventThrewSecurityException.get()); 153 assertTrue("Timed out!", latch.await(20, TimeUnit.SECONDS)); 154 assertTrue(injectEventWorked.get()); 155 } 156 157 @LargeTest 158 public void testInjectLargeString() throws InterruptedException { 159 sendActivity = getActivity(); 160 getInstrumentation().waitForIdleSync(); 161 final AtomicBoolean requestFocusSucceded = new AtomicBoolean(false); 162 163 getInstrumentation().runOnMainSync(new Runnable() { 164 @Override 165 public void run() { 166 final View view = sendActivity.findViewById(R.id.send_data_to_call_edit_text); 167 Log.i("TEST", HumanReadables.describe(view)); 168 requestFocusSucceded.set(view.requestFocus()); 169 Log.i("TEST-post", HumanReadables.describe(view)); 170 171 focusLatch.countDown(); 172 } 173 }); 174 175 assertTrue("requestFocus timed out!", focusLatch.await(2, TimeUnit.SECONDS)); 176 assertTrue("requestFocus failed.", requestFocusSucceded.get()); 177 178 getInstrumentation().runOnMainSync(new Runnable() { 179 @Override 180 public void run() { 181 try { 182 injectEventWorked.set(uiController.injectString("This is a string with 32 chars!!")); 183 latch.countDown(); 184 } catch (InjectEventSecurityException e) { 185 injectEventThrewSecurityException.set(true); 186 } 187 } 188 }); 189 190 assertFalse("SecurityException exception was thrown.", injectEventThrewSecurityException.get()); 191 assertTrue("Timed out!", latch.await(20, TimeUnit.SECONDS)); 192 assertTrue(injectEventWorked.get()); 193 } 194 195 @LargeTest 196 public void testInjectEmptyString() throws InterruptedException { 197 sendActivity = getActivity(); 198 getInstrumentation().waitForIdleSync(); 199 final AtomicBoolean requestFocusSucceded = new AtomicBoolean(false); 200 201 getInstrumentation().runOnMainSync(new Runnable() { 202 @Override 203 public void run() { 204 final View view = sendActivity.findViewById(R.id.send_data_to_call_edit_text); 205 requestFocusSucceded.set(view.requestFocus()); 206 focusLatch.countDown(); 207 } 208 }); 209 210 assertTrue("requestFocus timed out!", focusLatch.await(2, TimeUnit.SECONDS)); 211 assertTrue("requestFocus failed.", requestFocusSucceded.get()); 212 213 getInstrumentation().runOnMainSync(new Runnable() { 214 @Override 215 public void run() { 216 try { 217 injectEventWorked.set(uiController.injectString("")); 218 latch.countDown(); 219 } catch (InjectEventSecurityException e) { 220 injectEventThrewSecurityException.set(true); 221 } 222 } 223 }); 224 225 assertFalse("SecurityException exception was thrown.", injectEventThrewSecurityException.get()); 226 assertTrue("Timed out!", latch.await(20, TimeUnit.SECONDS)); 227 assertTrue(injectEventWorked.get()); 228 } 229 230 @LargeTest 231 public void testInjectStringSecurityException() throws InterruptedException { 232 getInstrumentation().runOnMainSync(new Runnable() { 233 @Override 234 public void run() { 235 try { 236 injectEventWorked.set(uiController.injectString("Hello! \n&*$$$")); 237 latch.countDown(); 238 } catch (InjectEventSecurityException e) { 239 injectEventThrewSecurityException.set(true); 240 } 241 } 242 }); 243 244 assertTrue("SecurityException exception was thrown.", injectEventThrewSecurityException.get()); 245 assertFalse("Did NOT time out!", latch.await(3, TimeUnit.SECONDS)); 246 assertFalse(injectEventWorked.get()); 247 } 248 249 @LargeTest 250 public void testInjectMotionEvent() throws InterruptedException { 251 sendActivity = getActivity(); 252 final int xy[] = getCoordinatesInMiddleOfSendButton(sendActivity, getInstrumentation()); 253 254 getInstrumentation().runOnMainSync(new Runnable() { 255 @Override 256 public void run() { 257 long downTime = SystemClock.uptimeMillis(); 258 try { 259 MotionEvent event = MotionEvent.obtain(downTime, 260 SystemClock.uptimeMillis(), 261 MotionEvent.ACTION_DOWN, 262 xy[0], 263 xy[1], 264 0); 265 266 injectEventWorked.set(uiController.injectMotionEvent(event)); 267 event.recycle(); 268 latch.countDown(); 269 } catch (InjectEventSecurityException e) { 270 injectEventThrewSecurityException.set(true); 271 } 272 } 273 }); 274 275 assertFalse("SecurityException exception was thrown.", injectEventThrewSecurityException.get()); 276 assertTrue("Timed out!", latch.await(10, TimeUnit.SECONDS)); 277 assertTrue(injectEventWorked.get()); 278 } 279 280 static int[] getCoordinatesInMiddleOfSendButton( 281 Activity activity, Instrumentation instrumentation) { 282 final View sendButton = activity.findViewById(R.id.send_button); 283 final int[] xy = new int[2]; 284 instrumentation.runOnMainSync(new Runnable() { 285 @Override 286 public void run() { 287 sendButton.getLocationOnScreen(xy); 288 } 289 }); 290 int x = xy[0] + (sendButton.getWidth() / 2); 291 int y = xy[1] + (sendButton.getHeight() / 2); 292 int[] xyMiddle = {x, y}; 293 return xyMiddle; 294 } 295} 296