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