1 /* 2 * Copyright (C) 2014 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 package com.android.systemui; 17 18 import static org.mockito.Mockito.spy; 19 import static org.mockito.Mockito.when; 20 21 import android.app.Instrumentation; 22 import android.content.Context; 23 import android.os.Handler; 24 import android.os.Looper; 25 import android.os.MessageQueue; 26 import android.support.test.InstrumentationRegistry; 27 import android.support.test.filters.SmallTest; 28 import android.testing.LeakCheck; 29 import android.util.Log; 30 31 import org.junit.After; 32 import org.junit.Before; 33 import org.junit.Rule; 34 35 import java.util.concurrent.ExecutionException; 36 import java.util.concurrent.Future; 37 38 /** 39 * Base class that does System UI specific setup. 40 */ 41 public abstract class SysuiTestCase { 42 43 private static final String TAG = "SysuiTestCase"; 44 45 private Handler mHandler; 46 @Rule 47 public SysuiTestableContext mContext = new SysuiTestableContext( 48 InstrumentationRegistry.getContext(), getLeakCheck()); 49 public TestableDependency mDependency = new TestableDependency(mContext); 50 private Instrumentation mRealInstrumentation; 51 52 @Before SysuiSetup()53 public void SysuiSetup() throws Exception { 54 System.setProperty("dexmaker.share_classloader", "true"); 55 SystemUIFactory.createFromConfig(mContext); 56 57 mRealInstrumentation = InstrumentationRegistry.getInstrumentation(); 58 Instrumentation inst = spy(mRealInstrumentation); 59 when(inst.getContext()).thenThrow(new RuntimeException( 60 "SysUI Tests should use SysuiTestCase#getContext or SysuiTestCase#mContext")); 61 when(inst.getTargetContext()).thenThrow(new RuntimeException( 62 "SysUI Tests should use SysuiTestCase#getContext or SysuiTestCase#mContext")); 63 InstrumentationRegistry.registerInstance(inst, InstrumentationRegistry.getArguments()); 64 } 65 66 @After SysuiTeardown()67 public void SysuiTeardown() { 68 InstrumentationRegistry.registerInstance(mRealInstrumentation, 69 InstrumentationRegistry.getArguments()); 70 } 71 getLeakCheck()72 protected LeakCheck getLeakCheck() { 73 return null; 74 } 75 getContext()76 public Context getContext() { 77 return mContext; 78 } 79 waitForIdleSync()80 protected void waitForIdleSync() { 81 if (mHandler == null) { 82 mHandler = new Handler(Looper.getMainLooper()); 83 } 84 waitForIdleSync(mHandler); 85 } 86 waitForUiOffloadThread()87 protected void waitForUiOffloadThread() { 88 Future<?> future = Dependency.get(UiOffloadThread.class).submit(() -> {}); 89 try { 90 future.get(); 91 } catch (InterruptedException | ExecutionException e) { 92 Log.e(TAG, "Failed to wait for ui offload thread.", e); 93 } 94 } 95 waitForIdleSync(Handler h)96 public static void waitForIdleSync(Handler h) { 97 validateThread(h.getLooper()); 98 Idler idler = new Idler(null); 99 h.getLooper().getQueue().addIdleHandler(idler); 100 // Ensure we are non-idle, so the idle handler can run. 101 h.post(new EmptyRunnable()); 102 idler.waitForIdle(); 103 } 104 validateThread(Looper l)105 private static final void validateThread(Looper l) { 106 if (Looper.myLooper() == l) { 107 throw new RuntimeException( 108 "This method can not be called from the looper being synced"); 109 } 110 } 111 112 public static final class EmptyRunnable implements Runnable { run()113 public void run() { 114 } 115 } 116 117 public static final class Idler implements MessageQueue.IdleHandler { 118 private final Runnable mCallback; 119 private boolean mIdle; 120 Idler(Runnable callback)121 public Idler(Runnable callback) { 122 mCallback = callback; 123 mIdle = false; 124 } 125 126 @Override queueIdle()127 public boolean queueIdle() { 128 if (mCallback != null) { 129 mCallback.run(); 130 } 131 synchronized (this) { 132 mIdle = true; 133 notifyAll(); 134 } 135 return false; 136 } 137 waitForIdle()138 public void waitForIdle() { 139 synchronized (this) { 140 while (!mIdle) { 141 try { 142 wait(); 143 } catch (InterruptedException e) { 144 } 145 } 146 } 147 } 148 } 149 } 150