1 /*
2  * Copyright (C) 2016 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 com.android.documentsui.testing;
18 
19 import static junit.framework.Assert.assertFalse;
20 import static junit.framework.Assert.assertTrue;
21 
22 import android.os.Handler;
23 import android.os.Looper;
24 import android.os.SystemClock;
25 
26 import java.util.ArrayList;
27 import java.util.Collection;
28 import java.util.Collections;
29 import java.util.List;
30 import java.util.concurrent.Callable;
31 import java.util.concurrent.CountDownLatch;
32 import java.util.concurrent.Delayed;
33 import java.util.concurrent.ExecutionException;
34 import java.util.concurrent.Future;
35 import java.util.concurrent.ScheduledExecutorService;
36 import java.util.concurrent.ScheduledFuture;
37 import java.util.concurrent.TimeUnit;
38 import java.util.concurrent.TimeoutException;
39 
40 public class TestScheduledExecutorService implements ScheduledExecutorService {
41 
42     private List<TestFuture> scheduled = new ArrayList<>();
43     private boolean shutdown;
44 
45     @Override
shutdown()46     public void shutdown() {
47         this.shutdown = true;
48     }
49 
50     @Override
shutdownNow()51     public List<Runnable> shutdownNow() {
52         this.shutdown = true;
53         return Collections.emptyList();
54     }
55 
assertShutdown()56     public void assertShutdown() {
57         assertTrue("Executor wasn't shut down.", shutdown);
58     }
59 
60     @Override
isShutdown()61     public boolean isShutdown() {
62         return shutdown;
63     }
64 
65     @Override
isTerminated()66     public boolean isTerminated() {
67         throw new UnsupportedOperationException();
68     }
69 
70     @Override
awaitTermination(long timeout, TimeUnit unit)71     public boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException {
72         throw new UnsupportedOperationException();
73     }
74 
75     @Override
submit(Callable<T> task)76     public <T> Future<T> submit(Callable<T> task) {
77         throw new UnsupportedOperationException();
78     }
79 
80     @Override
submit(Runnable task, T result)81     public <T> Future<T> submit(Runnable task, T result) {
82         throw new UnsupportedOperationException();
83     }
84 
85     @Override
submit(Runnable task)86     public Future<?> submit(Runnable task) {
87         return schedule(task, 0, TimeUnit.MILLISECONDS);
88     }
89 
90     @Override
invokeAll(Collection<? extends Callable<T>> tasks)91     public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks)
92             throws InterruptedException {
93         throw new UnsupportedOperationException();
94     }
95 
96     @Override
invokeAll(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit)97     public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks, long timeout,
98             TimeUnit unit) throws InterruptedException {
99         throw new UnsupportedOperationException();
100     }
101 
102     @Override
invokeAny(Collection<? extends Callable<T>> tasks)103     public <T> T invokeAny(Collection<? extends Callable<T>> tasks)
104             throws InterruptedException, ExecutionException {
105         throw new UnsupportedOperationException();
106     }
107 
108     @Override
invokeAny(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit)109     public <T> T invokeAny(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit)
110             throws InterruptedException, ExecutionException, TimeoutException {
111         throw new UnsupportedOperationException();
112     }
113 
114     @Override
execute(Runnable command)115     public void execute(Runnable command) {
116         schedule(command, 0, TimeUnit.MILLISECONDS);
117     }
118 
119     @Override
schedule(Runnable command, long delay, TimeUnit unit)120     public ScheduledFuture<?> schedule(Runnable command, long delay, TimeUnit unit) {
121         TestFuture future = new TestFuture(command, delay, unit);
122         scheduled.add(future);
123         return future;
124     }
125 
126     @Override
schedule(Callable<V> callable, long delay, TimeUnit unit)127     public <V> ScheduledFuture<V> schedule(Callable<V> callable, long delay, TimeUnit unit) {
128         throw new UnsupportedOperationException();
129     }
130 
131     @Override
scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit)132     public ScheduledFuture<?> scheduleAtFixedRate(Runnable command, long initialDelay, long period,
133             TimeUnit unit) {
134         throw new UnsupportedOperationException();
135     }
136 
137     @Override
scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit)138     public ScheduledFuture<?> scheduleWithFixedDelay(Runnable command, long initialDelay,
139             long delay, TimeUnit unit) {
140         throw new UnsupportedOperationException();
141     }
142 
runAll()143     public void runAll() {
144         while (!scheduled.isEmpty()) {
145             TestFuture future = scheduled.remove(scheduled.size() - 1);
146             future.runnable.run();
147         }
148     }
149 
run(int taskIndex)150     public void run(int taskIndex) {
151         scheduled.get(taskIndex).runnable.run();
152 
153         scheduled.remove(taskIndex);
154     }
155 
assertAlive()156     public void assertAlive() {
157         assertFalse(isShutdown());
158     }
159 
waitForTasks(long millisTimeout)160     public void waitForTasks(long millisTimeout) throws Exception {
161         millisTimeout = (millisTimeout > 0) ? millisTimeout : Long.MAX_VALUE;
162 
163         final long startTime = SystemClock.uptimeMillis();
164 
165         // We need to wait on all AsyncTasks to finish AND to post results back.
166         // *** Results are posted on main thread ***, but tests run in their own
167         // thread. So even with our test executor we still have races.
168         //
169         // To work around this issue post our own runnable to the main thread
170         // which we presume will be the *last* runnable (after any from AsyncTasks)
171         // and then wait for our runnable to be called.
172         while (!scheduled.isEmpty() && millisTimeout > 0) {
173             CountDownLatch latch = new CountDownLatch(1);
174             runAll();
175             new Handler(Looper.getMainLooper()).post(latch::countDown);
176             latch.await(millisTimeout, TimeUnit.MILLISECONDS);
177 
178             millisTimeout -= (SystemClock.uptimeMillis() - startTime);
179         }
180     }
181 
182     static class TestFuture implements ScheduledFuture<Void> {
183 
184         final Runnable runnable;
185         final long delay;
186         final TimeUnit unit;
187 
TestFuture(Runnable runnable, long delay, TimeUnit unit)188         public TestFuture(Runnable runnable, long delay, TimeUnit unit) {
189             this.runnable = runnable;
190             this.delay = delay;
191             this.unit = unit;
192         }
193 
194         @Override
getDelay(TimeUnit unit)195         public long getDelay(TimeUnit unit) {
196             return 0;
197         }
198 
199         @Override
compareTo(Delayed arg0)200         public int compareTo(Delayed arg0) {
201             return 0;
202         }
203 
204         @Override
cancel(boolean mayInterruptIfRunning)205         public boolean cancel(boolean mayInterruptIfRunning) {
206             return false;
207         }
208 
209         @Override
isCancelled()210         public boolean isCancelled() {
211             return false;
212         }
213 
214         @Override
isDone()215         public boolean isDone() {
216             return false;
217         }
218 
219         @Override
get()220         public Void get() throws InterruptedException, ExecutionException {
221             return null;
222         }
223 
224         @Override
get(long timeout, TimeUnit unit)225         public Void get(long timeout, TimeUnit unit)
226                 throws InterruptedException, ExecutionException, TimeoutException {
227             return null;
228         }
229     }
230 }
231