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.StringPool;
019 import com.liferay.portal.kernel.util.StringUtil;
020
021 import java.lang.reflect.Constructor;
022 import java.lang.reflect.InvocationTargetException;
023 import java.lang.reflect.Method;
024
025 import java.net.URL;
026 import java.net.URLClassLoader;
027
028 import java.util.ArrayList;
029 import java.util.Arrays;
030 import java.util.List;
031
032 import org.junit.rules.TestRule;
033 import org.junit.runner.Description;
034 import org.junit.runners.model.Statement;
035
036
039 public class CodeCoverageAssertor implements TestRule {
040
041 public static final CodeCoverageAssertor INSTANCE =
042 new CodeCoverageAssertor();
043
044 public CodeCoverageAssertor() {
045 this(null, null, true);
046 }
047
048 public CodeCoverageAssertor(
049 String[] includes, String[] excludes, boolean includeInnerClasses) {
050
051 _includes = includes;
052 _excludes = excludes;
053 _includeInnerClasses = includeInnerClasses;
054 }
055
056 public void appendAssertClasses(List<Class<?>> assertClasses) {
057 }
058
059 @Override
060 public Statement apply(
061 final Statement statement, final Description description) {
062
063 if (description.getMethodName() != null) {
064 return statement;
065 }
066
067 return new Statement() {
068
069 @Override
070 public void evaluate() throws Throwable {
071 String className = beforeClass(description);
072
073 String whipStaticInstrument = System.getProperty(
074 "whip.static.instrument");
075
076 System.setProperty("whip.static.instrument", StringPool.TRUE);
077
078 try {
079 statement.evaluate();
080 }
081 finally {
082 afterClass(description, className);
083
084 if (whipStaticInstrument == null) {
085 System.clearProperty("whip.static.instrument");
086 }
087 else {
088 System.setProperty(
089 "whip.static.instrument", whipStaticInstrument);
090 }
091 }
092 }
093
094 };
095 }
096
097 protected void afterClass(Description description, String className)
098 throws Throwable {
099
100 List<Class<?>> assertClasses = new ArrayList<>();
101
102 if (className != null) {
103 ClassLoader classLoader = getClassLoader();
104
105 Class<?> clazz = classLoader.loadClass(className);
106
107 assertClasses.add(clazz);
108 }
109
110 appendAssertClasses(assertClasses);
111
112 try {
113 _ASSERT_COVERAGE_METHOD.invoke(
114 null, _includeInnerClasses,
115 assertClasses.toArray(new Class<?>[assertClasses.size()]));
116 }
117 catch (InvocationTargetException ite) {
118 throw ite.getCause();
119 }
120 }
121
122 protected String beforeClass(Description description) throws Throwable {
123 String className = description.getClassName();
124
125 if (className.endsWith("Test")) {
126 className = className.substring(0, className.length() - 4);
127 }
128
129 String jvmClassPath = ClassPathUtil.getJVMClassPath(false);
130
131 URL[] urls = ClassPathUtil.getClassPathURLs(jvmClassPath);
132
133 ClassLoader classLoader = new URLClassLoader(urls, null);
134
135 try {
136 classLoader.loadClass(className);
137 }
138 catch (ClassNotFoundException cnfe) {
139 className = null;
140 }
141
142 String[] includes = _includes;
143
144 if (includes == null) {
145 includes = _generateIncludes(classLoader, className);
146 }
147
148 try {
149 _DYNAMICALLY_INSTRUMENT_METHOD.invoke(null, includes, _excludes);
150 }
151 catch (InvocationTargetException ite) {
152 throw ite.getCause();
153 }
154
155 return className;
156 }
157
158 protected ClassLoader getClassLoader() {
159 Class<?> clazz = getClass();
160
161 return clazz.getClassLoader();
162 }
163
164 private String[] _generateIncludes(
165 ClassLoader classLoader, String mainClassName)
166 throws Exception {
167
168 List<Class<?>> assertClasses = new ArrayList<>();
169
170 if (mainClassName != null) {
171 Class<?> mainClass = classLoader.loadClass(mainClassName);
172
173 assertClasses.add(mainClass);
174
175 if (_includeInnerClasses) {
176 assertClasses.addAll(
177 Arrays.asList(mainClass.getDeclaredClasses()));
178 }
179 }
180
181 if (getClass() != CodeCoverageAssertor.class) {
182 Class<?> clazz = getClass();
183
184 Class<?> reloadedClass = classLoader.loadClass(clazz.getName());
185
186 Method appendAssertClassesMethod = reloadedClass.getMethod(
187 "appendAssertClasses", List.class);
188
189 appendAssertClassesMethod.setAccessible(true);
190
191 Constructor<?> constructor = reloadedClass.getDeclaredConstructor();
192
193 constructor.setAccessible(true);
194
195 Object reloadedObject = constructor.newInstance();
196
197 appendAssertClassesMethod.invoke(reloadedObject, assertClasses);
198 }
199
200 String[] includes = new String[assertClasses.size()];
201
202 for (int i = 0; i < assertClasses.size(); i++) {
203 Class<?> assertClass = assertClasses.get(i);
204
205 includes[i] = StringUtil.replace(
206 assertClass.getName(), new char[] {'.', '$'},
207 new String[] {"/", "\\$"});
208 }
209
210 return includes;
211 }
212
213 private static final Method _ASSERT_COVERAGE_METHOD;
214
215 private static final Method _DYNAMICALLY_INSTRUMENT_METHOD;
216
217 static {
218 ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
219
220 try {
221 Class<?> instrumentationAgentClass = systemClassLoader.loadClass(
222 "com.liferay.whip.agent.InstrumentationAgent");
223
224 _ASSERT_COVERAGE_METHOD = instrumentationAgentClass.getMethod(
225 "assertCoverage", boolean.class, Class[].class);
226 _DYNAMICALLY_INSTRUMENT_METHOD =
227 instrumentationAgentClass.getMethod(
228 "dynamicallyInstrument", String[].class, String[].class);
229 }
230 catch (Exception e) {
231 throw new ExceptionInInitializerError(e);
232 }
233 }
234
235 private final String[] _excludes;
236 private final boolean _includeInnerClasses;
237 private final String[] _includes;
238
239 }