1930eccaf44239d010cbafb246651697996f567b5Chris Wren/* 2930eccaf44239d010cbafb246651697996f567b5Chris Wren * Copyright (C) 2014 The Android Open Source Project 3930eccaf44239d010cbafb246651697996f567b5Chris Wren * 4930eccaf44239d010cbafb246651697996f567b5Chris Wren * Licensed under the Apache License, Version 2.0 (the "License"); 5930eccaf44239d010cbafb246651697996f567b5Chris Wren * you may not use this file except in compliance with the License. 6930eccaf44239d010cbafb246651697996f567b5Chris Wren * You may obtain a copy of the License at 7930eccaf44239d010cbafb246651697996f567b5Chris Wren * 8930eccaf44239d010cbafb246651697996f567b5Chris Wren * http://www.apache.org/licenses/LICENSE-2.0 9930eccaf44239d010cbafb246651697996f567b5Chris Wren * 10930eccaf44239d010cbafb246651697996f567b5Chris Wren * Unless required by applicable law or agreed to in writing, software 11930eccaf44239d010cbafb246651697996f567b5Chris Wren * distributed under the License is distributed on an "AS IS" BASIS, 12930eccaf44239d010cbafb246651697996f567b5Chris Wren * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13930eccaf44239d010cbafb246651697996f567b5Chris Wren * See the License for the specific language governing permissions and 14930eccaf44239d010cbafb246651697996f567b5Chris Wren * limitations under the License. 15930eccaf44239d010cbafb246651697996f567b5Chris Wren */ 16930eccaf44239d010cbafb246651697996f567b5Chris Wrenpackage com.android.systemui; 17930eccaf44239d010cbafb246651697996f567b5Chris Wren 18893f0bdcb73785ce0e06e9f935f5a7027cf77f14Jason Monkimport static org.mockito.Mockito.spy; 19893f0bdcb73785ce0e06e9f935f5a7027cf77f14Jason Monkimport static org.mockito.Mockito.when; 20893f0bdcb73785ce0e06e9f935f5a7027cf77f14Jason Monk 21893f0bdcb73785ce0e06e9f935f5a7027cf77f14Jason Monkimport android.app.Instrumentation; 2286bc33188948e7b6beb07dbb5ddba59b5ea3c1fcJason Monkimport android.os.Handler; 2386bc33188948e7b6beb07dbb5ddba59b5ea3c1fcJason Monkimport android.os.Looper; 2486bc33188948e7b6beb07dbb5ddba59b5ea3c1fcJason Monkimport android.os.MessageQueue; 2516e185e60b449958b138d52a7bdb7dea998ebb79Winson Chungimport android.os.ParcelFileDescriptor; 26aa573e9e8632552d1fa8bdd6b0ee408ff9a93a6bJason Monkimport android.support.test.InstrumentationRegistry; 273150dbf7a57c7f0332db9c9197ad046023bd6eceAdrian Roosimport android.testing.DexmakerShareClassLoaderRule; 28340b0e5216b4fcc435e0459b1ca46155a572100dJason Monkimport android.testing.LeakCheck; 29e549a8d62108c7c7dabedbf4e77b9a653781723bJorim Jaggiimport android.util.Log; 30e97892844a5c21c91c7f82b96f82202b18a1a24dJason Monk 3130a2ae620956fd73ecb5e4e2ef3645377bdf0aaaAdrian Roosimport com.android.keyguard.KeyguardUpdateMonitor; 3230a2ae620956fd73ecb5e4e2ef3645377bdf0aaaAdrian Roos 33893f0bdcb73785ce0e06e9f935f5a7027cf77f14Jason Monkimport org.junit.After; 342c403db659db615831d8bd02661f8769c7762241Geoffrey Pitschimport org.junit.Before; 35340b0e5216b4fcc435e0459b1ca46155a572100dJason Monkimport org.junit.Rule; 36930eccaf44239d010cbafb246651697996f567b5Chris Wren 3716e185e60b449958b138d52a7bdb7dea998ebb79Winson Chungimport java.io.FileInputStream; 3816e185e60b449958b138d52a7bdb7dea998ebb79Winson Chungimport java.io.IOException; 39e549a8d62108c7c7dabedbf4e77b9a653781723bJorim Jaggiimport java.util.concurrent.ExecutionException; 40e549a8d62108c7c7dabedbf4e77b9a653781723bJorim Jaggiimport java.util.concurrent.Future; 41e549a8d62108c7c7dabedbf4e77b9a653781723bJorim Jaggi 42930eccaf44239d010cbafb246651697996f567b5Chris Wren/** 43930eccaf44239d010cbafb246651697996f567b5Chris Wren * Base class that does System UI specific setup. 44930eccaf44239d010cbafb246651697996f567b5Chris Wren */ 459abca5e9f88c47579f8334c6c48741a259185b9bJason Monkpublic abstract class SysuiTestCase { 4686bc33188948e7b6beb07dbb5ddba59b5ea3c1fcJason Monk 47e549a8d62108c7c7dabedbf4e77b9a653781723bJorim Jaggi private static final String TAG = "SysuiTestCase"; 48e549a8d62108c7c7dabedbf4e77b9a653781723bJorim Jaggi 4986bc33188948e7b6beb07dbb5ddba59b5ea3c1fcJason Monk private Handler mHandler; 50340b0e5216b4fcc435e0459b1ca46155a572100dJason Monk @Rule 51340b0e5216b4fcc435e0459b1ca46155a572100dJason Monk public SysuiTestableContext mContext = new SysuiTestableContext( 52340b0e5216b4fcc435e0459b1ca46155a572100dJason Monk InstrumentationRegistry.getContext(), getLeakCheck()); 533150dbf7a57c7f0332db9c9197ad046023bd6eceAdrian Roos @Rule 543150dbf7a57c7f0332db9c9197ad046023bd6eceAdrian Roos public final DexmakerShareClassLoaderRule mDexmakerShareClassLoaderRule = 553150dbf7a57c7f0332db9c9197ad046023bd6eceAdrian Roos new DexmakerShareClassLoaderRule(); 56340b0e5216b4fcc435e0459b1ca46155a572100dJason Monk public TestableDependency mDependency = new TestableDependency(mContext); 57893f0bdcb73785ce0e06e9f935f5a7027cf77f14Jason Monk private Instrumentation mRealInstrumentation; 582c403db659db615831d8bd02661f8769c7762241Geoffrey Pitsch 592c403db659db615831d8bd02661f8769c7762241Geoffrey Pitsch @Before 602c403db659db615831d8bd02661f8769c7762241Geoffrey Pitsch public void SysuiSetup() throws Exception { 616865b71e1a50a6273beb4837013c2f1248e5b499Lucas Dupin mContext.setTheme(R.style.Theme_SystemUI); 62685db72eb7ddfeef29af1d191e67e0a041191f58Jason Monk SystemUIFactory.createFromConfig(mContext); 63893f0bdcb73785ce0e06e9f935f5a7027cf77f14Jason Monk 64893f0bdcb73785ce0e06e9f935f5a7027cf77f14Jason Monk mRealInstrumentation = InstrumentationRegistry.getInstrumentation(); 65893f0bdcb73785ce0e06e9f935f5a7027cf77f14Jason Monk Instrumentation inst = spy(mRealInstrumentation); 66c429f690e54db1bdc4631aab7c0191e30ccf1d51Jason Monk when(inst.getContext()).thenAnswer(invocation -> { 67c429f690e54db1bdc4631aab7c0191e30ccf1d51Jason Monk throw new RuntimeException( 68c429f690e54db1bdc4631aab7c0191e30ccf1d51Jason Monk "SysUI Tests should use SysuiTestCase#getContext or SysuiTestCase#mContext"); 69c429f690e54db1bdc4631aab7c0191e30ccf1d51Jason Monk }); 70c429f690e54db1bdc4631aab7c0191e30ccf1d51Jason Monk when(inst.getTargetContext()).thenAnswer(invocation -> { 71c429f690e54db1bdc4631aab7c0191e30ccf1d51Jason Monk throw new RuntimeException( 72c429f690e54db1bdc4631aab7c0191e30ccf1d51Jason Monk "SysUI Tests should use SysuiTestCase#getContext or SysuiTestCase#mContext"); 73c429f690e54db1bdc4631aab7c0191e30ccf1d51Jason Monk }); 74893f0bdcb73785ce0e06e9f935f5a7027cf77f14Jason Monk InstrumentationRegistry.registerInstance(inst, InstrumentationRegistry.getArguments()); 7530a2ae620956fd73ecb5e4e2ef3645377bdf0aaaAdrian Roos KeyguardUpdateMonitor.disableHandlerCheckForTesting(inst); 76893f0bdcb73785ce0e06e9f935f5a7027cf77f14Jason Monk } 77893f0bdcb73785ce0e06e9f935f5a7027cf77f14Jason Monk 78893f0bdcb73785ce0e06e9f935f5a7027cf77f14Jason Monk @After 79893f0bdcb73785ce0e06e9f935f5a7027cf77f14Jason Monk public void SysuiTeardown() { 80893f0bdcb73785ce0e06e9f935f5a7027cf77f14Jason Monk InstrumentationRegistry.registerInstance(mRealInstrumentation, 81893f0bdcb73785ce0e06e9f935f5a7027cf77f14Jason Monk InstrumentationRegistry.getArguments()); 82e97892844a5c21c91c7f82b96f82202b18a1a24dJason Monk } 83e97892844a5c21c91c7f82b96f82202b18a1a24dJason Monk 84340b0e5216b4fcc435e0459b1ca46155a572100dJason Monk protected LeakCheck getLeakCheck() { 85340b0e5216b4fcc435e0459b1ca46155a572100dJason Monk return null; 862c403db659db615831d8bd02661f8769c7762241Geoffrey Pitsch } 872c403db659db615831d8bd02661f8769c7762241Geoffrey Pitsch 885daa47270bb591d186e723637af1abebbe1350ccJulia Reynolds public SysuiTestableContext getContext() { 892c403db659db615831d8bd02661f8769c7762241Geoffrey Pitsch return mContext; 90930eccaf44239d010cbafb246651697996f567b5Chris Wren } 9186bc33188948e7b6beb07dbb5ddba59b5ea3c1fcJason Monk 9216e185e60b449958b138d52a7bdb7dea998ebb79Winson Chung protected void runShellCommand(String command) throws IOException { 9316e185e60b449958b138d52a7bdb7dea998ebb79Winson Chung ParcelFileDescriptor pfd = mRealInstrumentation.getUiAutomation() 9416e185e60b449958b138d52a7bdb7dea998ebb79Winson Chung .executeShellCommand(command); 9516e185e60b449958b138d52a7bdb7dea998ebb79Winson Chung 9616e185e60b449958b138d52a7bdb7dea998ebb79Winson Chung // Read the input stream fully. 9716e185e60b449958b138d52a7bdb7dea998ebb79Winson Chung FileInputStream fis = new ParcelFileDescriptor.AutoCloseInputStream(pfd); 9816e185e60b449958b138d52a7bdb7dea998ebb79Winson Chung while (fis.read() != -1); 9916e185e60b449958b138d52a7bdb7dea998ebb79Winson Chung fis.close(); 10016e185e60b449958b138d52a7bdb7dea998ebb79Winson Chung } 10116e185e60b449958b138d52a7bdb7dea998ebb79Winson Chung 10286bc33188948e7b6beb07dbb5ddba59b5ea3c1fcJason Monk protected void waitForIdleSync() { 10386bc33188948e7b6beb07dbb5ddba59b5ea3c1fcJason Monk if (mHandler == null) { 10486bc33188948e7b6beb07dbb5ddba59b5ea3c1fcJason Monk mHandler = new Handler(Looper.getMainLooper()); 10586bc33188948e7b6beb07dbb5ddba59b5ea3c1fcJason Monk } 10686bc33188948e7b6beb07dbb5ddba59b5ea3c1fcJason Monk waitForIdleSync(mHandler); 10786bc33188948e7b6beb07dbb5ddba59b5ea3c1fcJason Monk } 10886bc33188948e7b6beb07dbb5ddba59b5ea3c1fcJason Monk 109e549a8d62108c7c7dabedbf4e77b9a653781723bJorim Jaggi protected void waitForUiOffloadThread() { 110e549a8d62108c7c7dabedbf4e77b9a653781723bJorim Jaggi Future<?> future = Dependency.get(UiOffloadThread.class).submit(() -> {}); 111e549a8d62108c7c7dabedbf4e77b9a653781723bJorim Jaggi try { 112e549a8d62108c7c7dabedbf4e77b9a653781723bJorim Jaggi future.get(); 113e549a8d62108c7c7dabedbf4e77b9a653781723bJorim Jaggi } catch (InterruptedException | ExecutionException e) { 114e549a8d62108c7c7dabedbf4e77b9a653781723bJorim Jaggi Log.e(TAG, "Failed to wait for ui offload thread.", e); 115e549a8d62108c7c7dabedbf4e77b9a653781723bJorim Jaggi } 116e549a8d62108c7c7dabedbf4e77b9a653781723bJorim Jaggi } 117e549a8d62108c7c7dabedbf4e77b9a653781723bJorim Jaggi 118de850bbcaa61c1874b803f2086443febbafd81a4Jason Monk public static void waitForIdleSync(Handler h) { 11986bc33188948e7b6beb07dbb5ddba59b5ea3c1fcJason Monk validateThread(h.getLooper()); 12086bc33188948e7b6beb07dbb5ddba59b5ea3c1fcJason Monk Idler idler = new Idler(null); 12186bc33188948e7b6beb07dbb5ddba59b5ea3c1fcJason Monk h.getLooper().getQueue().addIdleHandler(idler); 12286bc33188948e7b6beb07dbb5ddba59b5ea3c1fcJason Monk // Ensure we are non-idle, so the idle handler can run. 12386bc33188948e7b6beb07dbb5ddba59b5ea3c1fcJason Monk h.post(new EmptyRunnable()); 12486bc33188948e7b6beb07dbb5ddba59b5ea3c1fcJason Monk idler.waitForIdle(); 12586bc33188948e7b6beb07dbb5ddba59b5ea3c1fcJason Monk } 12686bc33188948e7b6beb07dbb5ddba59b5ea3c1fcJason Monk 12786bc33188948e7b6beb07dbb5ddba59b5ea3c1fcJason Monk private static final void validateThread(Looper l) { 12886bc33188948e7b6beb07dbb5ddba59b5ea3c1fcJason Monk if (Looper.myLooper() == l) { 12986bc33188948e7b6beb07dbb5ddba59b5ea3c1fcJason Monk throw new RuntimeException( 13086bc33188948e7b6beb07dbb5ddba59b5ea3c1fcJason Monk "This method can not be called from the looper being synced"); 13186bc33188948e7b6beb07dbb5ddba59b5ea3c1fcJason Monk } 13286bc33188948e7b6beb07dbb5ddba59b5ea3c1fcJason Monk } 13386bc33188948e7b6beb07dbb5ddba59b5ea3c1fcJason Monk 13486bc33188948e7b6beb07dbb5ddba59b5ea3c1fcJason Monk public static final class EmptyRunnable implements Runnable { 13586bc33188948e7b6beb07dbb5ddba59b5ea3c1fcJason Monk public void run() { 13686bc33188948e7b6beb07dbb5ddba59b5ea3c1fcJason Monk } 13786bc33188948e7b6beb07dbb5ddba59b5ea3c1fcJason Monk } 13886bc33188948e7b6beb07dbb5ddba59b5ea3c1fcJason Monk 13986bc33188948e7b6beb07dbb5ddba59b5ea3c1fcJason Monk public static final class Idler implements MessageQueue.IdleHandler { 14086bc33188948e7b6beb07dbb5ddba59b5ea3c1fcJason Monk private final Runnable mCallback; 14186bc33188948e7b6beb07dbb5ddba59b5ea3c1fcJason Monk private boolean mIdle; 14286bc33188948e7b6beb07dbb5ddba59b5ea3c1fcJason Monk 14386bc33188948e7b6beb07dbb5ddba59b5ea3c1fcJason Monk public Idler(Runnable callback) { 14486bc33188948e7b6beb07dbb5ddba59b5ea3c1fcJason Monk mCallback = callback; 14586bc33188948e7b6beb07dbb5ddba59b5ea3c1fcJason Monk mIdle = false; 14686bc33188948e7b6beb07dbb5ddba59b5ea3c1fcJason Monk } 14786bc33188948e7b6beb07dbb5ddba59b5ea3c1fcJason Monk 14886bc33188948e7b6beb07dbb5ddba59b5ea3c1fcJason Monk @Override 14986bc33188948e7b6beb07dbb5ddba59b5ea3c1fcJason Monk public boolean queueIdle() { 15086bc33188948e7b6beb07dbb5ddba59b5ea3c1fcJason Monk if (mCallback != null) { 15186bc33188948e7b6beb07dbb5ddba59b5ea3c1fcJason Monk mCallback.run(); 15286bc33188948e7b6beb07dbb5ddba59b5ea3c1fcJason Monk } 15386bc33188948e7b6beb07dbb5ddba59b5ea3c1fcJason Monk synchronized (this) { 15486bc33188948e7b6beb07dbb5ddba59b5ea3c1fcJason Monk mIdle = true; 15586bc33188948e7b6beb07dbb5ddba59b5ea3c1fcJason Monk notifyAll(); 15686bc33188948e7b6beb07dbb5ddba59b5ea3c1fcJason Monk } 15786bc33188948e7b6beb07dbb5ddba59b5ea3c1fcJason Monk return false; 15886bc33188948e7b6beb07dbb5ddba59b5ea3c1fcJason Monk } 15986bc33188948e7b6beb07dbb5ddba59b5ea3c1fcJason Monk 16086bc33188948e7b6beb07dbb5ddba59b5ea3c1fcJason Monk public void waitForIdle() { 16186bc33188948e7b6beb07dbb5ddba59b5ea3c1fcJason Monk synchronized (this) { 16286bc33188948e7b6beb07dbb5ddba59b5ea3c1fcJason Monk while (!mIdle) { 16386bc33188948e7b6beb07dbb5ddba59b5ea3c1fcJason Monk try { 16486bc33188948e7b6beb07dbb5ddba59b5ea3c1fcJason Monk wait(); 16586bc33188948e7b6beb07dbb5ddba59b5ea3c1fcJason Monk } catch (InterruptedException e) { 16686bc33188948e7b6beb07dbb5ddba59b5ea3c1fcJason Monk } 16786bc33188948e7b6beb07dbb5ddba59b5ea3c1fcJason Monk } 16886bc33188948e7b6beb07dbb5ddba59b5ea3c1fcJason Monk } 16986bc33188948e7b6beb07dbb5ddba59b5ea3c1fcJason Monk } 17086bc33188948e7b6beb07dbb5ddba59b5ea3c1fcJason Monk } 171930eccaf44239d010cbafb246651697996f567b5Chris Wren} 172