1package org.junit.internal.runners;
2
3import java.lang.reflect.InvocationTargetException;
4import java.lang.reflect.Method;
5import java.util.List;
6import java.util.concurrent.Callable;
7import java.util.concurrent.ExecutorService;
8import java.util.concurrent.Executors;
9import java.util.concurrent.Future;
10import java.util.concurrent.TimeUnit;
11import java.util.concurrent.TimeoutException;
12
13import org.junit.internal.AssumptionViolatedException;
14import org.junit.runner.Description;
15import org.junit.runner.notification.Failure;
16import org.junit.runner.notification.RunNotifier;
17import org.junit.runners.BlockJUnit4ClassRunner;
18import org.junit.runners.model.TestTimedOutException;
19
20/**
21 * @deprecated Included for backwards compatibility with JUnit 4.4. Will be
22 *             removed in the next major release. Please use
23 *             {@link BlockJUnit4ClassRunner} in place of {@link JUnit4ClassRunner}.
24 */
25@Deprecated
26public class MethodRoadie {
27    private final Object test;
28    private final RunNotifier notifier;
29    private final Description description;
30    private TestMethod testMethod;
31
32    public MethodRoadie(Object test, TestMethod method, RunNotifier notifier, Description description) {
33        this.test = test;
34        this.notifier = notifier;
35        this.description = description;
36        testMethod = method;
37    }
38
39    public void run() {
40        if (testMethod.isIgnored()) {
41            notifier.fireTestIgnored(description);
42            return;
43        }
44        notifier.fireTestStarted(description);
45        try {
46            long timeout = testMethod.getTimeout();
47            if (timeout > 0) {
48                runWithTimeout(timeout);
49            } else {
50                runTest();
51            }
52        } finally {
53            notifier.fireTestFinished(description);
54        }
55    }
56
57    private void runWithTimeout(final long timeout) {
58        runBeforesThenTestThenAfters(new Runnable() {
59
60            public void run() {
61                ExecutorService service = Executors.newSingleThreadExecutor();
62                Callable<Object> callable = new Callable<Object>() {
63                    public Object call() throws Exception {
64                        runTestMethod();
65                        return null;
66                    }
67                };
68                Future<Object> result = service.submit(callable);
69                service.shutdown();
70                try {
71                    boolean terminated = service.awaitTermination(timeout,
72                            TimeUnit.MILLISECONDS);
73                    if (!terminated) {
74                        service.shutdownNow();
75                    }
76                    result.get(0, TimeUnit.MILLISECONDS); // throws the exception if one occurred during the invocation
77                } catch (TimeoutException e) {
78                    addFailure(new TestTimedOutException(timeout, TimeUnit.MILLISECONDS));
79                } catch (Exception e) {
80                    addFailure(e);
81                }
82            }
83        });
84    }
85
86    public void runTest() {
87        runBeforesThenTestThenAfters(new Runnable() {
88            public void run() {
89                runTestMethod();
90            }
91        });
92    }
93
94    public void runBeforesThenTestThenAfters(Runnable test) {
95        try {
96            runBefores();
97            test.run();
98        } catch (FailedBefore e) {
99        } catch (Exception e) {
100            throw new RuntimeException("test should never throw an exception to this level");
101        } finally {
102            runAfters();
103        }
104    }
105
106    protected void runTestMethod() {
107        try {
108            testMethod.invoke(test);
109            if (testMethod.expectsException()) {
110                addFailure(new AssertionError("Expected exception: " + testMethod.getExpectedException().getName()));
111            }
112        } catch (InvocationTargetException e) {
113            Throwable actual = e.getTargetException();
114            if (actual instanceof AssumptionViolatedException) {
115                return;
116            } else if (!testMethod.expectsException()) {
117                addFailure(actual);
118            } else if (testMethod.isUnexpected(actual)) {
119                String message = "Unexpected exception, expected<" + testMethod.getExpectedException().getName() + "> but was<"
120                        + actual.getClass().getName() + ">";
121                addFailure(new Exception(message, actual));
122            }
123        } catch (Throwable e) {
124            addFailure(e);
125        }
126    }
127
128    private void runBefores() throws FailedBefore {
129        try {
130            try {
131                List<Method> befores = testMethod.getBefores();
132                for (Method before : befores) {
133                    before.invoke(test);
134                }
135            } catch (InvocationTargetException e) {
136                throw e.getTargetException();
137            }
138        } catch (AssumptionViolatedException e) {
139            throw new FailedBefore();
140        } catch (Throwable e) {
141            addFailure(e);
142            throw new FailedBefore();
143        }
144    }
145
146    private void runAfters() {
147        List<Method> afters = testMethod.getAfters();
148        for (Method after : afters) {
149            try {
150                after.invoke(test);
151            } catch (InvocationTargetException e) {
152                addFailure(e.getTargetException());
153            } catch (Throwable e) {
154                addFailure(e); // Untested, but seems impossible
155            }
156        }
157    }
158
159    protected void addFailure(Throwable e) {
160        notifier.fireTestFailure(new Failure(description, e));
161    }
162}
163
164