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