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 */ 16package com.android.systemui; 17 18import static org.mockito.Mockito.spy; 19import static org.mockito.Mockito.when; 20 21import android.app.Instrumentation; 22import android.os.Handler; 23import android.os.Looper; 24import android.os.MessageQueue; 25import android.os.ParcelFileDescriptor; 26import android.support.test.InstrumentationRegistry; 27import android.testing.DexmakerShareClassLoaderRule; 28import android.testing.LeakCheck; 29import android.util.Log; 30 31import com.android.keyguard.KeyguardUpdateMonitor; 32 33import org.junit.After; 34import org.junit.Before; 35import org.junit.Rule; 36 37import java.io.FileInputStream; 38import java.io.IOException; 39import java.util.concurrent.ExecutionException; 40import java.util.concurrent.Future; 41 42/** 43 * Base class that does System UI specific setup. 44 */ 45public abstract class SysuiTestCase { 46 47 private static final String TAG = "SysuiTestCase"; 48 49 private Handler mHandler; 50 @Rule 51 public SysuiTestableContext mContext = new SysuiTestableContext( 52 InstrumentationRegistry.getContext(), getLeakCheck()); 53 @Rule 54 public final DexmakerShareClassLoaderRule mDexmakerShareClassLoaderRule = 55 new DexmakerShareClassLoaderRule(); 56 public TestableDependency mDependency = new TestableDependency(mContext); 57 private Instrumentation mRealInstrumentation; 58 59 @Before 60 public void SysuiSetup() throws Exception { 61 mContext.setTheme(R.style.Theme_SystemUI); 62 SystemUIFactory.createFromConfig(mContext); 63 64 mRealInstrumentation = InstrumentationRegistry.getInstrumentation(); 65 Instrumentation inst = spy(mRealInstrumentation); 66 when(inst.getContext()).thenAnswer(invocation -> { 67 throw new RuntimeException( 68 "SysUI Tests should use SysuiTestCase#getContext or SysuiTestCase#mContext"); 69 }); 70 when(inst.getTargetContext()).thenAnswer(invocation -> { 71 throw new RuntimeException( 72 "SysUI Tests should use SysuiTestCase#getContext or SysuiTestCase#mContext"); 73 }); 74 InstrumentationRegistry.registerInstance(inst, InstrumentationRegistry.getArguments()); 75 KeyguardUpdateMonitor.disableHandlerCheckForTesting(inst); 76 } 77 78 @After 79 public void SysuiTeardown() { 80 InstrumentationRegistry.registerInstance(mRealInstrumentation, 81 InstrumentationRegistry.getArguments()); 82 } 83 84 protected LeakCheck getLeakCheck() { 85 return null; 86 } 87 88 public SysuiTestableContext getContext() { 89 return mContext; 90 } 91 92 protected void runShellCommand(String command) throws IOException { 93 ParcelFileDescriptor pfd = mRealInstrumentation.getUiAutomation() 94 .executeShellCommand(command); 95 96 // Read the input stream fully. 97 FileInputStream fis = new ParcelFileDescriptor.AutoCloseInputStream(pfd); 98 while (fis.read() != -1); 99 fis.close(); 100 } 101 102 protected void waitForIdleSync() { 103 if (mHandler == null) { 104 mHandler = new Handler(Looper.getMainLooper()); 105 } 106 waitForIdleSync(mHandler); 107 } 108 109 protected void waitForUiOffloadThread() { 110 Future<?> future = Dependency.get(UiOffloadThread.class).submit(() -> {}); 111 try { 112 future.get(); 113 } catch (InterruptedException | ExecutionException e) { 114 Log.e(TAG, "Failed to wait for ui offload thread.", e); 115 } 116 } 117 118 public static void waitForIdleSync(Handler h) { 119 validateThread(h.getLooper()); 120 Idler idler = new Idler(null); 121 h.getLooper().getQueue().addIdleHandler(idler); 122 // Ensure we are non-idle, so the idle handler can run. 123 h.post(new EmptyRunnable()); 124 idler.waitForIdle(); 125 } 126 127 private static final void validateThread(Looper l) { 128 if (Looper.myLooper() == l) { 129 throw new RuntimeException( 130 "This method can not be called from the looper being synced"); 131 } 132 } 133 134 public static final class EmptyRunnable implements Runnable { 135 public void run() { 136 } 137 } 138 139 public static final class Idler implements MessageQueue.IdleHandler { 140 private final Runnable mCallback; 141 private boolean mIdle; 142 143 public Idler(Runnable callback) { 144 mCallback = callback; 145 mIdle = false; 146 } 147 148 @Override 149 public boolean queueIdle() { 150 if (mCallback != null) { 151 mCallback.run(); 152 } 153 synchronized (this) { 154 mIdle = true; 155 notifyAll(); 156 } 157 return false; 158 } 159 160 public void waitForIdle() { 161 synchronized (this) { 162 while (!mIdle) { 163 try { 164 wait(); 165 } catch (InterruptedException e) { 166 } 167 } 168 } 169 } 170 } 171} 172