| // 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.session; |
| |
| import static org.mozc.android.inputmethod.japanese.testing.MozcMatcher.matchesBuilder; |
| import static org.mozc.android.inputmethod.japanese.testing.MozcMatcher.sameOptional; |
| import static org.easymock.EasyMock.anyLong; |
| import static org.easymock.EasyMock.capture; |
| import static org.easymock.EasyMock.expect; |
| import static org.easymock.EasyMock.getCurrentArguments; |
| import static org.easymock.EasyMock.same; |
| |
| import org.mozc.android.inputmethod.japanese.KeycodeConverter; |
| import org.mozc.android.inputmethod.japanese.KeycodeConverter.KeyEventInterface; |
| 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.CandidateWord; |
| import org.mozc.android.inputmethod.japanese.protobuf.ProtoCommands; |
| import org.mozc.android.inputmethod.japanese.protobuf.ProtoCommands.Capability; |
| import org.mozc.android.inputmethod.japanese.protobuf.ProtoCommands.Capability.TextDeletionCapabilityType; |
| 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; |
| 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.CommandType; |
| import org.mozc.android.inputmethod.japanese.protobuf.ProtoCommands.Input.TouchEvent; |
| import org.mozc.android.inputmethod.japanese.protobuf.ProtoCommands.KeyEvent.SpecialKey; |
| import org.mozc.android.inputmethod.japanese.protobuf.ProtoCommands.Output; |
| 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.ProtoCommands.SessionCommand.UsageStatsEvent; |
| import org.mozc.android.inputmethod.japanese.protobuf.ProtoConfig.Config; |
| import org.mozc.android.inputmethod.japanese.protobuf.ProtoUserDictionaryStorage.UserDictionaryCommand; |
| import org.mozc.android.inputmethod.japanese.protobuf.ProtoUserDictionaryStorage.UserDictionaryCommandStatus; |
| import org.mozc.android.inputmethod.japanese.session.SessionExecutor.AsynchronousEvaluationContext; |
| import org.mozc.android.inputmethod.japanese.session.SessionExecutor.CallbackHandler; |
| import org.mozc.android.inputmethod.japanese.session.SessionExecutor.EvaluationCallback; |
| import org.mozc.android.inputmethod.japanese.session.SessionExecutor.ExecutorMainCallback; |
| import org.mozc.android.inputmethod.japanese.session.SessionExecutor.SynchronousEvaluationContext; |
| import org.mozc.android.inputmethod.japanese.stresstest.StressTest; |
| import org.mozc.android.inputmethod.japanese.testing.InstrumentationTestCaseWithMock; |
| import org.mozc.android.inputmethod.japanese.testing.MemoryLogger; |
| import org.mozc.android.inputmethod.japanese.testing.Parameter; |
| import com.google.common.base.Optional; |
| import com.google.common.base.Strings; |
| import com.google.protobuf.ByteString; |
| |
| import android.content.Context; |
| import android.content.SharedPreferences; |
| import android.os.Handler; |
| import android.os.Looper; |
| import android.os.Message; |
| import android.preference.PreferenceManager; |
| import android.test.mock.MockContext; |
| import android.test.suitebuilder.annotation.LargeTest; |
| import android.test.suitebuilder.annotation.SmallTest; |
| import android.view.KeyEvent; |
| |
| import org.easymock.Capture; |
| import org.easymock.IAnswer; |
| |
| import java.util.Collections; |
| import java.util.List; |
| import java.util.Random; |
| import java.util.concurrent.CountDownLatch; |
| |
| /** |
| * |
| */ |
| public class SessionExecutorTest extends InstrumentationTestCaseWithMock { |
| |
| private SessionExecutor createSessionExecutorMock() { |
| return createMockBuilder(SessionExecutor.class) |
| .addMockedMethods("evaluateSynchronously", "evaluateAsynchronously") |
| .createMock(); |
| } |
| |
| @Override |
| protected void tearDown() throws Exception { |
| SessionExecutor.instance = Optional.absent(); |
| super.tearDown(); |
| } |
| |
| @SmallTest |
| public void testGetInstanceInitializedIfNecessary() { |
| Context context = new MockContext(); |
| SessionHandlerFactory factory = createMockBuilder(SessionHandlerFactory.class) |
| .withConstructor(Context.class) |
| .withArgs(getInstrumentation().getTargetContext()) |
| .createMock(); |
| SessionHandler mockHandler = createNiceMock(SessionHandler.class); |
| expect(factory.create()).andReturn(mockHandler); |
| replayAll(); |
| |
| SessionExecutor instance = SessionExecutor.getInstanceInitializedIfNecessary(factory, context); |
| |
| verifyAll(); |
| |
| // We don't expect re-setting up for the second time. |
| resetAll(); |
| replayAll(); |
| |
| SessionExecutor instance2 = SessionExecutor.getInstanceInitializedIfNecessary(factory, context); |
| |
| verifyAll(); |
| |
| // The same instance should be returned. |
| assertSame(instance, instance2); |
| } |
| |
| @SmallTest |
| public void testCallbackInitializeSessionHandler() { |
| Context context = getInstrumentation().getContext(); |
| SessionHandler sessionHandler = createMock(SessionHandler.class); |
| sessionHandler.initialize(context); |
| replayAll(); |
| |
| ExecutorMainCallback callback = new ExecutorMainCallback(sessionHandler); |
| Message message = new Message(); |
| message.obj = Object.class.cast(context); |
| message.what = ExecutorMainCallback.INITIALIZE_SESSION_HANDLER; |
| assertTrue(callback.handleMessage(message)); |
| |
| verifyAll(); |
| } |
| |
| @SmallTest |
| public void testCallbackEnsureSession() { |
| SessionHandler sessionHandler = createMock(SessionHandler.class); |
| long sessionId = 1; |
| Request initialRequest = Request.newBuilder() |
| .setMixedConversion(true) |
| .setZeroQuerySuggestion(true) |
| .setAutoPartialSuggestion(true) |
| .setUpdateInputModeFromSurroundingText(false) |
| .build(); |
| { |
| // Create session. |
| Input input = Input.newBuilder() |
| .setType(CommandType.CREATE_SESSION) |
| .setCapability(Capability.newBuilder() |
| .setTextDeletion(TextDeletionCapabilityType.DELETE_PRECEDING_TEXT)) |
| .build(); |
| Command inputCommand = Command.newBuilder() |
| .setInput(input).setOutput(Output.getDefaultInstance()).build(); |
| Command outputCommand = Command.newBuilder() |
| .setInput(input).setOutput(Output.newBuilder().setId(sessionId)).build(); |
| expect(sessionHandler.evalCommand(inputCommand)).andReturn(outputCommand); |
| } |
| { |
| // Set request. |
| Command inputCommand = Command.newBuilder() |
| .setInput(Input.newBuilder() |
| .setId(sessionId) |
| .setType(CommandType.SET_REQUEST) |
| .setRequest(initialRequest)) |
| .setOutput(Output.getDefaultInstance()) |
| .build(); |
| expect(sessionHandler.evalCommand(inputCommand)).andReturn(Command.getDefaultInstance()); |
| } |
| replayAll(); |
| |
| ExecutorMainCallback callback = new ExecutorMainCallback(sessionHandler); |
| callback.ensureSession(); |
| |
| verifyAll(); |
| |
| resetAll(); |
| { |
| // After createSession, sessionId and request should be automatically filled, for update() |
| // method. |
| Command inputCommand = Command.newBuilder() |
| .setInput(Input.newBuilder() |
| .setId(sessionId) |
| .setType(CommandType.SET_REQUEST) |
| .setRequest(initialRequest)) |
| .setOutput(Output.getDefaultInstance()) |
| .build(); |
| expect(sessionHandler.evalCommand(inputCommand)).andReturn(Command.getDefaultInstance()); |
| } |
| replayAll(); |
| |
| callback.updateRequest(Input.newBuilder()); |
| |
| verifyAll(); |
| } |
| |
| @SmallTest |
| public void testCallbackIsSquashable() { |
| Output squashableOutput = Output.newBuilder() |
| .setConsumed(true) |
| .setAllCandidateWords(CandidateList.newBuilder() |
| .addCandidates(CandidateWord.getDefaultInstance())) |
| .build(); |
| |
| assertTrue(ExecutorMainCallback.isSquashableOutput(squashableOutput)); |
| assertFalse(ExecutorMainCallback.isSquashableOutput(squashableOutput.toBuilder() |
| .setResult(Result.newBuilder().setType(ResultType.NONE).setValue("abc")) |
| .build())); |
| assertFalse(ExecutorMainCallback.isSquashableOutput(squashableOutput.toBuilder() |
| .setDeletionRange(DeletionRange.getDefaultInstance()) |
| .build())); |
| assertFalse(ExecutorMainCallback.isSquashableOutput(squashableOutput.toBuilder() |
| .setConsumed(false) |
| .build())); |
| assertFalse(ExecutorMainCallback.isSquashableOutput(squashableOutput.toBuilder() |
| .clearAllCandidateWords() |
| .build())); |
| assertFalse(ExecutorMainCallback.isSquashableOutput(Output.getDefaultInstance())); |
| } |
| |
| @SmallTest |
| public void testIsSessionIdRequired() { |
| assertFalse(ExecutorMainCallback.isSessionIdRequired(Input.newBuilder() |
| .setType(CommandType.SET_CONFIG))); |
| assertFalse(ExecutorMainCallback.isSessionIdRequired(Input.newBuilder() |
| .setType(CommandType.SET_IMPOSED_CONFIG))); |
| assertFalse(ExecutorMainCallback.isSessionIdRequired(Input.newBuilder() |
| .setType(CommandType.GET_CONFIG))); |
| |
| assertTrue(ExecutorMainCallback.isSessionIdRequired(Input.newBuilder() |
| .setType(CommandType.SEND_KEY))); |
| assertTrue(ExecutorMainCallback.isSessionIdRequired(Input.newBuilder() |
| .setType(CommandType.SEND_COMMAND))); |
| } |
| |
| @SmallTest |
| public void testCallbackEvaluateAsynchronously() { |
| class TestData extends Parameter { |
| final Output output; |
| final boolean expectRequestSuggestion; |
| final int expectedWhat; |
| TestData(Output output, boolean expectRequestSuggestion, int expectedWhat) { |
| this.output = output; |
| this.expectRequestSuggestion = expectRequestSuggestion; |
| this.expectedWhat = expectedWhat; |
| } |
| } |
| TestData[] testData = new TestData[] { |
| new TestData(Output.newBuilder() |
| .setConsumed(true) |
| .setAllCandidateWords(CandidateList.newBuilder() |
| .addCandidates(CandidateWord.getDefaultInstance())) |
| .build(), |
| true, |
| CallbackHandler.SQUASHABLE_OUTPUT), |
| new TestData(Output.getDefaultInstance(), true, CallbackHandler.UNSQUASHABLE_OUTPUT), |
| new TestData(Output.newBuilder() |
| .setConsumed(true) |
| .setAllCandidateWords(CandidateList.newBuilder() |
| .addCandidates(CandidateWord.getDefaultInstance())) |
| .build(), |
| false, |
| CallbackHandler.SQUASHABLE_OUTPUT), |
| new TestData(Output.getDefaultInstance(), false, CallbackHandler.UNSQUASHABLE_OUTPUT), |
| }; |
| Handler callbackHandler = createMock(Handler.class); |
| Handler currentHandler = new Handler(); |
| SessionHandler sessionHandler = createMock(SessionHandler.class); |
| for (TestData data : testData) { |
| resetAll(); |
| |
| Input.Builder inputBuilder = Input.newBuilder() |
| .setType(CommandType.SEND_KEY) |
| .setKey(ProtoCommands.KeyEvent.newBuilder() |
| .setSpecialKey(SpecialKey.BACKSPACE)); |
| |
| // Unfortunately we cannot mock the removeMessages because it is final method. Just skip it. |
| |
| // Note that Handler#obtainMessage and Handler#sendMessage cannot be mocked. |
| // So, we just mock sendMessageAtTime method instead. |
| Capture<Message> messageCapture = new Capture<Message>(); |
| expect(callbackHandler.sendMessageAtTime(capture(messageCapture), anyLong())).andReturn(true); |
| |
| AsynchronousEvaluationContext context = new AsynchronousEvaluationContext( |
| 0, inputBuilder, Optional.<KeyEventInterface>absent(), |
| Optional.<EvaluationCallback>absent(), Optional.of(callbackHandler)); |
| long sessionId = 1; |
| |
| { |
| Input.Builder builder = inputBuilder.clone(); |
| builder.setId(sessionId); |
| if (!data.expectRequestSuggestion) { |
| builder.setRequestSuggestion(false); |
| } |
| Command inputCommand = Command.newBuilder() |
| .setInput(builder) |
| .setOutput(Output.getDefaultInstance()) |
| .build(); |
| expect(sessionHandler.evalCommand(inputCommand)).andReturn(Command.newBuilder() |
| .setInput(inputCommand.getInput()) |
| .setOutput(data.output) |
| .build()); |
| } |
| |
| if (!data.expectRequestSuggestion) { |
| currentHandler.sendEmptyMessage(ExecutorMainCallback.EVALUATE_KEYEVENT_ASYNCHRONOUSLY); |
| } |
| replayAll(); |
| |
| ExecutorMainCallback callback = new ExecutorMainCallback(sessionHandler); |
| callback.sessionId = sessionId; |
| |
| callback.evaluateAsynchronously(context, currentHandler); |
| |
| verifyAll(); |
| Message message = messageCapture.getValue(); |
| assertEquals(data.expectedWhat, message.what); |
| assertSame(context, message.obj); |
| assertTrue(context.outCommand.isPresent()); |
| currentHandler.removeMessages(ExecutorMainCallback.EVALUATE_KEYEVENT_ASYNCHRONOUSLY); |
| } |
| } |
| |
| @SmallTest |
| public void testCallbackEvaluateSynchronously() { |
| Input input = Input.newBuilder().setType(Input.CommandType.GET_CONFIG).build(); |
| CountDownLatch countDownLatch = new CountDownLatch(1); |
| |
| SynchronousEvaluationContext context = new SynchronousEvaluationContext(input, countDownLatch); |
| |
| SessionHandler sessionHandler = createMock(SessionHandler.class); |
| { |
| Command inputCommand = Command.newBuilder() |
| .setInput(input).setOutput(Output.getDefaultInstance()).build(); |
| expect(sessionHandler.evalCommand(inputCommand)).andReturn(Command.getDefaultInstance()); |
| } |
| replayAll(); |
| |
| ExecutorMainCallback callback = new ExecutorMainCallback(sessionHandler); |
| |
| callback.evaluateSynchronously(context); |
| |
| verifyAll(); |
| assertEquals(0, countDownLatch.getCount()); |
| assertTrue(context.outCommand.isPresent()); |
| } |
| |
| @SmallTest |
| public void testCallbackUpdateRequest() { |
| Request.Builder initialRequest = |
| Request.newBuilder().setMixedConversion(true).setZeroQuerySuggestion(true); |
| Request diffRequest = Request.newBuilder().setZeroQuerySuggestion(false).build(); |
| Request updatedRequest = initialRequest.clone().mergeFrom(diffRequest).build(); |
| long sessionId = 1; |
| List<TouchEvent> touchEventList = Collections.singletonList(TouchEvent.getDefaultInstance()); |
| |
| SessionHandler sessionHandler = createMock(SessionHandler.class); |
| { |
| Command inputCommand = Command.newBuilder() |
| .setInput(Input.newBuilder() |
| .setId(sessionId) |
| .setType(CommandType.SET_REQUEST) |
| .setRequest(updatedRequest) |
| .addAllTouchEvents(touchEventList)) |
| .setOutput(Output.getDefaultInstance()) |
| .build(); |
| expect(sessionHandler.evalCommand(inputCommand)).andReturn(Command.getDefaultInstance()); |
| } |
| replayAll(); |
| |
| ExecutorMainCallback callback = new ExecutorMainCallback(sessionHandler); |
| callback.sessionId = sessionId; |
| callback.request = Optional.of(initialRequest); |
| |
| callback.updateRequest( |
| Input.newBuilder().setRequest(diffRequest).addAllTouchEvents(touchEventList)); |
| |
| verifyAll(); |
| resetAll(); |
| { |
| // Make sure that internal request is also updated by invoking updateRequest again. |
| Command inputCommand = Command.newBuilder() |
| .setInput(Input.newBuilder() |
| .setId(sessionId) |
| .setType(CommandType.SET_REQUEST) |
| .setRequest(initialRequest)) |
| .setOutput(Output.getDefaultInstance()) |
| .build(); |
| expect(sessionHandler.evalCommand(inputCommand)).andReturn(Command.getDefaultInstance()); |
| } |
| replayAll(); |
| callback.updateRequest(Input.newBuilder()); |
| verifyAll(); |
| } |
| |
| @SmallTest |
| public void testCallbackHandler() { |
| CallbackHandler callbackHandler = new CallbackHandler(Looper.myLooper()); |
| callbackHandler.cancelTimeStamp = 0; |
| |
| EvaluationCallback evaluationCallback = createMock(EvaluationCallback.class); |
| evaluationCallback.onCompleted( |
| Optional.<Command>absent(), Optional.<KeyEventInterface>absent()); |
| replayAll(); |
| |
| AsynchronousEvaluationContext context = new AsynchronousEvaluationContext( |
| 1, Input.newBuilder(), Optional.<KeyEventInterface>absent(), |
| Optional.of(evaluationCallback), Optional.<Handler>of(callbackHandler)); |
| Message message = callbackHandler.obtainMessage(CallbackHandler.UNSQUASHABLE_OUTPUT, context); |
| callbackHandler.handleMessage(message); |
| |
| verifyAll(); |
| |
| resetAll(); |
| replayAll(); |
| |
| callbackHandler.cancelTimeStamp = 1; |
| callbackHandler.handleMessage(message); |
| |
| verifyAll(); |
| } |
| |
| @SmallTest |
| public void testDeleteSession() { |
| Capture<Message> messageCapture = new Capture<Message>(); |
| Handler handler = createMock(Handler.class); |
| expect(handler.sendMessageAtTime(capture(messageCapture), anyLong())).andReturn(true); |
| replayAll(); |
| |
| SessionExecutor executor = new SessionExecutor(); |
| executor.handler = Optional.of(handler); |
| |
| executor.deleteSession(); |
| |
| verifyAll(); |
| assertEquals(ExecutorMainCallback.DELETE_SESSION, messageCapture.getValue().what); |
| } |
| |
| @SmallTest |
| public void testEvaluateAsynchronously() { |
| Capture<Message> messageCapture = new Capture<Message>(); |
| Handler handler = createMock(Handler.class); |
| expect(handler.sendMessageAtTime(capture(messageCapture), anyLong())).andReturn(true); |
| replayAll(); |
| |
| SessionExecutor executor = new SessionExecutor(); |
| executor.handler = Optional.of(handler); |
| |
| Input.Builder inputBuilder = Input.newBuilder(); |
| KeyEventInterface keyEvent = KeycodeConverter.getKeyEventInterface(KeyEvent.KEYCODE_A); |
| |
| executor.evaluateAsynchronously( |
| inputBuilder, Optional.of(keyEvent), Optional.<EvaluationCallback>absent()); |
| |
| verifyAll(); |
| Message message = messageCapture.getValue(); |
| assertEquals(ExecutorMainCallback.EVALUATE_KEYEVENT_ASYNCHRONOUSLY, message.what); |
| AsynchronousEvaluationContext context = AsynchronousEvaluationContext.class.cast(message.obj); |
| assertSame(inputBuilder, context.inputBuilder); |
| assertSame(keyEvent, context.triggeringKeyEvent.get()); |
| assertFalse(context.callback.isPresent()); |
| assertFalse(context.callbackHandler.isPresent()); |
| } |
| |
| @SmallTest |
| public void testEvaluateAsynchronously_withoutKeyEvent() { |
| Capture<Message> messageCapture = new Capture<Message>(); |
| Handler handler = createMock(Handler.class); |
| expect(handler.sendMessageAtTime(capture(messageCapture), anyLong())).andReturn(true); |
| replayAll(); |
| |
| SessionExecutor executor = new SessionExecutor(); |
| executor.handler = Optional.of(handler); |
| |
| Input.Builder inputBuilder = Input.newBuilder(); |
| |
| executor.evaluateAsynchronously( |
| inputBuilder, Optional.<KeyEventInterface>absent(), Optional.<EvaluationCallback>absent()); |
| |
| verifyAll(); |
| Message message = messageCapture.getValue(); |
| assertEquals(ExecutorMainCallback.EVALUATE_ASYNCHRONOUSLY, message.what); |
| AsynchronousEvaluationContext context = AsynchronousEvaluationContext.class.cast(message.obj); |
| assertSame(inputBuilder, context.inputBuilder); |
| assertFalse(context.triggeringKeyEvent.isPresent()); |
| assertFalse(context.callback.isPresent()); |
| assertFalse(context.callbackHandler.isPresent()); |
| } |
| |
| @SmallTest |
| public void testEvaluateSynchronously() { |
| Capture<Message> messageCapture = new Capture<Message>(); |
| Handler handler = createMock(Handler.class); |
| |
| // This emulates "accept message sent via handler, pseudo-execute of |
| // SessionHandlerCallback#evaluateSynchronously on this thread (instead of the worker thread) |
| // for synchronization purpose, and then return true as sendMessage's return value". |
| IAnswer<Boolean> mockExecutor = new IAnswer<Boolean>() { |
| @Override |
| public Boolean answer() throws Throwable { |
| // Emulate synchronous evaluation. |
| Message message = Message.class.cast(getCurrentArguments()[0]); |
| SynchronousEvaluationContext context = |
| SynchronousEvaluationContext.class.cast(message.obj); |
| context.outCommand = Optional.of(Command.newBuilder() |
| .setInput(Input.newBuilder().setType(CommandType.NO_OPERATION).build()) |
| .setOutput(Output.getDefaultInstance()) |
| .build()); |
| context.evaluationSynchronizer.countDown(); |
| return Boolean.TRUE; |
| } |
| }; |
| expect(handler.sendMessageAtTime(capture(messageCapture), anyLong())).andAnswer(mockExecutor); |
| replayAll(); |
| |
| SessionExecutor executor = new SessionExecutor(); |
| executor.handler = Optional.of(handler); |
| |
| executor.evaluateSynchronously(Input.getDefaultInstance()); |
| |
| verifyAll(); |
| assertEquals(ExecutorMainCallback.EVALUATE_SYNCHRONOUSLY, messageCapture.getValue().what); |
| } |
| |
| @SmallTest |
| public void testSendKey() { |
| SessionExecutor executor = createSessionExecutorMock(); |
| ProtoCommands.KeyEvent mozcKeyEvent = ProtoCommands.KeyEvent.getDefaultInstance(); |
| KeyEventInterface triggeringKeyEvent = |
| KeycodeConverter.getKeyEventInterface(KeyEvent.KEYCODE_A); |
| EvaluationCallback evaluationCallback = createNiceMock(EvaluationCallback.class); |
| List<TouchEvent> touchEventList = Collections.singletonList(TouchEvent.getDefaultInstance()); |
| executor.evaluateAsynchronously( |
| matchesBuilder(Input.newBuilder() |
| .setType(CommandType.SEND_KEY) |
| .setKey(mozcKeyEvent) |
| .addAllTouchEvents(touchEventList)), |
| sameOptional(triggeringKeyEvent), |
| sameOptional(evaluationCallback)); |
| replayAll(); |
| |
| executor.sendKey(mozcKeyEvent, triggeringKeyEvent, touchEventList, evaluationCallback); |
| |
| verifyAll(); |
| } |
| |
| @SmallTest |
| public void testSubmit() { |
| SessionExecutor executor = createSessionExecutorMock(); |
| EvaluationCallback evaluationCallback = createNiceMock(EvaluationCallback.class); |
| executor.evaluateAsynchronously( |
| matchesBuilder(Input.newBuilder() |
| .setType(CommandType.SEND_COMMAND) |
| .setCommand(SessionCommand.newBuilder() |
| .setType(SessionCommand.CommandType.SUBMIT))), |
| same(Optional.<KeyEventInterface>absent()), sameOptional(evaluationCallback)); |
| replayAll(); |
| |
| executor.submit(evaluationCallback); |
| |
| verifyAll(); |
| } |
| |
| @SmallTest |
| public void testSwitchInputMode() { |
| SessionExecutor executor = createSessionExecutorMock(); |
| EvaluationCallback callback = createNiceMock(EvaluationCallback.class); |
| |
| executor.evaluateAsynchronously( |
| matchesBuilder(Input.newBuilder() |
| .setType(CommandType.SEND_COMMAND) |
| .setCommand(SessionCommand.newBuilder() |
| .setType(SessionCommand.CommandType.SWITCH_INPUT_MODE) |
| .setCompositionMode(CompositionMode.DIRECT))), |
| same(Optional.<KeyEventInterface>absent()), sameOptional(callback)); |
| replayAll(); |
| |
| executor.switchInputMode( |
| Optional.<KeyEventInterface>absent(), CompositionMode.DIRECT, callback); |
| |
| verifyAll(); |
| } |
| |
| @SmallTest |
| public void testSubmitCandidate() { |
| SessionExecutor executor = createSessionExecutorMock(); |
| EvaluationCallback evaluationCallback = createNiceMock(EvaluationCallback.class); |
| executor.evaluateAsynchronously( |
| matchesBuilder(Input.newBuilder() |
| .setType(CommandType.SEND_COMMAND) |
| .setCommand(SessionCommand.newBuilder() |
| .setType(SessionCommand.CommandType.SUBMIT_CANDIDATE) |
| .setId(3))), |
| same(Optional.<KeyEventInterface>absent()), sameOptional(evaluationCallback)); |
| executor.evaluateAsynchronously( |
| matchesBuilder(Input.newBuilder() |
| .setType(CommandType.SEND_COMMAND) |
| .setCommand(SessionCommand.newBuilder() |
| .setType(SessionCommand.CommandType.USAGE_STATS_EVENT) |
| .setUsageStatsEvent(UsageStatsEvent.SUBMITTED_CANDIDATE_ROW_GE10))), |
| same(Optional.<KeyEventInterface>absent()), same(Optional.<EvaluationCallback>absent())); |
| replayAll(); |
| |
| executor.submitCandidate(3, Optional.<Integer>of(100), evaluationCallback); |
| |
| verifyAll(); |
| } |
| |
| @SmallTest |
| public void testResetContext() { |
| SessionExecutor executor = createSessionExecutorMock(); |
| executor.evaluateAsynchronously( |
| matchesBuilder(Input.newBuilder() |
| .setType(CommandType.SEND_COMMAND) |
| .setCommand(SessionCommand.newBuilder() |
| .setType(SessionCommand.CommandType.RESET_CONTEXT))), |
| same(Optional.<KeyEventInterface>absent()), same(Optional.<EvaluationCallback>absent())); |
| replayAll(); |
| |
| executor.resetContext(); |
| |
| verifyAll(); |
| } |
| |
| @SmallTest |
| public void testMoveCursor() { |
| SessionExecutor executor = createSessionExecutorMock(); |
| EvaluationCallback evaluationCallback = createNiceMock(EvaluationCallback.class); |
| executor.evaluateAsynchronously( |
| matchesBuilder(Input.newBuilder() |
| .setType(CommandType.SEND_COMMAND) |
| .setCommand( |
| SessionCommand.newBuilder() |
| .setType(ProtoCommands.SessionCommand.CommandType.MOVE_CURSOR) |
| .setCursorPosition(5))), |
| same(Optional.<KeyEventInterface>absent()), sameOptional(evaluationCallback)); |
| replayAll(); |
| |
| executor.moveCursor(5, evaluationCallback); |
| |
| verifyAll(); |
| } |
| |
| @SmallTest |
| public void testPageDownAndUp() { |
| SessionExecutor executor = createSessionExecutorMock(); |
| EvaluationCallback evaluationCallback = createNiceMock(EvaluationCallback.class); |
| executor.evaluateAsynchronously( |
| matchesBuilder(Input.newBuilder() |
| .setType(CommandType.SEND_COMMAND) |
| .setCommand(SessionCommand.newBuilder() |
| .setType(ProtoCommands.SessionCommand.CommandType.CONVERT_NEXT_PAGE))), |
| same(Optional.<KeyEventInterface>absent()), sameOptional(evaluationCallback)); |
| replayAll(); |
| executor.pageDown(evaluationCallback); |
| verifyAll(); |
| |
| resetAll(); |
| executor.evaluateAsynchronously( |
| matchesBuilder(Input.newBuilder() |
| .setType(CommandType.SEND_COMMAND) |
| .setCommand(SessionCommand.newBuilder() |
| .setType(ProtoCommands.SessionCommand.CommandType.CONVERT_PREV_PAGE))), |
| same(Optional.<KeyEventInterface>absent()), sameOptional(evaluationCallback)); |
| replayAll(); |
| executor.pageUp(evaluationCallback); |
| verifyAll(); |
| } |
| |
| @SmallTest |
| public void testSwitchInputFieldType() { |
| SessionExecutor executor = createSessionExecutorMock(); |
| executor.evaluateAsynchronously( |
| matchesBuilder(Input.newBuilder() |
| .setType(CommandType.SEND_COMMAND) |
| .setCommand(SessionCommand.newBuilder() |
| .setType(SessionCommand.CommandType.SWITCH_INPUT_FIELD_TYPE)) |
| .setContext(ProtoCommands.Context.newBuilder() |
| .setInputFieldType(InputFieldType.NORMAL))), |
| same(Optional.<KeyEventInterface>absent()), same(Optional.<EvaluationCallback>absent())); |
| replayAll(); |
| |
| executor.switchInputFieldType(InputFieldType.NORMAL); |
| |
| verifyAll(); |
| } |
| |
| @SmallTest |
| public void testUndoOrRewind() { |
| SessionExecutor executor = createSessionExecutorMock(); |
| EvaluationCallback evaluationCallback = createNiceMock(EvaluationCallback.class); |
| List<TouchEvent> touchEventList = Collections.singletonList(TouchEvent.getDefaultInstance()); |
| executor.evaluateAsynchronously( |
| matchesBuilder(Input.newBuilder() |
| .setType(CommandType.SEND_COMMAND) |
| .setCommand(SessionCommand.newBuilder() |
| .setType(SessionCommand.CommandType.UNDO_OR_REWIND)) |
| .addAllTouchEvents(touchEventList)), |
| same(Optional.<KeyEventInterface>absent()), sameOptional(evaluationCallback)); |
| replayAll(); |
| |
| executor.undoOrRewind(touchEventList, evaluationCallback); |
| |
| verifyAll(); |
| } |
| |
| @SmallTest |
| public void testInsertToStorage() { |
| SessionExecutor executor = createSessionExecutorMock(); |
| executor.evaluateAsynchronously( |
| matchesBuilder(Input.newBuilder() |
| .setType(CommandType.INSERT_TO_STORAGE) |
| .setStorageEntry(GenericStorageEntry.newBuilder() |
| .setType(StorageType.EMOTICON_HISTORY) |
| .setKey("TEST_KEY") |
| .addAllValue(Collections.singletonList(ByteString.copyFromUtf8("TEST_VALUE"))))), |
| same(Optional.<KeyEventInterface>absent()), same(Optional.<EvaluationCallback>absent())); |
| replayAll(); |
| |
| executor.insertToStorage( |
| StorageType.EMOTICON_HISTORY, "TEST_KEY", |
| Collections.singletonList(ByteString.copyFromUtf8("TEST_VALUE"))); |
| |
| verifyAll(); |
| } |
| |
| @SmallTest |
| public void testExpandSuggestion() { |
| SessionExecutor executor = createSessionExecutorMock(); |
| EvaluationCallback evaluationCallback = createNiceMock(EvaluationCallback.class); |
| executor.evaluateAsynchronously( |
| matchesBuilder(Input.newBuilder() |
| .setType(CommandType.SEND_COMMAND) |
| .setCommand( |
| SessionCommand.newBuilder().setType(SessionCommand.CommandType.EXPAND_SUGGESTION))), |
| same(Optional.<KeyEventInterface>absent()), sameOptional(evaluationCallback)); |
| replayAll(); |
| |
| executor.expandSuggestion(evaluationCallback); |
| |
| verifyAll(); |
| } |
| |
| @SmallTest |
| public void testPreferenceUsageStatsEvent() { |
| SessionExecutor executor = createSessionExecutorMock(); |
| |
| Context context = getInstrumentation().getContext(); |
| Context targetContext = getInstrumentation().getTargetContext(); |
| SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context); |
| assertNotNull(sharedPreferences); |
| |
| sharedPreferences.edit() |
| .putString(PreferenceUtil.PREF_LANDSCAPE_KEYBOARD_LAYOUT_KEY, KeyboardLayout.GODAN.name()) |
| .putString(PreferenceUtil.PREF_PORTRAIT_KEYBOARD_LAYOUT_KEY, KeyboardLayout.QWERTY.name()) |
| .putBoolean(PreferenceUtil.PREF_USE_PORTRAIT_KEYBOARD_SETTINGS_FOR_LANDSCAPE_KEY, false) |
| .commit(); |
| |
| resetAll(); |
| executor.evaluateAsynchronously( |
| matchesBuilder(Input.newBuilder() |
| .setType(CommandType.SEND_COMMAND) |
| .setCommand(SessionCommand.newBuilder() |
| .setType(SessionCommand.CommandType.USAGE_STATS_EVENT) |
| .setUsageStatsEvent(UsageStatsEvent.SOFTWARE_KEYBOARD_LAYOUT_LANDSCAPE) |
| .setUsageStatsEventIntValue(KeyboardLayout.GODAN.getId()))), |
| same(Optional.<KeyEventInterface>absent()), same(Optional.<EvaluationCallback>absent())); |
| executor.evaluateAsynchronously( |
| matchesBuilder(Input.newBuilder() |
| .setType(CommandType.SEND_COMMAND) |
| .setCommand(SessionCommand.newBuilder() |
| .setType(SessionCommand.CommandType.USAGE_STATS_EVENT) |
| .setUsageStatsEvent(UsageStatsEvent.SOFTWARE_KEYBOARD_LAYOUT_PORTRAIT) |
| .setUsageStatsEventIntValue(KeyboardLayout.QWERTY.getId()))), |
| same(Optional.<KeyEventInterface>absent()), same(Optional.<EvaluationCallback>absent())); |
| replayAll(); |
| executor.preferenceUsageStatsEvent(sharedPreferences, targetContext.getResources()); |
| verifyAll(); |
| |
| sharedPreferences.edit() |
| .putBoolean(PreferenceUtil.PREF_USE_PORTRAIT_KEYBOARD_SETTINGS_FOR_LANDSCAPE_KEY, true) |
| .commit(); |
| |
| resetAll(); |
| executor.evaluateAsynchronously( |
| matchesBuilder(Input.newBuilder() |
| .setType(CommandType.SEND_COMMAND) |
| .setCommand(SessionCommand.newBuilder() |
| .setType(SessionCommand.CommandType.USAGE_STATS_EVENT) |
| .setUsageStatsEvent(UsageStatsEvent.SOFTWARE_KEYBOARD_LAYOUT_LANDSCAPE) |
| .setUsageStatsEventIntValue(KeyboardLayout.QWERTY.getId()))), |
| same(Optional.<KeyEventInterface>absent()), same(Optional.<EvaluationCallback>absent())); |
| executor.evaluateAsynchronously( |
| matchesBuilder(Input.newBuilder() |
| .setType(CommandType.SEND_COMMAND) |
| .setCommand(SessionCommand.newBuilder() |
| .setType(SessionCommand.CommandType.USAGE_STATS_EVENT) |
| .setUsageStatsEvent(UsageStatsEvent.SOFTWARE_KEYBOARD_LAYOUT_PORTRAIT) |
| .setUsageStatsEventIntValue(KeyboardLayout.QWERTY.getId()))), |
| same(Optional.<KeyEventInterface>absent()), same(Optional.<EvaluationCallback>absent())); |
| replayAll(); |
| executor.preferenceUsageStatsEvent(sharedPreferences, targetContext.getResources()); |
| verifyAll(); |
| } |
| |
| @SmallTest |
| public void testTouchEventUsageStatsEvent() { |
| SessionExecutor executor = createSessionExecutorMock(); |
| |
| List<TouchEvent> touchEventList = Collections.singletonList(TouchEvent.getDefaultInstance()); |
| executor.evaluateAsynchronously( |
| matchesBuilder(Input.newBuilder() |
| .setType(CommandType.SEND_COMMAND) |
| .setCommand(SessionCommand.newBuilder() |
| .setType(SessionCommand.CommandType.USAGE_STATS_EVENT)) |
| .addAllTouchEvents(touchEventList)), |
| same(Optional.<KeyEventInterface>absent()), same(Optional.<EvaluationCallback>absent())); |
| replayAll(); |
| |
| executor.touchEventUsageStatsEvent(touchEventList); |
| |
| verifyAll(); |
| } |
| |
| @SmallTest |
| public void testSyncData() { |
| SessionExecutor executor = createSessionExecutorMock(); |
| executor.evaluateAsynchronously( |
| matchesBuilder(Input.newBuilder() |
| .setType(CommandType.SYNC_DATA)), |
| same(Optional.<KeyEventInterface>absent()), same(Optional.<EvaluationCallback>absent())); |
| replayAll(); |
| |
| executor.syncData(); |
| |
| verifyAll(); |
| } |
| |
| @SmallTest |
| public void testGetConfig() { |
| SessionExecutor executor = createSessionExecutorMock(); |
| |
| Config outputConfig = Config.newBuilder().build(); |
| expect(executor.evaluateSynchronously( |
| Input.newBuilder().setType(Input.CommandType.GET_CONFIG).build())) |
| .andReturn(Output.newBuilder().setConfig(outputConfig).build()); |
| replayAll(); |
| |
| assertSame(outputConfig, executor.getConfig()); |
| |
| verifyAll(); |
| } |
| |
| @SmallTest |
| public void testSetConfig() { |
| SessionExecutor executor = createSessionExecutorMock(); |
| |
| Config inputConfig = Config.newBuilder().build(); |
| executor.evaluateAsynchronously( |
| matchesBuilder(Input.newBuilder() |
| .setType(CommandType.SET_CONFIG) |
| .setConfig(inputConfig)), |
| same(Optional.<KeyEventInterface>absent()), same(Optional.<EvaluationCallback>absent())); |
| replayAll(); |
| |
| executor.setConfig(inputConfig); |
| |
| verifyAll(); |
| } |
| |
| @SmallTest |
| public void testSetImposedConfig() { |
| SessionExecutor executor = createSessionExecutorMock(); |
| |
| Config inputConfig = Config.newBuilder().build(); |
| executor.evaluateAsynchronously( |
| matchesBuilder(Input.newBuilder() |
| .setType(CommandType.SET_IMPOSED_CONFIG) |
| .setConfig(inputConfig)), |
| same(Optional.<KeyEventInterface>absent()), same(Optional.<EvaluationCallback>absent())); |
| replayAll(); |
| |
| executor.setImposedConfig(inputConfig); |
| |
| verifyAll(); |
| } |
| |
| @SmallTest |
| public void testClearUnusedUserPrediction() { |
| SessionExecutor executor = createSessionExecutorMock(); |
| expect(executor.evaluateSynchronously( |
| Input.newBuilder() |
| .setType(Input.CommandType.CLEAR_UNUSED_USER_PREDICTION) |
| .build())) |
| .andReturn(Output.getDefaultInstance()); |
| replayAll(); |
| |
| executor.clearUnusedUserPrediction(); |
| |
| verifyAll(); |
| } |
| |
| @SmallTest |
| public void testClearUserHistory() { |
| SessionExecutor executor = createSessionExecutorMock(); |
| expect(executor.evaluateSynchronously( |
| Input.newBuilder() |
| .setType(Input.CommandType.CLEAR_USER_HISTORY) |
| .build())) |
| .andReturn(Output.getDefaultInstance()); |
| replayAll(); |
| |
| executor.clearUserHistory(); |
| |
| verifyAll(); |
| } |
| |
| @SmallTest |
| public void testClearUserPrediction() { |
| SessionExecutor executor = createSessionExecutorMock(); |
| expect(executor.evaluateSynchronously( |
| Input.newBuilder() |
| .setType(Input.CommandType.CLEAR_USER_PREDICTION) |
| .build())) |
| .andReturn(Output.getDefaultInstance()); |
| replayAll(); |
| |
| executor.clearUserPrediction(); |
| |
| verifyAll(); |
| } |
| |
| @SmallTest |
| public void testClearStorage() { |
| SessionExecutor executor = createSessionExecutorMock(); |
| expect(executor.evaluateSynchronously( |
| Input.newBuilder() |
| .setType(Input.CommandType.CLEAR_STORAGE) |
| .setStorageEntry(GenericStorageEntry.newBuilder().setType(StorageType.EMOJI_HISTORY)) |
| .build())) |
| .andReturn(Output.getDefaultInstance()); |
| replayAll(); |
| |
| executor.clearStorage(StorageType.EMOJI_HISTORY); |
| |
| verifyAll(); |
| } |
| |
| @SmallTest |
| public void testReadAllFromStorage() { |
| SessionExecutor executor = createSessionExecutorMock(); |
| expect(executor.evaluateSynchronously( |
| Input.newBuilder() |
| .setType(CommandType.READ_ALL_FROM_STORAGE) |
| .setStorageEntry(GenericStorageEntry.newBuilder() |
| .setType(StorageType.EMOTICON_HISTORY)) |
| .build())) |
| .andReturn(Output.newBuilder() |
| .setStorageEntry(GenericStorageEntry.newBuilder() |
| .addAllValue(Collections.<ByteString>emptyList())) |
| .build()); |
| replayAll(); |
| |
| assertTrue(executor.readAllFromStorage(StorageType.EMOTICON_HISTORY).isEmpty()); |
| |
| verifyAll(); |
| } |
| |
| @SmallTest |
| public void testReload() { |
| SessionExecutor executor = createSessionExecutorMock(); |
| |
| executor.evaluateAsynchronously( |
| matchesBuilder(Input.newBuilder() |
| .setType(CommandType.RELOAD)), |
| same(Optional.<KeyEventInterface>absent()), same(Optional.<EvaluationCallback>absent())); |
| replayAll(); |
| |
| executor.reload(); |
| |
| verifyAll(); |
| } |
| |
| @SmallTest |
| public void testSendUserDictionaryCommand() { |
| SessionExecutor executor = createSessionExecutorMock(); |
| |
| UserDictionaryCommand command = UserDictionaryCommand.newBuilder() |
| .setType(UserDictionaryCommand.CommandType.NO_OPERATION) |
| .build(); |
| |
| expect(executor.evaluateSynchronously( |
| Input.newBuilder() |
| .setType(CommandType.SEND_USER_DICTIONARY_COMMAND) |
| .setUserDictionaryCommand(command) |
| .build())) |
| .andReturn(Output.getDefaultInstance()); |
| replayAll(); |
| |
| assertEquals(UserDictionaryCommandStatus.getDefaultInstance(), |
| executor.sendUserDictionaryCommand(command)); |
| |
| verifyAll(); |
| } |
| |
| @LargeTest |
| @StressTest |
| public void testStressConversion() throws InterruptedException { |
| MemoryLogger memoryLogger = new MemoryLogger("testStressConversion", 100); |
| |
| SessionExecutor session = SessionExecutor.getInstance(getInstrumentation().getContext()); |
| Context context = getInstrumentation().getTargetContext(); |
| session.reset(new SessionHandlerFactory(context), context); |
| |
| int actions = 1200; |
| char minHiragana = 0x3041; |
| char maxHiragana = 0x3096; |
| int compositionLength = 20; |
| Random random = new Random(0); // Make the result deterministic. |
| StringBuilder composition = new StringBuilder(); |
| memoryLogger.logMemory("start"); |
| for (int i = 0; i < actions; ++i) { |
| composition.setLength(0); |
| for (int j = 0; j < compositionLength; ++j) { |
| composition.append((char) (random.nextInt(maxHiragana - minHiragana + 1) + minHiragana)); |
| } |
| session.sendKey(ProtoCommands.KeyEvent.newBuilder() |
| .setKeyString(composition.toString()).build(), |
| createNiceMock(KeyEventInterface.class), Collections.<TouchEvent>emptyList(), |
| createNiceMock(EvaluationCallback.class)); |
| session.sendKey(ProtoCommands.KeyEvent.newBuilder() |
| .setSpecialKey(SpecialKey.SPACE).build(), |
| createNiceMock(KeyEventInterface.class), Collections.<TouchEvent>emptyList(), |
| createNiceMock(EvaluationCallback.class)); |
| final CountDownLatch latch = new CountDownLatch(1); |
| session.submit(new EvaluationCallback() { |
| @Override |
| public void onCompleted( |
| Optional<Command> command, Optional<KeyEventInterface> triggeringKeyEvent) { |
| latch.countDown(); |
| } |
| }); |
| latch.await(); |
| session.deleteSession(); |
| memoryLogger.logMemoryInterval(); |
| } |
| memoryLogger.logMemory("end"); |
| |
| Thread.sleep(30 * 1000); |
| memoryLogger.logMemory("waited"); |
| } |
| |
| @LargeTest |
| @StressTest |
| public void testStressConversionKao() throws InterruptedException { |
| MemoryLogger memoryLogger = new MemoryLogger("testStressConversionKao", 10); |
| |
| SessionExecutor session = SessionExecutor.getInstance(getInstrumentation().getContext()); |
| Context context = getInstrumentation().getTargetContext(); |
| session.reset(new SessionHandlerFactory(context), context); |
| |
| String composition = Strings.repeat("かお、", 100); |
| memoryLogger.logMemory("start"); |
| session.sendKey(ProtoCommands.KeyEvent.newBuilder() |
| .setKeyString(composition.toString()).build(), |
| createNiceMock(KeyEventInterface.class), Collections.<TouchEvent>emptyList(), |
| createNiceMock(EvaluationCallback.class)); |
| session.sendKey(ProtoCommands.KeyEvent.newBuilder() |
| .setSpecialKey(SpecialKey.SPACE).build(), |
| createNiceMock(KeyEventInterface.class), Collections.<TouchEvent>emptyList(), |
| createNiceMock(EvaluationCallback.class)); |
| final CountDownLatch latch = new CountDownLatch(1); |
| session.submit(new EvaluationCallback() { |
| @Override |
| public void onCompleted( |
| Optional<Command> command, Optional<KeyEventInterface> triggeringKeyEvent) { |
| latch.countDown(); |
| } |
| }); |
| latch.await(); |
| memoryLogger.logMemory("beforedelete"); |
| session.deleteSession(); |
| memoryLogger.logMemory("end"); |
| |
| Thread.sleep(60 * 1000); |
| memoryLogger.logMemory("waited"); |
| } |
| } |