aboutsummaryrefslogtreecommitdiffstats
path: root/tests/src/com/android/inputmethod/latin/InputLogicTests.java
diff options
context:
space:
mode:
Diffstat (limited to 'tests/src/com/android/inputmethod/latin/InputLogicTests.java')
-rw-r--r--tests/src/com/android/inputmethod/latin/InputLogicTests.java166
1 files changed, 166 insertions, 0 deletions
diff --git a/tests/src/com/android/inputmethod/latin/InputLogicTests.java b/tests/src/com/android/inputmethod/latin/InputLogicTests.java
index 78bd4d741..9d53b63f1 100644
--- a/tests/src/com/android/inputmethod/latin/InputLogicTests.java
+++ b/tests/src/com/android/inputmethod/latin/InputLogicTests.java
@@ -19,9 +19,13 @@ package com.android.inputmethod.latin;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
+import android.os.Looper;
+import android.os.MessageQueue;
import android.preference.PreferenceManager;
import android.test.ServiceTestCase;
import android.text.InputType;
+import android.text.SpannableStringBuilder;
+import android.text.style.SuggestionSpan;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.ViewGroup;
@@ -127,6 +131,51 @@ public class InputLogicTests extends ServiceTestCase<LatinIME> {
}
}
+ // We need to run the messages added to the handler from LatinIME. The only way to do
+ // that is to call Looper#loop() on the right looper, so we're going to get the looper
+ // object and call #loop() here. The messages in the handler actually run on the UI
+ // thread of the keyboard by design of the handler, so we want to call it synchronously
+ // on the same thread that the tests are running on to mimic the actual environment as
+ // closely as possible.
+ // Now, Looper#loop() never exits in normal operation unless the Looper#quit() method
+ // is called, so we need to do that at the right time so that #loop() returns at some
+ // point and we don't end up in an infinite loop.
+ // After we quit, the looper is still technically ready to process more messages but
+ // the handler will refuse to enqueue any because #quit() has been called and it
+ // explicitly tests for it on message enqueuing, so we'll have to reset it so that
+ // it lets us continue normal operation.
+ private void runMessages() {
+ // Here begins deep magic.
+ final Looper looper = mLatinIME.mHandler.getLooper();
+ mLatinIME.mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ looper.quit();
+ }
+ });
+ // The only way to get out of Looper#loop() is to call #quit() on it (or on its queue).
+ // Once #quit() is called remaining messages are not processed, which is why we post
+ // a message that calls it instead of calling it directly.
+ looper.loop();
+
+ // Once #quit() has been called, the message queue has an "mQuiting" field that prevents
+ // any subsequent post in this queue. However the queue itself is still fully functional!
+ // If we have a way of resetting "queue.mQuiting" then we can continue using it as normal,
+ // coming back to this method to run the messages.
+ MessageQueue queue = looper.getQueue();
+ try {
+ // However there is no way of doing it externally, and mQuiting is private.
+ // So... get out the big guns.
+ java.lang.reflect.Field f = MessageQueue.class.getDeclaredField("mQuiting");
+ f.setAccessible(true); // What do you mean "private"?
+ f.setBoolean(queue, false);
+ } catch (NoSuchFieldException e) {
+ throw new RuntimeException(e);
+ } catch (IllegalAccessException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
// type(int) and type(String): helper methods to send a code point resp. a string to LatinIME.
private void type(final int codePoint) {
// onPressKey and onReleaseKey are explicitly deactivated here, but they do happen in the
@@ -152,6 +201,13 @@ public class InputLogicTests extends ServiceTestCase<LatinIME> {
}
}
+ // Helper to avoid writing the try{}catch block each time
+ private static void sleep(final int milliseconds) {
+ try {
+ Thread.sleep(milliseconds);
+ } catch (InterruptedException e) {}
+ }
+
public void testTypeWord() {
final String WORD_TO_TYPE = "abcd";
type(WORD_TO_TYPE);
@@ -446,5 +502,115 @@ public class InputLogicTests extends ServiceTestCase<LatinIME> {
EXPECTED_RESULT, mTextView.getText().toString());
}
+ // A helper class to ease span tests
+ private static class Span {
+ final SpannableStringBuilder mInputText;
+ final SuggestionSpan mSpan;
+ final int mStart;
+ final int mEnd;
+ // The supplied CharSequence should be an instance of SpannableStringBuilder,
+ // and it should contain exactly zero or one SuggestionSpan. Otherwise, an exception
+ // is thrown.
+ public Span(final CharSequence inputText) {
+ mInputText = (SpannableStringBuilder)inputText;
+ final SuggestionSpan[] spans =
+ mInputText.getSpans(0, mInputText.length(), SuggestionSpan.class);
+ if (0 == spans.length) {
+ mSpan = null;
+ mStart = -1;
+ mEnd = -1;
+ } else if (1 == spans.length) {
+ mSpan = spans[0];
+ mStart = mInputText.getSpanStart(mSpan);
+ mEnd = mInputText.getSpanEnd(mSpan);
+ } else {
+ throw new RuntimeException("Expected one SuggestionSpan, found " + spans.length);
+ }
+ }
+ public boolean isAutoCorrectionIndicator() {
+ return 0 != (SuggestionSpan.FLAG_AUTO_CORRECTION & mSpan.getFlags());
+ }
+ }
+
+ static final int DELAY_TO_WAIT_FOR_UNDERLINE = 200; // The message is posted with a 100 ms delay
+ public void testBlueUnderline() {
+ final String STRING_TO_TYPE = "tgis";
+ final int EXPECTED_SPAN_START = 0;
+ final int EXPECTED_SPAN_END = 4;
+ type(STRING_TO_TYPE);
+ sleep(DELAY_TO_WAIT_FOR_UNDERLINE);
+ runMessages();
+ final Span span = new Span(mTextView.getText());
+ assertEquals("show blue underline, span start", EXPECTED_SPAN_START, span.mStart);
+ assertEquals("show blue underline, span end", EXPECTED_SPAN_END, span.mEnd);
+ assertEquals("show blue underline, span color", true, span.isAutoCorrectionIndicator());
+ }
+
+ public void testBlueUnderlineDisappears() {
+ final String STRING_1_TO_TYPE = "tgis";
+ final String STRING_2_TO_TYPE = "q";
+ final int EXPECTED_SPAN_START = 0;
+ final int EXPECTED_SPAN_END = 5;
+ type(STRING_1_TO_TYPE);
+ sleep(DELAY_TO_WAIT_FOR_UNDERLINE);
+ runMessages();
+ type(STRING_2_TO_TYPE);
+ // We haven't have time to look into the dictionary yet, so the line should still be
+ // blue to avoid any flicker.
+ final Span spanBefore = new Span(mTextView.getText());
+ assertEquals("extend blue underline, span start", EXPECTED_SPAN_START, spanBefore.mStart);
+ assertEquals("extend blue underline, span end", EXPECTED_SPAN_END, spanBefore.mEnd);
+ assertEquals("extend blue underline, span color", true,
+ spanBefore.isAutoCorrectionIndicator());
+ sleep(DELAY_TO_WAIT_FOR_UNDERLINE);
+ runMessages();
+ // Now we have been able to re-evaluate the word, there shouldn't be an auto-correction span
+ final Span spanAfter = new Span(mTextView.getText());
+ assertNull("hide blue underline", spanAfter.mSpan);
+ }
+
+ public void testBlueUnderlineOnBackspace() {
+ final String STRING_TO_TYPE = "tgis";
+ final int EXPECTED_SPAN_START = 0;
+ final int EXPECTED_SPAN_END = 4;
+ type(STRING_TO_TYPE);
+ sleep(DELAY_TO_WAIT_FOR_UNDERLINE);
+ runMessages();
+ type(Keyboard.CODE_SPACE);
+ sleep(DELAY_TO_WAIT_FOR_UNDERLINE);
+ runMessages();
+ type(Keyboard.CODE_DELETE);
+ sleep(DELAY_TO_WAIT_FOR_UNDERLINE);
+ runMessages();
+ type(Keyboard.CODE_DELETE);
+ sleep(DELAY_TO_WAIT_FOR_UNDERLINE);
+ runMessages();
+ final Span span = new Span(mTextView.getText());
+ assertEquals("show blue underline after backspace, span start",
+ EXPECTED_SPAN_START, span.mStart);
+ assertEquals("show blue underline after backspace, span end",
+ EXPECTED_SPAN_END, span.mEnd);
+ assertEquals("show blue underline after backspace, span color", true,
+ span.isAutoCorrectionIndicator());
+ }
+
+ public void testBlueUnderlineDisappearsWhenCursorMoved() {
+ final String STRING_TO_TYPE = "tgis";
+ final int NEW_CURSOR_POSITION = 0;
+ type(STRING_TO_TYPE);
+ sleep(DELAY_TO_WAIT_FOR_UNDERLINE);
+ // Simulate the onUpdateSelection() event
+ mLatinIME.onUpdateSelection(0, 0, STRING_TO_TYPE.length(), STRING_TO_TYPE.length(), -1, -1);
+ runMessages();
+ // Here the blue underline has been set. testBlueUnderline() is testing for this already,
+ // so let's not test it here again.
+ // Now simulate the user moving the cursor.
+ mInputConnection.setSelection(NEW_CURSOR_POSITION, NEW_CURSOR_POSITION);
+ mLatinIME.onUpdateSelection(0, 0, NEW_CURSOR_POSITION, NEW_CURSOR_POSITION, -1, -1);
+ sleep(DELAY_TO_WAIT_FOR_UNDERLINE);
+ runMessages();
+ final Span span = new Span(mTextView.getText());
+ assertNull("blue underline removed when cursor is moved", span.mSpan);
+ }
// TODO: Add some tests for non-BMP characters
}