1f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev/*
2f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev * Copyright (C) 2014 The Android Open Source Project
3f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev *
4f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev * Licensed under the Apache License, Version 2.0 (the "License");
5f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev * you may not use this file except in compliance with the License.
6f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev * You may obtain a copy of the License at
7f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev *
8f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev *   http://www.apache.org/licenses/LICENSE-2.0
9f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev *
10f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev * Unless required by applicable law or agreed to in writing, software
11f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev * distributed under the License is distributed on an "AS IS" BASIS,
12f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev * See the License for the specific language governing permissions and
14f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev * limitations under the License.
15f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev */
16f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev
17f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelevpackage com.google.android.apps.common.testing.ui.espresso.base;
18f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev
19f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelevimport com.google.android.apps.common.testing.testrunner.ActivityLifecycleCallback;
20f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelevimport com.google.android.apps.common.testing.testrunner.ActivityLifecycleMonitorRegistry;
21f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelevimport com.google.android.apps.common.testing.testrunner.Stage;
22f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelevimport com.google.android.apps.common.testing.ui.espresso.InjectEventSecurityException;
23f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelevimport com.google.android.apps.common.testing.ui.testapp.R;
24f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelevimport com.google.android.apps.common.testing.ui.testapp.SendActivity;
25f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev
26f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelevimport android.app.Activity;
27f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelevimport android.os.Build;
28f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelevimport android.os.SystemClock;
29f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelevimport android.test.ActivityInstrumentationTestCase2;
30f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelevimport android.test.suitebuilder.annotation.LargeTest;
31f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelevimport android.util.Log;
32f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelevimport android.view.KeyCharacterMap;
33f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelevimport android.view.KeyEvent;
34f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelevimport android.view.MotionEvent;
35f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelevimport android.view.View;
36f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev
37f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelevimport java.util.concurrent.CountDownLatch;
38f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelevimport java.util.concurrent.TimeUnit;
39f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelevimport java.util.concurrent.atomic.AtomicBoolean;
40f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev
41f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev/**
42f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev * Tests for {@link EventInjector}.
43f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev */
44f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelevpublic class EventInjectorTest extends ActivityInstrumentationTestCase2<SendActivity> {
45f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev  private static final String TAG = EventInjectorTest.class.getSimpleName();
46f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev  private Activity sendActivity;
47f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev  private EventInjector injector;
48f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev  final AtomicBoolean injectEventWorked = new AtomicBoolean(false);
49f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev  final AtomicBoolean injectEventThrewSecurityException = new AtomicBoolean(false);
50f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev  final CountDownLatch latch = new CountDownLatch(1);
51f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev
52f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev  @SuppressWarnings("deprecation")
53f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev  public EventInjectorTest() {
54f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev    // Supporting froyo.
55f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev    super("com.google.android.apps.common.testing.ui.testapp", SendActivity.class);
56f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev  }
57f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev
58f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev  @Override
59f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev  public void setUp() throws Exception {
60f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev    super.setUp();
61f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev    if (Build.VERSION.SDK_INT > 15) {
62f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev      InputManagerEventInjectionStrategy strat = new InputManagerEventInjectionStrategy();
63f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev      strat.initialize();
64f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev      injector = new EventInjector(strat);
65f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev    } else {
66f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev      WindowManagerEventInjectionStrategy strat = new WindowManagerEventInjectionStrategy();
67f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev      strat.initialize();
68f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev      injector = new EventInjector(strat);
69f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev    }
70f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev  }
71f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev
72f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev  @Override
73f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev  public void tearDown() throws Exception {
74f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev    super.tearDown();
75f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev  }
76f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev
77f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev  @LargeTest
78f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev  public void testInjectKeyEventUpWithNoDown() throws Exception {
79f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev    sendActivity = getActivity();
80f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev
81f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev    getInstrumentation().runOnMainSync(new Runnable() {
82f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev      @Override
83f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev      public void run() {
84f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev        View view = sendActivity.findViewById(R.id.send_data_edit_text);
85f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev        assertTrue(view.requestFocus());
86f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev        latch.countDown();
87f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev      }
88f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev    });
89f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev
90f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev    assertTrue("Timed out!", latch.await(10, TimeUnit.SECONDS));
91f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev    KeyCharacterMap keyCharacterMap = UiControllerImpl.getKeyCharacterMap();
92f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev    KeyEvent[] events = keyCharacterMap.getEvents("a".toCharArray());
93f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev    assertTrue(injector.injectKeyEvent(events[1]));
94f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev  }
95f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev
96f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev  @LargeTest
97f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev  public void testInjectStaleKeyEvent() throws Exception {
98f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev    sendActivity = getActivity();
99f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev
100f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev    getInstrumentation().runOnMainSync(new Runnable() {
101f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev      @Override
102f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev      public void run() {
103f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev        View view = sendActivity.findViewById(R.id.send_data_edit_text);
104f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev        assertTrue(view.requestFocus());
105f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev        latch.countDown();
106f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev      }
107f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev    });
108f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev
109f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev    assertTrue("Timed out!", latch.await(10, TimeUnit.SECONDS));
110f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev    assertFalse("SecurityException exception was thrown.", injectEventThrewSecurityException.get());
111f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev
112f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev    KeyCharacterMap keyCharacterMap = UiControllerImpl.getKeyCharacterMap();
113f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev    KeyEvent[] events = keyCharacterMap.getEvents("a".toCharArray());
114f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev    KeyEvent event = KeyEvent.changeTimeRepeat(events[0], 1, 0);
115f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev
116f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev    // Stale event does not fail for API < 13.
117f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev    if (Build.VERSION.SDK_INT < 13) {
118f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev      assertTrue(injector.injectKeyEvent(event));
119f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev    } else {
120f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev      assertFalse(injector.injectKeyEvent(event));
121f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev    }
122f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev  }
123f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev
124f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev  @LargeTest
125f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev  public void testInjectKeyEvent_securityException() {
126f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev    KeyCharacterMap keyCharacterMap = UiControllerImpl.getKeyCharacterMap();
127f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev    KeyEvent[] events = keyCharacterMap.getEvents("a".toCharArray());
128f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev    try {
129f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev      injector.injectKeyEvent(events[0]);
130f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev      fail("Should have thrown a security exception!");
131f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev    } catch (InjectEventSecurityException expected) { }
132f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev  }
133f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev
134f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev  @LargeTest
135f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev  public void testInjectMotionEvent_securityException() throws Exception {
136f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev    getInstrumentation().runOnMainSync(new Runnable() {
137f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev      @Override
138f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev      public void run() {
139f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev        MotionEvent down = MotionEvent.obtain(SystemClock.uptimeMillis(),
140f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev            SystemClock.uptimeMillis(),
141f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev            MotionEvent.ACTION_DOWN,
142f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev            0,
143f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev            0,
144f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev            0);
145f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev        try {
146f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev          injector.injectMotionEvent(down);
147f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev        } catch (InjectEventSecurityException expected) {
148f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev          injectEventThrewSecurityException.set(true);
149f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev        }
150f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev        latch.countDown();
151f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev      }
152f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev    });
153f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev
154f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev    latch.await(10, TimeUnit.SECONDS);
155f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev    assertTrue(injectEventThrewSecurityException.get());
156f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev  }
157f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev
158f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev  @LargeTest
159f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev  public void testInjectMotionEvent_upEventFailure() throws InterruptedException {
160f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev    final CountDownLatch activityStarted = new CountDownLatch(1);
161f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev    ActivityLifecycleCallback callback = new ActivityLifecycleCallback() {
162f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev      @Override
163f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev      public void onActivityLifecycleChanged(Activity activity, Stage stage) {
164f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev        if (Stage.RESUMED == stage && activity instanceof SendActivity) {
165f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev          activityStarted.countDown();
166f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev        }
167f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev      }
168f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev    };
169f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev    ActivityLifecycleMonitorRegistry
170f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev        .getInstance()
171f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev        .addLifecycleCallback(callback);
172f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev    try {
173f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev      getActivity();
174f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev      assertTrue(activityStarted.await(20, TimeUnit.SECONDS));
175f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev      final int[] xy = UiControllerImplIntegrationTest.getCoordinatesInMiddleOfSendButton(
176f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev          getActivity(), getInstrumentation());
177f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev
178f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev      getInstrumentation().runOnMainSync(new Runnable() {
179f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev        @Override
180f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev        public void run() {
181f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev          MotionEvent up = MotionEvent.obtain(SystemClock.uptimeMillis(),
182f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev              SystemClock.uptimeMillis(),
183f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev              MotionEvent.ACTION_UP,
184f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev              xy[0],
185f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev              xy[1],
186f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev              0);
187f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev
188f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev          try {
189f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev            injectEventWorked.set(injector.injectMotionEvent(up));
190f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev          } catch (InjectEventSecurityException e) {
191f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev            Log.e(TAG, "injectEvent threw a SecurityException");
192f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev          }
193f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev          up.recycle();
194f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev          latch.countDown();
195f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev        }
196f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev      });
197f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev
198f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev      latch.await(10, TimeUnit.SECONDS);
199f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev      assertFalse(injectEventWorked.get());
200f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev    } finally {
201f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev      ActivityLifecycleMonitorRegistry
202f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev          .getInstance()
203f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev          .removeLifecycleCallback(callback);
204f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev    }
205f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev
206f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev  }
207f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev}
208