001    /**
002     * Copyright (c) 2000-2013 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.ProcessException;
020    import com.liferay.portal.kernel.process.ProcessExecutor;
021    import com.liferay.portal.kernel.util.MethodKey;
022    import com.liferay.portal.kernel.util.PortalClassLoaderUtil;
023    import com.liferay.portal.kernel.util.StringBundler;
024    import com.liferay.portal.kernel.util.StringPool;
025    import com.liferay.portal.kernel.util.Validator;
026    
027    import java.io.Serializable;
028    
029    import java.lang.reflect.InvocationTargetException;
030    import java.lang.reflect.Method;
031    
032    import java.util.ArrayList;
033    import java.util.List;
034    import java.util.concurrent.ExecutionException;
035    import java.util.concurrent.Future;
036    
037    import org.junit.After;
038    import org.junit.Before;
039    import org.junit.runner.manipulation.Sorter;
040    import org.junit.runners.BlockJUnit4ClassRunner;
041    import org.junit.runners.model.FrameworkMethod;
042    import org.junit.runners.model.InitializationError;
043    import org.junit.runners.model.Statement;
044    import org.junit.runners.model.TestClass;
045    
046    /**
047     * @author Shuyang Zhou
048     */
049    public class NewJVMJUnitTestRunner extends BlockJUnit4ClassRunner {
050    
051            public NewJVMJUnitTestRunner(Class<?> clazz) throws InitializationError {
052                    super(clazz);
053    
054                    _classPath = ClassPathUtil.getJVMClassPath(false);
055    
056                    sort(new Sorter(new DescriptionComparator()));
057            }
058    
059            protected static void attachProcess(String message) {
060                    if (!Boolean.getBoolean("attached")) {
061                            ProcessExecutor.ProcessContext.attach(
062                                    message, 1000,
063                                    new ProcessExecutor.ShutdownHook() {
064    
065                                            @Override
066                                            public boolean shutdown(
067                                                    int shutdownCode, Throwable shutdownThrowable) {
068    
069                                                    System.exit(shutdownCode);
070    
071                                                    return true;
072                                            }
073    
074                                    });
075    
076                            System.setProperty("attached", StringPool.TRUE);
077                    }
078            }
079    
080            protected List<String> createArguments(FrameworkMethod frameworkMethod) {
081                    List<String> arguments = new ArrayList<String>();
082    
083                    String agentLine = System.getProperty("junit.cobertura.agent");
084    
085                    if (Validator.isNotNull(agentLine)) {
086                            arguments.add(agentLine);
087                            arguments.add("-Djunit.cobertura.agent=" + agentLine);
088                    }
089    
090                    boolean coberturaParentDynamicallyInstrumented = Boolean.getBoolean(
091                            "cobertura.parent.dynamically.instrumented");
092    
093                    if (coberturaParentDynamicallyInstrumented) {
094                            arguments.add("-Dcobertura.parent.dynamically.instrumented=true");
095                    }
096    
097                    boolean junitCodeCoverage = Boolean.getBoolean("junit.code.coverage");
098    
099                    if (junitCodeCoverage) {
100                            arguments.add("-Djunit.code.coverage=true");
101                    }
102    
103                    boolean junitDebug = Boolean.getBoolean("junit.debug");
104    
105                    if (junitDebug) {
106                            arguments.add(_JPDA_OPTIONS);
107                            arguments.add("-Djunit.debug=true");
108                    }
109    
110                    arguments.add("-Djava.net.preferIPv4Stack=true");
111    
112                    String fileName = System.getProperty(
113                            "net.sourceforge.cobertura.datafile");
114    
115                    if (fileName != null) {
116                            arguments.add("-Dnet.sourceforge.cobertura.datafile=" + fileName);
117                    }
118    
119                    return arguments;
120            }
121    
122            @Override
123            protected Statement methodBlock(FrameworkMethod frameworkMethod) {
124                    Thread currentThread = Thread.currentThread();
125    
126                    ClassLoader contextClassLoader = currentThread.getContextClassLoader();
127    
128                    PortalClassLoaderUtil.setClassLoader(contextClassLoader);
129    
130                    TestClass testClass = getTestClass();
131    
132                    List<FrameworkMethod> beforeFrameworkMethods =
133                            testClass.getAnnotatedMethods(Before.class);
134    
135                    List<FrameworkMethod> afterFrameworkMethods =
136                            testClass.getAnnotatedMethods(After.class);
137    
138                    List<String> arguments = createArguments(frameworkMethod);
139    
140                    Class<?> clazz = testClass.getJavaClass();
141    
142                    return new RunInNewJVMStatment(
143                            _classPath, arguments, clazz, beforeFrameworkMethods,
144                            frameworkMethod, afterFrameworkMethods);
145            }
146    
147            protected ProcessCallable<Serializable> processProcessCallable(
148                    ProcessCallable<Serializable> processCallable,
149                    MethodKey testMethodKey) {
150    
151                    return processCallable;
152            }
153    
154            private static final String _JPDA_OPTIONS =
155                    "-agentlib:jdwp=transport=dt_socket,address=8001,server=y,suspend=y";
156    
157            private String _classPath;
158    
159            private static class TestProcessCallable
160                    implements ProcessCallable<Serializable> {
161    
162                    public TestProcessCallable(
163                            String testClassName, List<MethodKey> beforeMethodKeys,
164                            MethodKey testMethodKey, List<MethodKey> afterMethodKeys) {
165    
166                            _testClassName = testClassName;
167                            _beforeMethodKeys = beforeMethodKeys;
168                            _testMethodKey = testMethodKey;
169                            _afterMethodKeys = afterMethodKeys;
170                    }
171    
172                    @Override
173                    public Serializable call() throws ProcessException {
174                            attachProcess("Attached " + toString());
175    
176                            Thread currentThread = Thread.currentThread();
177    
178                            ClassLoader contextClassLoader =
179                                    currentThread.getContextClassLoader();
180    
181                            try {
182                                    Class<?> clazz = contextClassLoader.loadClass(_testClassName);
183    
184                                    Object object = clazz.newInstance();
185    
186                                    for (MethodKey beforeMethodKey : _beforeMethodKeys) {
187                                            beforeMethodKey = beforeMethodKey.transform(
188                                                    contextClassLoader);
189    
190                                            _invoke(beforeMethodKey, object);
191                                    }
192    
193                                    MethodKey testMethodKey = _testMethodKey.transform(
194                                            contextClassLoader);
195    
196                                    _invoke(testMethodKey, object);
197    
198                                    for (MethodKey afterMethodKey : _afterMethodKeys) {
199                                            afterMethodKey = afterMethodKey.transform(
200                                                    contextClassLoader);
201    
202                                            _invoke(afterMethodKey, object);
203                                    }
204                            }
205                            catch (Exception e) {
206                                    throw new ProcessException(e);
207                            }
208    
209                            return StringPool.BLANK;
210                    }
211    
212                    @Override
213                    public String toString() {
214                            StringBundler sb = new StringBundler(4);
215    
216                            sb.append(_testClassName);
217                            sb.append(StringPool.PERIOD);
218                            sb.append(_testMethodKey.getMethodName());
219                            sb.append("()");
220    
221                            return sb.toString();
222                    }
223    
224                    private void _invoke(MethodKey methodKey, Object object)
225                            throws Exception {
226    
227                            Method method = methodKey.getMethod();
228    
229                            method.invoke(object);
230                    }
231    
232                    private static final long serialVersionUID = 1L;
233    
234                    private List<MethodKey> _afterMethodKeys;
235                    private List<MethodKey> _beforeMethodKeys;
236                    private String _testClassName;
237                    private MethodKey _testMethodKey;
238    
239            }
240    
241            private class RunInNewJVMStatment extends Statement {
242    
243                    public RunInNewJVMStatment(
244                            String classPath, List<String> arguments, Class<?> testClass,
245                            List<FrameworkMethod> beforeFrameworkMethods,
246                            FrameworkMethod testFrameworkMethod,
247                            List<FrameworkMethod> afterFrameworkMethods) {
248    
249                            _classPath = classPath;
250                            _arguments = arguments;
251                            _testClassName = testClass.getName();
252    
253                            _beforeMethodKeys = new ArrayList<MethodKey>(
254                                    beforeFrameworkMethods.size());
255    
256                            for (FrameworkMethod frameworkMethod : beforeFrameworkMethods) {
257                                    _beforeMethodKeys.add(
258                                            new MethodKey(frameworkMethod.getMethod()));
259                            }
260    
261                            _testMethodKey = new MethodKey(testFrameworkMethod.getMethod());
262    
263                            _afterMethodKeys = new ArrayList<MethodKey>(
264                                    afterFrameworkMethods.size());
265    
266                            for (FrameworkMethod frameworkMethod : afterFrameworkMethods) {
267                                    _afterMethodKeys.add(
268                                            new MethodKey(frameworkMethod.getMethod()));
269                            }
270                    }
271    
272                    @Override
273                    public void evaluate() throws Throwable {
274                            ProcessCallable<Serializable> processCallable =
275                                    new TestProcessCallable(
276                                            _testClassName, _beforeMethodKeys, _testMethodKey,
277                                            _afterMethodKeys);
278    
279                            processCallable = processProcessCallable(
280                                    processCallable, _testMethodKey);
281    
282                            Future<String> future = ProcessExecutor.execute(
283                                    _classPath, _arguments, processCallable);
284    
285                            try {
286                                    future.get();
287                            }
288                            catch (ExecutionException ee) {
289                                    Throwable cause = ee.getCause();
290    
291                                    while ((cause instanceof ProcessException) ||
292                                               (cause instanceof InvocationTargetException)) {
293    
294                                            cause = cause.getCause();
295                                    }
296    
297                                    throw cause;
298                            }
299                    }
300    
301                    private List<MethodKey> _afterMethodKeys;
302                    private List<String> _arguments;
303                    private List<MethodKey> _beforeMethodKeys;
304                    private String _classPath;
305                    private String _testClassName;
306                    private MethodKey _testMethodKey;
307    
308            }
309    
310    }