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