From d9f6a0ebb7aba7e5047034bcd92b9e5028fbc830 Mon Sep 17 00:00:00 2001
From: Jean Chalard <jchalard@google.com>
Date: Thu, 23 Feb 2012 20:23:01 -0800
Subject: [PATCH] Add a method to run the handler's messages from the tests

This is really brutal, but after trying many many things I didn't
find a better way. It works with the current implementation of
MessageQueue.
Since this only introduces a utility method it doesn't really do
anything, but the functionality it provides will be essential to
Bug: 5975557

Change-Id: I81c4113a08f9a8d8a88294d7dd3b6c8c483c8b1d
---
 .../inputmethod/latin/InputLogicTests.java    | 47 +++++++++++++++++++
 1 file changed, 47 insertions(+)

diff --git a/tests/src/com/android/inputmethod/latin/InputLogicTests.java b/tests/src/com/android/inputmethod/latin/InputLogicTests.java
index 78bd4d741d..6286019249 100644
--- a/tests/src/com/android/inputmethod/latin/InputLogicTests.java
+++ b/tests/src/com/android/inputmethod/latin/InputLogicTests.java
@@ -19,6 +19,8 @@ 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;
@@ -127,6 +129,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
-- 
GitLab