1848c4611ea929b7d36432b6f21942fff31ca713bYigit Boyar/*
2848c4611ea929b7d36432b6f21942fff31ca713bYigit Boyar * Copyright (C) 2017 The Android Open Source Project
3848c4611ea929b7d36432b6f21942fff31ca713bYigit Boyar *
4848c4611ea929b7d36432b6f21942fff31ca713bYigit Boyar * Licensed under the Apache License, Version 2.0 (the "License");
5848c4611ea929b7d36432b6f21942fff31ca713bYigit Boyar * you may not use this file except in compliance with the License.
6848c4611ea929b7d36432b6f21942fff31ca713bYigit Boyar * You may obtain a copy of the License at
7848c4611ea929b7d36432b6f21942fff31ca713bYigit Boyar *
8848c4611ea929b7d36432b6f21942fff31ca713bYigit Boyar *      http://www.apache.org/licenses/LICENSE-2.0
9848c4611ea929b7d36432b6f21942fff31ca713bYigit Boyar *
10848c4611ea929b7d36432b6f21942fff31ca713bYigit Boyar * Unless required by applicable law or agreed to in writing, software
11848c4611ea929b7d36432b6f21942fff31ca713bYigit Boyar * distributed under the License is distributed on an "AS IS" BASIS,
12848c4611ea929b7d36432b6f21942fff31ca713bYigit Boyar * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13848c4611ea929b7d36432b6f21942fff31ca713bYigit Boyar * See the License for the specific language governing permissions and
14848c4611ea929b7d36432b6f21942fff31ca713bYigit Boyar * limitations under the License.
15848c4611ea929b7d36432b6f21942fff31ca713bYigit Boyar */
16848c4611ea929b7d36432b6f21942fff31ca713bYigit Boyar
17ddee2b5170ae257a7b2494f8aaa8459ebed806dcAurimas Liutikaspackage androidx.arch.core.executor.testing;
18848c4611ea929b7d36432b6f21942fff31ca713bYigit Boyar
19848c4611ea929b7d36432b6f21942fff31ca713bYigit Boyarimport android.os.SystemClock;
20848c4611ea929b7d36432b6f21942fff31ca713bYigit Boyar
21ddee2b5170ae257a7b2494f8aaa8459ebed806dcAurimas Liutikasimport androidx.arch.core.executor.ArchTaskExecutor;
22ddee2b5170ae257a7b2494f8aaa8459ebed806dcAurimas Liutikasimport androidx.arch.core.executor.DefaultTaskExecutor;
23ba069d50913c3fb250bb60ec310439db36895337Alan Viverette
24848c4611ea929b7d36432b6f21942fff31ca713bYigit Boyarimport org.junit.rules.TestWatcher;
25848c4611ea929b7d36432b6f21942fff31ca713bYigit Boyarimport org.junit.runner.Description;
26848c4611ea929b7d36432b6f21942fff31ca713bYigit Boyar
27848c4611ea929b7d36432b6f21942fff31ca713bYigit Boyarimport java.util.concurrent.TimeUnit;
28848c4611ea929b7d36432b6f21942fff31ca713bYigit Boyarimport java.util.concurrent.TimeoutException;
29848c4611ea929b7d36432b6f21942fff31ca713bYigit Boyar
30848c4611ea929b7d36432b6f21942fff31ca713bYigit Boyar/**
31848c4611ea929b7d36432b6f21942fff31ca713bYigit Boyar * A JUnit Test Rule that swaps the background executor used by the Architecture Components with a
32848c4611ea929b7d36432b6f21942fff31ca713bYigit Boyar * different one which counts the tasks as they are start and finish.
33848c4611ea929b7d36432b6f21942fff31ca713bYigit Boyar * <p>
34848c4611ea929b7d36432b6f21942fff31ca713bYigit Boyar * You can use this rule for your host side tests that use Architecture Components.
35848c4611ea929b7d36432b6f21942fff31ca713bYigit Boyar */
36848c4611ea929b7d36432b6f21942fff31ca713bYigit Boyarpublic class CountingTaskExecutorRule extends TestWatcher {
37848c4611ea929b7d36432b6f21942fff31ca713bYigit Boyar    private final Object mCountLock = new Object();
38848c4611ea929b7d36432b6f21942fff31ca713bYigit Boyar    private int mTaskCount = 0;
39848c4611ea929b7d36432b6f21942fff31ca713bYigit Boyar
40848c4611ea929b7d36432b6f21942fff31ca713bYigit Boyar    @Override
41848c4611ea929b7d36432b6f21942fff31ca713bYigit Boyar    protected void starting(Description description) {
42848c4611ea929b7d36432b6f21942fff31ca713bYigit Boyar        super.starting(description);
43ae36c8b11a64d3cdc9ba6e37d9f3d1d250fdc4a8Yigit Boyar        ArchTaskExecutor.getInstance().setDelegate(new DefaultTaskExecutor() {
44848c4611ea929b7d36432b6f21942fff31ca713bYigit Boyar            @Override
45848c4611ea929b7d36432b6f21942fff31ca713bYigit Boyar            public void executeOnDiskIO(Runnable runnable) {
46848c4611ea929b7d36432b6f21942fff31ca713bYigit Boyar                super.executeOnDiskIO(new CountingRunnable(runnable));
47848c4611ea929b7d36432b6f21942fff31ca713bYigit Boyar            }
48848c4611ea929b7d36432b6f21942fff31ca713bYigit Boyar
49848c4611ea929b7d36432b6f21942fff31ca713bYigit Boyar            @Override
50848c4611ea929b7d36432b6f21942fff31ca713bYigit Boyar            public void postToMainThread(Runnable runnable) {
51848c4611ea929b7d36432b6f21942fff31ca713bYigit Boyar                super.postToMainThread(new CountingRunnable(runnable));
52848c4611ea929b7d36432b6f21942fff31ca713bYigit Boyar            }
53848c4611ea929b7d36432b6f21942fff31ca713bYigit Boyar        });
54848c4611ea929b7d36432b6f21942fff31ca713bYigit Boyar    }
55848c4611ea929b7d36432b6f21942fff31ca713bYigit Boyar
56848c4611ea929b7d36432b6f21942fff31ca713bYigit Boyar    @Override
57848c4611ea929b7d36432b6f21942fff31ca713bYigit Boyar    protected void finished(Description description) {
58848c4611ea929b7d36432b6f21942fff31ca713bYigit Boyar        super.finished(description);
59ae36c8b11a64d3cdc9ba6e37d9f3d1d250fdc4a8Yigit Boyar        ArchTaskExecutor.getInstance().setDelegate(null);
60848c4611ea929b7d36432b6f21942fff31ca713bYigit Boyar    }
61848c4611ea929b7d36432b6f21942fff31ca713bYigit Boyar
62848c4611ea929b7d36432b6f21942fff31ca713bYigit Boyar    private void increment() {
63848c4611ea929b7d36432b6f21942fff31ca713bYigit Boyar        synchronized (mCountLock) {
64848c4611ea929b7d36432b6f21942fff31ca713bYigit Boyar            mTaskCount++;
65848c4611ea929b7d36432b6f21942fff31ca713bYigit Boyar        }
66848c4611ea929b7d36432b6f21942fff31ca713bYigit Boyar    }
67848c4611ea929b7d36432b6f21942fff31ca713bYigit Boyar
68848c4611ea929b7d36432b6f21942fff31ca713bYigit Boyar    private void decrement() {
69848c4611ea929b7d36432b6f21942fff31ca713bYigit Boyar        synchronized (mCountLock) {
70848c4611ea929b7d36432b6f21942fff31ca713bYigit Boyar            mTaskCount--;
71848c4611ea929b7d36432b6f21942fff31ca713bYigit Boyar            if (mTaskCount == 0) {
72848c4611ea929b7d36432b6f21942fff31ca713bYigit Boyar                onIdle();
73848c4611ea929b7d36432b6f21942fff31ca713bYigit Boyar                mCountLock.notifyAll();
74848c4611ea929b7d36432b6f21942fff31ca713bYigit Boyar            }
75848c4611ea929b7d36432b6f21942fff31ca713bYigit Boyar        }
76848c4611ea929b7d36432b6f21942fff31ca713bYigit Boyar    }
77848c4611ea929b7d36432b6f21942fff31ca713bYigit Boyar
78848c4611ea929b7d36432b6f21942fff31ca713bYigit Boyar    /**
79848c4611ea929b7d36432b6f21942fff31ca713bYigit Boyar     * Called when the number of awaiting tasks reaches to 0.
80848c4611ea929b7d36432b6f21942fff31ca713bYigit Boyar     *
81848c4611ea929b7d36432b6f21942fff31ca713bYigit Boyar     * @see #isIdle()
82848c4611ea929b7d36432b6f21942fff31ca713bYigit Boyar     */
83848c4611ea929b7d36432b6f21942fff31ca713bYigit Boyar    protected void onIdle() {
84848c4611ea929b7d36432b6f21942fff31ca713bYigit Boyar
85848c4611ea929b7d36432b6f21942fff31ca713bYigit Boyar    }
86848c4611ea929b7d36432b6f21942fff31ca713bYigit Boyar
87848c4611ea929b7d36432b6f21942fff31ca713bYigit Boyar    /**
88848c4611ea929b7d36432b6f21942fff31ca713bYigit Boyar     * Returns false if there are tasks waiting to be executed, true otherwise.
89848c4611ea929b7d36432b6f21942fff31ca713bYigit Boyar     *
90848c4611ea929b7d36432b6f21942fff31ca713bYigit Boyar     * @return False if there are tasks waiting to be executed, true otherwise.
91848c4611ea929b7d36432b6f21942fff31ca713bYigit Boyar     *
92848c4611ea929b7d36432b6f21942fff31ca713bYigit Boyar     * @see #onIdle()
93848c4611ea929b7d36432b6f21942fff31ca713bYigit Boyar     */
94848c4611ea929b7d36432b6f21942fff31ca713bYigit Boyar    public boolean isIdle() {
95848c4611ea929b7d36432b6f21942fff31ca713bYigit Boyar        synchronized (mCountLock) {
96848c4611ea929b7d36432b6f21942fff31ca713bYigit Boyar            return mTaskCount == 0;
97848c4611ea929b7d36432b6f21942fff31ca713bYigit Boyar        }
98848c4611ea929b7d36432b6f21942fff31ca713bYigit Boyar    }
99848c4611ea929b7d36432b6f21942fff31ca713bYigit Boyar
100848c4611ea929b7d36432b6f21942fff31ca713bYigit Boyar    /**
101848c4611ea929b7d36432b6f21942fff31ca713bYigit Boyar     * Waits until all active tasks are finished.
102848c4611ea929b7d36432b6f21942fff31ca713bYigit Boyar     *
103848c4611ea929b7d36432b6f21942fff31ca713bYigit Boyar     * @param time The duration to wait
104848c4611ea929b7d36432b6f21942fff31ca713bYigit Boyar     * @param timeUnit The time unit for the {@code time} parameter
105848c4611ea929b7d36432b6f21942fff31ca713bYigit Boyar     *
106848c4611ea929b7d36432b6f21942fff31ca713bYigit Boyar     * @throws InterruptedException If thread is interrupted while waiting
107848c4611ea929b7d36432b6f21942fff31ca713bYigit Boyar     * @throws TimeoutException If tasks cannot be drained at the given time
108848c4611ea929b7d36432b6f21942fff31ca713bYigit Boyar     */
109848c4611ea929b7d36432b6f21942fff31ca713bYigit Boyar    public void drainTasks(int time, TimeUnit timeUnit)
110848c4611ea929b7d36432b6f21942fff31ca713bYigit Boyar            throws InterruptedException, TimeoutException {
111848c4611ea929b7d36432b6f21942fff31ca713bYigit Boyar        long end = SystemClock.uptimeMillis() + timeUnit.toMillis(time);
112848c4611ea929b7d36432b6f21942fff31ca713bYigit Boyar        synchronized (mCountLock) {
113848c4611ea929b7d36432b6f21942fff31ca713bYigit Boyar            while (mTaskCount != 0) {
114848c4611ea929b7d36432b6f21942fff31ca713bYigit Boyar                long now = SystemClock.uptimeMillis();
115848c4611ea929b7d36432b6f21942fff31ca713bYigit Boyar                long remaining = end - now;
116848c4611ea929b7d36432b6f21942fff31ca713bYigit Boyar                if (remaining > 0) {
117848c4611ea929b7d36432b6f21942fff31ca713bYigit Boyar                    mCountLock.wait(remaining);
118848c4611ea929b7d36432b6f21942fff31ca713bYigit Boyar                } else {
119848c4611ea929b7d36432b6f21942fff31ca713bYigit Boyar                    throw new TimeoutException("could not drain tasks");
120848c4611ea929b7d36432b6f21942fff31ca713bYigit Boyar                }
121848c4611ea929b7d36432b6f21942fff31ca713bYigit Boyar            }
122848c4611ea929b7d36432b6f21942fff31ca713bYigit Boyar        }
123848c4611ea929b7d36432b6f21942fff31ca713bYigit Boyar    }
124848c4611ea929b7d36432b6f21942fff31ca713bYigit Boyar
125848c4611ea929b7d36432b6f21942fff31ca713bYigit Boyar    class CountingRunnable implements Runnable {
126848c4611ea929b7d36432b6f21942fff31ca713bYigit Boyar        final Runnable mWrapped;
127848c4611ea929b7d36432b6f21942fff31ca713bYigit Boyar
128848c4611ea929b7d36432b6f21942fff31ca713bYigit Boyar        CountingRunnable(Runnable wrapped) {
129848c4611ea929b7d36432b6f21942fff31ca713bYigit Boyar            mWrapped = wrapped;
130848c4611ea929b7d36432b6f21942fff31ca713bYigit Boyar            increment();
131848c4611ea929b7d36432b6f21942fff31ca713bYigit Boyar        }
132848c4611ea929b7d36432b6f21942fff31ca713bYigit Boyar
133848c4611ea929b7d36432b6f21942fff31ca713bYigit Boyar        @Override
134848c4611ea929b7d36432b6f21942fff31ca713bYigit Boyar        public void run() {
135848c4611ea929b7d36432b6f21942fff31ca713bYigit Boyar            try {
136848c4611ea929b7d36432b6f21942fff31ca713bYigit Boyar                mWrapped.run();
137848c4611ea929b7d36432b6f21942fff31ca713bYigit Boyar            } finally {
138848c4611ea929b7d36432b6f21942fff31ca713bYigit Boyar                decrement();
139848c4611ea929b7d36432b6f21942fff31ca713bYigit Boyar            }
140848c4611ea929b7d36432b6f21942fff31ca713bYigit Boyar        }
141848c4611ea929b7d36432b6f21942fff31ca713bYigit Boyar    }
142848c4611ea929b7d36432b6f21942fff31ca713bYigit Boyar}
143