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