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

Unified 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 side-by-side diff with in-line comments
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 »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: content/public/android/junit/src/org/chromium/content/browser/input/ThreadedInputConnectionFactoryTest.java
diff --git a/content/public/android/junit/src/org/chromium/content/browser/input/ThreadedInputConnectionFactoryTest.java b/content/public/android/junit/src/org/chromium/content/browser/input/ThreadedInputConnectionFactoryTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..af40a611c46b2a5037d815fdfc96af624186158e
--- /dev/null
+++ b/content/public/android/junit/src/org/chromium/content/browser/input/ThreadedInputConnectionFactoryTest.java
@@ -0,0 +1,290 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.content.browser.input;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.inOrder;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.os.Handler;
+import android.view.View;
+import android.view.inputmethod.EditorInfo;
+import android.view.inputmethod.InputConnection;
+import android.view.inputmethod.InputMethodManager;
+
+import org.chromium.base.ThreadUtils;
+import org.chromium.base.test.util.Feature;
+import org.chromium.testing.local.LocalRobolectricTestRunner;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.InOrder;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
+import org.robolectric.Robolectric;
+import org.robolectric.annotation.Config;
+import org.robolectric.internal.ShadowExtractor;
+import org.robolectric.shadows.ShadowLooper;
+
+import java.util.concurrent.Callable;
+
+/**
+ * Unit tests for {@ThreadedInputConnectionFactory}.
+ */
+@RunWith(LocalRobolectricTestRunner.class)
+@Config(manifest = Config.NONE)
+public class ThreadedInputConnectionFactoryTest {
+
+ /**
+ * A testable version of ThreadedInputConnectionFactory.
+ */
+ private class TestFactory extends ThreadedInputConnectionFactory {
+
+ private boolean mSucceeded;
+ private boolean mFailed;
+
+ TestFactory(InputMethodManagerWrapper inputMethodManagerWrapper) {
+ super(inputMethodManagerWrapper);
+ }
+
+ @Override
+ protected Handler createHandler() {
+ mImeHandler = super.createHandler();
+ mImeShadowLooper = (ShadowLooper) ShadowExtractor.extract(mImeHandler.getLooper());
+ return mImeHandler;
+ }
+
+ @Override
+ protected ThreadedInputConnectionProxyView createProxyView(Handler handler,
+ View containerView) {
+ return mProxyView;
+ }
+
+ @Override
+ protected InputMethodUma createInputMethodUma() {
+ return null;
+ }
+
+ @Override
+ protected void onRegisterProxyViewSuccess() {
+ mSucceeded = true;
+ }
+
+ @Override
+ protected void onRegisterProxyViewFailure() {
+ mFailed = true;
+ }
+
+ public boolean hasFailed() {
+ return mFailed;
+ }
+ public boolean hasSucceeded() {
+ return mSucceeded;
+ }
+ }
+
+ @Mock
+ private ImeAdapter mImeAdapter;
+ @Mock
+ private View mContainerView;
+ @Mock
+ private ThreadedInputConnectionProxyView mProxyView;
+ @Mock
+ private InputMethodManager mInputMethodManager;
+ @Mock
+ private Context mContext;
+
+ private EditorInfo mEditorInfo;
+ private Handler mImeHandler;
+ private Handler mUiHandler;
+ private ShadowLooper mImeShadowLooper;
+ private TestFactory mFactory;
+ private InputConnection mInputConnection;
+ private InOrder mInOrder;
+ private boolean mWindowFocusChanged;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+
+ mEditorInfo = new EditorInfo();
+ mUiHandler = new Handler();
+
+ mContext = Mockito.mock(Context.class);
+ mContainerView = Mockito.mock(View.class);
+ mImeAdapter = Mockito.mock(ImeAdapter.class);
+ mInputMethodManager = Mockito.mock(InputMethodManager.class);
+
+ mFactory = new TestFactory(new InputMethodManagerWrapper(mContext));
+
+ when(mContext.getSystemService(Context.INPUT_METHOD_SERVICE))
+ .thenReturn(mInputMethodManager);
+ when(mContainerView.getContext()).thenReturn(mContext);
+ when(mContainerView.getHandler()).thenReturn(mUiHandler);
+ when(mContainerView.hasFocus()).thenReturn(true);
+ when(mContainerView.hasWindowFocus()).thenReturn(true);
+
+ mProxyView = Mockito.mock(ThreadedInputConnectionProxyView.class);
+ when(mProxyView.getContext()).thenReturn(mContext);
+ when(mProxyView.requestFocus()).thenReturn(true);
+ when(mProxyView.getHandler()).thenReturn(mImeHandler);
+ doAnswer(new Answer<Void>() {
+ @Override
+ public Void answer(InvocationOnMock invocation) throws Throwable {
+ mWindowFocusChanged = true;
+ return null;
+ }
+ }).when(mProxyView).onWindowFocusChanged(true);
+ final Callable<InputConnection> callable = new Callable<InputConnection>() {
+ @Override
+ public InputConnection call() throws Exception {
+ return mFactory.initializeAndGet(
+ mContainerView, mImeAdapter, 1, 0, 0, 0, mEditorInfo);
+ }
+ };
+ when(mProxyView.onCreateInputConnection(any(EditorInfo.class))).thenAnswer(
+ new Answer<InputConnection>() {
+ @Override
+ public InputConnection answer(InvocationOnMock invocation) throws Throwable {
+ return ThreadUtils.runOnUiThreadBlockingNoException(callable);
+ }
+ });
+
+ when(mInputMethodManager.isActive(mContainerView)).thenAnswer(new Answer<Boolean>() {
+ private int mCount;
+
+ @Override
+ public Boolean answer(InvocationOnMock invocation) throws Throwable {
+ mCount++;
+ if (mCount == 1 && mWindowFocusChanged) {
+ mInputConnection = mProxyView.onCreateInputConnection(mEditorInfo);
+ return false;
+ } else if (mCount == 2) {
+ return true;
+ }
+ fail();
+ return false;
+ }
+ });
+ when(mInputMethodManager.isActive(mProxyView)).thenReturn(true);
+
+ mInOrder = inOrder(mImeAdapter, mInputMethodManager, mContainerView, mProxyView);
+ }
+
+ private void activateInput() {
+ mUiHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ assertNull(mFactory.initializeAndGet(
+ mContainerView, mImeAdapter, 1, 0, 0, 0, mEditorInfo));
+ }
+ });
+ }
+
+ private void runOneUiTask() {
+ assertTrue(Robolectric.getForegroundThreadScheduler().runOneTask());
+ }
+
+ @Test
+ @Feature({"TextInput"})
+ public void testCreateInputConnection_Success() {
+ // Pause all the loopers.
+ Robolectric.getForegroundThreadScheduler().pause();
+ mImeShadowLooper.pause();
+
+ activateInput();
+
+ // The first onCreateInputConnection().
+ runOneUiTask();
+ mInOrder.verify(mContainerView).hasFocus();
+ mInOrder.verify(mContainerView).hasWindowFocus();
+ mInOrder.verify(mProxyView).requestFocus();
+ mInOrder.verify(mContainerView).getHandler();
+ mInOrder.verifyNoMoreInteractions();
+ assertNull(mInputConnection);
+
+ // The second onCreateInputConnection().
+ runOneUiTask();
+ mInOrder.verify(mProxyView).onWindowFocusChanged(true);
+ mInOrder.verify(mInputMethodManager).isActive(mContainerView);
+ mInOrder.verify(mProxyView).onCreateInputConnection(any(EditorInfo.class));
+ mInOrder.verify(mContainerView).getContext(); // BaseInputConnection#<init>
+ mInOrder.verifyNoMoreInteractions();
+ assertNotNull(mInputConnection);
+ assertTrue(ThreadedInputConnection.class.isInstance(mInputConnection));
+
+ // Verification process.
+ mImeShadowLooper.runOneTask();
+ runOneUiTask();
+
+ mInOrder.verify(mInputMethodManager).isActive(mProxyView);
+ mInOrder.verifyNoMoreInteractions();
+
+ assertTrue(mFactory.hasSucceeded());
+ assertFalse(mFactory.hasFailed());
+ }
+
+ @Test
+ @Feature({"TextInput"})
+ public void testCreateInputConnection_WindowFocusLostOnFirstLoop() {
+ // Somehow input was activated right after window focus was lost.
+ when(mContainerView.hasWindowFocus()).thenReturn(false);
+
+ // Pause all the loopers.
+ Robolectric.getForegroundThreadScheduler().pause();
+ mImeShadowLooper.pause();
+
+ activateInput();
+
+ // The first onCreateInputConnection().
+ runOneUiTask();
+ mInOrder.verify(mContainerView).hasFocus();
+ mInOrder.verify(mContainerView).hasWindowFocus();
+ mInOrder.verifyNoMoreInteractions();
+ assertNull(mInputConnection);
+ assertFalse(mFactory.hasSucceeded());
+ assertFalse(mFactory.hasFailed());
+ }
+
+ @Test
+ @Feature({"TextInput"})
+ public void testCreateInputConnection_WindowFocusLostOnSecondLoop() {
+ // Pause all the loopers.
+ Robolectric.getForegroundThreadScheduler().pause();
+ mImeShadowLooper.pause();
+
+ activateInput();
+
+ // The first onCreateInputConnection().
+ runOneUiTask();
+ mInOrder.verify(mContainerView).hasFocus();
+ mInOrder.verify(mContainerView).hasWindowFocus();
+ mInOrder.verify(mProxyView).requestFocus();
+ mInOrder.verify(mContainerView).getHandler();
+ mInOrder.verifyNoMoreInteractions();
+ assertNull(mInputConnection);
+
+ // Now window focus was lost before the second onCreateInputConnection().
+ mFactory.onWindowFocusChanged(false);
+
+ // The second onCreateInputConnection() gets ignored.
+ runOneUiTask();
+ mInOrder.verify(mProxyView).onOriginalViewWindowFocusChanged(false);
+ mInOrder.verifyNoMoreInteractions();
+ assertNull(mInputConnection);
+ assertFalse(mFactory.hasSucceeded());
+ assertFalse(mFactory.hasFailed());
+ }
+}
« 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