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