1 /*
2  * Copyright (C) 2017 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 
17 package androidx.arch.core.executor.testing;
18 
19 import static org.hamcrest.CoreMatchers.is;
20 import static org.hamcrest.MatcherAssert.assertThat;
21 
22 import androidx.arch.core.executor.ArchTaskExecutor;
23 import androidx.test.ext.junit.runners.AndroidJUnit4;
24 import androidx.test.filters.LargeTest;
25 
26 import org.junit.Rule;
27 import org.junit.Test;
28 import org.junit.runner.RunWith;
29 
30 import java.util.ArrayList;
31 import java.util.List;
32 import java.util.concurrent.CountDownLatch;
33 import java.util.concurrent.Semaphore;
34 import java.util.concurrent.TimeUnit;
35 import java.util.concurrent.TimeoutException;
36 
37 @RunWith(AndroidJUnit4.class)
38 @LargeTest
39 public class CountingTaskExecutorRuleTest {
40     private final Semaphore mOnIdleCount = new Semaphore(0);
41 
42     @Rule
43     public CountingTaskExecutorRule mRule = new CountingTaskExecutorRule() {
44         @Override
45         protected void onIdle() {
46             super.onIdle();
47             mOnIdleCount.release(1);
48         }
49     };
50 
51     @Test
initialIdle()52     public void initialIdle() {
53         assertThat(mRule.isIdle(), is(true));
54     }
55 
56     @Test
busyIO()57     public void busyIO() throws InterruptedException {
58         LatchRunnable task = runOnIO();
59         singleTaskTest(task);
60     }
61 
62     @Test
busyMain()63     public void busyMain() throws InterruptedException {
64         LatchRunnable task = runOnMain();
65         singleTaskTest(task);
66     }
67 
68     @Test
multipleTasks()69     public void multipleTasks() throws InterruptedException {
70         List<LatchRunnable> latches = new ArrayList<>(10);
71         for (int i = 0; i < 5; i++) {
72             latches.add(runOnIO());
73             latches.add(runOnMain());
74         }
75         assertNotIdle();
76         for (int i = 0; i < 9; i++) {
77             latches.get(i).start();
78         }
79         for (int i = 0; i < 9; i++) {
80             latches.get(i).await();
81         }
82         assertNotIdle();
83 
84         LatchRunnable another = runOnIO();
85         latches.get(9).startAndFinish();
86         assertNotIdle();
87 
88         another.startAndFinish();
89         assertBecomeIdle();
90 
91         LatchRunnable oneMore = runOnMain();
92 
93         assertNotIdle();
94 
95         oneMore.startAndFinish();
96         assertBecomeIdle();
97     }
98 
assertNotIdle()99     private void assertNotIdle() throws InterruptedException {
100         assertThat(mOnIdleCount.tryAcquire(300, TimeUnit.MILLISECONDS), is(false));
101         assertThat(mRule.isIdle(), is(false));
102     }
103 
assertBecomeIdle()104     private void assertBecomeIdle() throws InterruptedException {
105         assertThat(mOnIdleCount.tryAcquire(1, TimeUnit.SECONDS), is(true));
106         assertThat(mRule.isIdle(), is(true));
107     }
108 
singleTaskTest(LatchRunnable task)109     private void singleTaskTest(LatchRunnable task)
110             throws InterruptedException {
111         assertNotIdle();
112         task.startAndFinish();
113         assertBecomeIdle();
114     }
115 
runOnIO()116     private LatchRunnable runOnIO() {
117         LatchRunnable latchRunnable = new LatchRunnable();
118         ArchTaskExecutor.getInstance().executeOnDiskIO(latchRunnable);
119         return latchRunnable;
120     }
121 
runOnMain()122     private LatchRunnable runOnMain() {
123         LatchRunnable latchRunnable = new LatchRunnable();
124         ArchTaskExecutor.getInstance().executeOnMainThread(latchRunnable);
125         return latchRunnable;
126     }
127 
128     @Test
drainFailure()129     public void drainFailure() throws InterruptedException {
130         runOnIO();
131         try {
132             mRule.drainTasks(300, TimeUnit.MILLISECONDS);
133             throw new AssertionError("drain should fail");
134         } catch (TimeoutException ignored) {
135         }
136     }
137 
138     @Test
drainSuccess()139     public void drainSuccess() throws TimeoutException, InterruptedException {
140         final LatchRunnable task = runOnIO();
141         new Thread(new Runnable() {
142             @Override
143             public void run() {
144                 try {
145                     Thread.sleep(300);
146                 } catch (InterruptedException ignored) {
147                 }
148                 task.start();
149             }
150         }).start();
151         mRule.drainTasks(1, TimeUnit.SECONDS);
152     }
153 
154     private static class LatchRunnable implements Runnable {
155         private final CountDownLatch mStart = new CountDownLatch(1);
156         private final CountDownLatch mEnd = new CountDownLatch(1);
157 
158         @Override
run()159         public void run() {
160             try {
161                 mStart.await(10, TimeUnit.SECONDS);
162                 mEnd.countDown();
163             } catch (InterruptedException e) {
164                 throw new AssertionError(e);
165             }
166         }
167 
await()168         void await() throws InterruptedException {
169             mEnd.await(10, TimeUnit.SECONDS);
170         }
171 
start()172         void start() {
173             mStart.countDown();
174         }
175 
startAndFinish()176         private void startAndFinish() throws InterruptedException {
177             start();
178             await();
179         }
180     }
181 }
182