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