5959import edu .rice .cs .plt .iter .IterUtil ;
6060import edu .rice .cs .plt .reflect .ShadowingClassLoader ;
6161
62+ import java .lang .reflect .Method ;
6263import java .lang .reflect .Modifier ;
6364
6465import static edu .rice .cs .plt .debug .DebugUtil .debug ;
@@ -122,8 +123,24 @@ public JUnitTestManager(JUnitModelCallback jmc, Lambda<ClassLoader, ClassLoader>
122123 }
123124
124125 /** @return result of the last JUnit run */
125- public JUnitResultTuple getLastResult () {
126- return this .lastResult ;
126+ public JUnitResultTuple getLastResult () { return this .lastResult ; }
127+
128+ /** Only called from findTestClasses which sets many fields of this. */
129+ private ClassLoader makeCoverageLoader (ClassLoader parentLoader , final ArrayList <byte []> instrumenteds ) {
130+ final ClassLoader pathLoader = _loaderFactory .value (parentLoader );
131+ final ClassLoader shadowingLoader = ShadowingClassLoader .blackList (pathLoader , classNames );
132+ final MemoryClassLoader memoryLoader = new MemoryClassLoader (shadowingLoader );
133+ for (int i = 0 ; i < classNames .size (); i ++) {
134+ String name = classNames .get (i );
135+ byte [] code = instrumenteds .get (i );
136+ _log .log ("Defining class file for '" + name + "' (" + code .length + " bytes) in MemoryClassLoader" );
137+ memoryLoader .addDefinition (classNames .get (i ), instrumenteds .get (i ));
138+ _log .log ("Adding definition of class file for " + name + " to MemoryClassLoader" );
139+ }
140+
141+ // Need to construct Jacoco class loader with pathLoader as parent! What is fully qualified name of that loader?
142+ // ClassLoader memoryLoader = shadowingLoader;
143+ return memoryLoader ;
127144 }
128145
129146 /** Find the test classes among the given classNames and accumulate them in
@@ -133,60 +150,52 @@ public JUnitResultTuple getLastResult() {
133150 * @param coverageMetadata metadata to be used to generate the coverage report
134151 * @return list of test class names
135152 */
136- public List <String > findTestClasses (final List <String > classNames ,
137- final List < File > files , CoverageMetadata coverageMetadata ) {
153+ public List <String > findTestClasses (final List <String > classNames , final List < File > files ,
154+ final CoverageMetadata coverageMetadata ) {
138155
139156 _log .log ("findTestClasses(" + classNames + ", " + files + ", " + coverageMetadata + ") called" );
140157 boolean doCoverage = coverageMetadata .getFlag ();
141158
142159 // Set up the loader
160+ final ClassLoader defaultLoader = JUnitTestManager .class .getClassLoader ();
161+ final ClassLoader testingLoader = _loaderFactory .value (defaultLoader );
143162 final ClassLoader loader ;
144- if (! doCoverage ) {
145- loader = JUnitTestManager .class .getClassLoader ();
146- } else {
163+ if (! doCoverage ) loader = testingLoader ;
164+ else {
147165
148- // JaCoCo: Create instrumented versions of class files.
166+ // JaCoCo: Create instrumented versions of class files and save
149167 this .coverageOutdir = coverageMetadata .getOutdirPath ();
150168 this .runtime = new LoggerRuntime ();
151169 this .myData = new RuntimeData ();
152170 this .classNames = classNames ;
153171 this .files = files ;
154172 final ArrayList <byte []> instrumenteds = new ArrayList <byte []>();
155173
156- // The Instrumenter creates a modified version of our test target class
157- // that contains additional probes for execution data recording:
174+ /* The jacoco instrumenter creates a modified version of our test classes that invoke jacoco (as a Java agent)
175+ * to insert byte code to monitor code coverage. */
158176 for (int i = 0 ; i < files .size () ; i ++) {
159-
160- // Instrument the i-th file
161- try {
162- final Instrumenter instr = new Instrumenter (this .runtime );
163- final byte [] instrumented = instr .instrument (
164- new FileInputStream (files .get (i ).getCanonicalPath ().
165- replace (".java" , ".class" )), classNames .get (i ));
166- String [] pathParts = files .get (i ).getAbsolutePath ().split ("/" );
167- instrumenteds .add (instrumented );
168-
169- } catch (Exception e ) {
170- StringWriter stackTrace = new StringWriter ();
171- e .printStackTrace (new PrintWriter (stackTrace ));
172- _log .log ("Exception during instrumentation: " + stackTrace .toString ());
173- }
174- }
175-
176- loader = new MemoryClassLoader (JUnitTestManager .class .getClassLoader ());
177- for (int i = 0 ; i < classNames .size (); i ++) {
178- String name = classNames .get (i );
179- byte [] code = instrumenteds .get (i );
180- _log .log ("Loading class file for '" + name + "' consisting of " + code .length + " bytes" );
181- ((MemoryClassLoader )loader ).addDefinition (classNames .get (i ), instrumenteds .get (i ));
177+ // Instrument the i-th file
178+ try {
179+ final Instrumenter instr = new Instrumenter (this .runtime );
180+ final byte [] instrumented =
181+ instr .instrument (new FileInputStream (files .get (i ).getCanonicalPath ().replace (".java" , ".class" )),
182+ classNames .get (i ));
183+ // String[] pathParts = files.get(i).getAbsolutePath().split("/");
184+ instrumenteds .add (instrumented );
185+ } catch (Exception e ) {
186+ StringWriter stackTrace = new StringWriter ();
187+ e .printStackTrace (new PrintWriter (stackTrace ));
188+ _log .log ("Exception during instrumentation: " + stackTrace .toString ());
189+ }
182190 }
183-
184- _log .log ("Instrumented class files defined in MemoryClassLoader" );
185- try {
186- this .runtime .startup (myData );
187- } catch (Exception e ) {
188- _log .log ("In code coverage startup, throwing wrapped exception " + e );
189- throw new UnexpectedException (e );
191+
192+ _log .log ("Instrumented test class files for MemoryClassLoader" );
193+
194+ loader = makeCoverageLoader (testingLoader , instrumenteds );
195+ try { this .runtime .startup (myData ); }
196+ catch (Exception e ) {
197+ _log .log ("In code coverage startup, throwing the wrapped exception " + e );
198+ throw new UnexpectedException (e );
190199 }
191200 }
192201
@@ -200,6 +209,7 @@ public List<String> findTestClasses(final List<String> classNames,
200209 _testFiles = new ArrayList <File >();
201210 _suite = new TestSuite ();
202211
212+ // Assemble test suite (as _suite) and return list of test class names
203213 for (Pair <String , File > pair : IterUtil .zip (classNames , files )) {
204214 String cName = pair .first ();
205215 try {
@@ -335,31 +345,26 @@ private void _reset() {
335345 _testFiles = null ;
336346 _log .log ("test manager state reset" );
337347 }
338-
339-
340348
341349 /** Determines if the given class is a junit Test.
342350 * @param c the class to check
343351 * @return true iff the given class is an instance of junit.framework.Test
344352 */
345353 private boolean _isJUnitTest (Class <?> c ) {
346- boolean isAssignable = Test . class . isAssignableFrom ( c );
354+ _log . log ( "Testing class " + c + " to determine if it is a JUnit test class" );
347355 boolean isAbstract = Modifier .isAbstract (c .getModifiers ());
348356 boolean isInterface = Modifier .isInterface (c .getModifiers ());
349- JUnit4TestAdapter a = new JUnit4TestAdapter (c );
350- _log .log ("a.getTests() = " + a .getTests ());
351- boolean isJUnit4Test = (a .getTests ().size () > 0 ) && ! a .getTests ().get (0 ).toString ().contains ("initializationError" );
352- //had to add specific check for initializationError. Is there a better way of checking if a class contains a test?
357+ if (isAbstract || isInterface ) return false ;
353358
354- _log . log ( "isAssignable = " + isAssignable + " isAbstract = " + isAbstract + " isInterface = " + isInterface +
355- " isJUnit4Test = " + isJUnit4Test );
356-
357- boolean result = ( isAssignable && ! isAbstract && ! isInterface ) || isJUnit4Test ;
358-
359- _log . log ( "isJUnitTest(" + c + ") = " + result );
360- return result ;
359+ if ( Test . class . isAssignableFrom ( c )) return true ; // JUnit 3 test class
360+
361+ boolean result = false ;
362+ for ( Method method : Test . class . getDeclaredMethods ()) {
363+ if ( method . isAnnotationPresent ( org . junit . Test . class )) return true ;
364+ }
365+ return false ;
361366 }
362-
367+
363368 /** Constructs a new JUnitError from a TestFailure
364369 * @param failure A given TestFailure
365370 * @param classNames The classes that were used for this test suite
0 commit comments