/******************************************************************************* * Copyright (c) 2012 Google, Inc. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Google, Inc. - initial API and implementation *******************************************************************************/ package com.windowtester.runtime.internal.junit4.mirror.runners; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.concurrent.Callable; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import org.junit.After; import org.junit.Before; import org.junit.runner.Description; import org.junit.runner.notification.Failure; import org.junit.runner.notification.RunNotifier; public class TestMethodRunner extends BeforeAndAfterRunner { private final Object fTest; private final Method fMethod; private final RunNotifier fNotifier; private final TestIntrospector fTestIntrospector; protected final Description fDescription; //!pq: made protected public TestMethodRunner(Object test, Method method, RunNotifier notifier, Description description) { super(test.getClass(), Before.class, After.class, test); fTest= test; fMethod= method; fNotifier= notifier; fTestIntrospector= new TestIntrospector(test.getClass()); fDescription= description; } public void run() { if (fTestIntrospector.isIgnored(fMethod)) { fNotifier.fireTestIgnored(fDescription); return; } fNotifier.fireTestStarted(fDescription); try { long timeout= fTestIntrospector.getTimeout(fMethod); if (timeout > 0) runWithTimeout(timeout); else runMethod(); } finally { fNotifier.fireTestFinished(fDescription); } } private void runWithTimeout(long timeout) { ExecutorService service= Executors.newSingleThreadExecutor(); Callable<Object> callable= new Callable<Object>() { public Object call() throws Exception { runMethod(); return null; } }; Future<Object> result= service.submit(callable); service.shutdown(); try { boolean terminated= service.awaitTermination(timeout, TimeUnit.MILLISECONDS); if (!terminated) service.shutdownNow(); result.get(0, TimeUnit.MILLISECONDS); // throws the exception if one occurred during the invocation } catch (TimeoutException e) { addFailure(new Exception(String.format("test timed out after %d milliseconds", timeout))); } catch (Exception e) { addFailure(e); } } private void runMethod() { runProtected(); } @Override protected void runUnprotected() { try { executeMethodBody(); if (expectsException()) addFailure(new AssertionError("Expected exception: " + expectedException().getName())); } catch (InvocationTargetException e) { Throwable actual= e.getTargetException(); if (!expectsException()) addFailure(actual); else if (isUnexpected(actual)) { String message= "Unexpected exception, expected<" + expectedException().getName() + "> but was<" + actual.getClass().getName() + ">"; addFailure(new Exception(message, actual)); } } catch (Throwable e) { addFailure(e); } } protected void executeMethodBody() throws IllegalAccessException, InvocationTargetException { fMethod.invoke(fTest); } @Override protected void addFailure(Throwable e) { fNotifier.fireTestFailure(new Failure(fDescription, e)); } private boolean expectsException() { return expectedException() != null; } private Class<? extends Throwable> expectedException() { return fTestIntrospector.expectedException(fMethod); } private boolean isUnexpected(Throwable exception) { return ! expectedException().isAssignableFrom(exception.getClass()); } }