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 List<MethodKey> _afterMethodKeys;
209                    private List<MethodKey> _beforeMethodKeys;
210                    private String _testClassName;
211                    private MethodKey _testMethodKey;
212    
213            }
214    
215            private class RunInNewJVMStatment extends Statement {
216    
217                    public RunInNewJVMStatment(
218                            String classPath, List<String> arguments, Class<?> testClass,
219                            List<FrameworkMethod> beforeFrameworkMethods,
220                            FrameworkMethod testFrameworkMethod,
221                            List<FrameworkMethod> afterFrameworkMethods) {
222    
223                            _classPath = classPath;
224                            _arguments = arguments;
225                            _testClassName = testClass.getName();
226    
227                            _beforeMethodKeys = new ArrayList<MethodKey>(
228                                    beforeFrameworkMethods.size());
229    
230                            for (FrameworkMethod frameworkMethod : beforeFrameworkMethods) {
231                                    _beforeMethodKeys.add(
232                                            new MethodKey(frameworkMethod.getMethod()));
233                            }
234    
235                            _testMethodKey = new MethodKey(testFrameworkMethod.getMethod());
236    
237                            _afterMethodKeys = new ArrayList<MethodKey>(
238                                    afterFrameworkMethods.size());
239    
240                            for (FrameworkMethod frameworkMethod : afterFrameworkMethods) {
241                                    _afterMethodKeys.add(
242                                            new MethodKey(frameworkMethod.getMethod()));
243                            }
244                    }
245    
246                    @Override
247                    public void evaluate() throws Throwable {
248                            ProcessCallable<Serializable> processCallable =
249                                    new TestProcessCallable(
250                                            _testClassName, _beforeMethodKeys, _testMethodKey,
251                                            _afterMethodKeys);
252    
253                            processCallable = processProcessCallable(
254                                    processCallable, _testMethodKey);
255    
256                            Future<String> future = ProcessExecutor.execute(
257                                    _classPath, _arguments, processCallable);
258    
259                            try {
260                                    future.get();
261                            }
262                            catch (ExecutionException ee) {
263                                    Throwable cause = ee.getCause();
264    
265                                    while ((cause instanceof ProcessException) ||
266                                            (cause instanceof InvocationTargetException)) {
267    
268                                            cause = cause.getCause();
269                                    }
270    
271                                    throw cause;
272                            }
273                    }
274    
275                    private List<MethodKey> _afterMethodKeys;
276                    private List<String> _arguments;
277                    private List<MethodKey> _beforeMethodKeys;
278                    private String _classPath;
279                    private String _testClassName;
280                    private MethodKey _testMethodKey;
281    
282            }
283    
284    }