001
014
015 package com.liferay.portal.kernel.test.rule;
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
038 public class CodeCoverageAssertor implements TestRule {
039
040 public static final CodeCoverageAssertor INSTANCE =
041 new CodeCoverageAssertor();
042
043 public CodeCoverageAssertor() {
044 this(null, null, true);
045 }
046
047 public CodeCoverageAssertor(
048 String[] includes, String[] excludes, boolean includeInnerClasses) {
049
050 _includes = includes;
051 _excludes = excludes;
052 _includeInnerClasses = includeInnerClasses;
053 }
054
055 public void appendAssertClasses(List<Class<?>> assertClasses) {
056 }
057
058 @Override
059 public Statement apply(
060 final Statement statement, final Description description) {
061
062 if (description.getMethodName() != null) {
063 return statement;
064 }
065
066 return new Statement() {
067
068 @Override
069 public void evaluate() throws Throwable {
070 String className = beforeClass(description);
071
072 try {
073 statement.evaluate();
074 }
075 finally {
076 afterClass(description, className);
077 }
078 }
079
080 };
081 }
082
083 protected void afterClass(Description description, String className)
084 throws Throwable {
085
086 List<Class<?>> assertClasses = new ArrayList<>();
087
088 ClassLoader classLoader = getClassLoader();
089
090 Class<?> clazz = classLoader.loadClass(className);
091
092 assertClasses.add(clazz);
093
094 appendAssertClasses(assertClasses);
095
096 try {
097 _ASSERT_COVERAGE_METHOD.invoke(
098 null, _includeInnerClasses,
099 assertClasses.toArray(new Class<?>[assertClasses.size()]));
100 }
101 catch (InvocationTargetException ite) {
102 throw ite.getCause();
103 }
104 }
105
106 protected String beforeClass(Description description) throws Throwable {
107 String className = description.getClassName();
108
109 if (className.endsWith("Test")) {
110 className = className.substring(0, className.length() - 4);
111 }
112
113 String[] includes = _includes;
114
115 if (includes == null) {
116 includes = _generateIncludes(className);
117 }
118
119 try {
120 _DYNAMICALLY_INSTRUMENT_METHOD.invoke(null, includes, _excludes);
121 }
122 catch (InvocationTargetException ite) {
123 throw ite.getCause();
124 }
125
126 return className;
127 }
128
129 protected ClassLoader getClassLoader() {
130 Class<?> clazz = getClass();
131
132 return clazz.getClassLoader();
133 }
134
135 private String[] _generateIncludes(String mainClassName) throws Exception {
136 List<Class<?>> assertClasses = new ArrayList<>();
137
138 String jvmClassPath = ClassPathUtil.getJVMClassPath(false);
139
140 URL[] urls = ClassPathUtil.getClassPathURLs(jvmClassPath);
141
142 ClassLoader classLoader = new URLClassLoader(urls, null);
143
144 Class<?> mainClass = classLoader.loadClass(mainClassName);
145
146 assertClasses.add(mainClass);
147
148 if (_includeInnerClasses) {
149 assertClasses.addAll(Arrays.asList(mainClass.getDeclaredClasses()));
150 }
151
152 if (getClass() != CodeCoverageAssertor.class) {
153 Class<?> reloadedClass = classLoader.loadClass(
154 getClass().getName());
155
156 Method appendAssertClassesMethod = reloadedClass.getMethod(
157 "appendAssertClasses", List.class);
158
159 appendAssertClassesMethod.setAccessible(true);
160
161 Constructor<?> constructor = reloadedClass.getDeclaredConstructor();
162
163 constructor.setAccessible(true);
164
165 Object reloadedObject = constructor.newInstance();
166
167 appendAssertClassesMethod.invoke(reloadedObject, assertClasses);
168 }
169
170 String[] includes = new String[assertClasses.size()];
171
172 for (int i = 0; i < assertClasses.size(); i++) {
173 Class<?> assertClass = assertClasses.get(i);
174
175 includes[i] = StringUtil.replace(
176 assertClass.getName(), new String[] {".", "$"},
177 new String[] {"/", "\\$"});
178 }
179
180 return includes;
181 }
182
183 private static final Method _ASSERT_COVERAGE_METHOD;
184
185 private static final Method _DYNAMICALLY_INSTRUMENT_METHOD;
186
187 static {
188 ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
189
190 try {
191 Class<?> instrumentationAgentClass = systemClassLoader.loadClass(
192 "com.liferay.whip.agent.InstrumentationAgent");
193
194 _ASSERT_COVERAGE_METHOD = instrumentationAgentClass.getMethod(
195 "assertCoverage", boolean.class, Class[].class);
196 _DYNAMICALLY_INSTRUMENT_METHOD =
197 instrumentationAgentClass.getMethod(
198 "dynamicallyInstrument", String[].class, String[].class);
199 }
200 catch (Exception e) {
201 throw new ExceptionInInitializerError(e);
202 }
203 }
204
205 private final String[] _excludes;
206 private final boolean _includeInnerClasses;
207 private final String[] _includes;
208
209 }