• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2020 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;
18 
19 import java.util.ArrayList;
20 import java.util.Collection;
21 import java.util.List;
22 import java.util.concurrent.Callable;
23 import java.util.concurrent.CompletableFuture;
24 import java.util.concurrent.Delayed;
25 import java.util.concurrent.ExecutionException;
26 import java.util.concurrent.Future;
27 import java.util.concurrent.ScheduledExecutorService;
28 import java.util.concurrent.ScheduledFuture;
29 import java.util.concurrent.TimeUnit;
30 import java.util.concurrent.TimeoutException;
31 
32 /**
33  * An implementation of ExecutorService that just runs the requested task on the thread that it
34  * was called on for testing purposes.
35  */
36 public class TestExecutorService implements ScheduledExecutorService {
37 
38     private static class CompletedFuture<T> implements Future<T>, ScheduledFuture<T> {
39 
40         private final Callable<T> mTask;
41         private final long mDelayMs;
42         // Wrap callable in a CompletableFuture to support delays in execution.
43         private final CompletableFuture<T> mFuture = new CompletableFuture<>();
44 
CompletedFuture(Callable<T> task)45         CompletedFuture(Callable<T> task) {
46             mTask = task;
47             mDelayMs = 0;
48         }
49 
CompletedFuture(Callable<T> task, long delayMs)50         CompletedFuture(Callable<T> task, long delayMs) {
51             mTask = task;
52             mDelayMs = delayMs;
53         }
54 
55         @Override
cancel(boolean mayInterruptIfRunning)56         public boolean cancel(boolean mayInterruptIfRunning) {
57             return mFuture.cancel(mayInterruptIfRunning);
58         }
59 
60         @Override
isCancelled()61         public boolean isCancelled() {
62             return mFuture.isCancelled();
63         }
64 
65         @Override
isDone()66         public boolean isDone() {
67             return mFuture.isDone();
68         }
69 
70         @Override
get()71         public T get() throws InterruptedException, ExecutionException {
72             return mFuture.get();
73         }
74 
75         @Override
get(long timeout, TimeUnit unit)76         public T get(long timeout, TimeUnit unit)
77                 throws InterruptedException, ExecutionException, TimeoutException {
78             // delays not implemented, this should complete via completeTask for better control.
79             return mFuture.get(timeout, unit);
80         }
81 
82         @Override
getDelay(TimeUnit unit)83         public long getDelay(TimeUnit unit) {
84             if (unit == TimeUnit.MILLISECONDS) {
85                 return mDelayMs;
86             } else {
87                 // not implemented
88                 return 0;
89             }
90         }
91 
92         @Override
compareTo(Delayed o)93         public int compareTo(Delayed o) {
94             if (o == null) return 1;
95             if (o.getDelay(TimeUnit.MILLISECONDS) > mDelayMs) return -1;
96             if (o.getDelay(TimeUnit.MILLISECONDS) < mDelayMs) return 1;
97             return 0;
98         }
99 
completeTask()100         public void completeTask() {
101             try {
102                 mFuture.complete(mTask.call());
103             } catch (Exception e) {
104                 mFuture.completeExceptionally(e);
105             }
106         }
107     }
108 
109     private final ArrayList<Runnable> mPendingRunnables = new ArrayList<>();
110     private final boolean mWaitToComplete;
111     private boolean mIsShutdown = false;
112 
TestExecutorService()113     public TestExecutorService() {
114         mWaitToComplete = false;
115     }
116 
117     /**
118      * Create a test executor service that also allows the constructor to provide a parameter to
119      * control when pending Runnables are executed.
120      * @param waitToComplete If true, this executor will wait to complete any pending Runnables
121      *                       until {@link #executePending()}} is called.
122      */
TestExecutorService(boolean waitToComplete)123     public TestExecutorService(boolean waitToComplete) {
124         mWaitToComplete = waitToComplete;
125     }
126 
127     @Override
shutdown()128     public void shutdown() {
129         mIsShutdown = true;
130         for (Runnable r : mPendingRunnables) {
131             r.run();
132         }
133     }
134 
135     @Override
shutdownNow()136     public List<Runnable> shutdownNow() {
137         mIsShutdown = true;
138         List<Runnable> runnables = new ArrayList<>(mPendingRunnables);
139         mPendingRunnables.clear();
140         return runnables;
141     }
142 
143     @Override
isShutdown()144     public boolean isShutdown() {
145         return mIsShutdown;
146     }
147 
148     @Override
isTerminated()149     public boolean isTerminated() {
150         return mIsShutdown;
151     }
152 
153     @Override
awaitTermination(long timeout, TimeUnit unit)154     public boolean awaitTermination(long timeout, TimeUnit unit) {
155         shutdown();
156         return true;
157     }
158 
159     @Override
submit(Callable<T> task)160     public <T> Future<T> submit(Callable<T> task) {
161         CompletedFuture<T> f = new CompletedFuture<>(task);
162         onExecute(f::completeTask);
163         return f;
164     }
165 
166     @Override
submit(Runnable task, T result)167     public <T> Future<T> submit(Runnable task, T result) {
168         throw new UnsupportedOperationException("Not implemented");
169     }
170 
171     @Override
submit(Runnable task)172     public Future<?> submit(Runnable task) {
173         CompletedFuture<Void> f = new CompletedFuture<>(() -> {
174             task.run();
175             return null;
176         });
177         onExecute(f::completeTask);
178         return f;
179     }
180 
181     @Override
invokeAll(Collection<? extends Callable<T>> tasks)182     public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks) {
183         throw new UnsupportedOperationException("Not implemented");
184     }
185 
186     @Override
invokeAll(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit)187     public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks, long timeout,
188             TimeUnit unit) {
189         throw new UnsupportedOperationException("Not implemented");
190     }
191 
192     @Override
invokeAny(Collection<? extends Callable<T>> tasks)193     public <T> T invokeAny(Collection<? extends Callable<T>> tasks) {
194         throw new UnsupportedOperationException("Not implemented");
195     }
196 
197     @Override
invokeAny(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit)198     public <T> T invokeAny(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit) {
199         throw new UnsupportedOperationException("Not implemented");
200     }
201 
202     @Override
schedule(Runnable command, long delay, TimeUnit unit)203     public ScheduledFuture<?> schedule(Runnable command, long delay, TimeUnit unit) {
204         long millisDelay = TimeUnit.MILLISECONDS.convert(delay, unit);
205         CompletedFuture<Void> f = new CompletedFuture<>(() -> {
206             command.run();
207             return null;
208         }, millisDelay);
209         onExecute(f::completeTask);
210         return f;
211     }
212 
213     @Override
schedule(Callable<V> callable, long delay, TimeUnit unit)214     public <V> ScheduledFuture<V> schedule(Callable<V> callable, long delay, TimeUnit unit) {
215         long millisDelay = TimeUnit.MILLISECONDS.convert(delay, unit);
216         CompletedFuture<V> f = new CompletedFuture<>(callable, millisDelay);
217         onExecute(f::completeTask);
218         return f;
219     }
220 
221     @Override
scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit)222     public ScheduledFuture<?> scheduleAtFixedRate(Runnable command, long initialDelay, long period,
223             TimeUnit unit) {
224         throw new UnsupportedOperationException("Not implemented");
225     }
226 
227     @Override
scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit)228     public ScheduledFuture<?> scheduleWithFixedDelay(Runnable command, long initialDelay,
229             long delay, TimeUnit unit) {
230         throw new UnsupportedOperationException("Not implemented");
231     }
232 
233     @Override
execute(Runnable command)234     public void execute(Runnable command) {
235         onExecute(command);
236     }
237 
onExecute(Runnable command)238     private void onExecute(Runnable command) {
239         if (mWaitToComplete) {
240             mPendingRunnables.add(command);
241         } else {
242             command.run();
243         }
244     }
245 
executePending()246     public void executePending() {
247         for (Runnable r : mPendingRunnables) {
248             r.run();
249         }
250         mPendingRunnables.clear();
251     }
252 }
253