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 ClassLoader classLoader = getClassLoader();
103
104 Class<?> clazz = classLoader.loadClass(className);
105
106 assertClasses.add(clazz);
107
108 appendAssertClasses(assertClasses);
109
110 try {
111 _ASSERT_COVERAGE_METHOD.invoke(
112 null, _includeInnerClasses,
113 assertClasses.toArray(new Class<?>[assertClasses.size()]));
114 }
115 catch (InvocationTargetException ite) {
116 throw ite.getCause();
117 }
118 }
119
120 protected String beforeClass(Description description) throws Throwable {
121 String className = description.getClassName();
122
123 if (className.endsWith("Test")) {
124 className = className.substring(0, className.length() - 4);
125 }
126
127 String[] includes = _includes;
128
129 if (includes == null) {
130 includes = _generateIncludes(className);
131 }
132
133 try {
134 _DYNAMICALLY_INSTRUMENT_METHOD.invoke(null, includes, _excludes);
135 }
136 catch (InvocationTargetException ite) {
137 throw ite.getCause();
138 }
139
140 return className;
141 }
142
143 protected ClassLoader getClassLoader() {
144 Class<?> clazz = getClass();
145
146 return clazz.getClassLoader();
147 }
148
149 private String[] _generateIncludes(String mainClassName) throws Exception {
150 List<Class<?>> assertClasses = new ArrayList<>();
151
152 String jvmClassPath = ClassPathUtil.getJVMClassPath(false);
153
154 URL[] urls = ClassPathUtil.getClassPathURLs(jvmClassPath);
155
156 ClassLoader classLoader = new URLClassLoader(urls, null);
157
158 Class<?> mainClass = classLoader.loadClass(mainClassName);
159
160 assertClasses.add(mainClass);
161
162 if (_includeInnerClasses) {
163 assertClasses.addAll(Arrays.asList(mainClass.getDeclaredClasses()));
164 }
165
166 if (getClass() != CodeCoverageAssertor.class) {
167 Class<?> reloadedClass = classLoader.loadClass(
168 getClass().getName());
169
170 Method appendAssertClassesMethod = reloadedClass.getMethod(
171 "appendAssertClasses", List.class);
172
173 appendAssertClassesMethod.setAccessible(true);
174
175 Constructor<?> constructor = reloadedClass.getDeclaredConstructor();
176
177 constructor.setAccessible(true);
178
179 Object reloadedObject = constructor.newInstance();
180
181 appendAssertClassesMethod.invoke(reloadedObject, assertClasses);
182 }
183
184 String[] includes = new String[assertClasses.size()];
185
186 for (int i = 0; i < assertClasses.size(); i++) {
187 Class<?> assertClass = assertClasses.get(i);
188
189 includes[i] = StringUtil.replace(
190 assertClass.getName(), new String[] {".", "$"},
191 new String[] {"/", "\\$"});
192 }
193
194 return includes;
195 }
196
197 private static final Method _ASSERT_COVERAGE_METHOD;
198
199 private static final Method _DYNAMICALLY_INSTRUMENT_METHOD;
200
201 static {
202 ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
203
204 try {
205 Class<?> instrumentationAgentClass = systemClassLoader.loadClass(
206 "com.liferay.whip.agent.InstrumentationAgent");
207
208 _ASSERT_COVERAGE_METHOD = instrumentationAgentClass.getMethod(
209 "assertCoverage", boolean.class, Class[].class);
210 _DYNAMICALLY_INSTRUMENT_METHOD =
211 instrumentationAgentClass.getMethod(
212 "dynamicallyInstrument", String[].class, String[].class);
213 }
214 catch (Exception e) {
215 throw new ExceptionInInitializerError(e);
216 }
217 }
218
219 private final String[] _excludes;
220 private final boolean _includeInnerClasses;
221 private final String[] _includes;
222
223 }