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