Revert "Integrate new looper apis into testables"
This reverts commit f715f417d23d0605abcac309135340ac6ce311ce.
Change-Id: I4c0c12d8687426cecccf0f8dad710ea42699727b
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BluetoothControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BluetoothControllerImplTest.java
index f516d74..8808988 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BluetoothControllerImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BluetoothControllerImplTest.java
@@ -20,9 +20,7 @@
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothProfile;
-import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
-import android.testing.TestableLooper.RunWithLooper;
import com.android.settingslib.bluetooth.BluetoothEventManager;
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
@@ -33,13 +31,10 @@
import org.junit.Before;
import org.junit.Test;
-import org.junit.runner.RunWith;
import java.util.ArrayList;
import java.util.List;
-@RunWith(AndroidTestingRunner.class)
-@RunWithLooper
public class BluetoothControllerImplTest extends SysuiTestCase {
private LocalBluetoothManager mMockBluetoothManager;
@@ -52,7 +47,7 @@
@Before
public void setup() throws Exception {
- mTestableLooper = TestableLooper.get(this);
+ mTestableLooper = new TestableLooper();
mMockBluetoothManager = mDependency.injectMockDependency(LocalBluetoothManager.class);
mDevices = new ArrayList<>();
mMockDeviceManager = mock(CachedBluetoothDeviceManager.class);
diff --git a/tests/testables/src/android/testing/AndroidTestingRunner.java b/tests/testables/src/android/testing/AndroidTestingRunner.java
index a425f70..816ed03 100644
--- a/tests/testables/src/android/testing/AndroidTestingRunner.java
+++ b/tests/testables/src/android/testing/AndroidTestingRunner.java
@@ -18,7 +18,7 @@
import android.support.test.internal.runner.junit4.statement.RunBefores;
import android.support.test.internal.runner.junit4.statement.UiThreadStatement;
-import android.testing.TestableLooper.LooperFrameworkMethod;
+import android.testing.TestableLooper.LooperStatement;
import android.testing.TestableLooper.RunWithLooper;
import org.junit.After;
@@ -30,7 +30,6 @@
import org.junit.runners.model.InitializationError;
import org.junit.runners.model.Statement;
-import java.util.ArrayList;
import java.util.List;
/**
@@ -50,21 +49,28 @@
@Override
protected Statement methodInvoker(FrameworkMethod method, Object test) {
- method = looperWrap(method, test, method);
- final Statement statement = super.methodInvoker(method, test);
- return shouldRunOnUiThread(method) ? new UiThreadStatement(statement, true) : statement;
+ return shouldRunOnUiThread(method) ? new UiThreadStatement(
+ methodInvokerInt(method, test), true) : methodInvokerInt(method, test);
+ }
+
+ protected Statement methodInvokerInt(FrameworkMethod method, Object test) {
+ RunWithLooper annotation = method.getAnnotation(RunWithLooper.class);
+ if (annotation == null) annotation = mKlass.getAnnotation(RunWithLooper.class);
+ if (annotation != null) {
+ return new LooperStatement(super.methodInvoker(method, test),
+ annotation.setAsMainLooper(), test);
+ }
+ return super.methodInvoker(method, test);
}
protected Statement withBefores(FrameworkMethod method, Object target, Statement statement) {
- List befores = looperWrap(method, target,
- this.getTestClass().getAnnotatedMethods(Before.class));
+ List befores = this.getTestClass().getAnnotatedMethods(Before.class);
return befores.isEmpty() ? statement : new RunBefores(method, statement,
befores, target);
}
protected Statement withAfters(FrameworkMethod method, Object target, Statement statement) {
- List afters = looperWrap(method, target,
- this.getTestClass().getAnnotatedMethods(After.class));
+ List afters = this.getTestClass().getAnnotatedMethods(After.class);
return afters.isEmpty() ? statement : new RunAfters(method, statement, afters,
target);
}
@@ -82,30 +88,6 @@
return annotation == null ? 0L : annotation.timeout();
}
- protected List<FrameworkMethod> looperWrap(FrameworkMethod method, Object test,
- List<FrameworkMethod> methods) {
- RunWithLooper annotation = method.getAnnotation(RunWithLooper.class);
- if (annotation == null) annotation = mKlass.getAnnotation(RunWithLooper.class);
- if (annotation != null) {
- methods = new ArrayList<>(methods);
- for (int i = 0; i < methods.size(); i++) {
- methods.set(i, LooperFrameworkMethod.get(methods.get(i),
- annotation.setAsMainLooper(), test));
- }
- }
- return methods;
- }
-
- protected FrameworkMethod looperWrap(FrameworkMethod method, Object test,
- FrameworkMethod base) {
- RunWithLooper annotation = method.getAnnotation(RunWithLooper.class);
- if (annotation == null) annotation = mKlass.getAnnotation(RunWithLooper.class);
- if (annotation != null) {
- return LooperFrameworkMethod.get(base, annotation.setAsMainLooper(), test);
- }
- return base;
- }
-
public boolean shouldRunOnUiThread(FrameworkMethod method) {
if (mKlass.getAnnotation(UiThreadTest.class) != null) {
return true;
diff --git a/tests/testables/src/android/testing/TestableLooper.java b/tests/testables/src/android/testing/TestableLooper.java
index 62490bc..8a33cf9 100644
--- a/tests/testables/src/android/testing/TestableLooper.java
+++ b/tests/testables/src/android/testing/TestableLooper.java
@@ -15,21 +15,20 @@
package android.testing;
import android.os.Handler;
-import android.os.HandlerThread;
import android.os.Looper;
import android.os.Message;
import android.os.MessageQueue;
-import android.os.TestLooperManager;
-import android.support.test.InstrumentationRegistry;
import android.util.ArrayMap;
-import org.junit.runners.model.FrameworkMethod;
+import org.junit.runners.model.Statement;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
+import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
+import java.lang.reflect.Method;
import java.util.Map;
/**
@@ -39,35 +38,65 @@
*/
public class TestableLooper {
+ private final Method mNext;
+ private final Method mRecycleUnchecked;
+
private Looper mLooper;
private MessageQueue mQueue;
private boolean mMain;
private Object mOriginalMain;
private MessageHandler mMessageHandler;
+ private int mParsedCount;
private Handler mHandler;
private Message mEmptyMessage;
- private TestLooperManager mQueueWrapper;
- public TestableLooper(Looper l) throws Exception {
- this(InstrumentationRegistry.getInstrumentation().acquireLooperManager(l), l);
+ public TestableLooper() throws Exception {
+ this(true);
}
- private TestableLooper(TestLooperManager wrapper, Looper l) throws Exception {
- mQueueWrapper = wrapper;
- setupQueue(l);
- }
-
- private TestableLooper(Looper looper, boolean b) throws Exception {
- setupQueue(looper);
+ public TestableLooper(boolean setMyLooper) throws Exception {
+ setupQueue(setMyLooper);
+ mNext = mQueue.getClass().getDeclaredMethod("next");
+ mNext.setAccessible(true);
+ mRecycleUnchecked = Message.class.getDeclaredMethod("recycleUnchecked");
+ mRecycleUnchecked.setAccessible(true);
}
public Looper getLooper() {
return mLooper;
}
- private void setupQueue(Looper l) throws Exception {
- mLooper = l;
+ private void clearLooper() throws NoSuchFieldException, IllegalAccessException {
+ Field field = Looper.class.getDeclaredField("sThreadLocal");
+ field.setAccessible(true);
+ ThreadLocal<Looper> sThreadLocal = (ThreadLocal<Looper>) field.get(null);
+ sThreadLocal.set(null);
+ }
+
+ private boolean setForCurrentThread() throws NoSuchFieldException, IllegalAccessException {
+ if (Looper.myLooper() != mLooper) {
+ Field field = Looper.class.getDeclaredField("sThreadLocal");
+ field.setAccessible(true);
+ ThreadLocal<Looper> sThreadLocal = (ThreadLocal<Looper>) field.get(null);
+ sThreadLocal.set(mLooper);
+ return true;
+ }
+ return false;
+ }
+
+ private void setupQueue(boolean setMyLooper) throws Exception {
+ if (setMyLooper) {
+ clearLooper();
+ Looper.prepare();
+ mLooper = Looper.myLooper();
+ } else {
+ Constructor<Looper> constructor = Looper.class.getDeclaredConstructor(
+ boolean.class);
+ constructor.setAccessible(true);
+ mLooper = constructor.newInstance(true);
+ }
+
mQueue = mLooper.getQueue();
mHandler = new Handler(mLooper);
}
@@ -92,7 +121,9 @@
* tests.
*/
public void destroy() throws NoSuchFieldException, IllegalAccessException {
- mQueueWrapper.release();
+ if (Looper.myLooper() == mLooper) {
+ clearLooper();
+ }
if (mMain && mOriginalMain != null) {
Field field = mLooper.getClass().getDeclaredField("sMainLooper");
field.setAccessible(true);
@@ -133,26 +164,26 @@
private boolean parseMessageInt() {
try {
- Message result = mQueueWrapper.next();
+ Message result = (Message) mNext.invoke(mQueue);
if (result != null) {
// This is a break message.
if (result == mEmptyMessage) {
- mQueueWrapper.recycle(result);
+ mRecycleUnchecked.invoke(result);
return false;
}
if (mMessageHandler != null) {
if (mMessageHandler.onMessageHandled(result)) {
result.getTarget().dispatchMessage(result);
- mQueueWrapper.recycle(result);
+ mRecycleUnchecked.invoke(result);
} else {
- mQueueWrapper.recycle(result);
+ mRecycleUnchecked.invoke(result);
// Message handler indicated it doesn't want us to continue.
return false;
}
} else {
result.getTarget().dispatchMessage(result);
- mQueueWrapper.recycle(result);
+ mRecycleUnchecked.invoke(result);
}
} else {
// No messages, don't continue parsing
@@ -168,14 +199,10 @@
* Runs an executable with myLooper set and processes all messages added.
*/
public void runWithLooper(RunnableWithException runnable) throws Exception {
- new Handler(getLooper()).post(() -> {
- try {
- runnable.run();
- } catch (Exception e) {
- throw new RuntimeException(e);
- }
- });
+ boolean set = setForCurrentThread();
+ runnable.run();
processAllMessages();
+ if (set) clearLooper();
}
public interface RunnableWithException {
@@ -194,131 +221,33 @@
return sLoopers.get(test);
}
- public static class LooperFrameworkMethod extends FrameworkMethod {
- private HandlerThread mHandlerThread;
+ public static class LooperStatement extends Statement {
+ private final boolean mSetAsMain;
+ private final Statement mBase;
+ private final TestableLooper mLooper;
- private final TestableLooper mTestableLooper;
- private final Looper mLooper;
- private final Handler mHandler;
-
- public LooperFrameworkMethod(FrameworkMethod base, boolean setAsMain, Object test) {
- super(base.getMethod());
+ public LooperStatement(Statement base, boolean setAsMain, Object test) {
+ mBase = base;
try {
- mLooper = setAsMain ? Looper.getMainLooper() : createLooper();
- mTestableLooper = new TestableLooper(mLooper, false);
+ mLooper = new TestableLooper(false);
+ sLoopers.put(test, mLooper);
+ mSetAsMain = setAsMain;
} catch (Exception e) {
throw new RuntimeException(e);
}
- sLoopers.put(test, mTestableLooper);
- mHandler = new Handler(mLooper);
- }
-
- public LooperFrameworkMethod(TestableLooper other, FrameworkMethod base) {
- super(base.getMethod());
- mLooper = other.mLooper;
- mTestableLooper = other;
- mHandler = new Handler(mLooper);
- }
-
- public static FrameworkMethod get(FrameworkMethod base, boolean setAsMain, Object test) {
- if (sLoopers.containsKey(test)) {
- return new LooperFrameworkMethod(sLoopers.get(test), base);
- }
- return new LooperFrameworkMethod(base, setAsMain, test);
}
@Override
- public Object invokeExplosively(Object target, Object... params) throws Throwable {
- if (Looper.myLooper() == mLooper) {
- // Already on the right thread from another statement, just execute then.
- return super.invokeExplosively(target, params);
+ public void evaluate() throws Throwable {
+ mLooper.setForCurrentThread();
+ if (mSetAsMain) {
+ mLooper.setAsMainLooper();
}
- boolean set = mTestableLooper.mQueueWrapper == null;
- if (set) {
- mTestableLooper.mQueueWrapper = InstrumentationRegistry.getInstrumentation()
- .acquireLooperManager(mLooper);
- }
+
try {
- Object[] ret = new Object[1];
- // Run the execution on the looper thread.
- Runnable execute = () -> {
- try {
- ret[0] = super.invokeExplosively(target, params);
- } catch (Throwable throwable) {
- throw new LooperException(throwable);
- }
- };
- mHandler.post(execute);
- // Try to wait for the message to be queued.
- for (int i = 0; i < 10; i++) {
- if (!mTestableLooper.mQueueWrapper.hasMessages(mHandler, null, execute)) {
- Thread.sleep(1);
- }
- }
- if (!mTestableLooper.mQueueWrapper.hasMessages(mHandler, null, execute)) {
- throw new RuntimeException("Message didn't queue...");
- }
- Message m = mTestableLooper.mQueueWrapper.next();
- // Parse all other messages until we get to ours.
- while (m.getTarget() != mHandler) {
- try {
- mTestableLooper.mQueueWrapper.execute(m);
- } catch (LooperException e) {
- throw e.getSource();
- } finally {
- mTestableLooper.mQueueWrapper.recycle(m);
- }
- m = mTestableLooper.mQueueWrapper.next();
- }
- // Dispatch our message.
- try {
- mTestableLooper.mQueueWrapper.execute(m);
- } catch (LooperException e) {
- throw e.getSource();
- } catch (RuntimeException re) {
- // If the TestLooperManager has to post, it will wrap what it throws in a
- // RuntimeException, make sure we grab the actual source.
- if (re.getCause() instanceof LooperException) {
- throw ((LooperException) re.getCause()).getSource();
- } else {
- throw re.getCause();
- }
- } finally {
- mTestableLooper.mQueueWrapper.recycle(m);
- }
- return ret[0];
+ mBase.evaluate();
} finally {
- if (set) {
- mTestableLooper.mQueueWrapper.release();
- mTestableLooper.mQueueWrapper = null;
- }
- }
- }
-
- private Looper createLooper() {
- // TODO: Find way to share these.
- mHandlerThread = new HandlerThread(TestableLooper.class.getSimpleName());
- mHandlerThread.start();
- return mHandlerThread.getLooper();
- }
-
- @Override
- protected void finalize() throws Throwable {
- super.finalize();
- if (mHandlerThread != null) {
- mHandlerThread.quit();
- }
- }
-
- private static class LooperException extends RuntimeException {
- private final Throwable mSource;
-
- public LooperException(Throwable t) {
- mSource = t;
- }
-
- public Throwable getSource() {
- return mSource;
+ mLooper.destroy();
}
}
}
diff --git a/tests/testables/tests/src/android/testing/TestableLooperTest.java b/tests/testables/tests/src/android/testing/TestableLooperTest.java
index 12f1d0a..18e5fff 100644
--- a/tests/testables/tests/src/android/testing/TestableLooperTest.java
+++ b/tests/testables/tests/src/android/testing/TestableLooperTest.java
@@ -24,16 +24,17 @@
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.testing.TestableLooper.MessageHandler;
import android.testing.TestableLooper.RunWithLooper;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
@RunWith(AndroidTestingRunner.class)
@RunWithLooper
public class TestableLooperTest {
@@ -45,6 +46,11 @@
mTestableLooper = TestableLooper.get(this);
}
+ @After
+ public void tearDown() throws Exception {
+ mTestableLooper.destroy();
+ }
+
@Test
public void testMessageExecuted() throws Exception {
Handler h = new Handler();
@@ -127,23 +133,39 @@
@Test
public void testMainLooper() throws Exception {
assertNotEquals(Looper.myLooper(), Looper.getMainLooper());
+
+ Looper originalMain = Looper.getMainLooper();
+ mTestableLooper.setAsMainLooper();
+ assertEquals(Looper.myLooper(), Looper.getMainLooper());
+ Runnable r = mock(Runnable.class);
+
+ new Handler(Looper.getMainLooper()).post(r);
+ mTestableLooper.processAllMessages();
+
+ verify(r).run();
+ mTestableLooper.destroy();
+
+ assertEquals(originalMain, Looper.getMainLooper());
+ }
+
+ @Test
+ public void testNotMyLooper() throws Exception {
+ TestableLooper looper = new TestableLooper(false);
+
+ assertEquals(Looper.myLooper(), mTestableLooper.getLooper());
+ assertNotEquals(Looper.myLooper(), looper.getLooper());
+
Runnable r = mock(Runnable.class);
Runnable r2 = mock(Runnable.class);
- TestableLooper testableLooper = new TestableLooper(Looper.getMainLooper());
+ new Handler().post(r);
+ new Handler(looper.getLooper()).post(r2);
- try {
- testableLooper.setMessageHandler(m -> {
- if (m.getCallback() == r) return true;
- return false;
- });
- new Handler(Looper.getMainLooper()).post(r);
- testableLooper.processAllMessages();
+ looper.processAllMessages();
+ verify(r2).run();
+ verify(r, never()).run();
- verify(r).run();
- verify(r2, never()).run();
- } finally {
- testableLooper.destroy();
- }
+ mTestableLooper.processAllMessages();
+ verify(r).run();
}
@Test