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 */ 16 17package com.google.android.apps.common.testing.ui.espresso.base; 18 19import junit.framework.TestCase; 20 21import java.util.concurrent.Callable; 22import java.util.concurrent.CountDownLatch; 23import java.util.concurrent.FutureTask; 24import java.util.concurrent.LinkedBlockingQueue; 25import java.util.concurrent.ThreadPoolExecutor; 26import java.util.concurrent.TimeUnit; 27import java.util.concurrent.atomic.AtomicBoolean; 28 29/** 30 * Unit test for {@link AsyncTaskPoolMonitor} 31 */ 32public class AsyncTaskPoolMonitorTest extends TestCase { 33 34 private final ThreadPoolExecutor testThreadPool = new ThreadPoolExecutor( 35 4, 4, 1, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>()); 36 37 private AsyncTaskPoolMonitor monitor = new AsyncTaskPoolMonitor(testThreadPool); 38 39 @Override 40 public void tearDown() throws Exception { 41 testThreadPool.shutdownNow(); 42 super.tearDown(); 43 } 44 45 public void testIsIdle_onEmptyPool() throws Exception { 46 assertTrue(monitor.isIdleNow()); 47 final AtomicBoolean isIdle = new AtomicBoolean(false); 48 // since we're already idle, this should be ran immedately on our thread. 49 monitor.notifyWhenIdle(new Runnable() { 50 @Override 51 public void run() { 52 isIdle.set(true); 53 } 54 }); 55 assertTrue(isIdle.get()); 56 } 57 58 public void testIsIdle_withRunningTask() throws Exception { 59 final CountDownLatch runLatch = new CountDownLatch(1); 60 testThreadPool.submit(new Runnable() { 61 @Override 62 public void run() { 63 runLatch.countDown(); 64 try { 65 Thread.sleep(50000); 66 } catch (InterruptedException ie) { 67 throw new RuntimeException(ie); 68 } 69 } 70 }); 71 assertTrue(runLatch.await(1, TimeUnit.SECONDS)); 72 assertFalse(monitor.isIdleNow()); 73 74 final AtomicBoolean isIdle = new AtomicBoolean(false); 75 monitor.notifyWhenIdle(new Runnable() { 76 @Override 77 public void run() { 78 isIdle.set(true); 79 } 80 }); 81 // runnable shouldn't be run ever.. 82 assertFalse(isIdle.get()); 83 } 84 85 86 public void testIdleNotificationAndRestart() throws Exception { 87 88 FutureTask<Thread> workerThreadFetchTask = new FutureTask<Thread>(new Callable<Thread>() { 89 @Override 90 public Thread call() { 91 return Thread.currentThread(); 92 } 93 }); 94 testThreadPool.submit(workerThreadFetchTask); 95 96 Thread workerThread = workerThreadFetchTask.get(); 97 98 final CountDownLatch runLatch = new CountDownLatch(1); 99 final CountDownLatch exitLatch = new CountDownLatch(1); 100 101 testThreadPool.submit(new Runnable() { 102 @Override 103 public void run() { 104 runLatch.countDown(); 105 try { 106 exitLatch.await(); 107 } catch (InterruptedException ie) { 108 throw new RuntimeException(ie); 109 } 110 } 111 }); 112 113 assertTrue(runLatch.await(1, TimeUnit.SECONDS)); 114 final CountDownLatch notificationLatch = new CountDownLatch(1); 115 monitor.notifyWhenIdle(new Runnable() { 116 @Override 117 public void run() { 118 notificationLatch.countDown(); 119 } 120 }); 121 // give some time for the idle detection threads to spin up. 122 Thread.sleep(2000); 123 // interrupt one of them 124 workerThread.interrupt(); 125 Thread.sleep(1000); 126 // unblock the dummy work item. 127 exitLatch.countDown(); 128 assertTrue(notificationLatch.await(1, TimeUnit.SECONDS)); 129 assertTrue(monitor.isIdleNow()); 130 } 131 132 public void testIdleNotification_extraWork() throws Exception { 133 final CountDownLatch firstRunLatch = new CountDownLatch(1); 134 final CountDownLatch firstExitLatch = new CountDownLatch(1); 135 136 testThreadPool.submit(new Runnable() { 137 @Override 138 public void run() { 139 firstRunLatch.countDown(); 140 try { 141 firstExitLatch.await(); 142 } catch (InterruptedException ie) { 143 throw new RuntimeException(ie); 144 } 145 } 146 }); 147 148 assertTrue(firstRunLatch.await(1, TimeUnit.SECONDS)); 149 150 final CountDownLatch notificationLatch = new CountDownLatch(1); 151 monitor.notifyWhenIdle(new Runnable() { 152 @Override 153 public void run() { 154 notificationLatch.countDown(); 155 } 156 }); 157 158 final CountDownLatch secondRunLatch = new CountDownLatch(1); 159 final CountDownLatch secondExitLatch = new CountDownLatch(1); 160 testThreadPool.submit(new Runnable() { 161 @Override 162 public void run() { 163 secondRunLatch.countDown(); 164 try { 165 secondExitLatch.await(); 166 } catch (InterruptedException ie) { 167 throw new RuntimeException(ie); 168 } 169 } 170 }); 171 172 assertFalse(notificationLatch.await(10, TimeUnit.MILLISECONDS)); 173 firstExitLatch.countDown(); 174 assertFalse(notificationLatch.await(500, TimeUnit.MILLISECONDS)); 175 secondExitLatch.countDown(); 176 assertTrue(notificationLatch.await(1, TimeUnit.SECONDS)); 177 assertTrue(monitor.isIdleNow()); 178 } 179} 180