Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(138)

Side by Side Diff: content/public/android/junit/src/org/chromium/content/browser/input/ThreadedInputConnectionFactoryTest.java

Issue 1721613002: Add JUnit tests for ImeThread activation (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: adjusted to robolectric 3.0 Created 4 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « content/public/android/java/src/org/chromium/content/browser/input/ThreadedInputConnectionFactory.java ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright 2016 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 package org.chromium.content.browser.input;
6
7 import static org.junit.Assert.assertFalse;
8 import static org.junit.Assert.assertNotNull;
9 import static org.junit.Assert.assertNull;
10 import static org.junit.Assert.assertTrue;
11 import static org.junit.Assert.fail;
12 import static org.mockito.ArgumentMatchers.any;
13 import static org.mockito.Mockito.doAnswer;
14 import static org.mockito.Mockito.inOrder;
15 import static org.mockito.Mockito.when;
16
17 import android.content.Context;
18 import android.os.Handler;
19 import android.view.View;
20 import android.view.inputmethod.EditorInfo;
21 import android.view.inputmethod.InputConnection;
22 import android.view.inputmethod.InputMethodManager;
23
24 import org.chromium.base.ThreadUtils;
25 import org.chromium.base.test.util.Feature;
26 import org.chromium.testing.local.LocalRobolectricTestRunner;
27 import org.junit.Before;
28 import org.junit.Test;
29 import org.junit.runner.RunWith;
30 import org.mockito.InOrder;
31 import org.mockito.Mock;
32 import org.mockito.Mockito;
33 import org.mockito.MockitoAnnotations;
34 import org.mockito.invocation.InvocationOnMock;
35 import org.mockito.stubbing.Answer;
36 import org.robolectric.Robolectric;
37 import org.robolectric.annotation.Config;
38 import org.robolectric.internal.ShadowExtractor;
39 import org.robolectric.shadows.ShadowLooper;
40
41 import java.util.concurrent.Callable;
42
43 /**
44 * Unit tests for {@ThreadedInputConnectionFactory}.
45 */
46 @RunWith(LocalRobolectricTestRunner.class)
47 @Config(manifest = Config.NONE)
48 public class ThreadedInputConnectionFactoryTest {
49
50 /**
51 * A testable version of ThreadedInputConnectionFactory.
52 */
53 private class TestFactory extends ThreadedInputConnectionFactory {
54
55 private boolean mSucceeded;
56 private boolean mFailed;
57
58 TestFactory(InputMethodManagerWrapper inputMethodManagerWrapper) {
59 super(inputMethodManagerWrapper);
60 }
61
62 @Override
63 protected Handler createHandler() {
64 mImeHandler = super.createHandler();
65 mImeShadowLooper = (ShadowLooper) ShadowExtractor.extract(mImeHandle r.getLooper());
66 return mImeHandler;
67 }
68
69 @Override
70 protected ThreadedInputConnectionProxyView createProxyView(Handler handl er,
71 View containerView) {
72 return mProxyView;
73 }
74
75 @Override
76 protected InputMethodUma createInputMethodUma() {
77 return null;
78 }
79
80 @Override
81 protected void onRegisterProxyViewSuccess() {
82 mSucceeded = true;
83 }
84
85 @Override
86 protected void onRegisterProxyViewFailure() {
87 mFailed = true;
88 }
89
90 public boolean hasFailed() {
91 return mFailed;
92 }
93 public boolean hasSucceeded() {
94 return mSucceeded;
95 }
96 }
97
98 @Mock
99 private ImeAdapter mImeAdapter;
100 @Mock
101 private View mContainerView;
102 @Mock
103 private ThreadedInputConnectionProxyView mProxyView;
104 @Mock
105 private InputMethodManager mInputMethodManager;
106 @Mock
107 private Context mContext;
108
109 private EditorInfo mEditorInfo;
110 private Handler mImeHandler;
111 private Handler mUiHandler;
112 private ShadowLooper mImeShadowLooper;
113 private TestFactory mFactory;
114 private InputConnection mInputConnection;
115 private InOrder mInOrder;
116 private boolean mWindowFocusChanged;
117
118 @Before
119 public void setUp() throws Exception {
120 MockitoAnnotations.initMocks(this);
121
122 mEditorInfo = new EditorInfo();
123 mUiHandler = new Handler();
124
125 mContext = Mockito.mock(Context.class);
126 mContainerView = Mockito.mock(View.class);
127 mImeAdapter = Mockito.mock(ImeAdapter.class);
128 mInputMethodManager = Mockito.mock(InputMethodManager.class);
129
130 mFactory = new TestFactory(new InputMethodManagerWrapper(mContext));
131
132 when(mContext.getSystemService(Context.INPUT_METHOD_SERVICE))
133 .thenReturn(mInputMethodManager);
134 when(mContainerView.getContext()).thenReturn(mContext);
135 when(mContainerView.getHandler()).thenReturn(mUiHandler);
136 when(mContainerView.hasFocus()).thenReturn(true);
137 when(mContainerView.hasWindowFocus()).thenReturn(true);
138
139 mProxyView = Mockito.mock(ThreadedInputConnectionProxyView.class);
140 when(mProxyView.getContext()).thenReturn(mContext);
141 when(mProxyView.requestFocus()).thenReturn(true);
142 when(mProxyView.getHandler()).thenReturn(mImeHandler);
143 doAnswer(new Answer<Void>() {
144 @Override
145 public Void answer(InvocationOnMock invocation) throws Throwable {
146 mWindowFocusChanged = true;
147 return null;
148 }
149 }).when(mProxyView).onWindowFocusChanged(true);
150 final Callable<InputConnection> callable = new Callable<InputConnection> () {
151 @Override
152 public InputConnection call() throws Exception {
153 return mFactory.initializeAndGet(
154 mContainerView, mImeAdapter, 1, 0, 0, 0, mEditorInfo);
155 }
156 };
157 when(mProxyView.onCreateInputConnection(any(EditorInfo.class))).thenAnsw er(
158 new Answer<InputConnection>() {
159 @Override
160 public InputConnection answer(InvocationOnMock invocation) t hrows Throwable {
161 return ThreadUtils.runOnUiThreadBlockingNoException(call able);
162 }
163 });
164
165 when(mInputMethodManager.isActive(mContainerView)).thenAnswer(new Answer <Boolean>() {
166 private int mCount;
167
168 @Override
169 public Boolean answer(InvocationOnMock invocation) throws Throwable {
170 mCount++;
171 if (mCount == 1 && mWindowFocusChanged) {
172 mInputConnection = mProxyView.onCreateInputConnection(mEdito rInfo);
173 return false;
174 } else if (mCount == 2) {
175 return true;
176 }
177 fail();
178 return false;
179 }
180 });
181 when(mInputMethodManager.isActive(mProxyView)).thenReturn(true);
182
183 mInOrder = inOrder(mImeAdapter, mInputMethodManager, mContainerView, mPr oxyView);
184 }
185
186 private void activateInput() {
187 mUiHandler.post(new Runnable() {
188 @Override
189 public void run() {
190 assertNull(mFactory.initializeAndGet(
191 mContainerView, mImeAdapter, 1, 0, 0, 0, mEditorInfo));
192 }
193 });
194 }
195
196 private void runOneUiTask() {
197 assertTrue(Robolectric.getForegroundThreadScheduler().runOneTask());
198 }
199
200 @Test
201 @Feature({"TextInput"})
202 public void testCreateInputConnection_Success() {
203 // Pause all the loopers.
204 Robolectric.getForegroundThreadScheduler().pause();
205 mImeShadowLooper.pause();
206
207 activateInput();
208
209 // The first onCreateInputConnection().
210 runOneUiTask();
211 mInOrder.verify(mContainerView).hasFocus();
212 mInOrder.verify(mContainerView).hasWindowFocus();
213 mInOrder.verify(mProxyView).requestFocus();
214 mInOrder.verify(mContainerView).getHandler();
215 mInOrder.verifyNoMoreInteractions();
216 assertNull(mInputConnection);
217
218 // The second onCreateInputConnection().
219 runOneUiTask();
220 mInOrder.verify(mProxyView).onWindowFocusChanged(true);
221 mInOrder.verify(mInputMethodManager).isActive(mContainerView);
222 mInOrder.verify(mProxyView).onCreateInputConnection(any(EditorInfo.class ));
223 mInOrder.verify(mContainerView).getContext(); // BaseInputConnection#<i nit>
224 mInOrder.verifyNoMoreInteractions();
225 assertNotNull(mInputConnection);
226 assertTrue(ThreadedInputConnection.class.isInstance(mInputConnection));
227
228 // Verification process.
229 mImeShadowLooper.runOneTask();
230 runOneUiTask();
231
232 mInOrder.verify(mInputMethodManager).isActive(mProxyView);
233 mInOrder.verifyNoMoreInteractions();
234
235 assertTrue(mFactory.hasSucceeded());
236 assertFalse(mFactory.hasFailed());
237 }
238
239 @Test
240 @Feature({"TextInput"})
241 public void testCreateInputConnection_WindowFocusLostOnFirstLoop() {
242 // Somehow input was activated right after window focus was lost.
243 when(mContainerView.hasWindowFocus()).thenReturn(false);
244
245 // Pause all the loopers.
246 Robolectric.getForegroundThreadScheduler().pause();
247 mImeShadowLooper.pause();
248
249 activateInput();
250
251 // The first onCreateInputConnection().
252 runOneUiTask();
253 mInOrder.verify(mContainerView).hasFocus();
254 mInOrder.verify(mContainerView).hasWindowFocus();
255 mInOrder.verifyNoMoreInteractions();
256 assertNull(mInputConnection);
257 assertFalse(mFactory.hasSucceeded());
258 assertFalse(mFactory.hasFailed());
259 }
260
261 @Test
262 @Feature({"TextInput"})
263 public void testCreateInputConnection_WindowFocusLostOnSecondLoop() {
264 // Pause all the loopers.
265 Robolectric.getForegroundThreadScheduler().pause();
266 mImeShadowLooper.pause();
267
268 activateInput();
269
270 // The first onCreateInputConnection().
271 runOneUiTask();
272 mInOrder.verify(mContainerView).hasFocus();
273 mInOrder.verify(mContainerView).hasWindowFocus();
274 mInOrder.verify(mProxyView).requestFocus();
275 mInOrder.verify(mContainerView).getHandler();
276 mInOrder.verifyNoMoreInteractions();
277 assertNull(mInputConnection);
278
279 // Now window focus was lost before the second onCreateInputConnection() .
280 mFactory.onWindowFocusChanged(false);
281
282 // The second onCreateInputConnection() gets ignored.
283 runOneUiTask();
284 mInOrder.verify(mProxyView).onOriginalViewWindowFocusChanged(false);
285 mInOrder.verifyNoMoreInteractions();
286 assertNull(mInputConnection);
287 assertFalse(mFactory.hasSucceeded());
288 assertFalse(mFactory.hasFailed());
289 }
290 }
OLDNEW
« no previous file with comments | « content/public/android/java/src/org/chromium/content/browser/input/ThreadedInputConnectionFactory.java ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698