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