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 import org.mockito.invocation.InvocationOnMock; 35 import org.mockito.stubbing.Answer; 36 37 import java.util.concurrent.ExecutionException; 38 import java.util.concurrent.Future; 39 40 /** 41 * Base class that does System UI specific setup. 42 */ 43 public abstract class SysuiTestCase { 44 45 private static final String TAG = "SysuiTestCase"; 46 47 private Handler mHandler; 48 @Rule 49 public SysuiTestableContext mContext = new SysuiTestableContext( 50 InstrumentationRegistry.getContext(), getLeakCheck()); 51 public TestableDependency mDependency = new TestableDependency(mContext); 52 private Instrumentation mRealInstrumentation; 53 54 @Before 55 public void SysuiSetup() throws Exception { 56 System.setProperty("dexmaker.share_classloader", "true"); 57 mContext.setTheme(R.style.Theme_SystemUI); 58 SystemUIFactory.createFromConfig(mContext); 59 60 mRealInstrumentation = InstrumentationRegistry.getInstrumentation(); 61 Instrumentation inst = spy(mRealInstrumentation); 62 when(inst.getContext()).thenAnswer(invocation -> { 63 throw new RuntimeException( 64 "SysUI Tests should use SysuiTestCase#getContext or SysuiTestCase#mContext"); 65 }); 66 when(inst.getTargetContext()).thenAnswer(invocation -> { 67 throw new RuntimeException( 68 "SysUI Tests should use SysuiTestCase#getContext or SysuiTestCase#mContext"); 69 }); 70 InstrumentationRegistry.registerInstance(inst, InstrumentationRegistry.getArguments()); 71 } 72 73 @After 74 public void SysuiTeardown() { 75 InstrumentationRegistry.registerInstance(mRealInstrumentation, 76 InstrumentationRegistry.getArguments()); 77 } 78 79 protected LeakCheck getLeakCheck() { 80 return null; 81 } 82 83 public Context getContext() { 84 return mContext; 85 } 86 87 protected void waitForIdleSync() { 88 if (mHandler == null) { 89 mHandler = new Handler(Looper.getMainLooper()); 90 } 91 waitForIdleSync(mHandler); 92 } 93 94 protected void waitForUiOffloadThread() { 95 Future<?> future = Dependency.get(UiOffloadThread.class).submit(() -> {}); 96 try { 97 future.get(); 98 } catch (InterruptedException | ExecutionException e) { 99 Log.e(TAG, "Failed to wait for ui offload thread.", e); 100 } 101 } 102 103 public static void waitForIdleSync(Handler h) { 104 validateThread(h.getLooper()); 105 Idler idler = new Idler(null); 106 h.getLooper().getQueue().addIdleHandler(idler); 107 // Ensure we are non-idle, so the idle handler can run. 108 h.post(new EmptyRunnable()); 109 idler.waitForIdle(); 110 } 111 112 private static final void validateThread(Looper l) { 113 if (Looper.myLooper() == l) { 114 throw new RuntimeException( 115 "This method can not be called from the looper being synced"); 116 } 117 } 118 119 public static final class EmptyRunnable implements Runnable { 120 public void run() { 121 } 122 } 123 124 public static final class Idler implements MessageQueue.IdleHandler { 125 private final Runnable mCallback; 126 private boolean mIdle; 127 128 public Idler(Runnable callback) { 129 mCallback = callback; 130 mIdle = false; 131 } 132 133 @Override 134 public boolean queueIdle() { 135 if (mCallback != null) { 136 mCallback.run(); 137 } 138 synchronized (this) { 139 mIdle = true; 140 notifyAll(); 141 } 142 return false; 143 } 144 145 public void waitForIdle() { 146 synchronized (this) { 147 while (!mIdle) { 148 try { 149 wait(); 150 } catch (InterruptedException e) { 151 } 152 } 153 } 154 } 155 } 156 } 157