| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent 5fe29f9 commit e648fef
3 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -36,6 +36,8 @@ | |||
| 36 | 36 | ||
| 37 | 37 | package edu.rice.cs.drjava.model.junit; | |
| 38 | 38 | ||
| 39 | + import edu.rice.cs.drjava.ui.coverage.ReportGenerator; | ||
| 40 | + | ||
| 39 | 41 | import java.awt.EventQueue; | |
| 40 | 42 | import java.io.File; | |
| 41 | 43 | import java.io.IOException; | |
@@ -44,6 +46,30 @@ | |||
| 44 | 46 | import java.io.FileReader; | |
| 45 | 47 | import java.rmi.RemoteException; | |
| 46 | 48 | ||
| 49 | + // For JaCoCo: | ||
| 50 | + import java.io.Writer; | ||
| 51 | + import java.io.BufferedWriter; | ||
| 52 | + import java.io.OutputStreamWriter; | ||
| 53 | + import java.io.FileOutputStream; | ||
| 54 | + import java.io.PrintWriter; | ||
| 55 | + import java.io.StringWriter; | ||
| 56 | + import org.jacoco.core.analysis.Analyzer; | ||
| 57 | + import org.jacoco.core.analysis.CoverageBuilder; | ||
| 58 | + import org.jacoco.core.analysis.IBundleCoverage; | ||
| 59 | + import org.jacoco.core.tools.ExecFileLoader; | ||
| 60 | + import org.jacoco.core.analysis.IClassCoverage; | ||
| 61 | + import org.jacoco.core.analysis.ICounter; | ||
| 62 | + import org.jacoco.core.data.ExecutionDataStore; | ||
| 63 | + import org.jacoco.core.data.SessionInfoStore; | ||
| 64 | + import org.jacoco.core.instr.Instrumenter; | ||
| 65 | + import org.jacoco.core.runtime.IRuntime; | ||
| 66 | + import org.jacoco.core.runtime.LoggerRuntime; | ||
| 67 | + import org.jacoco.core.runtime.RuntimeData; | ||
| 68 | + import org.jacoco.report.DirectorySourceFileLocator; | ||
| 69 | + import org.jacoco.report.FileMultiReportOutput; | ||
| 70 | + import org.jacoco.report.IReportVisitor; | ||
| 71 | + import org.jacoco.report.html.HTMLFormatter; | ||
| 72 | + | ||
| 47 | 73 | import java.util.List; | |
| 48 | 74 | import java.util.LinkedList; | |
| 49 | 75 | import java.util.ArrayList; | |
@@ -92,6 +118,8 @@ | |||
| 92 | 118 | */ | |
| 93 | 119 | public class DefaultJUnitModel implements JUnitModel, JUnitModelCallback { | |
| 94 | 120 | ||
| 121 | + private boolean coverage = false; | ||
| 122 | + | ||
| 95 | 123 | /** log for use in debugging */ | |
| 96 | 124 | private static Log _log = new Log("DefaultJUnitModel.txt", false); | |
| 97 | 125 | ||
@@ -141,6 +169,7 @@ public DefaultJUnitModel(MainJVM jvm, CompilerModel compilerModel, GlobalModel m | |||
| 141 | 169 | ||
| 142 | 170 | //-------------------------- Field Setters --------------------------------// | |
| 143 | 171 | ||
| 172 | + public void setCoverage(boolean c) { coverage = c; } | ||
| 144 | 173 | public void setForceTestSuffix(boolean b) { _forceTestSuffix = b; } | |
| 145 | 174 | ||
| 146 | 175 | //------------------------ Simple Predicates ------------------------------// | |
@@ -312,7 +341,8 @@ private void junitOpenDefDocs(final List<OpenDefinitionsDocument> lod, final boo | |||
| 312 | 341 | */ | |
| 313 | 342 | private void _rawJUnitOpenDefDocs(List<OpenDefinitionsDocument> lod, final boolean allTests) { | |
| 314 | 343 | File buildDir = _model.getBuildDirectory(); | |
| 315 | - // Utilities.show("Running JUnit tests. Build directory is " + buildDir); | ||
| 344 | + | ||
| 345 | + //Utilities.show("Running JUnit tests. Build directory is " + buildDir); | ||
| 316 | 346 | ||
| 317 | 347 | /** Open java source files */ | |
| 318 | 348 | HashSet<String> openDocFiles = new HashSet<String>(); | |
@@ -328,7 +358,7 @@ private void _rawJUnitOpenDefDocs(List<OpenDefinitionsDocument> lod, final boole | |||
| 328 | 358 | for (OpenDefinitionsDocument doc: lod) /* for all nonEmpty documents in lod */ { | |
| 329 | 359 | if (doc.isSourceFile()) { // excludes Untitled documents and open non-source files | |
| 330 | 360 | try { | |
| 331 | - // System.err.println("Processing " + doc); | ||
| 361 | + //System.err.println("Processing " + doc); | ||
| 332 | 362 | File sourceRoot = doc.getSourceRoot(); // may throw an InvalidPackageException | |
| 333 | 363 | ||
| 334 | 364 | // doc has valid package name; add it to list of open java source doc files | |
@@ -348,21 +378,21 @@ private void _rawJUnitOpenDefDocs(List<OpenDefinitionsDocument> lod, final boole | |||
| 348 | 378 | ||
| 349 | 379 | if (! classDirsAndRoots.containsKey(classFileDir)) { | |
| 350 | 380 | classDirsAndRoots.put(classFileDir, sourceDir); | |
| 351 | - // System.err.println("Adding " + classFileDir + " with source root " + sourceRoot + | ||
| 352 | - // " to list of class directories"); | ||
| 381 | + //System.err.println("Adding " + classFileDir + " with source root " + sourceRoot + | ||
| 382 | + //" to list of class directories"); | ||
| 353 | 383 | } | |
| 354 | 384 | } | |
| 355 | 385 | catch (InvalidPackageException e) { /* Skip the file, since it doesn't have a valid package */ } | |
| 356 | 386 | } | |
| 357 | 387 | } | |
| 358 | 388 | ||
| 359 | - // System.err.println("classDirs = " + classDirsAndRoots.keySet()); | ||
| 389 | + //System.err.println("classDirs = " + classDirsAndRoots.keySet()); | ||
| 360 | 390 | ||
| 361 | 391 | /** set of dirs potentially containing test classes */ | |
| 362 | 392 | Set<File> classDirs = classDirsAndRoots.keySet(); | |
| 363 | 393 | ||
| 364 | - // System.err.println("openDocFiles = " + openDocFiles); | ||
| 365 | - | ||
| 394 | + //System.err.println("openDocFiles = " + openDocFiles); | ||
| 395 | + | ||
| 366 | 396 | /* Names of test classes. */ | |
| 367 | 397 | final ArrayList<String> classNames = new ArrayList<String>(); | |
| 368 | 398 | ||
@@ -374,32 +404,31 @@ private void _rawJUnitOpenDefDocs(List<OpenDefinitionsDocument> lod, final boole | |||
| 374 | 404 | ||
| 375 | 405 | try { | |
| 376 | 406 | for (File dir: classDirs) { // foreach class file directory | |
| 377 | - // System.err.println("Examining directory " + dir); | ||
| 407 | + //System.err.println("Examining directory " + dir); | ||
| 378 | 408 | ||
| 379 | 409 | File[] listing = dir.listFiles(); | |
| 380 | 410 | ||
| 381 | - // System.err.println("Directory contains the files: " + Arrays.asList(listing)); | ||
| 411 | + //System.err.println("Directory contains the files: " + Arrays.asList(listing)); | ||
| 382 | 412 | ||
| 383 | 413 | if (listing != null) { // listFiles may return null if there's an IO error | |
| 384 | 414 | for (File entry : listing) { /* for each class file in the build directory */ | |
| 385 | 415 | ||
| 386 | - // System.err.println("Examining file " + entry); | ||
| 416 | + //System.err.println("Examining file " + entry); | ||
| 387 | 417 | ||
| 388 | 418 | /* ignore non-class files */ | |
| 389 | 419 | String name = entry.getName(); | |
| 390 | 420 | if (! name.endsWith(".class")) continue; | |
| 391 | 421 | ||
| 392 | 422 | /* Ignore class names that do not end in "Test" if FORCE_TEST_SUFFIX option is set */ | |
| 423 | + String noExtName = ""; | ||
| 393 | 424 | if (_forceTestSuffix) { | |
| 394 | - String noExtName = name.substring(0, name.length() - 6); // remove ".class" from name | ||
| 425 | + noExtName = name.substring(0, name.length() - 6); // remove ".class" from name | ||
| 395 | 426 | int indexOfLastDot = noExtName.lastIndexOf('.'); | |
| 396 | 427 | String simpleClassName = noExtName.substring(indexOfLastDot + 1); | |
| 397 | - // System.err.println("Simple class name is " + simpleClassName); | ||
| 428 | + //System.err.println("Simple class name is " + simpleClassName); | ||
| 398 | 429 | if (/*isProject &&*/ ! simpleClassName.endsWith("Test")) continue; | |
| 399 | 430 | } | |
| 400 | - | ||
| 401 | - // System.err.println("Found test class: " + noExtName); | ||
| 402 | - | ||
| 431 | + | ||
| 403 | 432 | /* ignore entries that do not correspond to files? Can this happen? */ | |
| 404 | 433 | if (! entry.isFile()) continue; | |
| 405 | 434 | ||
@@ -429,17 +458,18 @@ public void visitEnd() { } | |||
| 429 | 458 | ||
| 430 | 459 | /** The canonical pathname for the file (including the file name) */ | |
| 431 | 460 | String javaSourceFileName = getCanonicalPath(rootDir) + File.separator + sourceName.value(); | |
| 432 | - // System.err.println("Full java source fileName = " + javaSourceFileName); | ||
| 461 | + | ||
| 462 | + //System.err.println("Full java source fileName = " + javaSourceFileName); | ||
| 433 | 463 | ||
| 434 | 464 | /* The index in fileName of the dot preceding the extension ".java", ".dj", ".dj0*, ".dj1", or ".dj2" */ | |
| 435 | 465 | int indexOfExtDot = javaSourceFileName.lastIndexOf('.'); | |
| 436 | - // System.err.println("indexOfExtDot = " + indexOfExtDot); | ||
| 466 | + //System.err.println("indexOfExtDot = " + indexOfExtDot); | ||
| 437 | 467 | if (indexOfExtDot == -1) continue; // RMI stub class files return source file names without extensions | |
| 438 | - // System.err.println("File found in openDocFiles = " + openDocFiles.contains(sourceFileName)); | ||
| 468 | + //System.err.println("File found in openDocFiles = " + openDocFiles.contains(sourceFileName)); | ||
| 439 | 469 | ||
| 440 | 470 | /* Determine if this java source file was generated from a language levels file. */ | |
| 441 | 471 | String strippedName = javaSourceFileName.substring(0, indexOfExtDot); | |
| 442 | - // System.err.println("Stripped name = " + strippedName); | ||
| 472 | + //System.err.println("Stripped name = " + strippedName); | ||
| 443 | 473 | ||
| 444 | 474 | String sourceFileName; | |
| 445 | 475 | ||
@@ -466,10 +496,80 @@ else if (openDocFiles.contains(strippedName + OptionConstants.OLD_DJ2_FILE_EXTEN | |||
| 466 | 496 | } | |
| 467 | 497 | } | |
| 468 | 498 | catch(Exception e) { | |
| 469 | - // new ScrollableDialog(null, "UnexceptedExceptionThrown", e.toString(), "").show(); | ||
| 499 | + //new ScrollableDialog(null, "UnexceptedExceptionThrown", e.toString(), "").show(); | ||
| 470 | 500 | throw new UnexpectedException(e); // triggers _junitInterrupted which runs hourglassOff | |
| 471 | 501 | } | |
| 472 | 502 | ||
| 503 | + // JaCoCo: Create instrumented versions of class files. | ||
| 504 | + ReportGenerator rg = null; | ||
| 505 | + RuntimeData myData = null; | ||
| 506 | + ArrayList<File> myInstrumentedFiles = null; | ||
| 507 | + IRuntime myRuntime = null; | ||
| 508 | + | ||
| 509 | + if (coverage) { | ||
| 510 | + // Prepare to generate a JaCoCo report | ||
| 511 | + try { | ||
| 512 | + rg = new ReportGenerator(_model, | ||
| 513 | + _model.getDocumentNavigator().getSelectedDocuments(), | ||
| 514 | + new File("/tmp/JaCoCo")); | ||
| 515 | + } catch (Exception e) { | ||
| 516 | + throw new UnexpectedException(e); | ||
| 517 | + } | ||
| 518 | + | ||
| 519 | + myData = new RuntimeData(); | ||
| 520 | + myInstrumentedFiles = new ArrayList<File>(); | ||
| 521 | + myRuntime = new LoggerRuntime(); | ||
| 522 | + | ||
| 523 | + // The Instrumenter creates a modified version of our test target class | ||
| 524 | + // that contains additional probes for execution data recording: | ||
| 525 | + ArrayList<byte[]> instrumenteds = new ArrayList<byte[]>(); | ||
| 526 | + for (int i = 0 ; i< files.size() ; i++) { | ||
| 527 | + | ||
| 528 | + // Instrument the i-th file | ||
| 529 | + try { | ||
| 530 | + final Instrumenter instr = new Instrumenter(myRuntime); | ||
| 531 | + final byte[] instrumented = instr.instrument(rg.getTargetClass( | ||
| 532 | + files.get(i)), classNames.get(i)); | ||
| 533 | + String[] pathParts = files.get(i).getAbsolutePath().split("/"); | ||
| 534 | + | ||
| 535 | + // Write this instrumented class to a file in /tmp/ | ||
| 536 | + FileOutputStream fos = new FileOutputStream("/tmp/" + | ||
| 537 | + pathParts[pathParts.length - 1].substring(0, | ||
| 538 | + pathParts[pathParts.length - 1].length() - 4) + "class"); | ||
| 539 | + fos.write(instrumented); | ||
| 540 | + fos.close(); | ||
| 541 | + | ||
| 542 | + // Keep track of all of the instrumented files | ||
| 543 | + myInstrumentedFiles.add(new File("/tmp/" + | ||
| 544 | + pathParts[pathParts.length - 1])); | ||
| 545 | + instrumenteds.add(instrumented); | ||
| 546 | + | ||
| 547 | + } catch (Exception e) { | ||
| 548 | + StringWriter stackTrace = new StringWriter(); | ||
| 549 | + e.printStackTrace(new PrintWriter(stackTrace)); | ||
| 550 | + Utilities.show("INSTRUMENTATION EXCEPTION: " + stackTrace.toString()); | ||
| 551 | + continue; | ||
| 552 | + } | ||
| 553 | + } | ||
| 554 | + | ||
| 555 | + // Now we're ready to run our instrumented class and need to startup the | ||
| 556 | + // runtime first: | ||
| 557 | + try { | ||
| 558 | + myRuntime.startup(myData); | ||
| 559 | + } catch (Exception e) { | ||
| 560 | + throw new UnexpectedException(e); | ||
| 561 | + } | ||
| 562 | + | ||
| 563 | + _jvm.setWorkingDirectory(new File("/tmp")); | ||
| 564 | + Utilities.show("Set working directory."); | ||
| 565 | + _jvm.restartInterpreterJVM(true); | ||
| 566 | + Utilities.show("Restarted interpreter."); | ||
| 567 | + } | ||
| 568 | + | ||
| 569 | + final RuntimeData data = myData; | ||
| 570 | + final ArrayList<File> instrumentedFiles = myInstrumentedFiles; | ||
| 571 | + final IRuntime runtime = myRuntime; | ||
| 572 | + | ||
| 473 | 573 | /** Run the junit test suite that has already been set up on the slave JVM */ | |
| 474 | 574 | _testInProgress = true; | |
| 475 | 575 | // System.err.println("Spawning test thread"); | |
@@ -484,33 +584,82 @@ public void run() { | |||
| 484 | 584 | // _debugger.getPendingRequestManager().classPrepared(e); (which presumably | |
| 485 | 585 | // deals with preparing the class) on the event thread using invokeLater. | |
| 486 | 586 | // This, however, doesn't get executed because the event thread is still blocking --> deadlock. | |
| 487 | - | ||
| 488 | 587 | synchronized(_compilerModel.getCompilerLock()) { | |
| 489 | 588 | // synchronized over _compilerModel to ensure that compilation and junit testing are mutually exclusive. | |
| 490 | 589 | /** Set up junit test suite on slave JVM; get TestCase classes forming that suite */ | |
| 491 | - List<String> tests = _jvm.findTestClasses(classNames, files).unwrap(null); | ||
| 492 | - // System.err.println("tests = " + tests); | ||
| 590 | + List<String> tests; | ||
| 591 | + if (coverage) { | ||
| 592 | + tests = _jvm.findTestClasses(classNames, instrumentedFiles).unwrap(null); | ||
| 593 | + } else { | ||
| 594 | + tests = _jvm.findTestClasses(classNames, files).unwrap(null); | ||
| 595 | + } | ||
| 596 | + | ||
| 493 | 597 | if (tests == null || tests.isEmpty()) { | |
| 494 | 598 | nonTestCase(allTests, false); | |
| 495 | 599 | return; | |
| 496 | 600 | } | |
| 497 | 601 | } | |
| 498 | 602 | ||
| 499 | 603 | try { | |
| 500 | - // Utilities.show("Starting JUnit"); | ||
| 501 | - | ||
| 502 | 604 | _notifyJUnitStarted(); | |
| 503 | - boolean testsPresent = _jvm.runTestSuite(); // The false return value could be changed to an exception. | ||
| 504 | - if (! testsPresent) throw new RemoteException("No unit test classes were passed to the slave JVM"); | ||
| 605 | + // The false return value could be changed to an exception. | ||
| 606 | + boolean testsPresent = _jvm.runTestSuite(); | ||
| 607 | + if (! testsPresent) { | ||
| 608 | + throw new RemoteException("No unit test classes were passed to the slave JVM"); | ||
| 609 | + } | ||
| 505 | 610 | } | |
| 506 | - catch(RemoteException e) { // Unit testing aborted; cleanup; hourglassOff already called in junitStarted | ||
| 611 | + catch (RemoteException e) { | ||
| 612 | + // Unit testing aborted; cleanup; hourglassOff already called in junitStarted | ||
| 507 | 613 | _notifyJUnitEnded(); // balances junitStarted() | |
| 508 | 614 | _testInProgress = false; | |
| 509 | 615 | } | |
| 510 | 616 | } | |
| 617 | + | ||
| 511 | 618 | }).start(); | |
| 512 | - } | ||
| 513 | - | ||
| 619 | + | ||
| 620 | + // At the end of test execution we collect execution data and shutdown | ||
| 621 | + // the runtime: | ||
| 622 | + //highlight(rg, true); | ||
| 623 | + if (coverage) { | ||
| 624 | + Utilities.show("Collecting session info..."); | ||
| 625 | + final ExecutionDataStore executionData = new ExecutionDataStore(); | ||
| 626 | + final SessionInfoStore sessionInfos = new SessionInfoStore(); | ||
| 627 | + myData.collect(executionData, sessionInfos, false); | ||
| 628 | + runtime.shutdown(); | ||
| 629 | + | ||
| 630 | + // Together with the original class definition we can calculate coverage | ||
| 631 | + // information: | ||
| 632 | + final CoverageBuilder coverageBuilder = new CoverageBuilder(); | ||
| 633 | + final Analyzer analyzer = new Analyzer(executionData, coverageBuilder); | ||
| 634 | + | ||
| 635 | + try { | ||
| 636 | + for (int i = 0; i< classNames.size(); i++) { | ||
| 637 | + Utilities.show("className = " + classNames.get(i)); | ||
| 638 | + analyzer.analyzeClass(rg.getTargetClass(files.get(i)), | ||
| 639 | + classNames.get(i)); | ||
| 640 | + } | ||
| 641 | + | ||
| 642 | + //printCoverage(coverageBuilder); | ||
| 643 | + | ||
| 644 | + // Run the structure analyzer on a single class folder to build up | ||
| 645 | + // the coverage model. The process would be similar if your classes | ||
| 646 | + // were in a jar file. Typically you would create a bundle for each | ||
| 647 | + // class folder and each jar you want in your report. If you have | ||
| 648 | + // more than one bundle you will need to add a grouping node to your | ||
| 649 | + // report | ||
| 650 | + Utilities.show("rg.getSourceDirectory().getName() = " + rg.getSourceDirectory().getName()); | ||
| 651 | + final IBundleCoverage bundleCoverage = coverageBuilder.getBundle( | ||
| 652 | + rg.getSourceDirectory().getName()); | ||
| 653 | + rg.createReport(bundleCoverage, executionData, sessionInfos); | ||
| 654 | + } catch (Exception e) { | ||
| 655 | + StringWriter stackTrace = new StringWriter(); | ||
| 656 | + e.printStackTrace(new PrintWriter(stackTrace)); | ||
| 657 | + Utilities.show("ANALYSIS EXCEPTION: " + stackTrace.toString()); | ||
| 658 | + } | ||
| 659 | + | ||
| 660 | + } | ||
| 661 | + } | ||
| 662 | + | ||
| 514 | 663 | //-------------------------------- Helpers --------------------------------// | |
| 515 | 664 | ||
| 516 | 665 | /** Helper method to notify JUnitModel listeners that JUnit test suite execution has started. */ | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -47,6 +47,7 @@ public interface JUnitModel { | |||
| 47 | 47 | ||
| 48 | 48 | ||
| 49 | 49 | /** set the forceTestSuffix flag that forces class names in projects to end in "Test */ | |
| 50 | + public void setCoverage(boolean c); | ||
| 50 | 51 | public void setForceTestSuffix(boolean b); | |
| 51 | 52 | ||
| 52 | 53 | //-------------------------- Listener Management --------------------------// | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -5842,9 +5842,14 @@ private void _junitAll() { | |||
| 5842 | 5842 | updateStatusField("Running All Open Unit Tests"); | |
| 5843 | 5843 | hourglassOn(); // turned off in junitStarted/nonTestCase/_junitInterrupted | |
| 5844 | 5844 | _guiAvailabilityNotifier.junitStarted(); // JUNIT and COMPILER | |
| 5845 | - try { _model.getJUnitModel().junitAll(); } | ||
| 5846 | - catch(UnexpectedException e) { _junitInterrupted(e); } | ||
| 5847 | - catch(Exception e) { _junitInterrupted(new UnexpectedException(e)); } | ||
| 5845 | + try { | ||
| 5846 | + _model.getJUnitModel().setCoverage(true); | ||
| 5847 | + _model.getJUnitModel().junitAll(); | ||
| 5848 | + } catch(UnexpectedException e) { | ||
| 5849 | + _junitInterrupted(e); | ||
| 5850 | + } catch(Exception e) { | ||
| 5851 | + _junitInterrupted(new UnexpectedException(e)); | ||
| 5852 | + } | ||
| 5848 | 5853 | } | |
| 5849 | 5854 | ||
| 5850 | 5855 | // /** | |
| Back | FazBrowse Home | New Git URL |
0 commit comments