001    /**
002     * Copyright (c) 2000-present Liferay, Inc. All rights reserved.
003     *
004     * This library is free software; you can redistribute it and/or modify it under
005     * the terms of the GNU Lesser General Public License as published by the Free
006     * Software Foundation; either version 2.1 of the License, or (at your option)
007     * any later version.
008     *
009     * This library is distributed in the hope that it will be useful, but WITHOUT
010     * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
011     * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
012     * details.
013     */
014    
015    package com.liferay.portal.kernel.test;
016    
017    import com.liferay.portal.kernel.process.ClassPathUtil;
018    import com.liferay.portal.kernel.process.ProcessCallable;
019    import com.liferay.portal.kernel.process.ProcessChannel;
020    import com.liferay.portal.kernel.process.ProcessConfig;
021    import com.liferay.portal.kernel.process.ProcessConfig.Builder;
022    import com.liferay.portal.kernel.process.ProcessException;
023    import com.liferay.portal.kernel.process.ProcessExecutor;
024    import com.liferay.portal.kernel.process.local.LocalProcessExecutor;
025    import com.liferay.portal.kernel.process.local.LocalProcessLauncher;
026    import com.liferay.portal.kernel.util.MethodKey;
027    import com.liferay.portal.kernel.util.PortalClassLoaderUtil;
028    import com.liferay.portal.kernel.util.StringBundler;
029    import com.liferay.portal.kernel.util.StringPool;
030    import com.liferay.portal.kernel.util.Validator;
031    
032    import java.io.Serializable;
033    
034    import java.lang.reflect.InvocationTargetException;
035    import java.lang.reflect.Method;
036    
037    import java.util.ArrayList;
038    import java.util.List;
039    import java.util.concurrent.ExecutionException;
040    import java.util.concurrent.Future;
041    
042    import org.junit.After;
043    import org.junit.Before;
044    import org.junit.runner.manipulation.Sorter;
045    import org.junit.runners.BlockJUnit4ClassRunner;
046    import org.junit.runners.model.FrameworkMethod;
047    import org.junit.runners.model.InitializationError;
048    import org.junit.runners.model.Statement;
049    import org.junit.runners.model.TestClass;
050    
051    /**
052     * @author Shuyang Zhou
053     */
054    public class NewJVMJUnitTestRunner extends BlockJUnit4ClassRunner {
055    
056            public NewJVMJUnitTestRunner(Class<?> clazz) throws InitializationError {
057                    super(clazz);
058    
059                    _classPath = ClassPathUtil.getJVMClassPath(false);
060    
061                    sort(new Sorter(new DescriptionComparator()));
062            }
063    
064            protected static void attachProcess(String message) {
065                    if (!Boolean.getBoolean("attached")) {
066                            LocalProcessLauncher.ProcessContext.attach(
067                                    message, 1000,
068                                    new LocalProcessLauncher.ShutdownHook() {
069    
070                                            @Override
071                                            public boolean shutdown(
072                                                    int shutdownCode, Throwable shutdownThrowable) {
073    
074                                                    System.exit(shutdownCode);
075    
076                                                    return true;
077                                            }
078    
079                                    });
080    
081                            System.setProperty("attached", StringPool.TRUE);
082                    }
083            }
084    
085            protected List<String> createArguments(FrameworkMethod frameworkMethod) {
086                    List<String> arguments = new ArrayList<String>();
087    
088                    String agentLine = System.getProperty("junit.cobertura.agent");
089    
090                    if (Validator.isNotNull(agentLine)) {
091                            arguments.add(agentLine);
092                            arguments.add("-Djunit.cobertura.agent=" + agentLine);
093                    }
094    
095                    boolean coberturaParentDynamicallyInstrumented = Boolean.getBoolean(
096                            "cobertura.parent.dynamically.instrumented");
097    
098                    if (coberturaParentDynamicallyInstrumented) {
099                            arguments.add("-Dcobertura.parent.dynamically.instrumented=true");
100                    }
101    
102                    boolean junitCodeCoverage = Boolean.getBoolean("junit.code.coverage");
103    
104                    if (junitCodeCoverage) {
105                            arguments.add("-Djunit.code.coverage=true");
106                    }
107    
108                    boolean junitCodeCoverageDump = Boolean.getBoolean(
109                            "junit.code.coverage.dump");
110    
111                    if (junitCodeCoverageDump) {
112                            arguments.add("-Djunit.code.coverage.dump=true");
113                    }
114    
115                    boolean junitDebug = Boolean.getBoolean("junit.debug");
116    
117                    if (junitDebug) {
118                            arguments.add(_JPDA_OPTIONS);
119                            arguments.add("-Djunit.debug=true");
120                    }
121    
122                    arguments.add("-Djava.net.preferIPv4Stack=true");
123    
124                    String fileName = System.getProperty(
125                            "net.sourceforge.cobertura.datafile");
126    
127                    if (fileName != null) {
128                            arguments.add("-Dnet.sourceforge.cobertura.datafile=" + fileName);
129                    }
130    
131                    return arguments;
132            }
133    
134            @Override
135            protected Statement methodBlock(FrameworkMethod frameworkMethod) {
136                    Builder builder = new Builder();
137    
138                    builder.setArguments(createArguments(frameworkMethod));
139                    builder.setBootstrapClassPath(_classPath);
140                    builder.setRuntimeClassPath(_classPath);
141    
142                    Thread currentThread = Thread.currentThread();
143    
144                    ClassLoader contextClassLoader = currentThread.getContextClassLoader();
145    
146                    PortalClassLoaderUtil.setClassLoader(contextClassLoader);
147    
148                    TestClass testClass = getTestClass();
149    
150                    Class<?> clazz = testClass.getJavaClass();
151    
152                    List<FrameworkMethod> beforeFrameworkMethods =
153                            testClass.getAnnotatedMethods(Before.class);
154    
155                    List<FrameworkMethod> afterFrameworkMethods =
156                            testClass.getAnnotatedMethods(After.class);
157    
158                    return new RunInNewJVMStatment(
159                            builder.build(), clazz, beforeFrameworkMethods, frameworkMethod,
160                            afterFrameworkMethods);
161            }
162    
163            protected ProcessCallable<Serializable> processProcessCallable(
164                    ProcessCallable<Serializable> processCallable,
165                    MethodKey testMethodKey) {
166    
167                    return processCallable;
168            }
169    
170            private static final String _JPDA_OPTIONS =
171                    "-agentlib:jdwp=transport=dt_socket,address=8001,server=y,suspend=y";
172    
173            private static final ProcessExecutor _processExecutor =
174                    new LocalProcessExecutor();
175    
176            private final String _classPath;
177    
178            private static class TestProcessCallable
179                    implements ProcessCallable<Serializable> {
180    
181                    public TestProcessCallable(
182                            String testClassName, List<MethodKey> beforeMethodKeys,
183                            MethodKey testMethodKey, List<MethodKey> afterMethodKeys) {
184    
185                            _testClassName = testClassName;
186                            _beforeMethodKeys = beforeMethodKeys;
187                            _testMethodKey = testMethodKey;
188                            _afterMethodKeys = afterMethodKeys;
189                    }
190    
191                    @Override
192                    public Serializable call() throws ProcessException {
193                            attachProcess("Attached " + toString());
194    
195                            Thread currentThread = Thread.currentThread();
196    
197                            ClassLoader contextClassLoader =
198                                    currentThread.getContextClassLoader();
199    
200                            try {
201                                    Class<?> clazz = contextClassLoader.loadClass(_testClassName);
202    
203                                    Object object = clazz.newInstance();
204    
205                                    for (MethodKey beforeMethodKey : _beforeMethodKeys) {
206                                            beforeMethodKey = beforeMethodKey.transform(
207                                                    contextClassLoader);
208    
209                                            _invoke(beforeMethodKey, object);
210                                    }
211    
212                                    MethodKey testMethodKey = _testMethodKey.transform(
213                                            contextClassLoader);
214    
215                                    _invoke(testMethodKey, object);
216    
217                                    for (MethodKey afterMethodKey : _afterMethodKeys) {
218                                            afterMethodKey = afterMethodKey.transform(
219                                                    contextClassLoader);
220    
221                                            _invoke(afterMethodKey, object);
222                                    }
223                            }
224                            catch (Exception e) {
225                                    throw new ProcessException(e);
226                            }
227    
228                            return StringPool.BLANK;
229                    }
230    
231                    @Override
232                    public String toString() {
233                            StringBundler sb = new StringBundler(4);
234    
235                            sb.append(_testClassName);
236                            sb.append(StringPool.PERIOD);
237                            sb.append(_testMethodKey.getMethodName());
238                            sb.append("()");
239    
240                            return sb.toString();
241                    }
242    
243                    private void _invoke(MethodKey methodKey, Object object)
244                            throws Exception {
245    
246                            Method method = methodKey.getMethod();
247    
248                            method.invoke(object);
249                    }
250    
251                    private static final long serialVersionUID = 1L;
252    
253                    private List<MethodKey> _afterMethodKeys;
254                    private List<MethodKey> _beforeMethodKeys;
255                    private String _testClassName;
256                    private MethodKey _testMethodKey;
257    
258            }
259    
260            private class RunInNewJVMStatment extends Statement {
261    
262                    public RunInNewJVMStatment(
263                            ProcessConfig processConfig, Class<?> testClass,
264                            List<FrameworkMethod> beforeFrameworkMethods,
265                            FrameworkMethod testFrameworkMethod,
266                            List<FrameworkMethod> afterFrameworkMethods) {
267    
268                            _processConfig = processConfig;
269                            _testClassName = testClass.getName();
270    
271                            _beforeMethodKeys = new ArrayList<MethodKey>(
272                                    beforeFrameworkMethods.size());
273    
274                            for (FrameworkMethod frameworkMethod : beforeFrameworkMethods) {
275                                    _beforeMethodKeys.add(
276                                            new MethodKey(frameworkMethod.getMethod()));
277                            }
278    
279                            _testMethodKey = new MethodKey(testFrameworkMethod.getMethod());
280    
281                            _afterMethodKeys = new ArrayList<MethodKey>(
282                                    afterFrameworkMethods.size());
283    
284                            for (FrameworkMethod frameworkMethod : afterFrameworkMethods) {
285                                    _afterMethodKeys.add(
286                                            new MethodKey(frameworkMethod.getMethod()));
287                            }
288                    }
289    
290                    @Override
291                    public void evaluate() throws Throwable {
292                            ProcessCallable<Serializable> processCallable =
293                                    new TestProcessCallable(
294                                            _testClassName, _beforeMethodKeys, _testMethodKey,
295                                            _afterMethodKeys);
296    
297                            processCallable = processProcessCallable(
298                                    processCallable, _testMethodKey);
299    
300                            ProcessChannel<Serializable> processChannel =
301                                    _processExecutor.execute(_processConfig, processCallable);
302    
303                            Future<Serializable> future =
304                                    processChannel.getProcessNoticeableFuture();
305    
306                            try {
307                                    future.get();
308                            }
309                            catch (ExecutionException ee) {
310                                    Throwable cause = ee.getCause();
311    
312                                    while ((cause instanceof ProcessException) ||
313                                               (cause instanceof InvocationTargetException)) {
314    
315                                            cause = cause.getCause();
316                                    }
317    
318                                    throw cause;
319                            }
320                    }
321    
322                    private List<MethodKey> _afterMethodKeys;
323                    private List<MethodKey> _beforeMethodKeys;
324                    private final ProcessConfig _processConfig;
325                    private String _testClassName;
326                    private MethodKey _testMethodKey;
327    
328            }
329    
330    }