| // Copyright 2010-2015, Google Inc. |
| // All rights reserved. |
| // |
| // Redistribution and use in source and binary forms, with or without |
| // modification, are permitted provided that the following conditions are |
| // met: |
| // |
| // * Redistributions of source code must retain the above copyright |
| // notice, this list of conditions and the following disclaimer. |
| // * Redistributions in binary form must reproduce the above |
| // copyright notice, this list of conditions and the following disclaimer |
| // in the documentation and/or other materials provided with the |
| // distribution. |
| // * Neither the name of Google Inc. nor the names of its |
| // contributors may be used to endorse or promote products derived from |
| // this software without specific prior written permission. |
| // |
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| |
| package org.mozc.android.inputmethod.japanese; |
| |
| import static org.mozc.android.inputmethod.japanese.testing.MozcMatcher.matchesKeyEvent; |
| import static org.mozc.android.inputmethod.japanese.testing.MozcMatcher.sameOptional; |
| import static org.easymock.EasyMock.anyBoolean; |
| import static org.easymock.EasyMock.anyObject; |
| import static org.easymock.EasyMock.capture; |
| import static org.easymock.EasyMock.eq; |
| import static org.easymock.EasyMock.expect; |
| import static org.easymock.EasyMock.expectLastCall; |
| import static org.easymock.EasyMock.isA; |
| import static org.easymock.EasyMock.same; |
| |
| import org.mozc.android.inputmethod.japanese.DependencyFactory.Dependency; |
| import org.mozc.android.inputmethod.japanese.FeedbackManager.FeedbackEvent; |
| import org.mozc.android.inputmethod.japanese.FeedbackManager.FeedbackListener; |
| import org.mozc.android.inputmethod.japanese.KeycodeConverter.KeyEventInterface; |
| import org.mozc.android.inputmethod.japanese.MozcService.SymbolHistoryStorageImpl; |
| import org.mozc.android.inputmethod.japanese.ViewManagerInterface.LayoutAdjustment; |
| import org.mozc.android.inputmethod.japanese.emoji.EmojiProviderType; |
| import org.mozc.android.inputmethod.japanese.hardwarekeyboard.HardwareKeyboard.CompositionSwitchMode; |
| import org.mozc.android.inputmethod.japanese.keyboard.Keyboard.KeyboardSpecification; |
| import org.mozc.android.inputmethod.japanese.model.JapaneseSoftwareKeyboardModel; |
| import org.mozc.android.inputmethod.japanese.model.SelectionTracker; |
| import org.mozc.android.inputmethod.japanese.model.SymbolCandidateStorage.SymbolHistoryStorage; |
| import org.mozc.android.inputmethod.japanese.model.SymbolMajorCategory; |
| import org.mozc.android.inputmethod.japanese.preference.ClientSidePreference; |
| import org.mozc.android.inputmethod.japanese.preference.ClientSidePreference.HardwareKeyMap; |
| import org.mozc.android.inputmethod.japanese.preference.ClientSidePreference.InputStyle; |
| import org.mozc.android.inputmethod.japanese.preference.ClientSidePreference.KeyboardLayout; |
| import org.mozc.android.inputmethod.japanese.preference.PreferenceUtil; |
| import org.mozc.android.inputmethod.japanese.protobuf.ProtoCandidates.CandidateList; |
| import org.mozc.android.inputmethod.japanese.protobuf.ProtoCandidates.Category; |
| import org.mozc.android.inputmethod.japanese.protobuf.ProtoCommands; |
| import org.mozc.android.inputmethod.japanese.protobuf.ProtoCommands.Command; |
| import org.mozc.android.inputmethod.japanese.protobuf.ProtoCommands.CompositionMode; |
| import org.mozc.android.inputmethod.japanese.protobuf.ProtoCommands.Context.InputFieldType; |
| import org.mozc.android.inputmethod.japanese.protobuf.ProtoCommands.DeletionRange; |
| import org.mozc.android.inputmethod.japanese.protobuf.ProtoCommands.GenericStorageEntry.StorageType; |
| import org.mozc.android.inputmethod.japanese.protobuf.ProtoCommands.Input; |
| import org.mozc.android.inputmethod.japanese.protobuf.ProtoCommands.Input.TouchEvent; |
| import org.mozc.android.inputmethod.japanese.protobuf.ProtoCommands.Output; |
| import org.mozc.android.inputmethod.japanese.protobuf.ProtoCommands.Preedit; |
| import org.mozc.android.inputmethod.japanese.protobuf.ProtoCommands.Preedit.Segment; |
| import org.mozc.android.inputmethod.japanese.protobuf.ProtoCommands.Preedit.Segment.Annotation; |
| import org.mozc.android.inputmethod.japanese.protobuf.ProtoCommands.Request; |
| import org.mozc.android.inputmethod.japanese.protobuf.ProtoCommands.Result; |
| import org.mozc.android.inputmethod.japanese.protobuf.ProtoCommands.Result.ResultType; |
| import org.mozc.android.inputmethod.japanese.protobuf.ProtoCommands.SessionCommand; |
| import org.mozc.android.inputmethod.japanese.protobuf.ProtoConfig.Config; |
| import org.mozc.android.inputmethod.japanese.protobuf.ProtoConfig.Config.SelectionShortcut; |
| import org.mozc.android.inputmethod.japanese.session.SessionExecutor; |
| import org.mozc.android.inputmethod.japanese.session.SessionExecutor.EvaluationCallback; |
| import org.mozc.android.inputmethod.japanese.session.SessionHandlerFactory; |
| import org.mozc.android.inputmethod.japanese.testing.ApiLevel; |
| import org.mozc.android.inputmethod.japanese.testing.InstrumentationTestCaseWithMock; |
| import org.mozc.android.inputmethod.japanese.testing.Parameter; |
| import org.mozc.android.inputmethod.japanese.ui.MenuDialog.MenuDialogListener; |
| import org.mozc.android.inputmethod.japanese.util.ImeSwitcherFactory.ImeSwitcher; |
| import org.mozc.android.inputmethod.japanese.view.Skin; |
| import org.mozc.android.inputmethod.japanese.view.SkinType; |
| import com.google.common.base.Optional; |
| import com.google.protobuf.ByteString; |
| |
| import android.annotation.TargetApi; |
| import android.content.Context; |
| import android.content.SharedPreferences; |
| import android.content.SharedPreferences.Editor; |
| import android.content.SharedPreferences.OnSharedPreferenceChangeListener; |
| import android.content.res.Configuration; |
| import android.content.res.Resources; |
| import android.media.AudioManager; |
| import android.os.Build; |
| import android.os.Handler; |
| import android.preference.PreferenceManager; |
| import android.test.UiThreadTest; |
| import android.test.suitebuilder.annotation.SmallTest; |
| import android.text.InputType; |
| import android.text.SpannableStringBuilder; |
| import android.util.Pair; |
| import android.view.InputDevice; |
| import android.view.KeyEvent; |
| import android.view.MotionEvent; |
| import android.view.inputmethod.EditorInfo; |
| import android.view.inputmethod.InputConnection; |
| import android.view.inputmethod.InputConnectionWrapper; |
| |
| import org.easymock.Capture; |
| |
| import java.util.ArrayList; |
| import java.util.Collections; |
| import java.util.List; |
| |
| /** |
| * Due to limitation of AndroidMock (in more precise EasyMock 2.4 used by AndroidMock), |
| * It's difficult to write appropriate expectation based on IMocksControl. |
| * So, this test contains bad hack; |
| * - If the mock's state is before "replay", all arguments of method invocations are ignored, |
| * and default values (0, false or null) are returned. |
| * Thus, we abuse it as "a kind of nice mock", and invoke "reset" method before writing |
| * actual expectations. |
| * TODO(hidehiko): Remove the hack, after switching. |
| * |
| */ |
| public class MozcServiceTest extends InstrumentationTestCaseWithMock { |
| |
| /** |
| * Store registered listeners to unregister them on {@code clearSharedPreferences}. |
| */ |
| private static final List<OnSharedPreferenceChangeListener> sharedPreferenceChangeListeners = |
| new ArrayList<OnSharedPreferenceChangeListener>(1); |
| |
| private static class AlwaysShownMozcService extends MozcService { |
| |
| @Override |
| public boolean isInputViewShown() { |
| return true; |
| } |
| } |
| |
| @Override |
| protected void setUp() throws Exception { |
| super.setUp(); |
| DependencyFactory.setDependency(Optional.<Dependency>absent()); |
| clearSharedPreferences(); |
| } |
| |
| @Override |
| protected void tearDown() throws Exception { |
| clearSharedPreferences(); |
| DependencyFactory.setDependency(Optional.<Dependency>absent()); |
| super.tearDown(); |
| } |
| |
| private ViewManager createViewManagerMock(Context context, ViewEventListener viewEventListener) { |
| return createMockBuilder(ViewManager.class) |
| .withConstructor(Context.class, ViewEventListener.class, SymbolHistoryStorage.class, |
| ImeSwitcher.class, MenuDialogListener.class) |
| .withArgs(context, viewEventListener, createNiceMock(SymbolHistoryStorage.class), |
| createNiceMock(ImeSwitcher.class), createNiceMock(MenuDialogListener.class)) |
| .createMock(); |
| } |
| |
| // Consider using createInitializedService() instead of this method. |
| private MozcService createService() { |
| return initializeService(new MozcService()); |
| } |
| |
| private MozcService initializeService(MozcService service) { |
| service.sendSyncDataCommandHandler = new Handler(); |
| service.attachBaseContext(getInstrumentation().getTargetContext()); |
| return service; |
| } |
| |
| private MozcService createInitializedService(SessionExecutor sessionExecutor) { |
| MozcService service = createService(); |
| invokeOnCreateInternal( |
| service, null, getSharedPreferences(), getDefaultDeviceConfiguration(), sessionExecutor); |
| return service; |
| } |
| |
| private MozcService initializeMozcService(MozcService service, |
| SessionExecutor sessionExecutor) { |
| invokeOnCreateInternal(initializeService(service), null, getSharedPreferences(), |
| getDefaultDeviceConfiguration(), sessionExecutor); |
| return service; |
| } |
| |
| private SharedPreferences getSharedPreferences() { |
| return PreferenceManager.getDefaultSharedPreferences(getInstrumentation().getContext()); |
| } |
| |
| private void clearSharedPreferences() { |
| SharedPreferences sharedPreferences = getSharedPreferences(); |
| |
| // Clear listeners registered by {@code invokeOnCreateInternal}. |
| for (OnSharedPreferenceChangeListener listener : sharedPreferenceChangeListeners) { |
| sharedPreferences.unregisterOnSharedPreferenceChangeListener(listener); |
| } |
| sharedPreferenceChangeListeners.clear(); |
| |
| Editor editor = sharedPreferences.edit(); |
| editor.clear(); |
| editor.commit(); |
| } |
| |
| private Configuration getDefaultDeviceConfiguration() { |
| Configuration configuration = new Configuration(); |
| configuration.orientation = Configuration.ORIENTATION_PORTRAIT; |
| // Note: Some other fields might be needed. |
| // But currently only orientation field causes flaky test results. |
| return configuration; |
| } |
| |
| private static void invokeOnCreateInternal(MozcService service, |
| ViewManager viewManager, |
| SharedPreferences sharedPreferences, |
| Configuration deviceConfiguration, |
| SessionExecutor sessionExecutor) { |
| ViewEventListener eventListener = service.new MozcEventListener(); |
| service.onCreateInternal( |
| eventListener, viewManager, sharedPreferences, deviceConfiguration, sessionExecutor); |
| sharedPreferenceChangeListeners.add(service.sharedPreferenceChangeListener); |
| } |
| |
| @SmallTest |
| // UiThreadTest annotation to handle callback of shared preference. |
| @UiThreadTest |
| public void testOnCreate() { |
| SharedPreferences preferences = getSharedPreferences(); |
| // Client-side config |
| preferences.edit() |
| .putBoolean("pref_haptic_feedback_key", true) |
| .putBoolean("pref_other_anonimous_mode_key", true) |
| .commit(); |
| |
| SessionExecutor sessionExecutor = createStrictMock(SessionExecutor.class); |
| sessionExecutor.reset(isA(SessionHandlerFactory.class), isA(Context.class)); |
| sessionExecutor.setLogging(anyBoolean()); |
| sessionExecutor.setImposedConfig(isA(Config.class)); |
| sessionExecutor.setConfig(ConfigUtil.toConfig(preferences)); |
| sessionExecutor.switchInputMode(same(Optional.<KeyEventInterface>absent()), |
| isA(CompositionMode.class), |
| anyObject(EvaluationCallback.class)); |
| expectLastCall().asStub(); |
| sessionExecutor.updateRequest(isA(Request.class), eq(Collections.<TouchEvent>emptyList())); |
| expectLastCall().asStub(); |
| sessionExecutor.preferenceUsageStatsEvent( |
| anyObject(SharedPreferences.class), anyObject(Resources.class)); |
| |
| ViewManager viewManager = createMockBuilder(ViewManager.class) |
| .withConstructor(Context.class, ViewEventListener.class, |
| SymbolHistoryStorage.class, ImeSwitcher.class, MenuDialogListener.class) |
| .withArgs(getInstrumentation().getTargetContext(), |
| createNiceMock(ViewEventListener.class), |
| createNiceMock(SymbolHistoryStorage.class), |
| createNiceMock(ImeSwitcher.class), |
| createNiceMock(MenuDialogListener.class)) |
| .addMockedMethod("onConfigurationChanged") |
| .createMock(); |
| viewManager.onConfigurationChanged(anyObject(Configuration.class)); |
| |
| replayAll(); |
| |
| MozcService service = createService(); |
| |
| // Both sessionExecutor and viewManager is not initialized. |
| assertNull(service.sessionExecutor); |
| assertNull(service.viewManager); |
| |
| // A window is not created. |
| assertNull(service.getWindow()); |
| |
| // Initialize the service. |
| try { |
| // Emulate non-dev channel situation. |
| MozcUtil.setDevChannel(Optional.of(false)); |
| invokeOnCreateInternal(service, viewManager, preferences, getDefaultDeviceConfiguration(), |
| sessionExecutor); |
| } finally { |
| MozcUtil.setDevChannel(Optional.<Boolean>absent()); |
| } |
| |
| verifyAll(); |
| |
| // The following variables must have been initialized after the initialization of the service. |
| assertNotNull(service.getWindow()); |
| assertSame(sessionExecutor, service.sessionExecutor); |
| assertNotNull(service.viewManager); |
| assertTrue(service.feedbackManager.isHapticFeedbackEnabled()); |
| |
| // SYNC_DATA should be sent periodically. |
| assertTrue(service.sendSyncDataCommandHandler.hasMessages(0)); |
| |
| // SharedPreferences should be cached. |
| assertSame(preferences, service.sharedPreferences); |
| } |
| |
| @SmallTest |
| public void testOnCreate_viewManager() { |
| MozcService service = createService(); |
| try { |
| invokeOnCreateInternal(service, null, getSharedPreferences(), getDefaultDeviceConfiguration(), |
| createNiceMock(SessionExecutor.class)); |
| assertSame(ViewManager.class, service.viewManager.getClass()); |
| |
| } finally { |
| } |
| } |
| |
| @SmallTest |
| public void testOnCreateInputView() { |
| MozcService service = createService(); |
| // A test which calls onCreateInputView before onCreate is not needed |
| // because IMF ensures calling onCreate before onCreateInputView. |
| SessionExecutor sessionExecutor = createNiceMock(SessionExecutor.class); |
| replayAll(); |
| |
| invokeOnCreateInternal( |
| service, null, getSharedPreferences(), getDefaultDeviceConfiguration(), sessionExecutor); |
| |
| verifyAll(); |
| assertNotNull(service.onCreateInputView()); |
| // Created view is tested in CandidateViewTest. |
| } |
| |
| @SmallTest |
| public void testKeyboardInitialized() { |
| MozcService service = createService(); |
| SessionExecutor sessionExecutor = createNiceMock(SessionExecutor.class); |
| KeyboardSpecification defaultSpecification = service.currentKeyboardSpecification; |
| sessionExecutor.updateRequest( |
| MozcUtil.getRequestBuilder( |
| service.getResources(), defaultSpecification, getDefaultDeviceConfiguration()).build(), |
| Collections.<TouchEvent>emptyList()); |
| |
| replayAll(); |
| invokeOnCreateInternal( |
| service, null, getSharedPreferences(), getDefaultDeviceConfiguration(), sessionExecutor); |
| verifyAll(); |
| } |
| |
| @SmallTest |
| public void testOnFinishInput() { |
| SessionExecutor sessionExecutor = createMock(SessionExecutor.class); |
| MozcService service = createInitializedService(sessionExecutor); |
| |
| resetAll(); |
| sessionExecutor.resetContext(); |
| |
| ViewEventListener eventListener = createNiceMock(ViewEventListener.class); |
| Context context = getInstrumentation().getTargetContext(); |
| ViewManager viewManager = createViewManagerMock(context, eventListener); |
| service.viewManager = viewManager; |
| viewManager.reset(); |
| replayAll(); |
| |
| // When onFinishInput is called, the inputHandler should receive CLEANUP command. |
| service.onFinishInput(); |
| |
| verifyAll(); |
| } |
| |
| @SmallTest |
| public void testOnStartInput_notRestarting() { |
| SessionExecutor sessionExecutor = createMock(SessionExecutor.class); |
| SelectionTracker selectionTracker = createMock(SelectionTracker.class); |
| MozcService service = createInitializedService(sessionExecutor); |
| Capture<Request> requestCapture = new Capture<Request>(); |
| |
| resetAll(); |
| sessionExecutor.resetContext(); |
| // Exactly one message is issued to switch input field type by onStartInput(). |
| // It should not render a result in order to preserve |
| // the existent text. b/4422455 |
| sessionExecutor.switchInputFieldType(InputFieldType.NORMAL); |
| |
| // Setting for Emoji attributes. |
| sessionExecutor.updateRequest( |
| capture(requestCapture), eq(Collections.<TouchEvent>emptyList())); |
| |
| selectionTracker.onStartInput(1, 2, false); |
| replayAll(); |
| |
| service.selectionTracker = selectionTracker; |
| |
| EditorInfo editorInfo = new EditorInfo(); |
| editorInfo.initialSelStart = 1; |
| editorInfo.initialSelEnd = 2; |
| service.onStartInput(editorInfo, false); |
| |
| verifyAll(); |
| assertTrue(requestCapture.getValue().hasEmojiRewriterCapability()); |
| assertTrue(requestCapture.getValue().hasAvailableEmojiCarrier()); |
| } |
| |
| @SmallTest |
| public void testOnStartInput_restarting() { |
| SessionExecutor sessionExecutor = createMock(SessionExecutor.class); |
| SelectionTracker selectionTracker = createMock(SelectionTracker.class); |
| MozcService service = createInitializedService(sessionExecutor); |
| service.selectionTracker = selectionTracker; |
| Capture<Request> requestCapture = new Capture<Request>(); |
| |
| resetAll(); |
| sessionExecutor.resetContext(); |
| // Exactly one message is issued to switch input field type by onStartInput(). |
| // It should not render a result in order to preserve |
| // the existent text. b/4422455 |
| sessionExecutor.switchInputFieldType(InputFieldType.NORMAL); |
| |
| // Setting for Emoji attributes. |
| sessionExecutor.updateRequest( |
| capture(requestCapture), eq(Collections.<TouchEvent>emptyList())); |
| |
| selectionTracker.onStartInput(1, 2, false); |
| replayAll(); |
| |
| EditorInfo editorInfo = new EditorInfo(); |
| editorInfo.initialSelStart = 1; |
| editorInfo.initialSelEnd = 2; |
| service.onStartInput(editorInfo, true); |
| |
| verifyAll(); |
| assertTrue(requestCapture.getValue().hasEmojiRewriterCapability()); |
| assertTrue(requestCapture.getValue().hasAvailableEmojiCarrier()); |
| } |
| |
| @SmallTest |
| public void testOnCompositionModeChange() { |
| SessionExecutor sessionExecutor = createMock(SessionExecutor.class); |
| MozcService service = createInitializedService(sessionExecutor); |
| |
| service.currentKeyboardSpecification = KeyboardSpecification.TWELVE_KEY_TOGGLE_ALPHABET; |
| resetAll(); |
| sessionExecutor.updateRequest(anyObject(ProtoCommands.Request.class), |
| eq(Collections.<TouchEvent>emptyList())); |
| // Exactly one message is issued to switch composition mode. |
| // It should not render a result in order to preserve |
| // the existent text. b/5181946 |
| sessionExecutor.switchInputMode(same(Optional.<KeyEventInterface>absent()), |
| eq(CompositionMode.HIRAGANA), |
| anyObject(EvaluationCallback.class)); |
| replayAll(); |
| |
| ViewEventListener listener = service.new MozcEventListener(); |
| listener.onKeyEvent(null, null, KeyboardSpecification.TWELVE_KEY_TOGGLE_KANA, |
| Collections.<TouchEvent>emptyList()); |
| |
| verifyAll(); |
| } |
| |
| @SmallTest |
| public void testSendKeyEventBackToApplication() { |
| SessionExecutor sessionExecutor = createMock(SessionExecutor.class); |
| MozcService service = createInitializedService(sessionExecutor); |
| |
| resetAll(); |
| sessionExecutor.sendKeyEvent(isA(KeyEventInterface.class), isA(EvaluationCallback.class)); |
| replayAll(); |
| ViewEventListener listener = service.new MozcEventListener(); |
| listener.onKeyEvent(null, KeycodeConverter.getKeyEventInterface(KeyEvent.KEYCODE_BACK), null, |
| Collections.<TouchEvent>emptyList()); |
| |
| verifyAll(); |
| } |
| |
| @SmallTest |
| public void testOnKeyUp() { |
| SessionExecutor sessionExecutor = createMockBuilder(SessionExecutor.class) |
| .addMockedMethod("sendKeyEvent") |
| .createMock(); |
| replayAll(); |
| MozcService service = initializeMozcService(new AlwaysShownMozcService(), sessionExecutor); |
| |
| KeyEvent[] keyEventsPassedToCallback = { |
| new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_BACK), |
| new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_SHIFT_LEFT), |
| new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_SHIFT_RIGHT), |
| new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_ALT_LEFT), |
| new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_ALT_RIGHT), |
| new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_CTRL_LEFT), |
| new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_CTRL_RIGHT), |
| }; |
| for (KeyEvent keyEvent : keyEventsPassedToCallback) { |
| resetAll(); |
| sessionExecutor.sendKeyEvent(matchesKeyEvent(keyEvent), isA(EvaluationCallback.class)); |
| replayAll(); |
| assertTrue(service.onKeyUp(keyEvent.getKeyCode(), keyEvent)); |
| verifyAll(); |
| } |
| } |
| |
| @SmallTest |
| // UiThreadTest annotation to handle callback of shared preference. |
| @UiThreadTest |
| public void testOnKeyDownByHardwareKeyboard() { |
| Context context = getInstrumentation().getTargetContext(); |
| |
| MozcService service = createMockBuilder(MozcService.class) |
| .addMockedMethods("isInputViewShown", "sendKeyWithKeyboardSpecification") |
| .createMock(); |
| SessionExecutor sessionExecutor = createNiceMock(SessionExecutor.class); |
| ViewEventListener eventListener = createNiceMock(ViewEventListener.class); |
| ViewManager viewManager = createMockBuilder(ViewManager.class) |
| .withConstructor(Context.class, ViewEventListener.class, |
| SymbolHistoryStorage.class, ImeSwitcher.class, MenuDialogListener.class) |
| .withArgs(context, eventListener, createNiceMock(SymbolHistoryStorage.class), |
| createNiceMock(ImeSwitcher.class), createNiceMock(MenuDialogListener.class)) |
| .addMockedMethods("onHardwareKeyEvent") |
| .createNiceMock(); |
| service.sendKeyWithKeyboardSpecification( |
| same(ProtoCommands.KeyEvent.class.cast(null)), same(KeyEventInterface.class.cast(null)), |
| anyObject(KeyboardSpecification.class), anyObject(Configuration.class), |
| eq(Collections.<TouchEvent>emptyList())); |
| expectLastCall().asStub(); |
| |
| // Before test, we need to setup mock instances with some method calls. |
| // Because the calls is out of this test, we do it by NiceMock and replayAll. |
| replayAll(); |
| initializeMozcService(service, sessionExecutor); |
| SharedPreferences sharedPreferences = getSharedPreferences(); |
| invokeOnCreateInternal(service, viewManager, sharedPreferences, |
| context.getResources().getConfiguration(), sessionExecutor); |
| sharedPreferences.edit().remove(PreferenceUtil.PREF_HARDWARE_KEYMAP).commit(); |
| verifyAll(); |
| |
| // Real test part. |
| resetAll(); |
| |
| KeyEvent event = new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_A); |
| expect(service.isInputViewShown()).andStubReturn(true); |
| ClientSidePreference clientSidePreference = service.propagatedClientSidePreference; |
| assertNotNull(clientSidePreference); |
| assertEquals(HardwareKeyMap.DEFAULT, clientSidePreference.getHardwareKeyMap()); |
| viewManager.onHardwareKeyEvent(event); |
| |
| replayAll(); |
| |
| assertTrue(service.onKeyDownInternal(0, event, getDefaultDeviceConfiguration())); |
| |
| verifyAll(); |
| } |
| |
| |
| |
| @SmallTest |
| public void testSendKeyWithKeyboardSpecification_switchKeyboardSpecification() { |
| SessionExecutor sessionExecutor = createMock(SessionExecutor.class); |
| MozcService service = createInitializedService(sessionExecutor); |
| |
| EvaluationCallback renderResultCallback = service.renderResultCallback; |
| |
| service.currentKeyboardSpecification = KeyboardSpecification.TWELVE_KEY_FLICK_KANA; |
| |
| // Send "a" without keyboard specification change. [TWELVE_KEY_FLICK_KANA] |
| resetAll(); |
| |
| ProtoCommands.KeyEvent mozcKeyEvent = |
| ProtoCommands.KeyEvent.newBuilder().setKeyCode('a').build(); |
| KeyEventInterface keyEvent = KeycodeConverter.getKeyEventInterface(KeyEvent.KEYCODE_A); |
| KeyboardSpecification keyboardSpecification = KeyboardSpecification.TWELVE_KEY_FLICK_KANA; |
| |
| sessionExecutor.sendKey( |
| mozcKeyEvent, keyEvent, Collections.<TouchEvent>emptyList(), renderResultCallback); |
| |
| replayAll(); |
| |
| service.sendKeyWithKeyboardSpecification( |
| mozcKeyEvent, keyEvent, keyboardSpecification, getDefaultDeviceConfiguration(), |
| Collections.<TouchEvent>emptyList()); |
| |
| verifyAll(); |
| |
| // Send "a" with keyboard specification change. [HARDWARE_QWERTY_KANA] |
| // This is transition from software to hardware keyboard, so submit() is called. |
| resetAll(); |
| |
| mozcKeyEvent = ProtoCommands.KeyEvent.newBuilder().setKeyCode('a').build(); |
| keyEvent = KeycodeConverter.getKeyEventInterface(KeyEvent.KEYCODE_A); |
| keyboardSpecification = KeyboardSpecification.HARDWARE_QWERTY_KANA; |
| |
| sessionExecutor.submit(same(service.renderResultCallback)); |
| sessionExecutor.updateRequest( |
| MozcUtil.getRequestBuilder( |
| service.getResources(), keyboardSpecification, getDefaultDeviceConfiguration()).build(), |
| Collections.<TouchEvent>emptyList()); |
| sessionExecutor.sendKey( |
| ProtoCommands.KeyEvent.newBuilder(mozcKeyEvent) |
| .setMode(keyboardSpecification.getCompositionMode()) |
| .build(), |
| keyEvent, Collections.<TouchEvent>emptyList(), renderResultCallback); |
| |
| replayAll(); |
| |
| service.sendKeyWithKeyboardSpecification( |
| mozcKeyEvent, keyEvent, keyboardSpecification, getDefaultDeviceConfiguration(), |
| Collections.<TouchEvent>emptyList()); |
| |
| verifyAll(); |
| |
| // Send "a" without keyboard specification change. [HARDWARE_QWERTY_KANA] |
| resetAll(); |
| |
| mozcKeyEvent = ProtoCommands.KeyEvent.newBuilder().setKeyCode('a').build(); |
| keyEvent = KeycodeConverter.getKeyEventInterface(KeyEvent.KEYCODE_A); |
| keyboardSpecification = KeyboardSpecification.HARDWARE_QWERTY_KANA; |
| |
| sessionExecutor.sendKey( |
| mozcKeyEvent, keyEvent, Collections.<TouchEvent>emptyList(), renderResultCallback); |
| |
| replayAll(); |
| |
| service.sendKeyWithKeyboardSpecification( |
| mozcKeyEvent, keyEvent, keyboardSpecification, getDefaultDeviceConfiguration(), |
| Collections.<TouchEvent>emptyList()); |
| |
| verifyAll(); |
| |
| // Change keyboard specification. [TWELVE_KEY_FLICK_KANA] |
| resetAll(); |
| |
| mozcKeyEvent = null; |
| keyEvent = null; |
| keyboardSpecification = KeyboardSpecification.TWELVE_KEY_FLICK_KANA; |
| |
| sessionExecutor.updateRequest( |
| MozcUtil.getRequestBuilder( |
| service.getResources(), keyboardSpecification, getDefaultDeviceConfiguration()).build(), |
| Collections.<TouchEvent>emptyList()); |
| sessionExecutor.switchInputMode(same(Optional.<KeyEventInterface>absent()), |
| eq(keyboardSpecification.getCompositionMode()), |
| same(renderResultCallback)); |
| |
| replayAll(); |
| |
| service.sendKeyWithKeyboardSpecification( |
| mozcKeyEvent, keyEvent, keyboardSpecification, getDefaultDeviceConfiguration(), |
| Collections.<TouchEvent>emptyList()); |
| |
| verifyAll(); |
| } |
| |
| @SmallTest |
| public void testMetaKeyHandling_b13238551() { |
| SessionExecutor sessionExecutor = createMock(SessionExecutor.class); |
| MozcService service = createInitializedService(sessionExecutor); |
| service.currentKeyboardSpecification = KeyboardSpecification.TWELVE_KEY_FLICK_KANA; |
| ProtoCommands.KeyEvent mozcKeyEvent = null; |
| KeyEventInterface keyEvent = KeycodeConverter.getKeyEventInterface(KeyEvent.KEYCODE_SHIFT_LEFT); |
| |
| // Send shift key only without keyboard specification change. |
| resetAll(); |
| |
| KeyboardSpecification keyboardSpecification = KeyboardSpecification.TWELVE_KEY_FLICK_KANA; |
| sessionExecutor.sendKeyEvent(keyEvent, service.sendKeyToApplicationCallback); |
| |
| replayAll(); |
| |
| service.sendKeyWithKeyboardSpecification( |
| mozcKeyEvent, keyEvent, keyboardSpecification, getDefaultDeviceConfiguration(), |
| Collections.<TouchEvent>emptyList()); |
| |
| verifyAll(); |
| |
| // Send shift key only with keyboard specification change. |
| resetAll(); |
| |
| keyboardSpecification = KeyboardSpecification.HARDWARE_QWERTY_KANA; |
| sessionExecutor.submit(same(service.renderResultCallback)); |
| sessionExecutor.updateRequest( |
| MozcUtil.getRequestBuilder( |
| service.getResources(), keyboardSpecification, getDefaultDeviceConfiguration()).build(), |
| Collections.<TouchEvent>emptyList()); |
| sessionExecutor.switchInputMode( |
| sameOptional(keyEvent), isA(CompositionMode.class), same(service.renderResultCallback)); |
| |
| replayAll(); |
| |
| service.sendKeyWithKeyboardSpecification( |
| mozcKeyEvent, keyEvent, keyboardSpecification, getDefaultDeviceConfiguration(), |
| Collections.<TouchEvent>emptyList()); |
| |
| verifyAll(); |
| |
| // Handle meta key on renderInputConnection invoked by renderResultCallback. |
| resetAll(); |
| |
| service.sendKeyEvent(keyEvent); |
| |
| replayAll(); |
| |
| service.renderInputConnection( |
| Command.newBuilder() |
| .setInput(Input.newBuilder() |
| .setType(Input.CommandType.SEND_COMMAND) |
| .setCommand(SessionCommand.newBuilder() |
| .setType(SessionCommand.CommandType.SWITCH_INPUT_MODE))) |
| .setOutput(Output.newBuilder() |
| .setConsumed(true)) |
| .build(), |
| keyEvent); |
| |
| verifyAll(); |
| } |
| |
| @SmallTest |
| public void testShowWindow() { |
| final InputConnection inputConnection = createMock(InputConnection.class); |
| expect(inputConnection.beginBatchEdit()).andStubReturn(true); |
| expect(inputConnection.endBatchEdit()).andStubReturn(true); |
| // Expect that setComposingText() is not called. |
| // If it is called, when the IME is shown existing selection range is cleared unexpectedly. |
| // Unfortunately EasyMock#times() methods doesn't accept parameter 0 (meaning that |
| // mocked method shouldn't be called) so such indirect approach is employed. |
| replayAll(); |
| |
| MozcService service = new MozcService() { |
| @Override |
| public InputConnection getCurrentInputConnection() { |
| return inputConnection; |
| } |
| }; |
| service.attachBaseContext(getInstrumentation().getTargetContext()); |
| |
| DependencyFactory.setDependency(Optional.of(DependencyFactory.TOUCH_FRAGMENT_PREF)); |
| service.onCreate(); |
| service.onWindowShown(); |
| service.sessionExecutor.waitForAllQueuesForEmpty(); |
| verifyAll(); |
| } |
| |
| @SmallTest |
| public void testHideWindow() { |
| SessionExecutor sessionExecutor = createStrictMock(SessionExecutor.class); |
| MozcService service = createInitializedService(sessionExecutor); |
| InputConnection inputConnection = createMock(InputConnection.class); |
| // restartInput() can be used to install our InputConnection safely. |
| service.onCreateInputMethodInterface().restartInput(inputConnection, new EditorInfo()); |
| |
| // When the IME is binded to certain view and its window has been shown by showWindow(), |
| // "finishComposingText" is called from InputMethodService::hideWindow() so that |
| // the preedit is submitted. |
| // However "finishComposingText" is not called in this test case because |
| // the inputConnection is not binded to a view. |
| resetAll(); |
| sessionExecutor.removePendingEvaluations(); |
| sessionExecutor.resetContext(); |
| replayAll(); |
| |
| service.onWindowHidden(); |
| |
| verifyAll(); |
| } |
| |
| @SmallTest |
| public void testRenderInputConnection_updatingPreedit() { |
| SessionExecutor sessionExecutor = createNiceMock(SessionExecutor.class); |
| MozcService service = createInitializedService(sessionExecutor); |
| SelectionTracker selectionTracker = createMock(SelectionTracker.class); |
| service.selectionTracker = selectionTracker; |
| InputConnection inputConnection = createStrictMock(InputConnection.class); |
| service.onCreateInputMethodInterface().restartInput(inputConnection, new EditorInfo()); |
| |
| Preedit preedit = Preedit.newBuilder() |
| .addSegment(Segment.newBuilder() |
| .setValue("\uD83D\uDC31") // a character meaning a cat |
| .setAnnotation(Annotation.HIGHLIGHT) |
| .setValueLength(1)) |
| .addSegment(Segment.newBuilder() |
| .setValue("1") |
| .setAnnotation(Annotation.UNDERLINE) |
| .setValueLength(1)) |
| .addSegment(Segment.newBuilder() |
| .setValue("2") |
| .setAnnotation(Annotation.UNDERLINE) |
| .setValueLength(1)) |
| .setCursor(1) |
| .build(); |
| Command command = Command.newBuilder() |
| .setInput(Input.newBuilder().setType(Input.CommandType.SEND_KEY)) |
| .setOutput(Output.newBuilder() |
| .setConsumed(true).setPreedit(preedit) |
| .setAllCandidateWords(CandidateList.newBuilder().setCategory(Category.SUGGESTION))) |
| .build(); |
| |
| resetAll(); |
| expect(inputConnection.beginBatchEdit()).andReturn(true); |
| Capture<SpannableStringBuilder> spannableStringBuilderCapture = |
| new Capture<SpannableStringBuilder>(); |
| // the cursor is at the tail |
| expect(inputConnection.setComposingText(capture(spannableStringBuilderCapture), eq(1))) |
| .andReturn(true); |
| expect(selectionTracker.getPreeditStartPosition()).andReturn(0); |
| expect(inputConnection.setSelection(1, 1)).andReturn(true); |
| selectionTracker.onRender(null, null, preedit); |
| expect(inputConnection.endBatchEdit()).andReturn(true); |
| replayAll(); |
| |
| service.renderInputConnection(command, KeycodeConverter.getKeyEventInterface('\0')); |
| |
| verifyAll(); |
| SpannableStringBuilder sb = spannableStringBuilderCapture.getValue(); |
| assertEquals("\uD83D\uDC3112", sb.toString()); |
| assertEquals(0, sb.getSpanStart(MozcService.SPAN_UNDERLINE)); |
| assertEquals(4, sb.getSpanEnd(MozcService.SPAN_UNDERLINE)); |
| assertEquals(2, sb.getSpanStart(MozcService.SPAN_PARTIAL_SUGGESTION_COLOR)); |
| assertEquals(4, sb.getSpanEnd(MozcService.SPAN_PARTIAL_SUGGESTION_COLOR)); |
| assertEquals(0, sb.getSpanStart(MozcService.SPAN_BEFORE_CURSOR)); |
| assertEquals(2, sb.getSpanEnd(MozcService.SPAN_BEFORE_CURSOR)); |
| } |
| |
| @SmallTest |
| public void testRenderInputConnection_updatingPreeditAtCursorMiddle() { |
| // Updating preedit (cursor is at the middle) |
| SessionExecutor sessionExecutor = createMock(SessionExecutor.class); |
| MozcService service = createInitializedService(sessionExecutor); |
| SelectionTracker selectionTracker = createMock(SelectionTracker.class); |
| service.selectionTracker = selectionTracker; |
| InputConnection inputConnection = createStrictMock(InputConnection.class); |
| service.onCreateInputMethodInterface().restartInput(inputConnection, new EditorInfo()); |
| |
| Preedit preedit = Preedit.newBuilder() |
| .addSegment(Segment.newBuilder() |
| .setValue("0") |
| .setAnnotation(Annotation.UNDERLINE) |
| .setValueLength(1)) |
| .addSegment(Segment.newBuilder() |
| .setValue("1") |
| .setAnnotation(Annotation.UNDERLINE) |
| .setValueLength(1)) |
| .addSegment(Segment.newBuilder() |
| .setValue("2") |
| .setAnnotation(Annotation.UNDERLINE) |
| .setValueLength(1)) |
| .setCursor(2) |
| .build(); |
| Command command = Command.newBuilder() |
| .setInput(Input.newBuilder().setType(Input.CommandType.SEND_KEY)) |
| .setOutput(Output.newBuilder().setConsumed(true).setPreedit(preedit)) |
| .build(); |
| |
| resetAll(); |
| expect(inputConnection.beginBatchEdit()).andReturn(true); |
| Capture<SpannableStringBuilder> spannableStringBuilderCapture = |
| new Capture<SpannableStringBuilder>(); |
| expect(inputConnection.setComposingText( |
| capture(spannableStringBuilderCapture), eq(MozcUtil.CURSOR_POSITION_TAIL))) |
| .andReturn(true); |
| expect(selectionTracker.getPreeditStartPosition()).andReturn(0); |
| expect(inputConnection.setSelection(2, 2)).andReturn(true); |
| selectionTracker.onRender(null, null, preedit); |
| expect(inputConnection.endBatchEdit()).andReturn(true); |
| replayAll(); |
| |
| service.renderInputConnection(command, KeycodeConverter.getKeyEventInterface('\0')); |
| |
| verifyAll(); |
| SpannableStringBuilder sb = spannableStringBuilderCapture.getValue(); |
| assertEquals("012", sb.toString()); |
| assertEquals(0, sb.getSpanStart(MozcService.SPAN_UNDERLINE)); |
| assertEquals(3, sb.getSpanEnd(MozcService.SPAN_UNDERLINE)); |
| assertEquals(0, sb.getSpanStart(MozcService.SPAN_BEFORE_CURSOR)); |
| assertEquals(2, sb.getSpanEnd(MozcService.SPAN_BEFORE_CURSOR)); |
| } |
| |
| @SmallTest |
| public void testRenderInputConnection_webView() { |
| // Updating preedit (cursor is at the middle) |
| SessionExecutor sessionExecutor = createNiceMock(SessionExecutor.class); |
| SelectionTracker selectionTracker = createMock(SelectionTracker.class); |
| MozcService service = createInitializedService(sessionExecutor); |
| InputConnection inputConnection = createStrictMock(InputConnection.class); |
| EditorInfo editorInfo = new EditorInfo(); |
| editorInfo.inputType = InputType.TYPE_TEXT_VARIATION_WEB_EDIT_TEXT; |
| service.onCreateInputMethodInterface().restartInput(inputConnection, editorInfo); |
| |
| Preedit preedit = Preedit.newBuilder() |
| .addSegment(Segment.newBuilder() |
| .setValue("0") |
| .setAnnotation(Annotation.UNDERLINE) |
| .setValueLength(1)) |
| .addSegment(Segment.newBuilder() |
| .setValue("1") |
| .setAnnotation(Annotation.UNDERLINE) |
| .setValueLength(1)) |
| .addSegment(Segment.newBuilder() |
| .setValue("2") |
| .setAnnotation(Annotation.UNDERLINE) |
| .setValueLength(1)) |
| .setCursor(2) |
| .build(); |
| |
| resetAll(); |
| expect(inputConnection.beginBatchEdit()).andReturn(true); |
| Capture<SpannableStringBuilder> spannableStringBuilderCapture = |
| new Capture<SpannableStringBuilder>(); |
| expect(inputConnection.setComposingText( |
| capture(spannableStringBuilderCapture), eq(MozcUtil.CURSOR_POSITION_TAIL))) |
| .andReturn(true); |
| expect(selectionTracker.getPreeditStartPosition()).andReturn(0); |
| expect(inputConnection.setSelection(2, 2)).andReturn(true); |
| selectionTracker.onRender(null, null, preedit); |
| expect(inputConnection.endBatchEdit()).andReturn(true); |
| replayAll(); |
| |
| service.selectionTracker = selectionTracker; |
| |
| Command command = Command.newBuilder() |
| .setInput(Input.newBuilder().setType(Input.CommandType.SEND_KEY)) |
| .setOutput(Output.newBuilder().setConsumed(true).setPreedit(preedit)) |
| .build(); |
| service.renderInputConnection(command, KeycodeConverter.getKeyEventInterface('\0')); |
| |
| verifyAll(); |
| SpannableStringBuilder sb = spannableStringBuilderCapture.getValue(); |
| assertEquals("012", sb.toString()); |
| assertEquals(0, sb.getSpanStart(MozcService.SPAN_UNDERLINE)); |
| assertEquals(3, sb.getSpanEnd(MozcService.SPAN_UNDERLINE)); |
| assertEquals(0, sb.getSpanStart(MozcService.SPAN_BEFORE_CURSOR)); |
| assertEquals(2, sb.getSpanEnd(MozcService.SPAN_BEFORE_CURSOR)); |
| } |
| |
| @SmallTest |
| public void testRenderInputConnection_commit() { |
| SessionExecutor sessionExecutor = createNiceMock(SessionExecutor.class); |
| MozcService service = createInitializedService(sessionExecutor); |
| SelectionTracker selectionTracker = createMock(SelectionTracker.class); |
| service.selectionTracker = selectionTracker; |
| |
| InputConnection inputConnection = createStrictMock(InputConnection.class); |
| service.onCreateInputMethodInterface().restartInput(inputConnection, new EditorInfo()); |
| |
| Preedit preedit = Preedit.newBuilder() |
| .addSegment(Segment.newBuilder() |
| .setValue("0") |
| .setAnnotation(Annotation.UNDERLINE) |
| .setValueLength(1)) |
| .addSegment(Segment.newBuilder() |
| .setValue("1") |
| .setAnnotation(Annotation.UNDERLINE) |
| .setValueLength(1)) |
| .addSegment(Segment.newBuilder() |
| .setValue("2") |
| .setAnnotation(Annotation.HIGHLIGHT) |
| .setValueLength(1)) |
| .setCursor(3) |
| .build(); |
| Command command = Command.newBuilder() |
| .setInput(Input.newBuilder().setType(Input.CommandType.SEND_KEY)) |
| .setOutput(Output.newBuilder() |
| .setConsumed(true) |
| .setResult(Result.newBuilder().setValue("commit").setType(ResultType.STRING)) |
| .setPreedit(preedit) |
| .setAllCandidateWords(CandidateList.newBuilder().setCategory(Category.CONVERSION))) |
| .build(); |
| |
| resetAll(); |
| expect(inputConnection.beginBatchEdit()).andReturn(true); |
| expect(inputConnection.commitText("commit", MozcUtil.CURSOR_POSITION_TAIL)).andReturn(true); |
| Capture<SpannableStringBuilder> spannableStringBuilderCapture = |
| new Capture<SpannableStringBuilder>(); |
| expect(inputConnection.setComposingText( |
| capture(spannableStringBuilderCapture), eq(MozcUtil.CURSOR_POSITION_TAIL))) |
| .andReturn(true); |
| selectionTracker.onRender(null, "commit", preedit); |
| expect(inputConnection.endBatchEdit()).andReturn(true); |
| replayAll(); |
| |
| service.renderInputConnection(command, KeycodeConverter.getKeyEventInterface('\0')); |
| |
| verifyAll(); |
| SpannableStringBuilder sb = spannableStringBuilderCapture.getValue(); |
| assertEquals("012", sb.toString()); |
| assertEquals(0, sb.getSpanStart(MozcService.SPAN_UNDERLINE)); |
| assertEquals(3, sb.getSpanEnd(MozcService.SPAN_UNDERLINE)); |
| assertEquals(2, sb.getSpanStart(MozcService.SPAN_CONVERT_HIGHLIGHT)); |
| assertEquals(3, sb.getSpanEnd(MozcService.SPAN_CONVERT_HIGHLIGHT)); |
| } |
| |
| @SmallTest |
| public void testRenderInputConnection_directInput() { |
| // If mozc service doesn't consume the KeyEvent, delegate it to the sendKeyEvent. |
| MozcService service = createMockBuilder(MozcService.class) |
| .addMockedMethods("sendKeyEvent") |
| .createMock(); |
| SessionExecutor sessionExecutor = createNiceMock(SessionExecutor.class); |
| initializeMozcService(service, sessionExecutor); |
| InputConnection inputConnection = createStrictMock(InputConnection.class); |
| service.onCreateInputMethodInterface().restartInput(inputConnection, new EditorInfo()); |
| |
| KeyEventInterface keyEvent = KeycodeConverter.getKeyEventInterface(KeyEvent.KEYCODE_A); |
| resetAll(); |
| service.sendKeyEvent(same(keyEvent)); |
| replayAll(); |
| |
| Command command = Command.newBuilder() |
| .setInput(Input.newBuilder().setType(Input.CommandType.SEND_KEY)) |
| .setOutput(Output.newBuilder().setConsumed(false)) |
| .build(); |
| |
| service.renderInputConnection(command, keyEvent); |
| |
| verifyAll(); |
| } |
| |
| /** |
| * If the output is for SWITCH_INPUT_MODE, composing text of InputConnection |
| * must not be updated by setComposingText. |
| */ |
| @SmallTest |
| public void testRenderInputConnection_switchInputMode() { |
| MozcService service = createInitializedService(createNiceMock(SessionExecutor.class)); |
| InputConnection inputConnection = |
| new InputConnectionWrapper(createNiceMock(InputConnection.class), false) { |
| @Override |
| public boolean setComposingText(CharSequence text, int newCursorPosition) { |
| fail("setComposingText shouldn't be called for SWITCH_INPUT_MODE"); |
| return true; |
| } |
| }; |
| replayAll(); |
| |
| service.onCreateInputMethodInterface().restartInput(inputConnection, new EditorInfo()); |
| |
| Command command = Command.newBuilder() |
| .setInput(Input.newBuilder() |
| .setType(Input.CommandType.SEND_COMMAND) |
| .setCommand(SessionCommand.newBuilder() |
| .setType(SessionCommand.CommandType.SWITCH_INPUT_MODE))) |
| .setOutput(Output.newBuilder().setConsumed(true)) |
| .build(); |
| |
| service.renderInputConnection(command, null); |
| } |
| |
| @SmallTest |
| public void testSendKeyEvent() { |
| MozcService service = createMockBuilder(MozcService.class) |
| .addMockedMethods("requestHideSelf", "sendDownUpKeyEvents", "isInputViewShown", |
| "sendDefaultEditorAction", "getCurrentInputConnection") |
| .createMock(); |
| ViewEventListener eventListener = createNiceMock(ViewEventListener.class); |
| ViewManager viewManager = |
| createViewManagerMock(getInstrumentation().getTargetContext(), eventListener); |
| service.viewManager = viewManager; |
| |
| // If the given KeyEvent is null, do nothing. |
| resetAll(); |
| replayAll(); |
| |
| service.sendKeyEvent(null); |
| |
| verifyAll(); |
| |
| // If the keyEvent is other than back key or enter key, |
| // just forward it to the connected application. |
| resetAll(); |
| expect(service.getCurrentInputConnection()).andStubReturn(createMock(InputConnection.class)); |
| service.sendDownUpKeyEvents(KeyEvent.KEYCODE_A); |
| replayAll(); |
| |
| service.sendKeyEvent(KeycodeConverter.getKeyEventInterface(KeyEvent.KEYCODE_A)); |
| |
| verifyAll(); |
| |
| // For meta keys, captured once. |
| { |
| resetAll(); |
| InputConnection inputConnection = createMock(InputConnection.class); |
| expect(service.getCurrentInputConnection()).andStubReturn(inputConnection); |
| Capture<KeyEvent> captureKeyEvent = new Capture<KeyEvent>(); |
| expect(inputConnection.sendKeyEvent(capture(captureKeyEvent))).andReturn(true); |
| KeyEvent keyEvent = new KeyEvent( |
| MozcUtil.getUptimeMillis(), |
| MozcUtil.getUptimeMillis(), |
| KeyEvent.ACTION_UP, |
| KeyEvent.KEYCODE_SHIFT_LEFT, |
| 3, |
| KeyEvent.META_SHIFT_ON, |
| 5, |
| 0xff, |
| KeyEvent.FLAG_LONG_PRESS); |
| replayAll(); |
| service.sendKeyEvent(KeycodeConverter.getKeyEventInterface(keyEvent)); |
| verifyAll(); |
| KeyEvent captured = captureKeyEvent.getValue(); |
| assertEquals(keyEvent.getDownTime(), captured.getDownTime()); |
| // Sent KeyEvent should have event time delayed to original KeyEvent. |
| assertTrue(keyEvent.getEventTime() <= captured.getEventTime()); |
| assertEquals(keyEvent.getAction(), captured.getAction()); |
| assertEquals(keyEvent.getKeyCode(), captured.getKeyCode()); |
| assertEquals(keyEvent.getRepeatCount(), captured.getRepeatCount()); |
| assertEquals(keyEvent.getMetaState(), captured.getMetaState()); |
| assertEquals(keyEvent.getDeviceId(), captured.getDeviceId()); |
| assertEquals(keyEvent.getScanCode(), captured.getScanCode()); |
| assertEquals(keyEvent.getFlags(), captured.getFlags()); |
| } |
| |
| // For other keys, captured twice. |
| { |
| resetAll(); |
| InputConnection inputConnection = createStrictMock(InputConnection.class); |
| expect(service.getCurrentInputConnection()).andStubReturn(inputConnection); |
| Capture<KeyEvent> captureKeyEventDown = new Capture<KeyEvent>(); |
| Capture<KeyEvent> captureKeyEventUp = new Capture<KeyEvent>(); |
| expect(inputConnection.sendKeyEvent(capture(captureKeyEventDown))).andReturn(true); |
| expect(inputConnection.sendKeyEvent(capture(captureKeyEventUp))).andReturn(true); |
| KeyEvent keyEvent = new KeyEvent( |
| MozcUtil.getUptimeMillis(), |
| MozcUtil.getUptimeMillis(), |
| KeyEvent.ACTION_UP, |
| KeyEvent.KEYCODE_A, |
| 3, |
| KeyEvent.META_SHIFT_ON, |
| 5, |
| 0xff, |
| KeyEvent.FLAG_LONG_PRESS); |
| replayAll(); |
| service.sendKeyEvent(KeycodeConverter.getKeyEventInterface(keyEvent)); |
| verifyAll(); |
| KeyEvent capturedDown = captureKeyEventDown.getValue(); |
| assertEquals(keyEvent.getDownTime(), capturedDown.getDownTime()); |
| // Sent KeyEvent should have event time delayed to original KeyEvent. |
| assertTrue(keyEvent.getEventTime() <= capturedDown.getEventTime()); |
| assertEquals(KeyEvent.ACTION_DOWN, capturedDown.getAction()); |
| assertEquals(keyEvent.getKeyCode(), capturedDown.getKeyCode()); |
| assertEquals(0, capturedDown.getRepeatCount()); |
| assertEquals(keyEvent.getMetaState(), capturedDown.getMetaState()); |
| assertEquals(keyEvent.getDeviceId(), capturedDown.getDeviceId()); |
| assertEquals(keyEvent.getScanCode(), capturedDown.getScanCode()); |
| assertEquals(keyEvent.getFlags(), capturedDown.getFlags()); |
| KeyEvent capturedUp = captureKeyEventUp.getValue(); |
| assertEquals(keyEvent.getDownTime(), capturedUp.getDownTime()); |
| // Sent KeyEvent should have event time delayed to original KeyEvent. |
| assertTrue(keyEvent.getEventTime() <= capturedUp.getEventTime()); |
| assertEquals(KeyEvent.ACTION_UP, capturedUp.getAction()); |
| assertEquals(keyEvent.getKeyCode(), capturedUp.getKeyCode()); |
| assertEquals(0, capturedUp.getRepeatCount()); |
| assertEquals(keyEvent.getMetaState(), capturedUp.getMetaState()); |
| assertEquals(keyEvent.getDeviceId(), capturedUp.getDeviceId()); |
| assertEquals(keyEvent.getScanCode(), capturedUp.getScanCode()); |
| assertEquals(keyEvent.getFlags(), capturedUp.getFlags()); |
| } |
| } |
| |
| @SmallTest |
| public void testSendKeyEventBack() { |
| MozcService service = createMockBuilder(MozcService.class) |
| .addMockedMethods("requestHideSelf", "sendDownUpKeyEvents", "isInputViewShown", |
| "sendDefaultEditorAction") |
| .createMock(); |
| ViewEventListener eventListener = createNiceMock(ViewEventListener.class); |
| ViewManager viewManager = |
| createViewManagerMock(getInstrumentation().getTargetContext(), eventListener); |
| service.viewManager = viewManager; |
| |
| KeyEventInterface[] keyEventList = { |
| // Software key event. |
| KeycodeConverter.getKeyEventInterface(KeyEvent.KEYCODE_BACK), |
| // Hardware key event. |
| KeycodeConverter.getKeyEventInterface(new KeyEvent(KeyEvent.ACTION_DOWN, |
| KeyEvent.KEYCODE_BACK)), |
| }; |
| |
| for (KeyEventInterface keyEvent : keyEventList) { |
| // Test for back key. |
| // If the input view is not shown, don't handle inside the mozc, and pass it through to the |
| // connected application. |
| resetAll(); |
| expect(service.isInputViewShown()).andReturn(false); |
| service.sendDownUpKeyEvents(KeyEvent.KEYCODE_BACK); |
| replayAll(); |
| |
| service.sendKeyEvent(keyEvent); |
| |
| verifyAll(); |
| |
| // If SymbolInputView is shown, close it and do not pass the event |
| // to the client application. |
| resetAll(); |
| expect(service.isInputViewShown()).andReturn(true); |
| expect(viewManager.hideSubInputView()).andReturn(true); |
| replayAll(); |
| |
| service.sendKeyEvent(keyEvent); |
| |
| verifyAll(); |
| |
| // If the main view (software keyboard) is shown, close it and do nto pass the event to the |
| // client application. |
| resetAll(); |
| expect(service.isInputViewShown()).andReturn(true); |
| expect(viewManager.hideSubInputView()).andReturn(false); |
| service.requestHideSelf(0); |
| replayAll(); |
| |
| service.sendKeyEvent(keyEvent); |
| |
| verifyAll(); |
| } |
| } |
| |
| @SmallTest |
| public void testSendKeyEventEnter() { |
| MozcService service = createMockBuilder(MozcService.class) |
| .addMockedMethods("requestHideSelf", "sendDownUpKeyEvents", "isInputViewShown", |
| "sendDefaultEditorAction", "getCurrentInputEditorInfo") |
| .createMock(); |
| ViewEventListener eventListener = createNiceMock(ViewEventListener.class); |
| ViewManager viewManager = |
| createViewManagerMock(getInstrumentation().getTargetContext(), eventListener); |
| service.viewManager = viewManager; |
| |
| KeyEventInterface[] keyEventList = { |
| // Software key event. |
| KeycodeConverter.getKeyEventInterface(KeyEvent.KEYCODE_ENTER), |
| // Hardware key event. |
| KeycodeConverter.getKeyEventInterface(new KeyEvent(KeyEvent.ACTION_DOWN, |
| KeyEvent.KEYCODE_ENTER)), |
| }; |
| |
| for (KeyEventInterface keyEvent : keyEventList) { |
| // Test for enter key. |
| // If the input view is not shown, don't handle inside the mozc. |
| resetAll(); |
| expect(service.isInputViewShown()).andReturn(false); |
| service.sendDownUpKeyEvents(KeyEvent.KEYCODE_ENTER); |
| replayAll(); |
| |
| service.sendKeyEvent(keyEvent); |
| |
| verifyAll(); |
| |
| // If the not-consumed key event is enter, fallback to default editor action. |
| resetAll(); |
| expect(service.isInputViewShown()).andReturn(true); |
| expect(service.sendDefaultEditorAction(true)).andReturn(true); |
| expect(service.getCurrentInputEditorInfo()).andStubReturn(new EditorInfo()); |
| replayAll(); |
| |
| service.sendKeyEvent(keyEvent); |
| |
| verifyAll(); |
| } |
| } |
| |
| @SmallTest |
| public void testRenderInputConnection_directInputDeletionRange() { |
| SessionExecutor sessionExecutor = createNiceMock(SessionExecutor.class); |
| MozcService service = createInitializedService(sessionExecutor); |
| SelectionTracker selectionTracker = createMock(SelectionTracker.class); |
| service.selectionTracker = selectionTracker; |
| InputConnection inputConnection = createStrictMock(InputConnection.class); |
| service.onCreateInputMethodInterface().restartInput(inputConnection, new EditorInfo()); |
| |
| class TestData extends Parameter { |
| final DeletionRange deletionRange; |
| final Pair<Integer, Integer> expectedRange; |
| |
| TestData(DeletionRange deletionRange, Pair<Integer, Integer> expectedRange) { |
| this.deletionRange = deletionRange; |
| this.expectedRange = expectedRange; |
| } |
| } |
| |
| TestData [] testDataList = { |
| new TestData( |
| DeletionRange.newBuilder().setOffset(-3).setLength(3).build(), Pair.create(3, 0)), |
| new TestData( |
| DeletionRange.newBuilder().setOffset(0).setLength(3).build(), Pair.create(0, 3)), |
| new TestData( |
| DeletionRange.newBuilder().setOffset(-3).setLength(6).build(), Pair.create(3, 3)), |
| new TestData( |
| DeletionRange.newBuilder().setOffset(0).setLength(0).build(), Pair.create(0, 0)), |
| new TestData(DeletionRange.newBuilder().setOffset(-3).setLength(2).build(), null), |
| new TestData(DeletionRange.newBuilder().setOffset(1).setLength(2).build(), null), |
| }; |
| |
| for (TestData testData : testDataList) { |
| resetAll(); |
| expect(inputConnection.beginBatchEdit()).andReturn(true); |
| if (testData.expectedRange != null) { |
| expect(inputConnection.deleteSurroundingText( |
| testData.expectedRange.first, testData.expectedRange.second)) |
| .andReturn(true); |
| } |
| Capture<CharSequence> composingTextCapture = new Capture<CharSequence>(); |
| expect(inputConnection.setComposingText(capture(composingTextCapture), eq(0))) |
| .andReturn(true); |
| selectionTracker.onRender(testData.deletionRange, null, null); |
| expect(inputConnection.endBatchEdit()).andReturn(true); |
| replayAll(); |
| |
| Command command = Command.newBuilder() |
| .setInput(Input.newBuilder().setType(Input.CommandType.SEND_KEY)) |
| .setOutput(Output.newBuilder() |
| .setConsumed(true) |
| .setDeletionRange(testData.deletionRange)) |
| .build(); |
| service.renderInputConnection(command, null); |
| |
| verifyAll(); |
| assertEquals("", composingTextCapture.getValue().toString()); |
| } |
| } |
| |
| @SmallTest |
| public void testSwitchKeyboard() throws SecurityException, IllegalArgumentException { |
| // Prepare the service. |
| SessionExecutor sessionExecutor = createMock(SessionExecutor.class); |
| MozcService service = createInitializedService(sessionExecutor); |
| |
| // Prepares the mock. |
| resetAll(); |
| List<TouchEvent> touchEventList = Collections.singletonList(TouchEvent.getDefaultInstance()); |
| KeyboardSpecification newSpecification = KeyboardSpecification.QWERTY_KANA; |
| |
| sessionExecutor.updateRequest( |
| MozcUtil.getRequestBuilder( |
| service.getResources(), newSpecification, getDefaultDeviceConfiguration()).build(), |
| touchEventList); |
| sessionExecutor.switchInputMode(same(Optional.<KeyEventInterface>absent()), |
| eq(newSpecification.getCompositionMode()), |
| anyObject(EvaluationCallback.class)); |
| |
| replayAll(); |
| |
| // Get the event listener and execute. |
| ViewEventListener listener = service.new MozcEventListener(); |
| |
| { |
| // To stabilize the unittest, overwrite orientation and write back once after the invocation. |
| Configuration backup = new Configuration(service.getResources().getConfiguration()); |
| try { |
| service.getResources().getConfiguration().orientation = Configuration.ORIENTATION_PORTRAIT; |
| listener.onKeyEvent(null, null, newSpecification, touchEventList); |
| } finally { |
| service.getResources().getConfiguration().updateFrom(backup); |
| assertEquals(backup, service.getResources().getConfiguration()); |
| } |
| } |
| |
| // Verify. |
| assertEquals(newSpecification, service.currentKeyboardSpecification); |
| verifyAll(); |
| } |
| |
| @SmallTest |
| // UiThreadTest annotation to handle callback of shared preference. |
| @UiThreadTest |
| public void testPreferenceInitialization() { |
| MozcService service = createService(); |
| SharedPreferences sharedPreferences = getSharedPreferences(); |
| sharedPreferences.edit() |
| .putString("pref_portrait_keyboard_layout_key", KeyboardLayout.QWERTY.name()) |
| .commit(); |
| |
| SessionExecutor sessionExecutor = createMock(SessionExecutor.class); |
| sessionExecutor.reset(isA(SessionHandlerFactory.class), same(service)); |
| sessionExecutor.setLogging(anyBoolean()); |
| sessionExecutor.setImposedConfig(isA(Config.class)); |
| sessionExecutor.updateRequest(isA(Request.class), eq(Collections.<TouchEvent>emptyList())); |
| expectLastCall().asStub(); |
| sessionExecutor.switchInputMode(same(Optional.<KeyEventInterface>absent()), |
| isA(CompositionMode.class), |
| anyObject(EvaluationCallback.class)); |
| expectLastCall().asStub(); |
| sessionExecutor.setConfig(isA(Config.class)); |
| sessionExecutor.preferenceUsageStatsEvent( |
| sharedPreferences, getInstrumentation().getTargetContext().getResources()); |
| replayAll(); |
| |
| invokeOnCreateInternal(service, null, sharedPreferences, getDefaultDeviceConfiguration(), |
| sessionExecutor); |
| |
| |
| verifyAll(); |
| |
| // Make sure the UI is initialized expectedly. |
| assertEquals(KeyboardLayout.QWERTY, |
| service.viewManager.getActiveSoftwareKeyboardModel().getKeyboardLayout()); |
| } |
| |
| @SmallTest |
| public void testSymbolHistoryStorageImpl_getAllHistory() { |
| SessionExecutor sessionExecutor = createMock(SessionExecutor.class); |
| SymbolHistoryStorage storage = new SymbolHistoryStorageImpl(sessionExecutor); |
| |
| class TestData extends Parameter { |
| final SymbolMajorCategory majorCategory; |
| final StorageType expectedStorageType; |
| final String expectedValue; |
| |
| TestData(SymbolMajorCategory majorCategory, StorageType expectedStorageType, |
| String expectedValue) { |
| this.majorCategory = majorCategory; |
| this.expectedStorageType = expectedStorageType; |
| this.expectedValue = expectedValue; |
| } |
| } |
| |
| TestData[] testDataList = { |
| new TestData(SymbolMajorCategory.SYMBOL, StorageType.SYMBOL_HISTORY, "SYMBOL_HISTORY"), |
| new TestData(SymbolMajorCategory.EMOTICON, StorageType.EMOTICON_HISTORY, |
| "EMOTICON_HISTORY"), |
| new TestData(SymbolMajorCategory.EMOJI, StorageType.EMOJI_HISTORY, "EMOJI_HISTORY"), |
| }; |
| |
| for (TestData testData : testDataList) { |
| resetAll(); |
| expect(sessionExecutor.readAllFromStorage(testData.expectedStorageType)) |
| .andReturn(Collections.singletonList(ByteString.copyFromUtf8(testData.expectedValue))); |
| replayAll(); |
| |
| assertEquals(testData.toString(), |
| Collections.singletonList(testData.expectedValue), |
| storage.getAllHistory(testData.majorCategory)); |
| |
| verifyAll(); |
| } |
| } |
| |
| @SmallTest |
| public void testSymbolHistoryStorageImpl_addHistory() { |
| SessionExecutor sessionExecutor = createMock(SessionExecutor.class); |
| SymbolHistoryStorage storage = new SymbolHistoryStorageImpl(sessionExecutor); |
| |
| class TestData extends Parameter { |
| final SymbolMajorCategory majorCategory; |
| final StorageType expectedStorageType; |
| final String expectedValue; |
| |
| TestData(SymbolMajorCategory majorCategory, StorageType expectedStorageType, |
| String expectedValue) { |
| this.majorCategory = majorCategory; |
| this.expectedStorageType = expectedStorageType; |
| this.expectedValue = expectedValue; |
| } |
| } |
| |
| TestData[] testDataList = { |
| new TestData(SymbolMajorCategory.SYMBOL, StorageType.SYMBOL_HISTORY, "SYMBOL_HISTORY"), |
| new TestData(SymbolMajorCategory.EMOTICON, StorageType.EMOTICON_HISTORY, |
| "EMOTICON_HISTORY"), |
| new TestData(SymbolMajorCategory.EMOJI, StorageType.EMOJI_HISTORY, "EMOJI_HISTORY"), |
| }; |
| |
| for (TestData testData : testDataList) { |
| resetAll(); |
| sessionExecutor.insertToStorage( |
| testData.expectedStorageType, |
| testData.expectedValue, |
| Collections.singletonList(ByteString.copyFromUtf8(testData.expectedValue))); |
| replayAll(); |
| |
| storage.addHistory(testData.majorCategory, testData.expectedValue); |
| |
| verifyAll(); |
| } |
| } |
| |
| @SmallTest |
| public void testMozcEventListener_onConversionCandidateSelected() { |
| SessionExecutor sessionExecutor = createMock(SessionExecutor.class); |
| MozcService service = createInitializedService(sessionExecutor); |
| |
| // Set feedback listener and force to enable feedbacks. |
| resetAll(); |
| FeedbackListener feedbackListener = createMock(FeedbackListener.class); |
| FeedbackManager feedbackManager = new FeedbackManager(feedbackListener); |
| feedbackManager.setHapticFeedbackEnabled(true); |
| feedbackManager.setHapticFeedbackDuration(100); |
| feedbackManager.setSoundFeedbackEnabled(true); |
| feedbackManager.setSoundFeedbackVolume(0.5f); |
| service.feedbackManager = feedbackManager; |
| |
| // Expectation. |
| feedbackListener.onSound(AudioManager.FX_KEY_CLICK, 0.5f); |
| feedbackListener.onVibrate(100L); |
| |
| sessionExecutor.submitCandidate(eq(0), eq(Optional.<Integer>absent()), |
| isA(EvaluationCallback.class)); |
| replayAll(); |
| |
| // Invoke onConversionCandidateSelected. |
| service.viewManager.getEventListener() |
| .onConversionCandidateSelected(0, Optional.<Integer>absent()); |
| |
| verifyAll(); |
| } |
| |
| @SmallTest |
| public void testMozcEventListener_onSymbolCandidateSelected() { |
| SessionExecutor sessionExecutor = createMock(SessionExecutor.class); |
| MozcService service = createInitializedService(sessionExecutor); |
| InputConnection inputConnection = createStrictMock(InputConnection.class); |
| service.onCreateInputMethodInterface().restartInput(inputConnection, new EditorInfo()); |
| |
| // Set feedback listener and force to enable feedbacks. |
| resetAll(); |
| FeedbackListener feedbackListener = createMock(FeedbackListener.class); |
| FeedbackManager feedbackManager = new FeedbackManager(feedbackListener); |
| feedbackManager.setHapticFeedbackEnabled(true); |
| feedbackManager.setHapticFeedbackDuration(100); |
| feedbackManager.setSoundFeedbackEnabled(true); |
| feedbackManager.setSoundFeedbackVolume(0.5f); |
| service.feedbackManager = feedbackManager; |
| |
| // Expectation. |
| expect(inputConnection.beginBatchEdit()).andReturn(true); |
| expect(inputConnection.commitText("(^_^)", MozcUtil.CURSOR_POSITION_TAIL)) |
| .andReturn(true); |
| expect(inputConnection.endBatchEdit()).andReturn(true); |
| |
| feedbackListener.onSound(AudioManager.FX_KEY_CLICK, 0.5f); |
| feedbackListener.onVibrate(100); |
| |
| SymbolHistoryStorage historyStorage = createMock(SymbolHistoryStorage.class); |
| historyStorage.addHistory(SymbolMajorCategory.EMOTICON, "(^_^)"); |
| service.symbolHistoryStorage = historyStorage; |
| replayAll(); |
| |
| // Invoke onConversionCandidateSelected. |
| service.viewManager.getEventListener().onSymbolCandidateSelected( |
| SymbolMajorCategory.EMOTICON, "(^_^)", true); |
| |
| verifyAll(); |
| } |
| |
| @SuppressWarnings("unchecked") |
| @SmallTest |
| public void testMozcEventListener_onNarrowModeChanged() { |
| SessionExecutor sessionExecutorMock = createMock(SessionExecutor.class); |
| MozcService service = initializeMozcService(new MozcService(), sessionExecutorMock); |
| ViewManagerInterface viewManagerMock = createMock(ViewManagerInterface.class); |
| service.viewManager = viewManagerMock; |
| ViewEventListener viewEventListener = service.new MozcEventListener(); |
| |
| Capture<Config> configCapture = new Capture<Config>(); |
| |
| // On transition from narrow mode to non-narrow mode, submit() should be called. |
| resetAll(); |
| expect(viewManagerMock.isNarrowMode()).andStubReturn(false); |
| expect(viewManagerMock.isFloatingCandidateMode()).andStubReturn(false); |
| sessionExecutorMock.submit(same(service.renderResultCallback)); |
| sessionExecutorMock.setImposedConfig(capture(configCapture)); |
| replayAll(); |
| viewEventListener.onNarrowModeChanged(false); |
| verifyAll(); |
| assertEquals(SelectionShortcut.NO_SHORTCUT, |
| configCapture.getValue().getSelectionShortcut()); |
| |
| // On the opposite transition, submit() should not be called. |
| resetAll(); |
| expect(viewManagerMock.isNarrowMode()).andStubReturn(true); |
| expect(viewManagerMock.isFloatingCandidateMode()).andStubReturn(true); |
| sessionExecutorMock.setImposedConfig(capture(configCapture)); |
| replayAll(); |
| viewEventListener.onNarrowModeChanged(true); |
| verifyAll(); |
| assertEquals(SelectionShortcut.SHORTCUT_123456789, |
| configCapture.getValue().getSelectionShortcut()); |
| |
| // Don't use shortcut keys if floating candidate window is disabled. |
| resetAll(); |
| expect(viewManagerMock.isNarrowMode()).andStubReturn(true); |
| expect(viewManagerMock.isFloatingCandidateMode()).andStubReturn(false); |
| sessionExecutorMock.setImposedConfig(capture(configCapture)); |
| replayAll(); |
| viewEventListener.onNarrowModeChanged(true); |
| verifyAll(); |
| assertEquals(SelectionShortcut.NO_SHORTCUT, |
| configCapture.getValue().getSelectionShortcut()); |
| } |
| |
| @SmallTest |
| public void testOnUndo() { |
| SessionExecutor sessionExecutor = createMock(SessionExecutor.class); |
| MozcService service = createInitializedService(sessionExecutor); |
| |
| resetAll(); |
| List<TouchEvent> touchEventList = Collections.singletonList(TouchEvent.getDefaultInstance()); |
| sessionExecutor.undoOrRewind(eq(touchEventList), isA(EvaluationCallback.class)); |
| replayAll(); |
| |
| service.viewManager.getEventListener().onUndo(touchEventList); |
| |
| verifyAll(); |
| } |
| |
| @SmallTest |
| public void testOnSubmitPreedit() { |
| SessionExecutor sessionExecutor = createMock(SessionExecutor.class); |
| MozcService service = createInitializedService(sessionExecutor); |
| |
| resetAll(); |
| sessionExecutor.submit(isA(EvaluationCallback.class)); |
| replayAll(); |
| |
| // Call the method. |
| service.viewManager.getEventListener().onSubmitPreedit(); |
| |
| verifyAll(); |
| } |
| |
| @SmallTest |
| public void testOnExpandSuggestion() { |
| SessionExecutor sessionExecutor = createMock(SessionExecutor.class); |
| MozcService service = createInitializedService(sessionExecutor); |
| |
| resetAll(); |
| sessionExecutor.expandSuggestion(isA(EvaluationCallback.class)); |
| replayAll(); |
| |
| service.viewManager.getEventListener().onExpandSuggestion(); |
| |
| verifyAll(); |
| } |
| |
| @SmallTest |
| public void testPropagateClientSidePreference_touchUI() { |
| Resources resoureces = getInstrumentation().getTargetContext().getResources(); |
| SessionExecutor sessionExecutor = createNiceMock(SessionExecutor.class); |
| replayAll(); |
| MozcService service = createInitializedService(sessionExecutor); |
| |
| ViewManagerInterface viewManager = service.viewManager; |
| |
| // Initialization (all fields are propagated). |
| { |
| ClientSidePreference clientSidePreference = |
| new ClientSidePreference( |
| true, 100, true, 100, false, KeyboardLayout.QWERTY, InputStyle.TOGGLE, false, false, |
| 0, EmojiProviderType.DOCOMO, HardwareKeyMap.JAPANESE109A, SkinType.ORANGE_LIGHTGRAY, |
| true, LayoutAdjustment.FILL, 100); |
| service.propagateClientSidePreference(clientSidePreference); |
| |
| // Check all the fields are propagated. |
| FeedbackManager feedbackManager = service.feedbackManager; |
| assertTrue(feedbackManager.isHapticFeedbackEnabled()); |
| assertEquals(feedbackManager.getHapticFeedbackDuration(), 100L); |
| assertTrue(feedbackManager.isSoundFeedbackEnabled()); |
| assertEquals(feedbackManager.getSoundFeedbackVolume(), 0.8f); |
| assertFalse(viewManager.isPopupEnabled()); |
| JapaneseSoftwareKeyboardModel model = viewManager.getActiveSoftwareKeyboardModel(); |
| assertEquals(KeyboardLayout.QWERTY, model.getKeyboardLayout()); |
| assertEquals(InputStyle.TOGGLE, model.getInputStyle()); |
| assertFalse(model.isQwertyLayoutForAlphabet()); |
| assertFalse(viewManager.isFullscreenMode()); |
| assertFalse(service.onEvaluateFullscreenMode()); |
| assertEquals(EmojiProviderType.DOCOMO, viewManager.getEmojiProviderType()); |
| assertEquals(HardwareKeyMap.JAPANESE109A, service.viewManager.getHardwareKeyMap()); |
| assertEquals(LayoutAdjustment.FILL, viewManager.getLayoutAdjustment()); |
| assertEquals(100, viewManager.getKeyboardHeightRatio()); |
| } |
| |
| // Test for partial update of each attribute. |
| class TestData extends Parameter { |
| final ClientSidePreference clientSidePreference; |
| final boolean expectHapticFeedback; |
| final long expectHapticFeedbackDuration; |
| final boolean expectSoundFeedback; |
| final float expectSoundFeedbackVolume; |
| final boolean expectPopupFeedback; |
| final KeyboardLayout expectKeyboardLayout; |
| final InputStyle expectInputStyle; |
| final boolean expectQwertyLayoutForAlphabet; |
| final boolean expectFullscreenMode; |
| final int expectFlickSensitivity; |
| final EmojiProviderType expectEmojiProviderType; |
| final HardwareKeyMap expectHardwareKeyMap; |
| final Skin expectSkin; |
| final boolean expectMicrophoneButtonEnabledByPreference; |
| final LayoutAdjustment expectLayoutAdjustment; |
| final int expectKeyboardHeight; |
| |
| TestData(ClientSidePreference clientSidePreference, |
| boolean expectHapticFeedback, long expectHapticFeedbackDuration, |
| boolean expectSoundFeedback, float expectSoundFeedbackVolume, |
| boolean expectPopupFeedback, |
| KeyboardLayout expectKeyboardLayout, |
| InputStyle expectInputStyle, |
| boolean expectQwertyLayoutForAlphabet, |
| boolean expectFullscreenMode, |
| int expectFlickSensitivity, |
| EmojiProviderType expectEmojiProviderType, |
| HardwareKeyMap expectHardwareKeyMap, |
| Skin expectSkin, |
| boolean expectMicrophoneButtonEnabledByPreference, |
| LayoutAdjustment expectLayoutAdjustment, |
| int expectKeyboardHeight) { |
| this.clientSidePreference = clientSidePreference; |
| this.expectHapticFeedback = expectHapticFeedback; |
| this.expectHapticFeedbackDuration = expectHapticFeedbackDuration; |
| this.expectSoundFeedback = expectSoundFeedback; |
| this.expectSoundFeedbackVolume = expectSoundFeedbackVolume; |
| this.expectPopupFeedback = expectPopupFeedback; |
| this.expectKeyboardLayout = expectKeyboardLayout; |
| this.expectInputStyle = expectInputStyle; |
| this.expectQwertyLayoutForAlphabet = expectQwertyLayoutForAlphabet; |
| this.expectFullscreenMode = expectFullscreenMode; |
| this.expectFlickSensitivity = expectFlickSensitivity; |
| this.expectEmojiProviderType = expectEmojiProviderType; |
| this.expectHardwareKeyMap = expectHardwareKeyMap; |
| this.expectSkin = expectSkin; |
| this.expectMicrophoneButtonEnabledByPreference = expectMicrophoneButtonEnabledByPreference; |
| this.expectLayoutAdjustment = expectLayoutAdjustment; |
| this.expectKeyboardHeight = expectKeyboardHeight; |
| } |
| } |
| // Note: following test case should tested in this order, as former test cases should affect to |
| // latter ones. |
| TestData[] testDataList = { |
| new TestData( |
| new ClientSidePreference( |
| false, 10, true, 10, true, KeyboardLayout.QWERTY, InputStyle.TOGGLE, false, false, |
| 0, EmojiProviderType.DOCOMO, HardwareKeyMap.DEFAULT, SkinType.ORANGE_LIGHTGRAY, |
| true, LayoutAdjustment.FILL, 100), |
| false, |
| 10, |
| true, |
| 0.08f, |
| true, |
| KeyboardLayout.QWERTY, |
| InputStyle.TOGGLE, |
| false, |
| false, |
| 0, |
| EmojiProviderType.DOCOMO, |
| HardwareKeyMap.DEFAULT, |
| SkinType.ORANGE_LIGHTGRAY.getSkin(resoureces), |
| true, |
| LayoutAdjustment.FILL, |
| 100), |
| new TestData( |
| new ClientSidePreference( |
| false, 20, true, 20, false, KeyboardLayout.QWERTY, InputStyle.TOGGLE, false, false, |
| -1, EmojiProviderType.DOCOMO, HardwareKeyMap.DEFAULT, SkinType.ORANGE_LIGHTGRAY, |
| false, LayoutAdjustment.FILL, 100), |
| false, |
| 20, |
| true, |
| 0.16f, |
| false, |
| KeyboardLayout.QWERTY, |
| InputStyle.TOGGLE, |
| false, |
| false, |
| -1, |
| EmojiProviderType.DOCOMO, |
| HardwareKeyMap.DEFAULT, |
| SkinType.ORANGE_LIGHTGRAY.getSkin(resoureces), |
| false, |
| LayoutAdjustment.FILL, |
| 100), |
| new TestData( |
| new ClientSidePreference( |
| false, 30, false, 30, true, KeyboardLayout.QWERTY, InputStyle.TOGGLE, false, false, |
| 2, EmojiProviderType.KDDI, HardwareKeyMap.DEFAULT, SkinType.ORANGE_LIGHTGRAY, |
| true, LayoutAdjustment.FILL, 100), |
| false, |
| 30, |
| false, |
| 0.24f, |
| true, |
| KeyboardLayout.QWERTY, |
| InputStyle.TOGGLE, |
| false, |
| false, |
| 2, |
| EmojiProviderType.KDDI, |
| HardwareKeyMap.DEFAULT, |
| SkinType.ORANGE_LIGHTGRAY.getSkin(resoureces), |
| true, |
| LayoutAdjustment.FILL, |
| 100), |
| new TestData( |
| new ClientSidePreference( |
| false, 40, false, 40, true, KeyboardLayout.TWELVE_KEYS, InputStyle.TOGGLE, |
| false, false, -3, EmojiProviderType.SOFTBANK, HardwareKeyMap.DEFAULT, |
| SkinType.ORANGE_LIGHTGRAY, true, LayoutAdjustment.FILL, 100), |
| false, |
| 40, |
| false, |
| 0.32f, |
| true, |
| KeyboardLayout.TWELVE_KEYS, |
| InputStyle.TOGGLE, |
| false, |
| false, |
| -3, |
| EmojiProviderType.SOFTBANK, |
| HardwareKeyMap.DEFAULT, |
| SkinType.ORANGE_LIGHTGRAY.getSkin(resoureces), |
| true, |
| LayoutAdjustment.FILL, |
| 100), |
| new TestData( |
| new ClientSidePreference( |
| false, 50, false, 50, true, KeyboardLayout.TWELVE_KEYS, InputStyle.FLICK, |
| false, false, 4, EmojiProviderType.DOCOMO, HardwareKeyMap.DEFAULT, |
| SkinType.ORANGE_LIGHTGRAY, false, LayoutAdjustment.FILL, 100), |
| false, |
| 50, |
| false, |
| 0.4f, |
| true, |
| KeyboardLayout.TWELVE_KEYS, |
| InputStyle.FLICK, |
| false, |
| false, |
| 4, |
| EmojiProviderType.DOCOMO, |
| HardwareKeyMap.DEFAULT, |
| SkinType.ORANGE_LIGHTGRAY.getSkin(resoureces), |
| false, |
| LayoutAdjustment.FILL, |
| 100), |
| new TestData( |
| new ClientSidePreference( |
| false, 60, false, 60, true, KeyboardLayout.TWELVE_KEYS, InputStyle.FLICK, |
| true, false, -5, EmojiProviderType.KDDI, HardwareKeyMap.DEFAULT, |
| SkinType.ORANGE_LIGHTGRAY, true, LayoutAdjustment.FILL, 100), |
| false, |
| 60, |
| false, |
| 0.48f, |
| true, |
| KeyboardLayout.TWELVE_KEYS, |
| InputStyle.FLICK, |
| true, |
| false, |
| -5, |
| EmojiProviderType.KDDI, |
| HardwareKeyMap.DEFAULT, |
| SkinType.ORANGE_LIGHTGRAY.getSkin(resoureces), |
| true, |
| LayoutAdjustment.FILL, |
| 100), |
| new TestData( |
| new ClientSidePreference( |
| false, 70, false, 70, true, KeyboardLayout.TWELVE_KEYS, InputStyle.FLICK, |
| true, true, 6, EmojiProviderType.SOFTBANK, HardwareKeyMap.DEFAULT, |
| SkinType.ORANGE_LIGHTGRAY, false, LayoutAdjustment.FILL, 100), |
| false, |
| 70, |
| false, |
| 0.56f, |
| true, |
| KeyboardLayout.TWELVE_KEYS, |
| InputStyle.FLICK, |
| true, |
| true, |
| 6, |
| EmojiProviderType.SOFTBANK, |
| HardwareKeyMap.DEFAULT, |
| SkinType.ORANGE_LIGHTGRAY.getSkin(resoureces), |
| false, |
| LayoutAdjustment.FILL, 100), |
| new TestData( |
| new ClientSidePreference( |
| false, 80, false, 80, true, KeyboardLayout.TWELVE_KEYS, InputStyle.FLICK, |
| true, true, -7, EmojiProviderType.SOFTBANK, HardwareKeyMap.JAPANESE109A, |
| SkinType.ORANGE_LIGHTGRAY, false, LayoutAdjustment.FILL, 100), |
| false, |
| 80, |
| false, |
| 0.64f, |
| true, |
| KeyboardLayout.TWELVE_KEYS, |
| InputStyle.FLICK, |
| true, |
| true, |
| -7, |
| EmojiProviderType.SOFTBANK, |
| HardwareKeyMap.JAPANESE109A, |
| SkinType.ORANGE_LIGHTGRAY.getSkin(resoureces), |
| false, |
| LayoutAdjustment.FILL, |
| 100), |
| new TestData( |
| new ClientSidePreference( |
| false, 90, false, 90, true, KeyboardLayout.TWELVE_KEYS, InputStyle.FLICK, |
| true, true, 8, EmojiProviderType.SOFTBANK, HardwareKeyMap.DEFAULT, |
| SkinType.ORANGE_LIGHTGRAY, true, LayoutAdjustment.RIGHT, 100), |
| false, |
| 90, |
| false, |
| 0.72f, |
| true, |
| KeyboardLayout.TWELVE_KEYS, |
| InputStyle.FLICK, |
| true, |
| true, |
| 8, |
| EmojiProviderType.SOFTBANK, |
| HardwareKeyMap.DEFAULT, |
| SkinType.ORANGE_LIGHTGRAY.getSkin(resoureces), |
| true, |
| LayoutAdjustment.RIGHT, |
| 100), |
| new TestData( |
| new ClientSidePreference( |
| false, 100, false, 100, true, KeyboardLayout.TWELVE_KEYS, InputStyle.FLICK, |
| true, true, 8, EmojiProviderType.SOFTBANK, HardwareKeyMap.DEFAULT, |
| SkinType.ORANGE_LIGHTGRAY, true, LayoutAdjustment.RIGHT, 120), |
| false, |
| 100, |
| false, |
| 0.8f, |
| true, |
| KeyboardLayout.TWELVE_KEYS, |
| InputStyle.FLICK, |
| true, |
| true, |
| 8, |
| EmojiProviderType.SOFTBANK, |
| HardwareKeyMap.DEFAULT, |
| SkinType.ORANGE_LIGHTGRAY.getSkin(resoureces), |
| true, |
| LayoutAdjustment.RIGHT, |
| 120), |
| }; |
| |
| for (TestData testData : testDataList) { |
| service.propagateClientSidePreference(testData.clientSidePreference); |
| FeedbackManager feedbackManager = service.feedbackManager; |
| |
| // Make sure all configurations are actually applied. |
| assertEquals(testData.toString(), |
| testData.expectHapticFeedback, feedbackManager.isHapticFeedbackEnabled()); |
| assertEquals(testData.toString(), |
| testData.expectHapticFeedbackDuration, |
| feedbackManager.getHapticFeedbackDuration()); |
| assertEquals(testData.toString(), |
| testData.expectSoundFeedback, feedbackManager.isSoundFeedbackEnabled()); |
| // Check with an epsilon. |
| assertEquals(testData.toString(), |
| testData.expectSoundFeedbackVolume, feedbackManager.getSoundFeedbackVolume(), |
| 0.0001f); |
| assertEquals(testData.toString(), |
| testData.expectPopupFeedback, |
| viewManager.isPopupEnabled()); |
| JapaneseSoftwareKeyboardModel model = viewManager.getActiveSoftwareKeyboardModel(); |
| assertEquals(testData.toString(), testData.expectKeyboardLayout, model.getKeyboardLayout()); |
| assertEquals(testData.toString(), testData.expectInputStyle, model.getInputStyle()); |
| assertEquals(testData.toString(), |
| testData.expectQwertyLayoutForAlphabet, model.isQwertyLayoutForAlphabet()); |
| assertEquals(testData.toString(), |
| testData.expectFullscreenMode, |
| viewManager.isFullscreenMode()); |
| assertEquals(testData.toString(), |
| testData.expectFullscreenMode, service.onEvaluateFullscreenMode()); |
| assertEquals(testData.toString(), |
| testData.expectFlickSensitivity, |
| viewManager.getFlickSensitivity()); |
| assertEquals(testData.toString(), |
| testData.expectEmojiProviderType, |
| viewManager.getEmojiProviderType()); |
| assertEquals(testData.toString(), |
| testData.expectHardwareKeyMap, service.viewManager.getHardwareKeyMap()); |
| assertEquals(testData.toString(), |
| testData.expectSkin, |
| viewManager.getSkin()); |
| assertEquals(testData.toString(), |
| testData.expectMicrophoneButtonEnabledByPreference, |
| viewManager.isMicrophoneButtonEnabledByPreference()); |
| assertEquals(testData.toString(), |
| testData.expectLayoutAdjustment, |
| viewManager.getLayoutAdjustment()); |
| assertEquals(testData.toString(), |
| testData.expectKeyboardHeight, |
| viewManager.getKeyboardHeightRatio()); |
| } |
| } |
| |
| /** |
| * We cannot check whether a sound is played or not. |
| * Just a smoke test. |
| */ |
| @SmallTest |
| public void testOnSound() { |
| SessionExecutor sessionExecutor = createNiceMock(SessionExecutor.class); |
| replayAll(); |
| MozcService service = createInitializedService(sessionExecutor); |
| FeedbackListener listener = service.feedbackManager.feedbackListener; |
| listener.onSound(AudioManager.FX_KEYPRESS_STANDARD, 0.1f); |
| listener.onSound(FeedbackEvent.NO_SOUND, 0.1f); |
| } |
| |
| @SmallTest |
| public void testOnConfigurationChanged() throws SecurityException, IllegalArgumentException { |
| SessionExecutor sessionExecutor = createMock(SessionExecutor.class); |
| SelectionTracker selectionTracker = createMock(SelectionTracker.class); |
| ViewManager viewManager = createMockBuilder(ViewManager.class) |
| .withConstructor(Context.class, ViewEventListener.class, |
| SymbolHistoryStorage.class, ImeSwitcher.class, MenuDialogListener.class) |
| .withArgs(getInstrumentation().getTargetContext(), |
| createNiceMock(ViewEventListener.class), |
| createNiceMock(SymbolHistoryStorage.class), |
| createNiceMock(ImeSwitcher.class), |
| createNiceMock(MenuDialogListener.class)) |
| .addMockedMethods("onConfigurationChanged") |
| .createMock(); |
| MozcService service = createService(); |
| invokeOnCreateInternal( |
| service, viewManager, getSharedPreferences(), getDefaultDeviceConfiguration(), |
| sessionExecutor); |
| service.selectionTracker = selectionTracker; |
| service.inputBound = true; |
| |
| Configuration deviceConfig = new Configuration(); |
| deviceConfig.keyboard = Configuration.KEYBOARD_NOKEYS; |
| deviceConfig.keyboardHidden = Configuration.KEYBOARDHIDDEN_YES; |
| deviceConfig.hardKeyboardHidden = Configuration.KEYBOARDHIDDEN_UNDEFINED; |
| |
| InputConnection inputConnection = createMock(InputConnection.class); |
| // restartInput() can be used to install our InputConnection safely. |
| service.onCreateInputMethodInterface().restartInput(inputConnection, new EditorInfo()); |
| |
| resetAll(); |
| expect(inputConnection.finishComposingText()).andReturn(true); |
| |
| expect(selectionTracker.getLastSelectionStart()).andReturn(0); |
| expect(selectionTracker.getLastSelectionEnd()).andReturn(0); |
| selectionTracker.onConfigurationChanged(); |
| |
| sessionExecutor.resetContext(); |
| sessionExecutor.switchInputMode(same(Optional.<KeyEventInterface>absent()), |
| isA(CompositionMode.class), |
| anyObject(EvaluationCallback.class)); |
| expectLastCall().asStub(); |
| sessionExecutor.updateRequest(isA(Request.class), eq(Collections.<TouchEvent>emptyList())); |
| expectLastCall().asStub(); |
| |
| viewManager.onConfigurationChanged(deviceConfig); |
| |
| replayAll(); |
| |
| // Test on no-hardware-keyboard configuration. |
| service.onConfigurationChangedInternal(deviceConfig); |
| |
| verifyAll(); |
| |
| Handler configurationChangedHandler = service.configurationChangedHandler; |
| assertTrue(configurationChangedHandler.hasMessages(0)); |
| // Clean up the pending message. |
| configurationChangedHandler.removeMessages(0); |
| } |
| |
| @SmallTest |
| public void testOnUpdateSelection_exactMatch() { |
| // Prepare the service. |
| SessionExecutor sessionExecutor = createMock(SessionExecutor.class); |
| SelectionTracker selectionTracker = createMock(SelectionTracker.class); |
| MozcService service = createInitializedService(sessionExecutor); |
| service.selectionTracker = selectionTracker; |
| |
| resetAll(); |
| // No events for SessionExecutor are expected. |
| expect(selectionTracker.onUpdateSelection(0, 0, 1, 1, 0, 1)) |
| .andReturn(SelectionTracker.DO_NOTHING); |
| replayAll(); |
| |
| service.onUpdateSelection(0, 0, 1, 1, 0, 1); |
| |
| verifyAll(); |
| } |
| |
| @SmallTest |
| public void testOnUpdateSelection_resetContext() { |
| // Prepare the service. |
| MozcService service = createMockBuilder(MozcService.class) |
| .addMockedMethods("isInputViewShown") |
| .createMock(); |
| SessionExecutor sessionExecutor = createMock(SessionExecutor.class); |
| initializeMozcService(service, sessionExecutor); |
| service.inputBound = true; |
| |
| InputConnection inputConnection = createMock(InputConnection.class); |
| service.onCreateInputMethodInterface().restartInput(inputConnection, new EditorInfo()); |
| |
| SelectionTracker selectionTracker = createMock(SelectionTracker.class); |
| service.selectionTracker = selectionTracker; |
| |
| resetAll(); |
| expect(selectionTracker.onUpdateSelection(0, 0, 1, 1, 0, 1)) |
| .andReturn(SelectionTracker.RESET_CONTEXT); |
| |
| // If the keyboard view is shown, reset the composing text here. |
| expect(service.isInputViewShown()).andReturn(true); |
| expect(inputConnection.finishComposingText()).andReturn(true); |
| |
| // The session should be reset. |
| sessionExecutor.resetContext(); |
| replayAll(); |
| |
| service.onUpdateSelectionInternal(0, 0, 1, 1, 0, 1); |
| |
| verifyAll(); |
| } |
| |
| @SmallTest |
| public void testOnUpdateSelection_resetContextInivisibleKeyboard() { |
| // Prepare the service. |
| MozcService service = createMockBuilder(MozcService.class) |
| .addMockedMethods("isInputViewShown") |
| .createMock(); |
| SessionExecutor sessionExecutor = createMock(SessionExecutor.class); |
| initializeMozcService(service, sessionExecutor); |
| |
| InputConnection inputConnection = createMock(InputConnection.class); |
| service.onCreateInputMethodInterface().restartInput(inputConnection, new EditorInfo()); |
| |
| SelectionTracker selectionTracker = createMock(SelectionTracker.class); |
| service.selectionTracker = selectionTracker; |
| |
| resetAll(); |
| expect(selectionTracker.onUpdateSelection(0, 0, 1, 1, 0, 1)) |
| .andReturn(SelectionTracker.RESET_CONTEXT); |
| |
| // If the keyboard view isn't shown, do NOT reset the composing text. |
| expect(service.isInputViewShown()).andReturn(false); |
| |
| // The session should be reset. |
| sessionExecutor.resetContext(); |
| replayAll(); |
| |
| service.onUpdateSelectionInternal(0, 0, 1, 1, 0, 1); |
| |
| verifyAll(); |
| } |
| |
| @SmallTest |
| public void testOnUpdateSelection_moveCursor() { |
| // Prepare the service. |
| SessionExecutor sessionExecutor = createMock(SessionExecutor.class); |
| SelectionTracker selectionTracker = createMock(SelectionTracker.class); |
| MozcService service = createInitializedService(sessionExecutor); |
| service.selectionTracker = selectionTracker; |
| |
| resetAll(); |
| expect(selectionTracker.onUpdateSelection(0, 0, 1, 1, 0, 1)) |
| .andReturn(5); |
| // Send moveCursor event as selectionTracker says. |
| sessionExecutor.moveCursor(eq(5), isA(EvaluationCallback.class)); |
| replayAll(); |
| |
| service.onUpdateSelection(0, 0, 1, 1, 0, 1); |
| |
| verifyAll(); |
| } |
| |
| @SmallTest |
| public void testGetInputFieldType() { |
| class TestData extends Parameter { |
| final int inputType; |
| final InputFieldType expectedMode; |
| |
| TestData (int inputType, InputFieldType expectedMode) { |
| this.inputType = inputType; |
| this.expectedMode = expectedMode; |
| } |
| } |
| TestData[] testDataList = { |
| new TestData((InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD), |
| InputFieldType.PASSWORD), |
| new TestData((InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_URI |
| | InputType.TYPE_TEXT_FLAG_AUTO_COMPLETE), |
| InputFieldType.NORMAL), |
| new TestData((InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD |
| | InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS), |
| InputFieldType.PASSWORD), |
| new TestData((InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_SHORT_MESSAGE |
| | InputType.TYPE_TEXT_FLAG_AUTO_CORRECT), |
| InputFieldType.NORMAL), |
| new TestData((InputType.TYPE_CLASS_NUMBER | InputType.TYPE_NUMBER_FLAG_SIGNED), |
| InputFieldType.NUMBER), |
| new TestData(InputType.TYPE_CLASS_PHONE, |
| InputFieldType.TEL), |
| new TestData(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_WEB_PASSWORD, |
| InputFieldType.PASSWORD), |
| }; |
| |
| for (TestData testData : testDataList) { |
| EditorInfo editorInfo = new EditorInfo(); |
| editorInfo.inputType = testData.inputType; |
| assertEquals(testData.expectedMode, MozcService.getInputFieldType(editorInfo)); |
| } |
| } |
| |
| @SmallTest |
| public void testOnHardwareKeyboardCompositionModeChange() { |
| MozcService service = initializeService(createMockBuilder(MozcService.class) |
| .addMockedMethod("isInputViewShown").createMock()); |
| Context context = getInstrumentation().getTargetContext(); |
| ViewManager viewManager = createMockBuilder(ViewManager.class) |
| .withConstructor(Context.class, ViewEventListener.class, |
| SymbolHistoryStorage.class, ImeSwitcher.class, MenuDialogListener.class) |
| .withArgs(context, service.new MozcEventListener(), |
| createNiceMock(SymbolHistoryStorage.class), |
| createNiceMock(ImeSwitcher.class), |
| createNiceMock(MenuDialogListener.class)) |
| .addMockedMethod("switchHardwareKeyboardCompositionMode") |
| .createMock(); |
| invokeOnCreateInternal( |
| service, viewManager, getSharedPreferences(), getDefaultDeviceConfiguration(), |
| createNiceMock(SessionExecutor.class)); |
| MozcView mozcView = viewManager.createMozcView(context); |
| |
| resetAll(); |
| |
| expect(service.isInputViewShown()).andStubReturn(true); |
| viewManager.switchHardwareKeyboardCompositionMode(CompositionSwitchMode.TOGGLE); |
| |
| replayAll(); |
| |
| mozcView.findViewById(R.id.hardware_composition_button).performClick(); |
| |
| verifyAll(); |
| } |
| |
| @SmallTest |
| public void testMaybeSetNarrowMode() { |
| ViewManager viewManager = createViewManagerMock( |
| getInstrumentation().getTargetContext(), |
| createNiceMock(ViewEventListener.class)); |
| MozcService service = createService(); |
| |
| resetAll(); |
| SessionExecutor sessionExecutorMock = createNiceMock(SessionExecutor.class); |
| expect(viewManager.isNarrowMode()).andStubReturn(false); |
| expect(viewManager.isFloatingCandidateMode()).andStubReturn(false); |
| viewManager.onConfigurationChanged(anyObject(Configuration.class)); |
| replayAll(); |
| invokeOnCreateInternal( |
| service, viewManager, null, getDefaultDeviceConfiguration(), sessionExecutorMock); |
| verifyAll(); |
| |
| Configuration configuration = new Configuration(); |
| |
| resetAll(); |
| expect(viewManager.isNarrowMode()).andReturn(false); |
| expect(viewManager.hideSubInputView()).andReturn(true); |
| viewManager.setNarrowMode(true); |
| replayAll(); |
| configuration.hardKeyboardHidden = Configuration.HARDKEYBOARDHIDDEN_NO; |
| service.maybeSetNarrowMode(configuration); |
| verifyAll(); |
| |
| resetAll(); |
| expect(viewManager.isNarrowMode()).andReturn(true); |
| viewManager.setNarrowMode(false); |
| replayAll(); |
| configuration.hardKeyboardHidden = Configuration.HARDKEYBOARDHIDDEN_YES; |
| service.maybeSetNarrowMode(configuration); |
| verifyAll(); |
| |
| resetAll(); |
| replayAll(); |
| configuration.hardKeyboardHidden = Configuration.HARDKEYBOARDHIDDEN_YES; |
| service.maybeSetNarrowMode(configuration); |
| verifyAll(); |
| |
| resetAll(); |
| replayAll(); |
| configuration.hardKeyboardHidden = Configuration.HARDKEYBOARDHIDDEN_UNDEFINED; |
| service.maybeSetNarrowMode(configuration); |
| verifyAll(); |
| } |
| |
| @SmallTest |
| @ApiLevel(17) |
| @TargetApi(17) |
| public void testHandleGenericMotionEvent() { |
| MozcService service = createMockBuilder(MozcService.class) |
| .addMockedMethods("isInputViewShown") |
| .createMock(); |
| ViewEventListener eventListener = createNiceMock(ViewEventListener.class); |
| ViewManager viewManager = |
| createViewManagerMock(getInstrumentation().getTargetContext(), eventListener); |
| service.viewManager = viewManager; |
| |
| MotionEvent motionEvent = MotionEvent.obtain(0, 0, 0, 0, 0, 0); |
| try { |
| expect(service.isInputViewShown()).andReturn(true); |
| expect(service.viewManager.isGenericMotionToConsume(motionEvent)).andReturn(true); |
| expect(service.viewManager.consumeGenericMotion(motionEvent)).andReturn(true); |
| replayAll(); |
| |
| assertTrue(service.onGenericMotionEvent(motionEvent)); |
| |
| // Do not handle the event |
| resetAll(); |
| expect(service.isInputViewShown()).andReturn(false); |
| replayAll(); |
| |
| assertFalse(service.onGenericMotionEvent(motionEvent)); |
| } finally { |
| motionEvent.recycle(); |
| } |
| } |
| |
| |
| |
| } |