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.util.StringUtil;
019    
020    import java.lang.reflect.Constructor;
021    import java.lang.reflect.InvocationTargetException;
022    import java.lang.reflect.Method;
023    
024    import java.net.URL;
025    import java.net.URLClassLoader;
026    
027    import java.util.ArrayList;
028    import java.util.Arrays;
029    import java.util.List;
030    
031    import org.junit.rules.TestRule;
032    import org.junit.runner.Description;
033    import org.junit.runners.model.Statement;
034    
035    /**
036     * @author Shuyang Zhou
037     */
038    public class CodeCoverageAssertor implements TestRule {
039    
040            public CodeCoverageAssertor() {
041                    this(null, null, true);
042            }
043    
044            public CodeCoverageAssertor(
045                    String[] includes, String[] excludes, boolean includeInnerClasses) {
046    
047                    _includes = includes;
048                    _excludes = excludes;
049                    _includeInnerClasses = includeInnerClasses;
050            }
051    
052            public void appendAssertClasses(List<Class<?>> assertClasses) {
053            }
054    
055            @Override
056            public Statement apply(
057                    final Statement statement, final Description description) {
058    
059                    return new Statement() {
060    
061                            @Override
062                            public void evaluate() throws Throwable {
063                                    String className = description.getClassName();
064    
065                                    if (className.endsWith("Test")) {
066                                            className = className.substring(0, className.length() - 4);
067                                    }
068    
069                                    String[] includes = _includes;
070    
071                                    if (includes == null) {
072                                            includes = _generateIncludes(className);
073                                    }
074    
075                                    try {
076                                            _dynamicallyInstrumentMethod.invoke(
077                                                    null, includes, _excludes);
078                                    }
079                                    catch (InvocationTargetException ite) {
080                                            throw ite.getCause();
081                                    }
082    
083                                    try {
084                                            statement.evaluate();
085                                    }
086                                    finally {
087                                            List<Class<?>> assertClasses = new ArrayList<Class<?>>();
088    
089                                            ClassLoader classLoader = getClassLoader();
090    
091                                            Class<?> clazz = classLoader.loadClass(className);
092    
093                                            assertClasses.add(clazz);
094    
095                                            appendAssertClasses(assertClasses);
096    
097                                            try {
098                                                    _assertCoverageMethod.invoke(
099                                                            null, _includeInnerClasses,
100                                                            assertClasses.toArray(
101                                                                    new Class<?>[assertClasses.size()]));
102                                            }
103                                            catch (InvocationTargetException ite) {
104                                                    throw ite.getCause();
105                                            }
106                                    }
107                            }
108    
109                    };
110            }
111    
112            protected ClassLoader getClassLoader() {
113                    Class<?> clazz = getClass();
114    
115                    return clazz.getClassLoader();
116            }
117    
118            private String[] _generateIncludes(String mainClassName) throws Exception {
119                    List<Class<?>> assertClasses = new ArrayList<Class<?>>();
120    
121                    String jvmClassPath = ClassPathUtil.getJVMClassPath(false);
122    
123                    URL[] urls = ClassPathUtil.getClassPathURLs(jvmClassPath);
124    
125                    ClassLoader classLoader = new URLClassLoader(urls, null);
126    
127                    Class<?> mainClass = classLoader.loadClass(mainClassName);
128    
129                    assertClasses.add(mainClass);
130    
131                    if (_includeInnerClasses) {
132                            assertClasses.addAll(Arrays.asList(mainClass.getDeclaredClasses()));
133                    }
134    
135                    if (getClass() != CodeCoverageAssertor.class) {
136                            Class<?> reloadedClass = classLoader.loadClass(
137                                    getClass().getName());
138    
139                            Method appendAssertClassesMethod = reloadedClass.getMethod(
140                                    "appendAssertClasses", List.class);
141    
142                            appendAssertClassesMethod.setAccessible(true);
143    
144                            Constructor<?> constructor = reloadedClass.getDeclaredConstructor();
145    
146                            constructor.setAccessible(true);
147    
148                            Object reloadedObject = constructor.newInstance();
149    
150                            appendAssertClassesMethod.invoke(reloadedObject, assertClasses);
151                    }
152    
153                    String[] includes = new String[assertClasses.size()];
154    
155                    for (int i = 0; i < assertClasses.size(); i++) {
156                            Class<?> assertClass = assertClasses.get(i);
157    
158                            includes[i] = StringUtil.replace(
159                                    assertClass.getName(), new String[] {".", "$"},
160                                    new String[] {"/", "\\$"});
161                    }
162    
163                    return includes;
164            }
165    
166            private static final Method _assertCoverageMethod;
167            private static final Method _dynamicallyInstrumentMethod;
168    
169            private final String[] _excludes;
170            private final boolean _includeInnerClasses;
171            private final String[] _includes;
172    
173            static {
174                    ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
175    
176                    try {
177                            Class<?> instrumentationAgentClass = systemClassLoader.loadClass(
178                                    "net.sourceforge.cobertura.instrument.InstrumentationAgent");
179    
180                            _assertCoverageMethod = instrumentationAgentClass.getMethod(
181                                    "assertCoverage", boolean.class, Class[].class);
182                            _dynamicallyInstrumentMethod = instrumentationAgentClass.getMethod(
183                                    "dynamicallyInstrument", String[].class, String[].class);
184                    }
185                    catch (Exception e) {
186                            throw new ExceptionInInitializerError(e);
187                    }
188            }
189    
190    }