1654f21617c60f23069912900a1e1ef11e9e1c742Paul Duffin/* 2654f21617c60f23069912900a1e1ef11e9e1c742Paul Duffin * Copyright (C) 2016 The Android Open Source Project 3654f21617c60f23069912900a1e1ef11e9e1c742Paul Duffin * 4654f21617c60f23069912900a1e1ef11e9e1c742Paul Duffin * Licensed under the Apache License, Version 2.0 (the "License"); 5654f21617c60f23069912900a1e1ef11e9e1c742Paul Duffin * you may not use this file except in compliance with the License. 6654f21617c60f23069912900a1e1ef11e9e1c742Paul Duffin * You may obtain a copy of the License at 7654f21617c60f23069912900a1e1ef11e9e1c742Paul Duffin * 8654f21617c60f23069912900a1e1ef11e9e1c742Paul Duffin * http://www.apache.org/licenses/LICENSE-2.0 9654f21617c60f23069912900a1e1ef11e9e1c742Paul Duffin * 10654f21617c60f23069912900a1e1ef11e9e1c742Paul Duffin * Unless required by applicable law or agreed to in writing, software 11654f21617c60f23069912900a1e1ef11e9e1c742Paul Duffin * distributed under the License is distributed on an "AS IS" BASIS, 12654f21617c60f23069912900a1e1ef11e9e1c742Paul Duffin * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13654f21617c60f23069912900a1e1ef11e9e1c742Paul Duffin * See the License for the specific language governing permissions and 14654f21617c60f23069912900a1e1ef11e9e1c742Paul Duffin * limitations under the License. 15654f21617c60f23069912900a1e1ef11e9e1c742Paul Duffin */ 16654f21617c60f23069912900a1e1ef11e9e1c742Paul Duffin 17654f21617c60f23069912900a1e1ef11e9e1c742Paul Duffinpackage vogar.target.junit; 18654f21617c60f23069912900a1e1ef11e9e1c742Paul Duffin 19654f21617c60f23069912900a1e1ef11e9e1c742Paul Duffinimport java.util.concurrent.Callable; 20654f21617c60f23069912900a1e1ef11e9e1c742Paul Duffinimport java.util.concurrent.ExecutionException; 21654f21617c60f23069912900a1e1ef11e9e1c742Paul Duffinimport java.util.concurrent.ExecutorService; 22654f21617c60f23069912900a1e1ef11e9e1c742Paul Duffinimport java.util.concurrent.Executors; 23654f21617c60f23069912900a1e1ef11e9e1c742Paul Duffinimport java.util.concurrent.Future; 24654f21617c60f23069912900a1e1ef11e9e1c742Paul Duffinimport java.util.concurrent.TimeUnit; 25654f21617c60f23069912900a1e1ef11e9e1c742Paul Duffinimport java.util.concurrent.TimeoutException; 26654f21617c60f23069912900a1e1ef11e9e1c742Paul Duffinimport java.util.concurrent.atomic.AtomicReference; 27654f21617c60f23069912900a1e1ef11e9e1c742Paul Duffinimport org.junit.rules.TestRule; 28654f21617c60f23069912900a1e1ef11e9e1c742Paul Duffinimport org.junit.runner.Description; 29654f21617c60f23069912900a1e1ef11e9e1c742Paul Duffinimport org.junit.runners.model.Statement; 30654f21617c60f23069912900a1e1ef11e9e1c742Paul Duffinimport vogar.util.Threads; 31654f21617c60f23069912900a1e1ef11e9e1c742Paul Duffin 32654f21617c60f23069912900a1e1ef11e9e1c742Paul Duffin/** 33654f21617c60f23069912900a1e1ef11e9e1c742Paul Duffin * Times a test out and then aborts the test run. 34654f21617c60f23069912900a1e1ef11e9e1c742Paul Duffin */ 35654f21617c60f23069912900a1e1ef11e9e1c742Paul Duffinpublic class TimeoutAndAbortRunRule implements TestRule { 36654f21617c60f23069912900a1e1ef11e9e1c742Paul Duffin 37654f21617c60f23069912900a1e1ef11e9e1c742Paul Duffin private final ExecutorService executor = Executors.newCachedThreadPool( 38654f21617c60f23069912900a1e1ef11e9e1c742Paul Duffin Threads.daemonThreadFactory(getClass().getName())); 39654f21617c60f23069912900a1e1ef11e9e1c742Paul Duffin 40654f21617c60f23069912900a1e1ef11e9e1c742Paul Duffin private final int timeoutSeconds; 41654f21617c60f23069912900a1e1ef11e9e1c742Paul Duffin 42654f21617c60f23069912900a1e1ef11e9e1c742Paul Duffin /** 43654f21617c60f23069912900a1e1ef11e9e1c742Paul Duffin * @param timeoutSeconds the timeout in seconds, if 0 then never times out. 44654f21617c60f23069912900a1e1ef11e9e1c742Paul Duffin */ 45654f21617c60f23069912900a1e1ef11e9e1c742Paul Duffin public TimeoutAndAbortRunRule(int timeoutSeconds) { 46654f21617c60f23069912900a1e1ef11e9e1c742Paul Duffin this.timeoutSeconds = timeoutSeconds; 47654f21617c60f23069912900a1e1ef11e9e1c742Paul Duffin } 48654f21617c60f23069912900a1e1ef11e9e1c742Paul Duffin 49654f21617c60f23069912900a1e1ef11e9e1c742Paul Duffin @Override 50654f21617c60f23069912900a1e1ef11e9e1c742Paul Duffin public Statement apply(final Statement base, Description description) { 51654f21617c60f23069912900a1e1ef11e9e1c742Paul Duffin return new Statement() { 52654f21617c60f23069912900a1e1ef11e9e1c742Paul Duffin @Override 53654f21617c60f23069912900a1e1ef11e9e1c742Paul Duffin public void evaluate() throws Throwable { 54654f21617c60f23069912900a1e1ef11e9e1c742Paul Duffin runWithTimeout(base); 55654f21617c60f23069912900a1e1ef11e9e1c742Paul Duffin } 56654f21617c60f23069912900a1e1ef11e9e1c742Paul Duffin }; 57654f21617c60f23069912900a1e1ef11e9e1c742Paul Duffin } 58654f21617c60f23069912900a1e1ef11e9e1c742Paul Duffin 59654f21617c60f23069912900a1e1ef11e9e1c742Paul Duffin /** 60654f21617c60f23069912900a1e1ef11e9e1c742Paul Duffin * Runs the test on another thread. If the test completes before the 61654f21617c60f23069912900a1e1ef11e9e1c742Paul Duffin * timeout, this reports the result normally. But if the test times out, 62654f21617c60f23069912900a1e1ef11e9e1c742Paul Duffin * this reports the timeout stack trace and begins the process of killing 63654f21617c60f23069912900a1e1ef11e9e1c742Paul Duffin * this no-longer-trustworthy process. 64654f21617c60f23069912900a1e1ef11e9e1c742Paul Duffin */ 65654f21617c60f23069912900a1e1ef11e9e1c742Paul Duffin private void runWithTimeout(final Statement base) throws Throwable { 66654f21617c60f23069912900a1e1ef11e9e1c742Paul Duffin // Start the test on a background thread. 67654f21617c60f23069912900a1e1ef11e9e1c742Paul Duffin final AtomicReference<Thread> executingThreadReference = new AtomicReference<>(); 68654f21617c60f23069912900a1e1ef11e9e1c742Paul Duffin Future<Throwable> result = executor.submit(new Callable<Throwable>() { 69654f21617c60f23069912900a1e1ef11e9e1c742Paul Duffin public Throwable call() throws Exception { 70654f21617c60f23069912900a1e1ef11e9e1c742Paul Duffin executingThreadReference.set(Thread.currentThread()); 71654f21617c60f23069912900a1e1ef11e9e1c742Paul Duffin try { 72654f21617c60f23069912900a1e1ef11e9e1c742Paul Duffin base.evaluate(); 73654f21617c60f23069912900a1e1ef11e9e1c742Paul Duffin return null; 74654f21617c60f23069912900a1e1ef11e9e1c742Paul Duffin } catch (Throwable throwable) { 75654f21617c60f23069912900a1e1ef11e9e1c742Paul Duffin return throwable; 76654f21617c60f23069912900a1e1ef11e9e1c742Paul Duffin } 77654f21617c60f23069912900a1e1ef11e9e1c742Paul Duffin } 78654f21617c60f23069912900a1e1ef11e9e1c742Paul Duffin }); 79654f21617c60f23069912900a1e1ef11e9e1c742Paul Duffin 80654f21617c60f23069912900a1e1ef11e9e1c742Paul Duffin // Wait until either the result arrives or the test times out. 81654f21617c60f23069912900a1e1ef11e9e1c742Paul Duffin Throwable thrown; 82654f21617c60f23069912900a1e1ef11e9e1c742Paul Duffin try { 83654f21617c60f23069912900a1e1ef11e9e1c742Paul Duffin thrown = getThrowable(result); 84654f21617c60f23069912900a1e1ef11e9e1c742Paul Duffin } catch (TimeoutException e) { 85654f21617c60f23069912900a1e1ef11e9e1c742Paul Duffin Thread executingThread = executingThreadReference.get(); 86654f21617c60f23069912900a1e1ef11e9e1c742Paul Duffin if (executingThread != null) { 87654f21617c60f23069912900a1e1ef11e9e1c742Paul Duffin executingThread.interrupt(); 88654f21617c60f23069912900a1e1ef11e9e1c742Paul Duffin e.setStackTrace(executingThread.getStackTrace()); 89654f21617c60f23069912900a1e1ef11e9e1c742Paul Duffin } 90654f21617c60f23069912900a1e1ef11e9e1c742Paul Duffin // Wrap it in an exception that will cause the current run to be aborted. 91654f21617c60f23069912900a1e1ef11e9e1c742Paul Duffin thrown = new VmIsUnstableException(e); 92654f21617c60f23069912900a1e1ef11e9e1c742Paul Duffin } 93654f21617c60f23069912900a1e1ef11e9e1c742Paul Duffin 94654f21617c60f23069912900a1e1ef11e9e1c742Paul Duffin if (thrown != null) { 95654f21617c60f23069912900a1e1ef11e9e1c742Paul Duffin throw thrown; 96654f21617c60f23069912900a1e1ef11e9e1c742Paul Duffin } 97654f21617c60f23069912900a1e1ef11e9e1c742Paul Duffin } 98654f21617c60f23069912900a1e1ef11e9e1c742Paul Duffin 99654f21617c60f23069912900a1e1ef11e9e1c742Paul Duffin @SuppressWarnings("ThrowableResultOfMethodCallIgnored") 100654f21617c60f23069912900a1e1ef11e9e1c742Paul Duffin private Throwable getThrowable(Future<Throwable> result) 101654f21617c60f23069912900a1e1ef11e9e1c742Paul Duffin throws InterruptedException, ExecutionException, TimeoutException { 102654f21617c60f23069912900a1e1ef11e9e1c742Paul Duffin Throwable thrown; 103654f21617c60f23069912900a1e1ef11e9e1c742Paul Duffin thrown = timeoutSeconds == 0 104654f21617c60f23069912900a1e1ef11e9e1c742Paul Duffin ? result.get() 105654f21617c60f23069912900a1e1ef11e9e1c742Paul Duffin : result.get(timeoutSeconds, TimeUnit.SECONDS); 106654f21617c60f23069912900a1e1ef11e9e1c742Paul Duffin return thrown; 107654f21617c60f23069912900a1e1ef11e9e1c742Paul Duffin } 108654f21617c60f23069912900a1e1ef11e9e1c742Paul Duffin} 109654f21617c60f23069912900a1e1ef11e9e1c742Paul Duffin 110