JUnit4TestRunner.java revision 10c223b7aa6bf34778079bb5ee1a494c83cd1239
1package org.testng.junit;
2
3import java.util.*;
4import java.util.regex.Pattern;
5import org.junit.runner.Description;
6import org.junit.runner.JUnitCore;
7import org.junit.runner.Request;
8import org.junit.runner.Result;
9import org.junit.runner.manipulation.Filter;
10import org.junit.runner.notification.Failure;
11import org.junit.runner.notification.RunListener;
12import org.testng.*;
13import org.testng.collections.Lists;
14import org.testng.internal.ITestResultNotifier;
15import org.testng.internal.InvokedMethod;
16import org.testng.internal.TestResult;
17
18/**
19 * A JUnit TestRunner that records/triggers all information/events necessary to
20 * TestNG.
21 *
22 * @author Lukas Jungmann
23 */
24public class JUnit4TestRunner implements IJUnitTestRunner {
25
26    private ITestResultNotifier m_parentRunner;
27    private List<ITestNGMethod> m_methods = Lists.newArrayList();
28    private List<ITestListener> m_listeners = Lists.newArrayList();
29    private List<IInvokedMethodListener> m_invokeListeners = Lists.newArrayList();
30
31    public JUnit4TestRunner() {
32    }
33
34    public JUnit4TestRunner(ITestResultNotifier tr) {
35        m_parentRunner = tr;
36        m_listeners = m_parentRunner.getTestListeners();
37    }
38
39    /**
40     * Needed from TestRunner in order to figure out what JUnit test methods
41     * were run.
42     *
43     * @return the list of all JUnit test methods run
44     */
45    @Override
46    public List<ITestNGMethod> getTestMethods() {
47        return m_methods;
48    }
49
50    @Override
51    public void setTestResultNotifier(ITestResultNotifier notifier) {
52        m_parentRunner = notifier;
53        m_listeners = m_parentRunner.getTestListeners();
54    }
55
56    public void setInvokedMethodListeners(List<IInvokedMethodListener> listeners) {
57        m_invokeListeners = listeners;
58    }
59
60    /**
61     * A
62     * <code>start</code> implementation that ignores the
63     * <code>TestResult</code>
64     *
65     * @param testClass the JUnit test class
66     */
67    @Override
68    public void run(Class testClass, String... methods) {
69        start(testClass, methods);
70    }
71
72    /**
73     * Starts a test run. Analyzes the command line arguments and runs the given
74     * test suite.
75     */
76    public Result start(final Class testCase, final String... methods) {
77        try {
78            JUnitCore core = new JUnitCore();
79            core.addListener(new RL());
80            Request r = Request.aClass(testCase);
81            return core.run(r.filterWith(new Filter() {
82
83                @Override
84                public boolean shouldRun(Description description) {
85                    if (description == null) {
86                        return false;
87                    }
88                    if (methods.length == 0) {
89                        //run everything
90                        return true;
91                    }
92                    for (String m: methods) {
93                        Pattern p = Pattern.compile(m);
94                        if (p.matcher(description.getMethodName()).matches()) {
95                            return true;
96                        }
97                    }
98                    return false;
99                }
100
101                @Override
102                public String describe() {
103                    return "TestNG method filter";
104                }
105            }));
106        } catch (Throwable t) {
107            throw new TestNGException("Failure in JUnit mode for class " + testCase.getName(), t);
108        }
109    }
110
111    private class RL extends RunListener {
112
113        private Map<Description, ITestResult> runs = new WeakHashMap<>();
114        private List<Description> notified = new LinkedList<>();
115
116        @Override
117        public void testAssumptionFailure(Failure failure) {
118            notified.add(failure.getDescription());
119            ITestResult tr = runs.get(failure.getDescription());
120            tr.setStatus(TestResult.SKIP);
121            tr.setEndMillis(Calendar.getInstance().getTimeInMillis());
122            tr.setThrowable(failure.getException());
123            m_parentRunner.addSkippedTest(tr.getMethod(), tr);
124            for (ITestListener l : m_listeners) {
125                l.onTestSkipped(tr);
126            }
127        }
128
129        @Override
130        public void testFailure(Failure failure) throws Exception {
131            if (isAssumptionFailed(failure)) {
132                this.testAssumptionFailure(failure);
133                return;
134            }
135            notified.add(failure.getDescription());
136            ITestResult tr = runs.get(failure.getDescription());
137            tr.setStatus(TestResult.FAILURE);
138            tr.setEndMillis(Calendar.getInstance().getTimeInMillis());
139            tr.setThrowable(failure.getException());
140            m_parentRunner.addFailedTest(tr.getMethod(), tr);
141            for (ITestListener l : m_listeners) {
142                l.onTestFailure(tr);
143            }
144        }
145
146        @Override
147        public void testFinished(Description description) throws Exception {
148            ITestResult tr = runs.get(description);
149            if (!notified.contains(description)) {
150                tr.setStatus(TestResult.SUCCESS);
151                tr.setEndMillis(Calendar.getInstance().getTimeInMillis());
152                m_parentRunner.addPassedTest(tr.getMethod(), tr);
153                for (ITestListener l : m_listeners) {
154                    l.onTestSuccess(tr);
155                }
156            }
157            m_methods.add(tr.getMethod());
158        }
159
160        @Override
161        public void testIgnored(Description description) throws Exception {
162            ITestResult tr = createTestResult(description);
163            tr.setStatus(TestResult.SKIP);
164            tr.setEndMillis(tr.getStartMillis());
165            m_parentRunner.addSkippedTest(tr.getMethod(), tr);
166            m_methods.add(tr.getMethod());
167            for (ITestListener l : m_listeners) {
168                l.onTestSkipped(tr);
169            }
170        }
171
172        @Override
173        public void testRunFinished(Result result) throws Exception {
174        }
175
176        @Override
177        public void testRunStarted(Description description) throws Exception {
178        }
179
180        @Override
181        public void testStarted(Description description) throws Exception {
182            ITestResult tr = createTestResult(description);
183            runs.put(description, tr);
184            for (ITestListener l : m_listeners) {
185                l.onTestStart(tr);
186            }
187        }
188
189        private ITestResult createTestResult(Description test) {
190            JUnit4TestClass tc = new JUnit4TestClass(test);
191            JUnitTestMethod tm = new JUnit4TestMethod(tc, test);
192
193            TestResult tr = new TestResult(tc,
194                    test,
195                    tm,
196                    null,
197                    Calendar.getInstance().getTimeInMillis(),
198                    0,
199                    null);
200
201            InvokedMethod im = new InvokedMethod(tr.getTestClass(), tr.getMethod(), new Object[0], tr.getStartMillis(), tr);
202            m_parentRunner.addInvokedMethod(im);
203            for (IInvokedMethodListener l: m_invokeListeners) {
204                l.beforeInvocation(im, tr);
205            }
206            return tr;
207        }
208    }
209
210    private static boolean isAssumptionFailed(Failure failure) {
211        if (failure == null) {
212            return false;
213        }
214        //noinspection ThrowableResultOfMethodCallIgnored
215        final Throwable exception = failure.getException();
216        //noinspection SimplifiableIfStatement
217        if (exception == null) {
218            return false;
219        }
220        return "org.junit.internal.AssumptionViolatedException".equals(exception.getClass().getCanonicalName());
221    }
222}
223