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