├── .codacy.yml ├── .github ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── ISSUE_TEMPLATE.md ├── PULL_REQUEST_TEMPLATE.md ├── dependabot.yml └── workflows │ ├── build.yml │ └── release.yml ├── .gitignore ├── .scala-steward.conf ├── .scalafmt.conf ├── LICENSE ├── README.md ├── build.sbt ├── project ├── build.properties └── plugins.sbt ├── scalastyle-config.xml └── src ├── main ├── paradox │ ├── contributors.md │ ├── coverage-services.md │ ├── getting-started.md │ ├── index.md │ ├── integration-tests.md │ ├── multi-project.md │ └── settings.md ├── resources │ └── epl-v10.html └── scala │ └── com │ └── github │ └── sbt │ └── jacoco │ ├── BaseJacocoPlugin.scala │ ├── JacocoItPlugin.scala │ ├── JacocoKeys.scala │ ├── JacocoPlugin.scala │ ├── coveralls │ ├── CoverallsClient.scala │ ├── CoverallsReportFormat.scala │ ├── CoverallsReportVisitor.scala │ ├── GitInfo.scala │ └── JacocoCoverallsPlugin.scala │ ├── data │ ├── ExecutionDataUtils.scala │ ├── InstrumentationUtils.scala │ └── ProjectData.scala │ ├── filter │ ├── AccessorDetector.scala │ ├── FilteringClassAnalyzer.scala │ ├── ScalaForwarderDetector.scala │ └── ScalaSyntheticMethod.scala │ ├── package.scala │ └── report │ ├── DirectoriesSourceFileLocator.scala │ ├── JacocoReportFormats.scala │ ├── JacocoReportSettings.scala │ ├── JacocoSourceSettings.scala │ ├── JacocoThresholds.scala │ ├── Report.scala │ ├── ReportUtils.scala │ └── formats │ ├── CSVReportFormat.scala │ ├── HTMLReportFormat.scala │ ├── JacocoReportFormat.scala │ ├── ScalaHTMLReportFormat.scala │ ├── ScalaLanguageNames.scala │ └── XMLReportFormat.scala ├── sbt-test └── sbt-jacoco │ ├── csv-report │ ├── build.sbt │ ├── project │ │ └── plugins.sbt │ ├── src │ │ ├── main │ │ │ └── scala │ │ │ │ └── jacocotest │ │ │ │ └── TestClass.scala │ │ └── test │ │ │ └── scala │ │ │ └── jacocotest │ │ │ └── TestClassTests.scala │ └── test │ ├── forked-integration │ ├── build.sbt │ ├── project │ │ └── plugins.sbt │ ├── src │ │ ├── it │ │ │ └── scala │ │ │ │ └── IntegrationTest.scala │ │ ├── main │ │ │ └── scala │ │ │ │ └── TestSubject.scala │ │ └── test │ │ │ └── scala │ │ │ └── UnitTest.scala │ └── test │ ├── forked │ ├── build.sbt │ ├── project │ │ └── plugins.sbt │ ├── src │ │ ├── main │ │ │ └── scala │ │ │ │ └── jacocotest │ │ │ │ └── TestClass.scala │ │ └── test │ │ │ └── scala │ │ │ └── jacocotest │ │ │ └── TestClassTests.scala │ └── test │ ├── integration-test-no-merge │ ├── build.sbt │ ├── project │ │ └── plugins.sbt │ ├── src │ │ ├── it │ │ │ └── scala │ │ │ │ └── IntegrationTest.scala │ │ ├── main │ │ │ └── scala │ │ │ │ └── TestSubject.scala │ │ └── test │ │ │ └── scala │ │ │ └── UnitTest.scala │ └── test │ ├── integration-test │ ├── build.sbt │ ├── project │ │ └── plugins.sbt │ ├── src │ │ ├── it │ │ │ └── scala │ │ │ │ └── IntegrationTest.scala │ │ ├── main │ │ │ └── scala │ │ │ │ └── TestSubject.scala │ │ └── test │ │ │ └── scala │ │ │ └── UnitTest.scala │ └── test │ ├── multi-build │ ├── build.sbt │ ├── common │ │ └── src │ │ │ ├── main │ │ │ └── scala │ │ │ │ └── jacocotest │ │ │ │ └── common │ │ │ │ └── Greeter.scala │ │ │ └── test │ │ │ └── scala │ │ │ └── jacocotest │ │ │ └── common │ │ │ └── GreeterSpec.scala │ ├── extras │ │ └── src │ │ │ ├── main │ │ │ └── scala │ │ │ │ └── jacocotest │ │ │ │ └── extra │ │ │ │ └── Money.scala │ │ │ └── test │ │ │ └── scala │ │ │ └── jacocotest │ │ │ └── extra │ │ │ └── MoneySpec.scala │ ├── project │ │ ├── SettingsPlugin.scala │ │ └── plugins.sbt │ └── test │ ├── report-settings │ ├── build.sbt │ ├── project │ │ └── plugins.sbt │ ├── src │ │ ├── main │ │ │ └── scala │ │ │ │ └── jacocotest │ │ │ │ └── TestClass.scala │ │ └── test │ │ │ └── scala │ │ │ └── jacocotest │ │ │ └── TestClassTests.scala │ └── test │ ├── simple-scoped │ ├── build.sbt │ ├── project │ │ └── plugins.sbt │ ├── src │ │ ├── main │ │ │ └── scala │ │ │ │ └── jacocotest │ │ │ │ └── TestClass.scala │ │ └── test │ │ │ └── scala │ │ │ └── jacocotest │ │ │ └── TestClassTests.scala │ └── test │ ├── simple │ ├── build.sbt │ ├── project │ │ └── plugins.sbt │ ├── src │ │ ├── main │ │ │ └── scala │ │ │ │ └── jacocotest │ │ │ │ └── TestClass.scala │ │ └── test │ │ │ └── scala │ │ │ └── jacocotest │ │ │ └── TestClassTests.scala │ └── test │ ├── source-settings │ ├── build.sbt │ ├── project │ │ └── plugins.sbt │ ├── src │ │ ├── main │ │ │ └── scala │ │ │ │ └── jacocotest │ │ │ │ └── TestClass.scala │ │ └── test │ │ │ └── scala │ │ │ └── jacocotest │ │ │ └── TestClassTests.scala │ └── test │ ├── unmanaged-resources │ ├── build.sbt │ ├── project │ │ └── plugins.sbt │ ├── src │ │ ├── main │ │ │ └── scala │ │ │ │ └── jacocotest │ │ │ │ └── PropertyUtils.scala │ │ ├── test │ │ │ └── scala │ │ │ │ └── jacocotest │ │ │ │ └── PropertyUtilsSpec.scala │ │ └── unmanaged │ │ │ └── info.properties │ └── test │ └── xml-report │ ├── build.sbt │ ├── project │ └── plugins.sbt │ ├── src │ ├── main │ │ └── scala │ │ │ └── jacocotest │ │ │ └── TestClass.scala │ └── test │ │ └── scala │ │ └── jacocotest │ │ └── TestClassTests.scala │ └── test └── test ├── scala-sbt-0.13 └── com │ └── github │ └── sbt │ └── jacoco │ └── TestCounters.scala ├── scala-sbt-1.0 └── com │ └── github │ └── sbt │ └── jacoco │ └── TestCounters.scala └── scala └── com └── github └── sbt └── jacoco └── ReportSpec.scala /.codacy.yml: -------------------------------------------------------------------------------- 1 | --- 2 | exclude_paths: 3 | - src/sbt-test/** 4 | -------------------------------------------------------------------------------- /.github/CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, gender identity and expression, level of experience, 9 | nationality, personal appearance, race, religion, or sexual identity and 10 | orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | * Trolling, insulting/derogatory comments, and personal or political attacks 28 | * Public or private harassment 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project e-mail 51 | address, posting via an official social media account, or acting as an appointed 52 | representative at an online or offline event. Representation of a project may be 53 | further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project team on [Gitter](https://gitter.im/sbt/sbt). All 59 | complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project team is 61 | obligated to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 71 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct/ 72 | 73 | [homepage]: https://www.contributor-covenant.org 74 | -------------------------------------------------------------------------------- /.github/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | When you find a bug in jacoco4sbt we want to hear about it. Your bug reports play an important part in making this 4 | plugin more reliable and usable. 5 | 6 | Effective bug reports are more likely to be fixed. These guidelines explain how to write such reports and pull requests. 7 | 8 | ## Before Reporting an Issue 9 | 10 | * Make sure that you are running the latest version of SBT (1.x) and the jacoco4sbt plugin. 11 | * Check the open [issues](https://github.com/sbt/jacoco4sbt/issues) and 12 | [pull requests](https://github.com/sbt/jacoco4sbt/pulls) for anything similar. If there is already an open issue 13 | and you have additional information please add it (comments such as +1 aren't helpful). 14 | 15 | ## How to Report an Issue 16 | 17 | It is important when opening a new issue to include as much information as possible including: 18 | 19 | * How to reproduce the issue: 20 | * What SBT tasks did you run before jacoco? 21 | * Does it always happen? 22 | * Does running `clean` beforehand solve it? 23 | * Details of your environment: 24 | * Version of SBT. 25 | * Version of the jacoco4sbt plugin. 26 | * Java version. 27 | * Scala versions (all versions if cross-building). 28 | * A link to a repo contining your project _or_ 29 | * A link to a Gist/Pastebin with a test case reproducing the issue. 30 | 31 | ## How to Submit a Pull Request 32 | 33 | We welcome code contributions to jacoco4sbt. To make it easier for us to include your contributions please ensure the 34 | following before creating a pull request: 35 | 36 | * Your branch is up to date with `main`. 37 | * All unit tests and integration tests pass. 38 | 39 | When opening a pull request please including any relevant information - such as _"fixes issue #x"_ or _"adds new feature y"_. 40 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ### Expected vs Actual Behaviour 2 | 3 | It should... 4 | 5 | Instead it... 6 | 7 | ### Steps to Reproduce 8 | 9 | ``` 10 | > jacoco:jacoco 11 | ... 12 | ``` 13 | 14 | ### Environment 15 | 16 | * SBT version: 17 | * Plugin version: 18 | * Scala version(s): _(include all versions if cross-building)_ 19 | * Java version: 20 | * Link to project source: _(if available)_ 21 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | Fixes #... (issue with ???) 2 | 3 | _(description of PR)_ 4 | 5 | #### Checklist 6 | 7 | - [ ] Unit tests pass 8 | - [ ] You have read the contributing guide linked above. 9 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "github-actions" 4 | directory: "/" 5 | schedule: 6 | interval: "weekly" 7 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | on: 3 | push: 4 | branches: 5 | - main 6 | pull_request: 7 | jobs: 8 | build_scala2_12: 9 | runs-on: ubuntu-latest 10 | env: 11 | # define Java options for both official sbt and sbt-extras 12 | JAVA_OPTS: -Xms2048M -Xmx2048M -Xss2M -XX:ReservedCodeCacheSize=256M -Dfile.encoding=UTF-8 13 | JVM_OPTS: -Xms2048M -Xmx2048M -Xss2M -XX:ReservedCodeCacheSize=256M -Dfile.encoding=UTF-8 14 | strategy: 15 | fail-fast: false 16 | matrix: 17 | java-version: [ 8, 17 ] 18 | steps: 19 | - name: Checkout 20 | uses: actions/checkout@v4 21 | with: 22 | fetch-depth: 0 23 | - name: Setup Java 24 | uses: actions/setup-java@v4 25 | with: 26 | distribution: temurin 27 | java-version: ${{matrix.java-version}} 28 | - name: Coursier cache 29 | uses: coursier/cache-action@v6 30 | - uses: sbt/setup-sbt@v1 31 | - name: Build and test 32 | shell: bash 33 | run: sbt -v clean scalafmtCheckAll headerCheck test scripted 34 | - name: Build site 35 | if: ${{ matrix.java-version == 8 }} 36 | shell: bash 37 | run: sbt -v makeSite 38 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | 3 | on: 4 | push: 5 | tags: 6 | - '*' 7 | 8 | jobs: 9 | build_sbt_1_0: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: actions/checkout@v4 13 | with: 14 | fetch-depth: 0 15 | # setup build environment 16 | - uses: coursier/cache-action@v6 17 | - uses: olafurpg/setup-scala@v14 18 | - name: sbt test and packagedArtifacts for sbt 1.x 19 | run: | 20 | sbt test packagedArtifacts 21 | - name: sbt Publish for sbt 1.x 22 | run: | 23 | echo 'sbt ci-release' 24 | sbt ci-release 25 | env: 26 | PGP_PASSPHRASE: ${{ secrets.PGP_PASSPHRASE }} 27 | PGP_SECRET: ${{ secrets.PGP_SECRET }} 28 | SONATYPE_PASSWORD: ${{ secrets.SONATYPE_PASSWORD }} 29 | SONATYPE_USERNAME: ${{ secrets.SONATYPE_USERNAME }} 30 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .cache 2 | .history 3 | .target 4 | .classpath 5 | .project 6 | .bsp 7 | bin/ 8 | .settings 9 | target/ 10 | .scala_dependencies 11 | project/target/ 12 | project/project/ 13 | project/plugins/project/ 14 | project/plugins/target/ 15 | .idea* 16 | *.iml 17 | src/test/resources/*/target 18 | src/test/resources/*/project/target 19 | src/test/resources/*/log 20 | -------------------------------------------------------------------------------- /.scala-steward.conf: -------------------------------------------------------------------------------- 1 | updates.pin = [ 2 | { groupId = "org.eclipse.jgit", version = "5." } // jgit 6.x require JDK 11 3 | { 4 | // https://github.com/mockito/mockito/commit/a7c7fdb4f972d7fb6736f692e5ee208034f214f3 5 | groupId = "org.mockito" 6 | artifactId = "mockito-core" 7 | version = "4." 8 | } 9 | ] 10 | 11 | updates.ignore = [ 12 | { groupId = "com.lightbend.paradox" } 13 | ] 14 | -------------------------------------------------------------------------------- /.scalafmt.conf: -------------------------------------------------------------------------------- 1 | version = 3.9.7 2 | 3 | runner.dialect = scala212source3 4 | preset = default 5 | docstrings.style = Asterisk 6 | maxColumn = 120 7 | 8 | align.openParenCallSite = false 9 | align.openParenDefnSite = false 10 | 11 | align.preset = none 12 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Eclipse Public License - v 1.0 2 | 3 | THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE PUBLIC 4 | LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION OF THE PROGRAM 5 | CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT. 6 | 7 | 1. DEFINITIONS 8 | 9 | "Contribution" means: 10 | 11 | a) in the case of the initial Contributor, the initial code and documentation 12 | distributed under this Agreement, and 13 | b) in the case of each subsequent Contributor: 14 | i) changes to the Program, and 15 | ii) additions to the Program; 16 | 17 | where such changes and/or additions to the Program originate from and are 18 | distributed by that particular Contributor. A Contribution 'originates' 19 | from a Contributor if it was added to the Program by such Contributor 20 | itself or anyone acting on such Contributor's behalf. Contributions do not 21 | include additions to the Program which: (i) are separate modules of 22 | software distributed in conjunction with the Program under their own 23 | license agreement, and (ii) are not derivative works of the Program. 24 | 25 | "Contributor" means any person or entity that distributes the Program. 26 | 27 | "Licensed Patents" mean patent claims licensable by a Contributor which are 28 | necessarily infringed by the use or sale of its Contribution alone or when 29 | combined with the Program. 30 | 31 | "Program" means the Contributions distributed in accordance with this 32 | Agreement. 33 | 34 | "Recipient" means anyone who receives the Program under this Agreement, 35 | including all Contributors. 36 | 37 | 2. GRANT OF RIGHTS 38 | a) Subject to the terms of this Agreement, each Contributor hereby grants 39 | Recipient a non-exclusive, worldwide, royalty-free copyright license to 40 | reproduce, prepare derivative works of, publicly display, publicly 41 | perform, distribute and sublicense the Contribution of such Contributor, 42 | if any, and such derivative works, in source code and object code form. 43 | b) Subject to the terms of this Agreement, each Contributor hereby grants 44 | Recipient a non-exclusive, worldwide, royalty-free patent license under 45 | Licensed Patents to make, use, sell, offer to sell, import and otherwise 46 | transfer the Contribution of such Contributor, if any, in source code and 47 | object code form. This patent license shall apply to the combination of 48 | the Contribution and the Program if, at the time the Contribution is 49 | added by the Contributor, such addition of the Contribution causes such 50 | combination to be covered by the Licensed Patents. The patent license 51 | shall not apply to any other combinations which include the Contribution. 52 | No hardware per se is licensed hereunder. 53 | c) Recipient understands that although each Contributor grants the licenses 54 | to its Contributions set forth herein, no assurances are provided by any 55 | Contributor that the Program does not infringe the patent or other 56 | intellectual property rights of any other entity. Each Contributor 57 | disclaims any liability to Recipient for claims brought by any other 58 | entity based on infringement of intellectual property rights or 59 | otherwise. As a condition to exercising the rights and licenses granted 60 | hereunder, each Recipient hereby assumes sole responsibility to secure 61 | any other intellectual property rights needed, if any. For example, if a 62 | third party patent license is required to allow Recipient to distribute 63 | the Program, it is Recipient's responsibility to acquire that license 64 | before distributing the Program. 65 | d) Each Contributor represents that to its knowledge it has sufficient 66 | copyright rights in its Contribution, if any, to grant the copyright 67 | license set forth in this Agreement. 68 | 69 | 3. REQUIREMENTS 70 | 71 | A Contributor may choose to distribute the Program in object code form under 72 | its own license agreement, provided that: 73 | 74 | a) it complies with the terms and conditions of this Agreement; and 75 | b) its license agreement: 76 | i) effectively disclaims on behalf of all Contributors all warranties 77 | and conditions, express and implied, including warranties or 78 | conditions of title and non-infringement, and implied warranties or 79 | conditions of merchantability and fitness for a particular purpose; 80 | ii) effectively excludes on behalf of all Contributors all liability for 81 | damages, including direct, indirect, special, incidental and 82 | consequential damages, such as lost profits; 83 | iii) states that any provisions which differ from this Agreement are 84 | offered by that Contributor alone and not by any other party; and 85 | iv) states that source code for the Program is available from such 86 | Contributor, and informs licensees how to obtain it in a reasonable 87 | manner on or through a medium customarily used for software exchange. 88 | 89 | When the Program is made available in source code form: 90 | 91 | a) it must be made available under this Agreement; and 92 | b) a copy of this Agreement must be included with each copy of the Program. 93 | Contributors may not remove or alter any copyright notices contained 94 | within the Program. 95 | 96 | Each Contributor must identify itself as the originator of its Contribution, 97 | if 98 | any, in a manner that reasonably allows subsequent Recipients to identify the 99 | originator of the Contribution. 100 | 101 | 4. COMMERCIAL DISTRIBUTION 102 | 103 | Commercial distributors of software may accept certain responsibilities with 104 | respect to end users, business partners and the like. While this license is 105 | intended to facilitate the commercial use of the Program, the Contributor who 106 | includes the Program in a commercial product offering should do so in a manner 107 | which does not create potential liability for other Contributors. Therefore, 108 | if a Contributor includes the Program in a commercial product offering, such 109 | Contributor ("Commercial Contributor") hereby agrees to defend and indemnify 110 | every other Contributor ("Indemnified Contributor") against any losses, 111 | damages and costs (collectively "Losses") arising from claims, lawsuits and 112 | other legal actions brought by a third party against the Indemnified 113 | Contributor to the extent caused by the acts or omissions of such Commercial 114 | Contributor in connection with its distribution of the Program in a commercial 115 | product offering. The obligations in this section do not apply to any claims 116 | or Losses relating to any actual or alleged intellectual property 117 | infringement. In order to qualify, an Indemnified Contributor must: 118 | a) promptly notify the Commercial Contributor in writing of such claim, and 119 | b) allow the Commercial Contributor to control, and cooperate with the 120 | Commercial Contributor in, the defense and any related settlement 121 | negotiations. The Indemnified Contributor may participate in any such claim at 122 | its own expense. 123 | 124 | For example, a Contributor might include the Program in a commercial product 125 | offering, Product X. That Contributor is then a Commercial Contributor. If 126 | that Commercial Contributor then makes performance claims, or offers 127 | warranties related to Product X, those performance claims and warranties are 128 | such Commercial Contributor's responsibility alone. Under this section, the 129 | Commercial Contributor would have to defend claims against the other 130 | Contributors related to those performance claims and warranties, and if a 131 | court requires any other Contributor to pay any damages as a result, the 132 | Commercial Contributor must pay those damages. 133 | 134 | 5. NO WARRANTY 135 | 136 | EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS PROVIDED ON AN 137 | "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR 138 | IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, 139 | NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each 140 | Recipient is solely responsible for determining the appropriateness of using 141 | and distributing the Program and assumes all risks associated with its 142 | exercise of rights under this Agreement , including but not limited to the 143 | risks and costs of program errors, compliance with applicable laws, damage to 144 | or loss of data, programs or equipment, and unavailability or interruption of 145 | operations. 146 | 147 | 6. DISCLAIMER OF LIABILITY 148 | 149 | EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT NOR ANY 150 | CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL, 151 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING WITHOUT LIMITATION 152 | LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 153 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 154 | ARISING IN ANY WAY OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE 155 | EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY 156 | OF SUCH DAMAGES. 157 | 158 | 7. GENERAL 159 | 160 | If any provision of this Agreement is invalid or unenforceable under 161 | applicable law, it shall not affect the validity or enforceability of the 162 | remainder of the terms of this Agreement, and without further action by the 163 | parties hereto, such provision shall be reformed to the minimum extent 164 | necessary to make such provision valid and enforceable. 165 | 166 | If Recipient institutes patent litigation against any entity (including a 167 | cross-claim or counterclaim in a lawsuit) alleging that the Program itself 168 | (excluding combinations of the Program with other software or hardware) 169 | infringes such Recipient's patent(s), then such Recipient's rights granted 170 | under Section 2(b) shall terminate as of the date such litigation is filed. 171 | 172 | All Recipient's rights under this Agreement shall terminate if it fails to 173 | comply with any of the material terms or conditions of this Agreement and does 174 | not cure such failure in a reasonable period of time after becoming aware of 175 | such noncompliance. If all Recipient's rights under this Agreement terminate, 176 | Recipient agrees to cease use and distribution of the Program as soon as 177 | reasonably practicable. However, Recipient's obligations under this Agreement 178 | and any licenses granted by Recipient relating to the Program shall continue 179 | and survive. 180 | 181 | Everyone is permitted to copy and distribute copies of this Agreement, but in 182 | order to avoid inconsistency the Agreement is copyrighted and may only be 183 | modified in the following manner. The Agreement Steward reserves the right to 184 | publish new versions (including revisions) of this Agreement from time to 185 | time. No one other than the Agreement Steward has the right to modify this 186 | Agreement. The Eclipse Foundation is the initial Agreement Steward. The 187 | Eclipse Foundation may assign the responsibility to serve as the Agreement 188 | Steward to a suitable separate entity. Each new version of the Agreement will 189 | be given a distinguishing version number. The Program (including 190 | Contributions) may always be distributed subject to the version of the 191 | Agreement under which it was received. In addition, after a new version of the 192 | Agreement is published, Contributor may elect to distribute the Program 193 | (including its Contributions) under the new version. Except as expressly 194 | stated in Sections 2(a) and 2(b) above, Recipient receives no rights or 195 | licenses to the intellectual property of any Contributor under this Agreement, 196 | whether expressly, by implication, estoppel or otherwise. All rights in the 197 | Program not expressly granted under this Agreement are reserved. 198 | 199 | This Agreement is governed by the laws of the State of New York and the 200 | intellectual property laws of the United States of America. No party to this 201 | Agreement will bring a legal action under this Agreement more than one year 202 | after the cause of action arose. Each party waives its rights to a jury trial in 203 | any resulting litigation. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # sbt-jacoco - Code Coverage via JaCoCo in sbt 2 | 3 | [![Build Status](https://github.com/sbt/sbt-jacoco/workflows/CI/badge.svg)](https://github.com/sbt/sbt-jacoco/actions?workflow=CI) 4 | [![Release Status](https://github.com/sbt/sbt-jacoco/workflows/Release/badge.svg)](https://github.com/sbt/sbt-jacoco/actions?workflow=Release) 5 | [![SBT 1.0 version](https://maven-badges.herokuapp.com/maven-central/com.github.sbt/sbt-jacoco/badge.svg)](https://repo1.maven.org/maven2/com/github/sbt/sbt-jacoco_2.12_1.0/) 6 | 7 | This is an [sbt](http://scala-sbt.org/) plugin for code coverage analysis via [JaCoCo](http://www.eclemma.org/jacoco/). 8 | Supports uploading results to [Coveralls](https://coveralls.io), [Codecov](https://codecov.io) and [Codacy](https://www.codacy.com/). 9 | 10 | Install the plugin by adding the following to `project/plugins.sbt`: 11 | 12 | ```scala 13 | addSbtPlugin("com.github.sbt" % "sbt-jacoco" % "") 14 | ``` 15 | 16 | And then execute the plugin with `sbt jacoco`. This will instrument and run the unit tests and output the coverage 17 | metrics: 18 | 19 | ``` 20 | [info] ------- Jacoco Coverage Report -------- 21 | [info] 22 | [info] Lines: 66.67% (>= required 0.0%) covered, 2 of 6 missed, OK 23 | [info] Instructions: 83.54% (>= required 0.0%) covered, 13 of 79 missed, OK 24 | [info] Branches: 0% (>= required 0.0%) covered, 0 of 0 missed, OK 25 | [info] Methods: 57.14% (>= required 0.0%) covered, 3 of 7 missed, OK 26 | [info] Complexity: 57.14% (>= required 0.0%) covered, 3 of 7 missed, OK 27 | [info] Class: 50% (>= required 0.0%) covered, 2 of 4 missed, OK 28 | [info] 29 | [info] Check /home/example/jacoco-test/target/scala-2.11/jacoco/report for detailed report 30 | ``` 31 | 32 | A detailed HTML report will also be generated in the directory shown that includes line level details of coverage. 33 | 34 | See the [docs](http://scala-sbt.org/sbt-jacoco) for details on configuration options. 35 | -------------------------------------------------------------------------------- /build.sbt: -------------------------------------------------------------------------------- 1 | lazy val jacocoVersion = "0.8.13" 2 | 3 | ThisBuild / version := { 4 | if ((ThisBuild / isSnapshot).value) "3.4.0" + "-SNAPSHOT" 5 | else (ThisBuild / version).value 6 | } 7 | 8 | lazy val jacocoPlugin = (project in file(".")) 9 | .enablePlugins(SbtPlugin) 10 | .enablePlugins(BuildInfoPlugin) 11 | .enablePlugins(ParadoxSitePlugin) 12 | .enablePlugins(GhpagesPlugin) 13 | .settings(nocomma { 14 | name := "sbt-jacoco" 15 | 16 | libraryDependencies ++= Seq( 17 | "org.jacoco" % "org.jacoco.core" % jacocoVersion, 18 | "org.jacoco" % "org.jacoco.report" % jacocoVersion, 19 | "com.jsuereth" %% "scala-arm" % "2.0", 20 | "com.fasterxml.jackson.core" % "jackson-core" % "2.19.0", 21 | "org.scalaj" %% "scalaj-http" % "2.4.2", 22 | "commons-codec" % "commons-codec" % "1.18.0", 23 | "org.eclipse.jgit" % "org.eclipse.jgit" % "5.13.3.202401111512-r", 24 | "org.scalatest" %% "scalatest" % "3.2.19" % Test, 25 | "org.mockito" % "mockito-core" % "4.11.0" % Test 26 | ) 27 | 28 | scalacOptions ++= Seq( 29 | "-unchecked", 30 | "-deprecation", 31 | "-feature", 32 | "-Xfuture", 33 | "-Ywarn-adapted-args", 34 | "-Ywarn-dead-code" 35 | ) 36 | 37 | buildInfoPackage := "com.github.sbt.jacoco.build" 38 | buildInfoKeys := Seq[BuildInfoKey]( 39 | Test / resourceDirectory, 40 | version, 41 | "jacocoVersion" -> jacocoVersion, 42 | "sbtVersion" -> sbtVersion.value 43 | ) 44 | 45 | Compile / paradoxNavigationDepth := 3 46 | git.remoteRepo := "git@github.com:sbt/sbt-jacoco.git" 47 | 48 | headerLicense := Some( 49 | HeaderLicense.Custom( 50 | """|This file is part of sbt-jacoco. 51 | | 52 | |Copyright (c) Joachim Hofer & contributors 53 | |All rights reserved. 54 | | 55 | |This program and the accompanying materials 56 | |are made available under the terms of the Eclipse Public License v1.0 57 | |which accompanies this distribution, and is available at 58 | |http://www.eclipse.org/legal/epl-v10.html 59 | |""".stripMargin 60 | ) 61 | ) 62 | 63 | scriptedLaunchOpts := { 64 | scriptedLaunchOpts.value ++ 65 | Seq("-Xmx1024M", "-Dplugin.version=" + version.value) 66 | } 67 | 68 | scriptedBufferLog := false 69 | }) 70 | 71 | Global / onChangedBuildSource := ReloadOnSourceChanges 72 | ThisBuild / organization := "com.github.sbt" 73 | ThisBuild / description := "an sbt plugin for JaCoCo Code Coverage" 74 | ThisBuild / homepage := Some(url("https://www.scala-sbt.org/sbt-jacoco/")) 75 | ThisBuild / licenses += (("Eclipse Public License v1.0", url("http://www.eclipse.org/legal/epl-v10.html"))) 76 | ThisBuild / developers := List( 77 | Developer( 78 | "jmhofer", 79 | "Joachim Hofer", 80 | "@jmhofer", 81 | url("https://github.com/jmhofer") 82 | ) 83 | ) 84 | ThisBuild / pomIncludeRepository := { _ => 85 | false 86 | } 87 | ThisBuild / publishTo := { 88 | val nexus = "https://oss.sonatype.org/" 89 | if (isSnapshot.value) Some("snapshots" at nexus + "content/repositories/snapshots") 90 | else Some("releases" at nexus + "service/local/staging/deploy/maven2") 91 | } 92 | ThisBuild / publishMavenStyle := true 93 | ThisBuild / dynverSonatypeSnapshots := true 94 | -------------------------------------------------------------------------------- /project/build.properties: -------------------------------------------------------------------------------- 1 | sbt.version=1.7.2 2 | -------------------------------------------------------------------------------- /project/plugins.sbt: -------------------------------------------------------------------------------- 1 | addSbtPlugin("com.github.sbt" % "sbt-ci-release" % "1.5.10") 2 | addSbtPlugin("com.eed3si9n" % "sbt-buildinfo" % "0.13.1") 3 | 4 | addSbtPlugin("org.scalameta" % "sbt-scalafmt" % "2.5.4") 5 | addSbtPlugin("de.heikoseeberger" % "sbt-header" % "5.10.0") 6 | addSbtPlugin("org.scalastyle" %% "scalastyle-sbt-plugin" % "1.0.0") 7 | addSbtPlugin("com.typesafe.sbt" % "sbt-site" % "1.4.1") 8 | addSbtPlugin("com.lightbend.paradox" % "sbt-paradox" % "0.9.2") 9 | addSbtPlugin("com.typesafe.sbt" % "sbt-ghpages" % "0.6.3") 10 | addSbtPlugin("com.eed3si9n" % "sbt-nocomma" % "0.1.2") 11 | -------------------------------------------------------------------------------- /scalastyle-config.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | ^[A-Z][A-Za-z]*$ 11 | 12 | 13 | 14 | 15 | ^[a-z][A-Za-z0-9]*$ 16 | 17 | ^[A-Za-z0-9]*$ 18 | 19 | 20 | 21 | 22 | ^[a-z][A-Za-z0-9]*$ 23 | 24 | 25 | 26 | 27 | ^[A-Za-z]*$ 28 | 29 | 30 | 31 | 32 | ^[a-z][A-Za-z]*$ 33 | 34 | 35 | 36 | 37 | 38 | 39 | 200 40 | 41 | 42 | 43 | 44 | 120 45 | 46 | 47 | 48 | 49 | 50 | 50 51 | 52 | 53 | 54 | 55 | 15 56 | 57 | 58 | 59 | 60 | 10 61 | 62 | 63 | 64 | 65 | 8 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | true 76 | false 77 | 78 | 79 | 80 | 81 | 82 | 83 | java,scala,sbt,others 84 | javax?\..+ 85 | scala\..+ 86 | sbt\..+ 87 | .+ 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | sun._,java.awt._> 107 | 108 | 109 | 110 | 111 | 112 | -1,0,1,2,3 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | println 132 | 133 | 134 | -------------------------------------------------------------------------------- /src/main/paradox/contributors.md: -------------------------------------------------------------------------------- 1 | # Contributors 2 | 3 | Version 1.0.0 of _sbt-jacoco_ (then known as _jacoco4sbt_) was first released in 2011 by 4 | [Joachim Hofer](https://github.com/jmhofer). Recently the project has been maintained by 5 | [Michael Stringer](https://github.com/stringbean). 6 | 7 | A big thanks to everyone who has contributed to _sbt-jacoco_ including: 8 | 9 | * [Alexey Pismenskiy](https://github.com/apismensky) 10 | * [Andreas Flierl](https://github.com/asflierl) 11 | * [Ben McCann](https://github.com/benmccann) 12 | * [Dragisa Krsmanovic](https://github.com/dragisak) 13 | * [Ethan Atkins](https://github.com/eatkins) 14 | * [Jacek Laskowski](https://github.com/jaceklaskowski) 15 | * [Jason Zaugg](https://github.com/retronym) 16 | * [Jerry Lin](https://github.com/linjer) 17 | * [Joost den Boer](https://github.com/diversit) 18 | * [Kenji Yoshida](https://github.com/xuwei-k) 19 | * [Michael Schleichardt](https://github.com/schleichardt) 20 | * [Patrick Mahoney](https://bitbucket.org/paddymahoney) 21 | * [Wei Chen](https://github.com/wchen9911) 22 | -------------------------------------------------------------------------------- /src/main/paradox/coverage-services.md: -------------------------------------------------------------------------------- 1 | # Coverage Services 2 | 3 | Examples of uploading to [Coveralls](https://coveralls.io/) and [Codecov](https://codecov.io/gh) can be found in the 4 | example project: [stringbean/sbt-jacoco-example](https://github.com/stringbean/sbt-jacoco-example) 5 | 6 | ## Coveralls 7 | 8 | If you have a public project built with Travis-CI you will just need to enable the Coveralls plugin: 9 | 10 | ```scala 11 | enablePlugins(JacocoCoverallsPlugin) 12 | ``` 13 | 14 | Then run `sbt jacocoCoveralls` to upload the results to Coveralls: 15 | 16 | ``` 17 | [info] Uploading coverage to coveralls.io... 18 | [info] Upload complete 19 | ``` 20 | 21 | For private projects you will need to set a few more settings: 22 | 23 | ```scala 24 | jacocoCoverallsServiceName := "jenkins" 25 | jacocoCoverallsJobId := sys.env.get("BUILD_ID") // If None, Coveralls sets its own job ID. 26 | jacocoCoverallsRepoToken := "" 27 | ``` 28 | 29 | More settings can found at @ref:[Coveralls Plugin](settings.md#coveralls) settings. 30 | 31 | ### GitHub Actions + Coveralls 32 | Add `COVERALLS_REPO_TOKEN` to `Secrets` in your GitHub project at `https://github.com///settings` 33 | 34 | Your GitHub Actions workflow yaml (e.g. `.github/workflows/build.yml`) should look like 35 | ```yaml 36 | name: Build 37 | 38 | on: [push] 39 | 40 | jobs: 41 | build_java_project: 42 | runs-on: ubuntu-latest 43 | steps: 44 | - uses: actions/checkout@v1 45 | - name: Build Project 46 | env: 47 | COVERALLS_REPO_TOKEN: ${{ secrets.COVERALLS_REPO_TOKEN }} 48 | run: | 49 | export CI_BRANCH="${GITHUB_REF#refs/heads/}" 50 | sbt clean jacoco jacocoCoveralls 51 | ``` 52 | Add the following `jacocoCoveralls*` settings to `build.sbt` 53 | 54 | ```sbt 55 | lazy val root = (project in file(".")) 56 | .settings( 57 | jacocoCoverallsServiceName := "github-actions", 58 | jacocoCoverallsBranch := sys.env.get("CI_BRANCH"), 59 | jacocoCoverallsPullRequest := sys.env.get("GITHUB_EVENT_NAME"), 60 | jacocoCoverallsRepoToken := sys.env.get("COVERALLS_REPO_TOKEN") 61 | ) 62 | ``` 63 | 64 | ## Codecov 65 | 66 | The [Codecov uploader](https://docs.codecov.com/docs/codecov-uploader) will upload coverage automatically if the XML formatter is enabled. 67 | Note that the legacy [bash based uploader](https://docs.codecov.com/docs/about-the-codecov-bash-uploader) has been deprecated and service will begin to brown out in the latter part of 2021. 68 | 69 | For example: 70 | 71 | ```scala 72 | jacocoReportSettings := JacocoReportSettings( 73 | "Jacoco Coverage Report", 74 | None, 75 | JacocoThresholds(), 76 | Seq(JacocoReportFormats.ScalaHTML, JacocoReportFormats.XML), // note XML formatter 77 | "utf-8") 78 | ``` 79 | 80 | With this enabled run the Codecov script after JaCoCo: 81 | 82 | ```sh 83 | sbt jacoco 84 | ./codecov 85 | ``` 86 | 87 | ## Codacy 88 | 89 | Similar to Codecov, the Codacy reporter script will upload coverage automatically if the XML formatter is enabled. 90 | Check the [documentation](https://support.codacy.com/hc/en-us/articles/207279819-Coverage) or the [GitHub repo](https://github.com/codacy/codacy-coverage-reporter#running-codacy-coverage-reporter) for more information. Example snippets: 91 | 92 | ```scala 93 | jacocoReportSettings := JacocoReportSettings( 94 | "Jacoco Coverage Report", 95 | None, 96 | JacocoThresholds(), 97 | Seq(JacocoReportFormats.ScalaHTML, JacocoReportFormats.XML), // note XML formatter 98 | "utf-8") 99 | ``` 100 | 101 | With this enabled run the Codacy script after JaCoCo: 102 | 103 | ```sh 104 | sbt jacoco 105 | bash <(curl -Ls https://coverage.codacy.com/get.sh) 106 | ``` 107 | -------------------------------------------------------------------------------- /src/main/paradox/getting-started.md: -------------------------------------------------------------------------------- 1 | # Getting Started 2 | 3 | ## Setup 4 | 5 | Install the plugin by adding the following to `project/plugins.sbt`: 6 | 7 | ```scala 8 | addSbtPlugin("com.github.sbt" % "sbt-jacoco" % "") 9 | ``` 10 | 11 | And then execute the plugin with `sbt jacoco`. This will instrument and run the unit tests and output the coverage 12 | metrics: 13 | 14 | ``` 15 | [info] ------- Jacoco Coverage Report -------- 16 | [info] 17 | [info] Lines: 66.67% (>= required 0.0%) covered, 2 of 6 missed, OK 18 | [info] Instructions: 83.54% (>= required 0.0%) covered, 13 of 79 missed, OK 19 | [info] Branches: 0% (>= required 0.0%) covered, 0 of 0 missed, OK 20 | [info] Methods: 57.14% (>= required 0.0%) covered, 3 of 7 missed, OK 21 | [info] Complexity: 57.14% (>= required 0.0%) covered, 3 of 7 missed, OK 22 | [info] Class: 50% (>= required 0.0%) covered, 2 of 4 missed, OK 23 | [info] 24 | [info] Check /home/example/jacoco-test/target/scala-2.11/jacoco/report for detailed report 25 | ``` 26 | 27 | A detailed HTML report will also be generated in the directory shown that includes line level details of coverage. 28 | 29 | ## Setting Minimum Coverage Levels 30 | 31 | Minimum code coverage levels can be defined so that the build will fail if the requirements are not met: 32 | 33 | ```scala 34 | jacocoReportSettings := JacocoReportSettings() 35 | .withThresholds( 36 | JacocoThresholds( 37 | instruction = 80, 38 | method = 100, 39 | branch = 100, 40 | complexity = 100, 41 | line = 90, 42 | clazz = 100) 43 | ) 44 | ``` 45 | 46 | If the coverage does not meet the thresholds defined the build will fail and the coverge message will highlight the 47 | failed thresholds: 48 | 49 | ``` 50 | [info] ------- Jacoco Coverage Report ------- 51 | [info] 52 | [info] Lines: 66.67% (< required 90.0%) covered, 1 of 3 missed, NOK 53 | [info] Instructions: 56.52% (< required 80.0%) covered, 10 of 23 missed, NOK 54 | [info] Branches: 0% (< required 100.0%) covered, 0 of 0 missed, NOK 55 | [info] Methods: 50% (< required 100.0%) covered, 2 of 4 missed, NOK 56 | [info] Complexity: 50% (< required 100.0%) covered, 2 of 4 missed, NOK 57 | [info] Class: 100% (>= required 100.0%) covered, 1 of 2 missed, OK 58 | [info] 59 | [info] Check /home/example/jacoco-test/scala-2.12/jacoco/report for detailed report 60 | [info] 61 | [error] Required coverage is not met 62 | ``` 63 | 64 | @@@ note 65 | By default the minimum coverage levels are set to 0%. 66 | @@@ 67 | -------------------------------------------------------------------------------- /src/main/paradox/index.md: -------------------------------------------------------------------------------- 1 | # sbt-jacoco 2 | 3 | _sbt-jacoco_ is an [sbt](http://scala-sbt.org/) plugin for code coverage analysis via 4 | [JaCoCo](http://www.eclemma.org/jacoco/). 5 | 6 | Key features of _sbt-jacoco_ include: 7 | 8 | * Coverage of Scala and Java code. 9 | * Aggregation of multi-project builds. 10 | * Support for unit and @ref:[integration](integration-tests.md) tests. 11 | * @ref:[Integrates](coverage-services.md) with Coveralls, Codecov and Codacy. 12 | 13 | @@@ index 14 | 15 | * [Getting Started](getting-started.md) 16 | * [Integration Tests](integration-tests.md) 17 | * [Multi-Project Builds](multi-project.md) 18 | * [Coverage Services](coverage-services.md) 19 | * [Settings Reference](settings.md) 20 | * [Contributors](contributors.md) 21 | 22 | @@@ 23 | -------------------------------------------------------------------------------- /src/main/paradox/integration-tests.md: -------------------------------------------------------------------------------- 1 | # Integration Tests 2 | 3 | _sbt-jacoco_ supports coverage of integration tests using an opt-in plugin which can be enabled by adding the following 4 | to your build config: 5 | 6 | ```scala 7 | enablePlugins(JacocoItPlugin) 8 | ``` 9 | 10 | Once this has been added you can cover your integration tests using `it:jacoco`. If you have previously run `jacoco` to 11 | cover your unit tests the two coverage reports will get merged into a single report showing you the full coverage. 12 | 13 | 14 | @@@ warning 15 | The `JacocoItPlugin` adds settings to the `IntegrationTest` configuration which get overwritten if you have the 16 | following in your build: 17 | 18 | ```scala 19 | configs(IntegrationTest) 20 | Defaults.itSettings 21 | ``` 22 | 23 | These get added automatically by the `JacocoItPlugin` in a way that they don't overwrite the extra settings. 24 | @@@ 25 | -------------------------------------------------------------------------------- /src/main/paradox/multi-project.md: -------------------------------------------------------------------------------- 1 | # Multi-Project Builds 2 | 3 | For the aggregated multi-project build shown below: 4 | 5 | ```scala 6 | lazy val common = project 7 | .in(file("common")) 8 | 9 | lazy val extras = project 10 | .in(file("extras")) 11 | 12 | lazy val root = project 13 | .in(file(".")) 14 | .aggregate( 15 | common, 16 | extras 17 | ) 18 | ``` 19 | 20 | Running `sbt jacocoAggregate` will run the unit tests for each sub-project with coverage and then aggregate the 21 | individual reports into a single report in the root project: 22 | 23 | ``` 24 | [info] ------- Jacoco Coverage Report ------- 25 | [info] 26 | [info] Lines: 100% (>= required 0.0%) covered, 0 of 3 missed, OK 27 | [info] Instructions: 100% (>= required 0.0%) covered, 0 of 29 missed, OK 28 | [info] Branches: 0% (>= required 0.0%) covered, 0 of 0 missed, OK 29 | [info] Methods: 100% (>= required 0.0%) covered, 0 of 2 missed, OK 30 | [info] Complexity: 100% (>= required 0.0%) covered, 0 of 2 missed, OK 31 | [info] Class: 100% (>= required 0.0%) covered, 0 of 1 missed, OK 32 | [info] 33 | [info] Check /home/example/jacoco-test/common/target/scala-2.12/jacoco/report for detailed report 34 | ... 35 | [info] ------- Jacoco Coverage Report ------- 36 | [info] 37 | [info] Lines: 100% (>= required 0.0%) covered, 0 of 1 missed, OK 38 | [info] Instructions: 68.75% (>= required 0.0%) covered, 5 of 16 missed, OK 39 | [info] Branches: 0% (>= required 0.0%) covered, 0 of 0 missed, OK 40 | [info] Methods: 50% (>= required 0.0%) covered, 1 of 2 missed, OK 41 | [info] Complexity: 50% (>= required 0.0%) covered, 1 of 2 missed, OK 42 | [info] Class: 50% (>= required 0.0%) covered, 1 of 2 missed, OK 43 | [info] 44 | [info] Check /home/example/jacoco-test/extras/target/scala-2.12/jacoco/report for detailed report 45 | ... 46 | [info] ------- Jacoco Aggregate Coverage Report ------- 47 | [info] 48 | [info] Lines: 100% (>= required 0.0%) covered, 0 of 4 missed, OK 49 | [info] Instructions: 88.89% (>= required 0.0%) covered, 5 of 45 missed, OK 50 | [info] Branches: 0% (>= required 0.0%) covered, 0 of 0 missed, OK 51 | [info] Methods: 75% (>= required 0.0%) covered, 1 of 4 missed, OK 52 | [info] Complexity: 75% (>= required 0.0%) covered, 1 of 4 missed, OK 53 | [info] Class: 66.67% (>= required 0.0%) covered, 1 of 3 missed, OK 54 | [info] 55 | [info] Check /home/example/jacoco-test/target/scala-2.12/jacoco/report/aggregate for detailed report 56 | ``` 57 | 58 | @@@ note 59 | Due to a limitation in the way that the aggregate report is generated, there no line-by-line source reports are 60 | generated in the aggregate coverage report. These reports can be viewed by opening the sub-project reports. 61 | @@@ 62 | 63 | ## Customising the Aggregate Report 64 | 65 | The aggregate report can be customised using the `jacocoAggregateReportSettings` key in the root project: 66 | 67 | ```scala 68 | lazy val root = project 69 | .in(file(".")) 70 | .aggregate( 71 | common, 72 | extras 73 | ) 74 | .settings( 75 | jacocoAggregateReportSettings := JacocoReportSettings( 76 | title = "Foo Project Coverage", 77 | formats = Seq(JacocoReportFormats.ScalaHTML) 78 | ) 79 | ) 80 | ``` 81 | -------------------------------------------------------------------------------- /src/main/paradox/settings.md: -------------------------------------------------------------------------------- 1 | --- 2 | toc.maxDepth: 3 3 | page.subheaders: true 4 | --- 5 | 6 | # Settings Reference 7 | 8 | ## Settings 9 | 10 | ### Common 11 | 12 | These are apply to both unit and integration tests. 13 | 14 | #### jacocoDirectory 15 | 16 | * **Description:** Where JaCoCo should store its execution data and reports. 17 | * **Accepts:** `java.io.File` 18 | * **Default:** `crossTarget / "jacoco"` 19 | 20 | #### jacocoReportDirectory 21 | 22 | * **Description:** Where JaCoCo should output reports to. 23 | * **Accepts:** `java.io.File` 24 | * **Default:** `jacocoDirectory / "report"` 25 | 26 | #### jacocoDataFile 27 | 28 | * **Description:** Execution data output file. 29 | * **Accepts:** `java.io.File` 30 | * **Default:** `jacocoDirectory / "data" / "jacoco.exec"` 31 | 32 | #### jacocoSourceSettings 33 | 34 | * **Description:** Input source code settings (encoding etc) for reporting. 35 | * **Accepts:** [JacocoSourceSettings](#jacocosourcesettings) 36 | * **Default:** 37 | 38 | ```scala 39 | JacocoSourceSettings(2, "utf-8") 40 | ``` 41 | 42 | #### jacocoReportSettings 43 | 44 | * **Description:** Settings for JaCoCo report (format, title etc). 45 | * **Accepts:** [JacocoReportSettings](#jacocoreportsettings) 46 | * **Default:** 47 | 48 | Unit tests: 49 | ```scala 50 | JacocoReportSettings( 51 | "Jacoco Coverage Report", 52 | None, 53 | JacocoThresholds(), 54 | Seq(JacocoReportFormats.ScalaHTML), 55 | "utf-8") 56 | ``` 57 | 58 | Integration tests: 59 | ```scala 60 | JacocoReportSettings( 61 | "Jacoco Integration Test Coverage Report", 62 | None, 63 | JacocoThresholds(), 64 | Seq(JacocoReportFormats.ScalaHTML), 65 | "utf-8") 66 | ``` 67 | 68 | #### jacocoIncludes 69 | 70 | * **Description:** Glob patterns specifying which classes to cover. 71 | * **Accepts:** `Seq[String]` 72 | * **Default:** `**/*` (all classes) 73 | 74 | @@@ note 75 | `jacocoExcludes` overrides `jacocoIncludes`. 76 | @@@ 77 | 78 | #### jacocoExcludes 79 | 80 | * **Description:** Glob patterns specifying which classes not to cover. 81 | * **Accepts:** `Seq[String]` 82 | * **Default:** none 83 | 84 | #### jacocoInstrumentedDirectory 85 | 86 | * **Description:** Directory containing the instrumented classes. 87 | * **Accepts:** `java.io.File` 88 | * **Default:** `jacocoDirectory / "instrumented-classes"` 89 | 90 | ### Multi-Project Tests 91 | 92 | These should be defined in the root project of a multi-project build and control the way that reports for sub-projects 93 | should be aggregated. 94 | 95 | #### jacocoAggregateReportSettings 96 | 97 | * **Description:** Settings for aggregate JaCoCo report (format, title etc). 98 | * **Accepts:** [JacocoReportSettings](#jacocoreportsettings) 99 | * **Default:** 100 | 101 | ```scala 102 | JacocoReportSettings( 103 | "Jacoco Merged Coverage Report", 104 | None, 105 | JacocoThresholds(), 106 | Seq(JacocoReportFormats.ScalaHTML), 107 | "utf-8") 108 | ``` 109 | 110 | ### Integration Tests 111 | 112 | These are only defined for integration tests and configure merging of unit and integration results. 113 | 114 | #### jacocoMergedDataFile 115 | 116 | * **Description:** Execution data file containing combined unit test and integration test data. 117 | * **Accepts:** `java.io.File` 118 | * **Default:** `jacocoDirectory / "jacoco-it.exec"` 119 | 120 | #### jacocoMergedReportSettings 121 | 122 | * **Description:** Settings for merged JaCoCo report (format, title etc). 123 | * **Accepts:** [JacocoReportSettings](#jacocoreportsettings) 124 | * **Default:** 125 | 126 | ```scala 127 | JacocoReportSettings( 128 | "Jacoco Merged Coverage Report", 129 | None, 130 | JacocoThresholds(), 131 | Seq(JacocoReportFormats.ScalaHTML), 132 | "utf-8") 133 | ``` 134 | 135 | #### jacocoAutoMerge 136 | 137 | * **Description:** Whether to merge the unit and integration test reports. 138 | * **Accepts:** `Boolean` 139 | * **Default:** `true` 140 | 141 | ### Coveralls 142 | 143 | These are only defined if the `JacocoCoverallsPlugin` is enabled. 144 | 145 | #### jacocoCoverallsServiceName 146 | 147 | * **Description:** Name of the CI service running this build. 148 | * **Accepts:** `String` 149 | * **Default:** `travis-ci` 150 | 151 | @@@ note 152 | If running on Travis Pro this should be set to `travis-pro`. 153 | @@@ 154 | 155 | #### jacocoCoverallsJobId 156 | 157 | * **Description:** Unique build identifier for this build. If `None`, Coveralls sets its own job ID. 158 | * **Accepts:** `Option[String]` 159 | * **Default:** `TRAVIS_JOB_ID` environment variable 160 | 161 | #### jacocoCoverallsBuildNumber 162 | 163 | * **Description:** Human readable build number 164 | * **Accepts:** `Option[String]` 165 | * **Default:** none (defaults to auto-incremented number) 166 | 167 | #### jacocoCoverallsBranch 168 | * **Description:** The current branch name set by CI. If `None`, it gets the git branch name from project's base directory. 169 | * **Accepts:** `Option[String]` 170 | * **Default:** `TRAVIS_BRANCH` environment variable 171 | 172 | #### jacocoCoverallsPullRequest 173 | 174 | * **Description:** ID of the current pull request that triggered the build. 175 | * **Accepts:** `Option[String]` 176 | * **Default:** none 177 | 178 | #### jacocoCoverallsRepoToken 179 | 180 | * **Description:** Coveralls repo secret key. 181 | * **Accepts:** `Option[String]` 182 | * **Default:** none (auto detected for public repos) 183 | 184 | ## Types 185 | 186 | All types are automatically imported in an `.sbt` based build file and can be imported into `.scala` based builds using: 187 | 188 | ```scala 189 | import com.github.sbt.jacoco.JacocoPlugin.autoImport._ 190 | ``` 191 | 192 | Each type has `.withXXX` methods and default values defined for all parameters giving you a choice of ways to configure: 193 | 194 | ```scala 195 | jacocoReportSettings := JacocoReportSettings(title = "Report Title", formats = Seq(JacocoReportFormats.HTML)) 196 | // or 197 | jacocoReportSettings := JacocoReportSettings() 198 | .withTitle("Report Title") 199 | .withFormats(Seq(JacocoReportFormats.HTML)) 200 | ``` 201 | 202 | ### JacocoSourceSettings 203 | 204 | Properties: 205 | 206 | * `tabWidth`: tab width of source files. 207 | * `fileEncoding`: file encoding of source files. 208 | 209 | ### JacocoReportSettings 210 | 211 | Properties: 212 | 213 | * `title`: title of the report. 214 | * `subDirectory`: sub-directory under `jacocoReportDirectory` to store the report. 215 | * `thresholds`: required coverage levels. 216 | * `formats`: list of report fomats to use. 217 | * `fileEncoding`: file encoding to use for reports. 218 | 219 | ### JacocoReportFormats 220 | 221 | * `ScalaHTML` - Enhanced version of the standard JaCoCo HTML report that supports Scala language constructs. 222 | * `HTML` 223 | * `XML` 224 | * `CSV` 225 | -------------------------------------------------------------------------------- /src/main/resources/epl-v10.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Eclipse Public License - Version 1.0 6 | 23 | 24 | 25 | 26 | 27 | 28 |

Eclipse Public License - v 1.0

29 | 30 |

THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE 31 | PUBLIC LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR 32 | DISTRIBUTION OF THE PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS 33 | AGREEMENT.

34 | 35 |

1. DEFINITIONS

36 | 37 |

"Contribution" means:

38 | 39 |

a) in the case of the initial Contributor, the initial 40 | code and documentation distributed under this Agreement, and

41 |

b) in the case of each subsequent Contributor:

42 |

i) changes to the Program, and

43 |

ii) additions to the Program;

44 |

where such changes and/or additions to the Program 45 | originate from and are distributed by that particular Contributor. A 46 | Contribution 'originates' from a Contributor if it was added to the 47 | Program by such Contributor itself or anyone acting on such 48 | Contributor's behalf. Contributions do not include additions to the 49 | Program which: (i) are separate modules of software distributed in 50 | conjunction with the Program under their own license agreement, and (ii) 51 | are not derivative works of the Program.

52 | 53 |

"Contributor" means any person or entity that distributes 54 | the Program.

55 | 56 |

"Licensed Patents" mean patent claims licensable by a 57 | Contributor which are necessarily infringed by the use or sale of its 58 | Contribution alone or when combined with the Program.

59 | 60 |

"Program" means the Contributions distributed in accordance 61 | with this Agreement.

62 | 63 |

"Recipient" means anyone who receives the Program under 64 | this Agreement, including all Contributors.

65 | 66 |

2. GRANT OF RIGHTS

67 | 68 |

a) Subject to the terms of this Agreement, each 69 | Contributor hereby grants Recipient a non-exclusive, worldwide, 70 | royalty-free copyright license to reproduce, prepare derivative works 71 | of, publicly display, publicly perform, distribute and sublicense the 72 | Contribution of such Contributor, if any, and such derivative works, in 73 | source code and object code form.

74 | 75 |

b) Subject to the terms of this Agreement, each 76 | Contributor hereby grants Recipient a non-exclusive, worldwide, 77 | royalty-free patent license under Licensed Patents to make, use, sell, 78 | offer to sell, import and otherwise transfer the Contribution of such 79 | Contributor, if any, in source code and object code form. This patent 80 | license shall apply to the combination of the Contribution and the 81 | Program if, at the time the Contribution is added by the Contributor, 82 | such addition of the Contribution causes such combination to be covered 83 | by the Licensed Patents. The patent license shall not apply to any other 84 | combinations which include the Contribution. No hardware per se is 85 | licensed hereunder.

86 | 87 |

c) Recipient understands that although each Contributor 88 | grants the licenses to its Contributions set forth herein, no assurances 89 | are provided by any Contributor that the Program does not infringe the 90 | patent or other intellectual property rights of any other entity. Each 91 | Contributor disclaims any liability to Recipient for claims brought by 92 | any other entity based on infringement of intellectual property rights 93 | or otherwise. As a condition to exercising the rights and licenses 94 | granted hereunder, each Recipient hereby assumes sole responsibility to 95 | secure any other intellectual property rights needed, if any. For 96 | example, if a third party patent license is required to allow Recipient 97 | to distribute the Program, it is Recipient's responsibility to acquire 98 | that license before distributing the Program.

99 | 100 |

d) Each Contributor represents that to its knowledge it 101 | has sufficient copyright rights in its Contribution, if any, to grant 102 | the copyright license set forth in this Agreement.

103 | 104 |

3. REQUIREMENTS

105 | 106 |

A Contributor may choose to distribute the Program in object code 107 | form under its own license agreement, provided that:

108 | 109 |

a) it complies with the terms and conditions of this 110 | Agreement; and

111 | 112 |

b) its license agreement:

113 | 114 |

i) effectively disclaims on behalf of all Contributors 115 | all warranties and conditions, express and implied, including warranties 116 | or conditions of title and non-infringement, and implied warranties or 117 | conditions of merchantability and fitness for a particular purpose;

118 | 119 |

ii) effectively excludes on behalf of all Contributors 120 | all liability for damages, including direct, indirect, special, 121 | incidental and consequential damages, such as lost profits;

122 | 123 |

iii) states that any provisions which differ from this 124 | Agreement are offered by that Contributor alone and not by any other 125 | party; and

126 | 127 |

iv) states that source code for the Program is available 128 | from such Contributor, and informs licensees how to obtain it in a 129 | reasonable manner on or through a medium customarily used for software 130 | exchange.

131 | 132 |

When the Program is made available in source code form:

133 | 134 |

a) it must be made available under this Agreement; and

135 | 136 |

b) a copy of this Agreement must be included with each 137 | copy of the Program.

138 | 139 |

Contributors may not remove or alter any copyright notices contained 140 | within the Program.

141 | 142 |

Each Contributor must identify itself as the originator of its 143 | Contribution, if any, in a manner that reasonably allows subsequent 144 | Recipients to identify the originator of the Contribution.

145 | 146 |

4. COMMERCIAL DISTRIBUTION

147 | 148 |

Commercial distributors of software may accept certain 149 | responsibilities with respect to end users, business partners and the 150 | like. While this license is intended to facilitate the commercial use of 151 | the Program, the Contributor who includes the Program in a commercial 152 | product offering should do so in a manner which does not create 153 | potential liability for other Contributors. Therefore, if a Contributor 154 | includes the Program in a commercial product offering, such Contributor 155 | ("Commercial Contributor") hereby agrees to defend and 156 | indemnify every other Contributor ("Indemnified Contributor") 157 | against any losses, damages and costs (collectively "Losses") 158 | arising from claims, lawsuits and other legal actions brought by a third 159 | party against the Indemnified Contributor to the extent caused by the 160 | acts or omissions of such Commercial Contributor in connection with its 161 | distribution of the Program in a commercial product offering. The 162 | obligations in this section do not apply to any claims or Losses 163 | relating to any actual or alleged intellectual property infringement. In 164 | order to qualify, an Indemnified Contributor must: a) promptly notify 165 | the Commercial Contributor in writing of such claim, and b) allow the 166 | Commercial Contributor to control, and cooperate with the Commercial 167 | Contributor in, the defense and any related settlement negotiations. The 168 | Indemnified Contributor may participate in any such claim at its own 169 | expense.

170 | 171 |

For example, a Contributor might include the Program in a commercial 172 | product offering, Product X. That Contributor is then a Commercial 173 | Contributor. If that Commercial Contributor then makes performance 174 | claims, or offers warranties related to Product X, those performance 175 | claims and warranties are such Commercial Contributor's responsibility 176 | alone. Under this section, the Commercial Contributor would have to 177 | defend claims against the other Contributors related to those 178 | performance claims and warranties, and if a court requires any other 179 | Contributor to pay any damages as a result, the Commercial Contributor 180 | must pay those damages.

181 | 182 |

5. NO WARRANTY

183 | 184 |

EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS 185 | PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS 186 | OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, 187 | ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY 188 | OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is solely 189 | responsible for determining the appropriateness of using and 190 | distributing the Program and assumes all risks associated with its 191 | exercise of rights under this Agreement , including but not limited to 192 | the risks and costs of program errors, compliance with applicable laws, 193 | damage to or loss of data, programs or equipment, and unavailability or 194 | interruption of operations.

195 | 196 |

6. DISCLAIMER OF LIABILITY

197 | 198 |

EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT 199 | NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, 200 | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING 201 | WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF 202 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 203 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OR 204 | DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED 205 | HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.

206 | 207 |

7. GENERAL

208 | 209 |

If any provision of this Agreement is invalid or unenforceable under 210 | applicable law, it shall not affect the validity or enforceability of 211 | the remainder of the terms of this Agreement, and without further action 212 | by the parties hereto, such provision shall be reformed to the minimum 213 | extent necessary to make such provision valid and enforceable.

214 | 215 |

If Recipient institutes patent litigation against any entity 216 | (including a cross-claim or counterclaim in a lawsuit) alleging that the 217 | Program itself (excluding combinations of the Program with other 218 | software or hardware) infringes such Recipient's patent(s), then such 219 | Recipient's rights granted under Section 2(b) shall terminate as of the 220 | date such litigation is filed.

221 | 222 |

All Recipient's rights under this Agreement shall terminate if it 223 | fails to comply with any of the material terms or conditions of this 224 | Agreement and does not cure such failure in a reasonable period of time 225 | after becoming aware of such noncompliance. If all Recipient's rights 226 | under this Agreement terminate, Recipient agrees to cease use and 227 | distribution of the Program as soon as reasonably practicable. However, 228 | Recipient's obligations under this Agreement and any licenses granted by 229 | Recipient relating to the Program shall continue and survive.

230 | 231 |

Everyone is permitted to copy and distribute copies of this 232 | Agreement, but in order to avoid inconsistency the Agreement is 233 | copyrighted and may only be modified in the following manner. The 234 | Agreement Steward reserves the right to publish new versions (including 235 | revisions) of this Agreement from time to time. No one other than the 236 | Agreement Steward has the right to modify this Agreement. The Eclipse 237 | Foundation is the initial Agreement Steward. The Eclipse Foundation may 238 | assign the responsibility to serve as the Agreement Steward to a 239 | suitable separate entity. Each new version of the Agreement will be 240 | given a distinguishing version number. The Program (including 241 | Contributions) may always be distributed subject to the version of the 242 | Agreement under which it was received. In addition, after a new version 243 | of the Agreement is published, Contributor may elect to distribute the 244 | Program (including its Contributions) under the new version. Except as 245 | expressly stated in Sections 2(a) and 2(b) above, Recipient receives no 246 | rights or licenses to the intellectual property of any Contributor under 247 | this Agreement, whether expressly, by implication, estoppel or 248 | otherwise. All rights in the Program not expressly granted under this 249 | Agreement are reserved.

250 | 251 |

This Agreement is governed by the laws of the State of New York and 252 | the intellectual property laws of the United States of America. No party 253 | to this Agreement will bring a legal action under this Agreement more 254 | than one year after the cause of action arose. Each party waives its 255 | rights to a jury trial in any resulting litigation.

256 | 257 | 258 | 259 | -------------------------------------------------------------------------------- /src/main/scala/com/github/sbt/jacoco/BaseJacocoPlugin.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of sbt-jacoco. 3 | * 4 | * Copyright (c) Joachim Hofer & contributors 5 | * All rights reserved. 6 | * 7 | * This program and the accompanying materials 8 | * are made available under the terms of the Eclipse Public License v1.0 9 | * which accompanies this distribution, and is available at 10 | * http://www.eclipse.org/legal/epl-v10.html 11 | */ 12 | 13 | package com.github.sbt.jacoco 14 | 15 | import java.io.File 16 | 17 | import com.github.sbt.jacoco.build.BuildInfo 18 | import com.github.sbt.jacoco.data.{ExecutionDataUtils, InstrumentationUtils, ProjectData} 19 | import com.github.sbt.jacoco.report.ReportUtils 20 | import sbt.Keys._ 21 | import sbt.plugins.JvmPlugin 22 | import sbt.{Def, _} 23 | 24 | private[jacoco] abstract class BaseJacocoPlugin extends AutoPlugin with JacocoKeys { 25 | override def requires: Plugins = JvmPlugin 26 | 27 | protected def srcConfig: Configuration 28 | 29 | override def projectSettings: Seq[Setting[_]] = 30 | dependencyValues ++ 31 | unscopedSettingValues ++ 32 | inConfig(srcConfig)(scopedSettingValues ++ taskValues) 33 | 34 | protected def dependencyValues: Seq[Setting[_]] = Seq( 35 | libraryDependencies ++= { 36 | if ((Test / fork).value) { 37 | // config is set to fork - need to add the jacoco agent to the classpath so it can process instrumentation 38 | Seq("org.jacoco" % "org.jacoco.agent" % BuildInfo.jacocoVersion % Test classifier "runtime") 39 | } else { 40 | Nil 41 | } 42 | } 43 | ) 44 | 45 | private def unscopedSettingValues = Seq( 46 | jacocoDirectory := crossTarget.value / "jacoco", 47 | jacocoReportDirectory := jacocoDirectory.value / "report", 48 | jacocoSourceSettings := JacocoSourceSettings(), 49 | jacocoReportSettings := JacocoReportSettings(), 50 | jacocoAggregateReportSettings := JacocoReportSettings(title = "Jacoco Aggregate Coverage Report"), 51 | jacocoIncludes := Seq("*"), 52 | jacocoExcludes := Seq(), 53 | jacocoInstrumentedDirectory := jacocoDirectory.value / "instrumented-classes", 54 | jacocoInstrumentationIncludes := Seq("*"), 55 | jacocoInstrumentationExcludes := Seq(), 56 | jacocoDataFile := jacocoDataDirectory.value / "jacoco.exec" 57 | ) 58 | 59 | private def scopedSettingValues = Seq( 60 | javaOptions ++= { 61 | val dest = jacocoDataFile.value 62 | 63 | if (fork.value) { 64 | Seq( 65 | s"-Djacoco-agent.destfile=${dest.absolutePath}" 66 | ) 67 | } else { 68 | Nil 69 | } 70 | } 71 | ) 72 | 73 | private def taskValues = Seq( 74 | jacoco := (jacocoReport dependsOn jacocoCheck).value, 75 | jacocoAggregate := (jacocoAggregateReport dependsOn submoduleCoverTasks).value, 76 | jacocoCheck := Def 77 | .task( 78 | ExecutionDataUtils 79 | .saveRuntimeData(projectData(thisProject.value), jacocoDataFile.value, fork.value, streams.value) 80 | ) 81 | .dependsOn(test) 82 | .value, 83 | jacocoReport := ReportUtils.generateReport( 84 | jacocoReportDirectory.value, 85 | jacocoDataFile.value, 86 | jacocoReportSettings.value, 87 | coveredSources.value, 88 | classesToCover.value, 89 | jacocoSourceSettings.value, 90 | streams.value 91 | ), 92 | jacocoAggregateReport := ReportUtils.generateAggregateReport( 93 | jacocoReportDirectory.value / "aggregate", 94 | aggregateExecutionDataFiles.value, 95 | jacocoAggregateReportSettings.value, 96 | aggregateCoveredSources.value, 97 | aggregateClassesToCover.value, 98 | jacocoSourceSettings.value, 99 | streams.value 100 | ), 101 | clean := jacocoDirectory.map(dir => if (dir.exists) IO delete dir.listFiles).value, 102 | fullClasspath := InstrumentationUtils.instrumentClasses( 103 | (Compile / products).value, 104 | filterClassesToInstrument( 105 | (Compile / products).value, 106 | (srcConfig / jacocoInstrumentationIncludes).value, 107 | (srcConfig / jacocoInstrumentationExcludes).value 108 | ), 109 | (srcConfig / fullClasspath).value, 110 | jacocoInstrumentedDirectory.value, 111 | update.value, 112 | fork.value, 113 | projectData(thisProject.value), 114 | streams.value 115 | ), 116 | definedTests := (srcConfig / definedTests).value, 117 | definedTestNames := (srcConfig / definedTestNames).value 118 | ) 119 | 120 | private def filterClassesToInstrument(products: Seq[File], incl: Seq[String], excl: Seq[String]) = { 121 | val inclFilters = incl map GlobFilter.apply 122 | val exclFilters = excl map GlobFilter.apply 123 | 124 | products.flatten { product => 125 | (PathFinder(product) ** new FileFilter { 126 | def accept(f: File): Boolean = 127 | IO.relativize(product, f) match { 128 | case Some(file) if !f.isDirectory && file.endsWith(".class") => 129 | val name = toClassName(file) 130 | inclFilters.exists(_ accept name) && !exclFilters.exists(_ accept name) 131 | case _ => false 132 | } 133 | }).get 134 | } 135 | } 136 | 137 | private def filterClassesToCover(classes: File, incl: Seq[String], excl: Seq[String]) = { 138 | val inclFilters = incl map GlobFilter.apply 139 | val exclFilters = excl map GlobFilter.apply 140 | 141 | (PathFinder(classes) ** new FileFilter { 142 | def accept(f: File): Boolean = IO.relativize(classes, f) match { 143 | case Some(file) if !f.isDirectory && file.endsWith(".class") => 144 | val name = toClassName(file) 145 | inclFilters.exists(_ accept name) && !exclFilters.exists(_ accept name) 146 | case _ => false 147 | } 148 | }).get 149 | } 150 | 151 | private def toClassName(entry: String): String = 152 | entry.stripSuffix(".class").replace(File.separatorChar, '.') 153 | 154 | protected lazy val submoduleSettingsTask: Def.Initialize[Task[(Seq[File], Option[File], Option[File])]] = Def.task { 155 | (classesToCover.value, (Compile / sourceDirectory).?.value, (srcConfig / jacocoDataFile).?.value) 156 | } 157 | 158 | protected lazy val submoduleSettings: Def.Initialize[Task[Seq[(Seq[File], Option[File], Option[File])]]] = 159 | submoduleSettingsTask.all(ScopeFilter(inAggregates(ThisProject), inConfigurations(Compile, srcConfig))) 160 | 161 | protected lazy val aggregateCoveredSources: Def.Initialize[Task[Seq[File]]] = Def.task { 162 | submoduleSettings.value.flatMap(_._2).distinct 163 | } 164 | 165 | protected lazy val classesToCover: Def.Initialize[Task[Seq[File]]] = Def.task { 166 | filterClassesToCover( 167 | (Compile / classDirectory).value, 168 | (srcConfig / jacocoIncludes).value, 169 | (srcConfig / jacocoExcludes).value 170 | ) 171 | } 172 | 173 | protected lazy val aggregateClassesToCover: Def.Initialize[Task[Seq[File]]] = Def.task { 174 | submoduleSettings.value.flatMap(_._1).distinct 175 | } 176 | 177 | protected lazy val aggregateExecutionDataFiles: Def.Initialize[Task[Seq[File]]] = Def.task { 178 | submoduleSettings.value.flatMap(_._3).distinct 179 | } 180 | 181 | protected lazy val coveredSources: Def.Initialize[Task[Seq[File]]] = Def.task { 182 | (Compile / sourceDirectories).value 183 | } 184 | 185 | protected lazy val jacocoDataDirectory: Def.Initialize[File] = Def.setting { 186 | jacocoDirectory.value / "data" 187 | } 188 | 189 | protected lazy val submoduleCoverTasks: Def.Initialize[Task[Seq[Unit]]] = { 190 | (srcConfig / jacoco).all(ScopeFilter(inAggregates(ThisProject))) 191 | } 192 | } 193 | -------------------------------------------------------------------------------- /src/main/scala/com/github/sbt/jacoco/JacocoItPlugin.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of sbt-jacoco. 3 | * 4 | * Copyright (c) Joachim Hofer & contributors 5 | * All rights reserved. 6 | * 7 | * This program and the accompanying materials 8 | * are made available under the terms of the Eclipse Public License v1.0 9 | * which accompanies this distribution, and is available at 10 | * http://www.eclipse.org/legal/epl-v10.html 11 | */ 12 | 13 | package com.github.sbt.jacoco 14 | 15 | import com.github.sbt.jacoco.build.BuildInfo 16 | import com.github.sbt.jacoco.data.ExecutionDataUtils 17 | import com.github.sbt.jacoco.report.{JacocoReportSettings, ReportUtils} 18 | import sbt.Keys._ 19 | import sbt.plugins.JvmPlugin 20 | import sbt.{Def, _} 21 | 22 | object JacocoItPlugin extends BaseJacocoPlugin { 23 | 24 | object autoImport { 25 | lazy val jacocoMergeData: TaskKey[Unit] = taskKey[Unit]("Merges all '*.exec' files into a single data file.") 26 | 27 | lazy val jacocoMergedDataFile: SettingKey[File] = 28 | settingKey[File]("Execution data file contain unit test and integration test data.") 29 | 30 | lazy val jacocoMergedReportSettings: SettingKey[JacocoReportSettings] = 31 | settingKey[JacocoReportSettings]("todo") 32 | 33 | lazy val jacocoAutoMerge: SettingKey[Boolean] = 34 | settingKey[Boolean]("Indication whether to merge the unittest and integration test reports. Defaults to true.") 35 | 36 | lazy val jacocoMergedReport: TaskKey[Unit] = 37 | taskKey[Unit]("generates a merged report") 38 | } 39 | 40 | import autoImport._ // scalastyle:ignore import.grouping 41 | 42 | override def requires: Plugins = JvmPlugin && JacocoPlugin 43 | 44 | protected lazy val srcConfig: Configuration = IntegrationTest 45 | 46 | private val autoMerge: Def.Initialize[Task[Unit]] = Def.taskDyn { 47 | if (jacocoAutoMerge.value) { 48 | Def.task { 49 | jacocoMergedReport.value 50 | } 51 | } else { 52 | Def.task {} 53 | } 54 | } 55 | 56 | override protected def dependencyValues: Seq[Setting[_]] = Seq( 57 | libraryDependencies ++= { 58 | if ((Test / fork).value || (IntegrationTest / fork).value) { 59 | // config is set to fork - need to add the jacoco agent to the classpath so it can process instrumentation 60 | Seq("org.jacoco" % "org.jacoco.agent" % BuildInfo.jacocoVersion % "test,it" classifier "runtime") 61 | } else { 62 | Nil 63 | } 64 | } 65 | ) 66 | 67 | override def projectSettings: Seq[Setting[_]] = 68 | Defaults.itSettings ++ 69 | super.projectSettings ++ 70 | // move the test reports to a subdirectory to disambiguate 71 | Seq(jacocoReportDirectory := (Test / jacocoDirectory).value / "report" / "test") ++ 72 | inConfig(IntegrationTest) { 73 | Seq( 74 | jacocoReportDirectory := jacocoDirectory.value / "report" / "it", 75 | jacocoDataFile := jacocoDataDirectory.value / "jacoco-it.exec", 76 | jacocoMergeData := ExecutionDataUtils.mergeExecutionData( 77 | Seq( 78 | (Test / jacocoDataFile).value, 79 | (IntegrationTest / jacocoDataFile).value 80 | ), 81 | (IntegrationTest / jacocoMergedDataFile).value, 82 | streams.value 83 | ), 84 | jacocoAutoMerge := true, 85 | jacocoMergedDataFile := jacocoDataDirectory.value / "jacoco-merged.exec", 86 | jacocoReportSettings := JacocoReportSettings("Jacoco Integration Test Coverage Report"), 87 | jacocoMergedReportSettings := JacocoReportSettings("Jacoco Merged Coverage Report"), 88 | jacocoMergedReport := Def 89 | .task( 90 | ReportUtils.generateReport( 91 | jacocoDirectory.value / "report" / "merged", 92 | jacocoMergedDataFile.value, 93 | jacocoMergedReportSettings.value, 94 | coveredSources.value, 95 | classesToCover.value, 96 | jacocoSourceSettings.value, 97 | streams.value 98 | ) 99 | ) 100 | .dependsOn(jacocoMergeData) 101 | .value, 102 | jacocoReport := Def.sequential(jacocoReport, autoMerge).value 103 | ) 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /src/main/scala/com/github/sbt/jacoco/JacocoKeys.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of sbt-jacoco. 3 | * 4 | * Copyright (c) Joachim Hofer & contributors 5 | * All rights reserved. 6 | * 7 | * This program and the accompanying materials 8 | * are made available under the terms of the Eclipse Public License v1.0 9 | * which accompanies this distribution, and is available at 10 | * http://www.eclipse.org/legal/epl-v10.html 11 | */ 12 | 13 | package com.github.sbt.jacoco 14 | 15 | import com.github.sbt.jacoco.report.{JacocoReportSettings, JacocoSourceSettings} 16 | import sbt._ 17 | 18 | // scalastyle:off line.size.limit 19 | object JacocoKeys extends JacocoKeys 20 | 21 | trait JacocoKeys { 22 | val jacoco: TaskKey[Unit] = taskKey[Unit]("Executes the tests and creates a JaCoCo coverage report.") 23 | 24 | val jacocoCheck: TaskKey[Unit] = taskKey[Unit]("Executes the tests and saves the execution data in 'jacoco.exec'.") 25 | val jacocoReport: TaskKey[Unit] = 26 | taskKey[Unit]("Generates a JaCoCo report. You can use the 'jacoco report' command alternatively.") 27 | 28 | val jacocoAggregate: TaskKey[Unit] = taskKey[Unit]( 29 | "Executes the tests and creates a JaCoCo coverage report as well as an aggregated report which merges all sub-projects." 30 | ) 31 | 32 | val jacocoAggregateReport: TaskKey[Unit] = taskKey[Unit]("Generates an aggregated JaCoCo report.") 33 | 34 | val jacocoDirectory: SettingKey[File] = 35 | settingKey[File]("Where JaCoCo should store its execution data and reports.") 36 | 37 | val jacocoReportDirectory: SettingKey[File] = settingKey[File]("Where JaCoCo should output reports to.") 38 | 39 | val jacocoDataFile: SettingKey[File] = settingKey[File]("Execution data output file.") 40 | 41 | val jacocoSourceSettings: SettingKey[JacocoSourceSettings] = 42 | settingKey[JacocoSourceSettings]("Input source code settings (encoding etc) for reporting.") 43 | 44 | val jacocoReportSettings: SettingKey[JacocoReportSettings] = 45 | settingKey[JacocoReportSettings]("Settings for JaCoCo report (format, title etc)") 46 | val jacocoAggregateReportSettings: SettingKey[JacocoReportSettings] = 47 | settingKey[JacocoReportSettings]("Settings for aggregate JaCoCo report (format, title etc)") 48 | 49 | val jacocoIncludes: SettingKey[Seq[String]] = settingKey[Seq[String]]( 50 | "glob patterns specifying which classes to cover; excludes override includes; default: all classes included" 51 | ) 52 | val jacocoExcludes: SettingKey[Seq[String]] = settingKey[Seq[String]]( 53 | "glob patterns specifying which classes not to cover; excludes override includes; default: no classes excluded" 54 | ) 55 | 56 | val jacocoInstrumentedDirectory: SettingKey[File] = 57 | settingKey[File]("Directory containing the instrumented classes.") 58 | val jacocoInstrumentationIncludes: SettingKey[Seq[String]] = settingKey[Seq[String]]( 59 | "glob patterns specifying which classes to instrument; excludes override includes; default: all classes included" 60 | ) 61 | val jacocoInstrumentationExcludes: SettingKey[Seq[String]] = settingKey[Seq[String]]( 62 | "glob patterns specifying which classes not to instrument; excludes override includes; default: no classes excluded" 63 | ) 64 | 65 | // scalastyle:off field.name 66 | // type aliases for auto import 67 | val JacocoThresholds: report.JacocoThresholds.type = report.JacocoThresholds 68 | val JacocoSourceSettings: report.JacocoSourceSettings.type = report.JacocoSourceSettings 69 | val JacocoReportSettings: report.JacocoReportSettings.type = report.JacocoReportSettings 70 | val JacocoReportFormats: report.JacocoReportFormats.type = report.JacocoReportFormats 71 | // scalastyle:on 72 | } 73 | -------------------------------------------------------------------------------- /src/main/scala/com/github/sbt/jacoco/JacocoPlugin.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of sbt-jacoco. 3 | * 4 | * Copyright (c) Joachim Hofer & contributors 5 | * All rights reserved. 6 | * 7 | * This program and the accompanying materials 8 | * are made available under the terms of the Eclipse Public License v1.0 9 | * which accompanies this distribution, and is available at 10 | * http://www.eclipse.org/legal/epl-v10.html 11 | */ 12 | 13 | package com.github.sbt.jacoco 14 | 15 | import sbt._ 16 | 17 | object JacocoPlugin extends BaseJacocoPlugin { 18 | 19 | object autoImport extends JacocoKeys 20 | 21 | protected lazy val srcConfig: Configuration = Test 22 | 23 | override def trigger: PluginTrigger = allRequirements 24 | } 25 | -------------------------------------------------------------------------------- /src/main/scala/com/github/sbt/jacoco/coveralls/CoverallsClient.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of sbt-jacoco. 3 | * 4 | * Copyright (c) Joachim Hofer & contributors 5 | * All rights reserved. 6 | * 7 | * This program and the accompanying materials 8 | * are made available under the terms of the Eclipse Public License v1.0 9 | * which accompanies this distribution, and is available at 10 | * http://www.eclipse.org/legal/epl-v10.html 11 | */ 12 | 13 | package com.github.sbt.jacoco.coveralls 14 | 15 | import java.io.{File, FileInputStream} 16 | 17 | import sbt.Keys.TaskStreams 18 | 19 | import scalaj.http.{Http, MultiPart} 20 | 21 | object CoverallsClient { 22 | private val jobsUrl = "https://coveralls.io/api/v1/jobs" 23 | 24 | def sendReport(reportFile: File, streams: TaskStreams): Unit = { 25 | streams.log.info("Uploading coverage to coveralls.io...") 26 | val response = Http(jobsUrl) 27 | .postMulti( 28 | MultiPart( 29 | "json_file", 30 | "json_file.json", 31 | "application/json", 32 | new FileInputStream(reportFile), 33 | reportFile.length(), 34 | _ => () 35 | ) 36 | ) 37 | .asString 38 | 39 | if (response.isSuccess) { 40 | streams.log.info("Upload complete") 41 | } else { 42 | streams.log.error(s"Unexpected response from coveralls: ${response.code}") 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/main/scala/com/github/sbt/jacoco/coveralls/CoverallsReportFormat.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of sbt-jacoco. 3 | * 4 | * Copyright (c) Joachim Hofer & contributors 5 | * All rights reserved. 6 | * 7 | * This program and the accompanying materials 8 | * are made available under the terms of the Eclipse Public License v1.0 9 | * which accompanies this distribution, and is available at 10 | * http://www.eclipse.org/legal/epl-v10.html 11 | */ 12 | 13 | package com.github.sbt.jacoco.coveralls 14 | 15 | import java.io.File 16 | 17 | import com.github.sbt.jacoco.report.formats.JacocoReportFormat 18 | import org.jacoco.report.IReportVisitor 19 | import sbt._ 20 | 21 | class CoverallsReportFormat( 22 | sourceDirs: Seq[File], 23 | projectRootDir: File, 24 | serviceName: String, 25 | jobId: Option[String], 26 | buildNumber: Option[String], 27 | ciBranch: Option[String], 28 | pullRequest: Option[String], 29 | repoToken: Option[String] 30 | ) extends JacocoReportFormat { 31 | 32 | override def createVisitor(directory: File, encoding: String): IReportVisitor = { 33 | IO.createDirectory(directory) 34 | 35 | new CoverallsReportVisitor( 36 | directory / "coveralls.json", 37 | sourceDirs, 38 | projectRootDir, 39 | serviceName, 40 | jobId, 41 | buildNumber, 42 | ciBranch, 43 | pullRequest, 44 | repoToken 45 | ) 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/main/scala/com/github/sbt/jacoco/coveralls/CoverallsReportVisitor.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of sbt-jacoco. 3 | * 4 | * Copyright (c) Joachim Hofer & contributors 5 | * All rights reserved. 6 | * 7 | * This program and the accompanying materials 8 | * are made available under the terms of the Eclipse Public License v1.0 9 | * which accompanies this distribution, and is available at 10 | * http://www.eclipse.org/legal/epl-v10.html 11 | */ 12 | 13 | package com.github.sbt.jacoco.coveralls 14 | 15 | import java.io.File 16 | import java.{util => ju} 17 | 18 | import com.fasterxml.jackson.core.{JsonEncoding, JsonFactory} 19 | import org.apache.commons.codec.digest.DigestUtils 20 | import org.jacoco.core.analysis.{IBundleCoverage, ILine, IPackageCoverage, ISourceFileCoverage} 21 | import org.jacoco.core.data.{ExecutionData, SessionInfo} 22 | import org.jacoco.report.{IReportGroupVisitor, IReportVisitor, ISourceFileLocator} 23 | import sbt._ 24 | 25 | import scala.collection.JavaConverters._ 26 | 27 | class CoverallsReportVisitor( 28 | output: File, 29 | sourceDirs: Seq[File], 30 | projectRootDir: File, 31 | serviceName: String, 32 | jobId: Option[String], 33 | buildNumber: Option[String], 34 | ciBranch: Option[String], 35 | pullRequest: Option[String], 36 | repoToken: Option[String] 37 | ) extends IReportVisitor 38 | with IReportGroupVisitor { 39 | 40 | private val digest = new DigestUtils("MD5") 41 | 42 | private val jsonFactory = new JsonFactory() 43 | private val json = jsonFactory.createGenerator(output, JsonEncoding.UTF8) 44 | 45 | writeBasicInfo() 46 | 47 | override def visitInfo(sessionInfos: ju.List[SessionInfo], executionData: ju.Collection[ExecutionData]): Unit = {} 48 | 49 | override def visitGroup(name: String): IReportGroupVisitor = this 50 | 51 | override def visitBundle(bundle: IBundleCoverage, locator: ISourceFileLocator): Unit = { 52 | bundle.getPackages.asScala foreach { pkg: IPackageCoverage => 53 | pkg.getSourceFiles.asScala foreach { source: ISourceFileCoverage => 54 | json.writeStartObject() 55 | 56 | val (filename, md5) = findFile(pkg.getName, source.getName) match { 57 | case Some(file) => 58 | (IO.relativize(projectRootDir, file).getOrElse(file.getName), digest.digestAsHex(file)) 59 | 60 | case None => 61 | (source.getName, "") 62 | } 63 | 64 | json.writeStringField("name", filename) 65 | json.writeStringField("source_digest", md5) 66 | 67 | json.writeArrayFieldStart("coverage") 68 | 69 | (0 to source.getLastLine) foreach { l => 70 | val line: ILine = source.getLine(l) 71 | 72 | if (line.getInstructionCounter.getTotalCount == 0) { 73 | // non-code line 74 | json.writeNull() 75 | } else { 76 | json.writeNumber(line.getInstructionCounter.getCoveredCount) 77 | } 78 | } 79 | 80 | json.writeEndArray() 81 | 82 | json.writeEndObject() 83 | } 84 | } 85 | } 86 | 87 | override def visitEnd(): Unit = { 88 | json.writeEndArray() 89 | json.writeEndObject() 90 | json.close() 91 | } 92 | 93 | private def findFile(packageName: String, fileName: String): Option[File] = { 94 | // TODO make common with source file locator 95 | sourceDirs.map(d => d / packageName / fileName).find(_.exists()) 96 | } 97 | 98 | private def writeBasicInfo(): Unit = { 99 | json.writeStartObject() 100 | 101 | repoToken foreach { token => 102 | json.writeStringField("repo_token", token) 103 | } 104 | 105 | json.writeStringField("service_name", serviceName) 106 | jobId foreach { jId => 107 | json.writeStringField("service_job_id", jId) 108 | } 109 | pullRequest foreach { pr => 110 | json.writeStringField("service_pull_request", pr) 111 | } 112 | buildNumber foreach { build => 113 | json.writeStringField("service_number", build) 114 | } 115 | 116 | writeGitInfo() 117 | 118 | json.writeArrayFieldStart("source_files") 119 | } 120 | 121 | private def writeGitInfo(): Unit = { 122 | GitInfo.extract(projectRootDir, ciBranch) foreach { info => 123 | json.writeObjectFieldStart("git") 124 | 125 | json.writeStringField("branch", info.branch) 126 | 127 | json.writeObjectFieldStart("head") 128 | json.writeStringField("id", info.hash) 129 | json.writeStringField("author_name", info.authorName) 130 | json.writeStringField("author_email", info.authorEmail) 131 | json.writeStringField("committer_name", info.committerName) 132 | json.writeStringField("committer_email", info.committerEmail) 133 | json.writeStringField("message", info.message) 134 | json.writeEndObject() 135 | 136 | json.writeArrayFieldStart("remotes") 137 | info.remotes foreach { remote => 138 | json.writeStartObject() 139 | json.writeStringField("name", remote.name) 140 | json.writeStringField("url", remote.url) 141 | json.writeEndObject() 142 | } 143 | json.writeEndArray() 144 | 145 | json.writeEndObject() 146 | } 147 | } 148 | } 149 | -------------------------------------------------------------------------------- /src/main/scala/com/github/sbt/jacoco/coveralls/GitInfo.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of sbt-jacoco. 3 | * 4 | * Copyright (c) Joachim Hofer & contributors 5 | * All rights reserved. 6 | * 7 | * This program and the accompanying materials 8 | * are made available under the terms of the Eclipse Public License v1.0 9 | * which accompanies this distribution, and is available at 10 | * http://www.eclipse.org/legal/epl-v10.html 11 | */ 12 | 13 | package com.github.sbt.jacoco.coveralls 14 | 15 | import java.io.{File, IOException} 16 | 17 | import org.eclipse.jgit.lib.{Constants, ObjectId} 18 | import org.eclipse.jgit.revwalk.RevWalk 19 | import org.eclipse.jgit.storage.file.FileRepositoryBuilder 20 | 21 | import scala.collection.JavaConverters._ 22 | 23 | case class GitInfo( 24 | hash: String, 25 | authorName: String, 26 | authorEmail: String, 27 | committerName: String, 28 | committerEmail: String, 29 | message: String, 30 | branch: String, 31 | remotes: Seq[GitRemote] 32 | ) 33 | 34 | case class GitRemote(name: String, url: String) 35 | 36 | object GitInfo { 37 | def extract(basedir: File, ciBranch: Option[String]): Option[GitInfo] = { 38 | try { 39 | val gitDir = new File(basedir, ".git") 40 | 41 | if (gitDir.isDirectory) { 42 | val repo = FileRepositoryBuilder.create(gitDir) 43 | val config = repo.getConfig 44 | val head = repo.findRef(Constants.HEAD) 45 | val walk = new RevWalk(repo) 46 | val commit = walk.parseCommit(head.getObjectId) 47 | 48 | val remotes = config.getSubsections("remote").asScala map { name => 49 | GitRemote(name, config.getString("remote", name, "url")) 50 | } 51 | 52 | val info = GitInfo( 53 | ObjectId.toString(head.getObjectId), 54 | commit.getAuthorIdent.getName, 55 | commit.getAuthorIdent.getEmailAddress, 56 | commit.getCommitterIdent.getName, 57 | commit.getCommitterIdent.getEmailAddress, 58 | commit.getShortMessage, 59 | ciBranch.getOrElse(repo.getBranch), 60 | remotes.toSeq 61 | ) 62 | 63 | Some(info) 64 | } else { 65 | None 66 | } 67 | } catch { 68 | case _: IOException => 69 | // don't have a git repo 70 | None 71 | } 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/main/scala/com/github/sbt/jacoco/coveralls/JacocoCoverallsPlugin.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of sbt-jacoco. 3 | * 4 | * Copyright (c) Joachim Hofer & contributors 5 | * All rights reserved. 6 | * 7 | * This program and the accompanying materials 8 | * are made available under the terms of the Eclipse Public License v1.0 9 | * which accompanies this distribution, and is available at 10 | * http://www.eclipse.org/legal/epl-v10.html 11 | */ 12 | 13 | package com.github.sbt.jacoco.coveralls 14 | 15 | import java.io.File 16 | 17 | import com.github.sbt.jacoco.{JacocoPlugin, _} 18 | import com.github.sbt.jacoco.report.ReportUtils 19 | import sbt.Keys._ 20 | import sbt._ 21 | 22 | object JacocoCoverallsPlugin extends BaseJacocoPlugin { 23 | override def requires: Plugins = JacocoPlugin 24 | override def trigger: PluginTrigger = noTrigger 25 | 26 | override protected def srcConfig = Test 27 | 28 | object autoImport { 29 | val jacocoCoveralls: TaskKey[Unit] = taskKey("Generate and upload JaCoCo reports to Coveralls") 30 | val jacocoCoverallsGenerateReport: TaskKey[Unit] = taskKey("Generate Coveralls report JSON") 31 | 32 | val jacocoCoverallsServiceName: SettingKey[String] = settingKey("CI service name") 33 | val jacocoCoverallsBuildNumber: SettingKey[Option[String]] = settingKey("Build number to send to Coveralls") 34 | val jacocoCoverallsJobId: SettingKey[Option[String]] = settingKey("Build job ID to send to Coveralls") 35 | val jacocoCoverallsBranch: SettingKey[Option[String]] = settingKey( 36 | "The current branch name to send to Coveralls. If not provided, it gets the branch from the project base dir. (default: The value of environment variable named \"TRAVIS_BRANCH\")" 37 | ) 38 | val jacocoCoverallsPullRequest: SettingKey[Option[String]] = settingKey("Pull request number to send to Coveralls") 39 | val jacocoCoverallsRepoToken: SettingKey[Option[String]] = settingKey("Coveralls repo secret key") 40 | } 41 | 42 | import autoImport._ // scalastyle:ignore import.grouping 43 | 44 | override def projectSettings: Seq[Setting[_]] = Seq( 45 | jacocoCoveralls := Def.task { 46 | CoverallsClient.sendReport(jacocoReportDirectory.value / "coveralls.json", streams.value) 47 | }.value, 48 | jacocoCoverallsGenerateReport := Def.taskDyn { 49 | Def.task { 50 | val coverallsFormat = 51 | new CoverallsReportFormat( 52 | coveredSources.value, 53 | baseDirectory.value, 54 | jacocoCoverallsServiceName.value, 55 | jacocoCoverallsJobId.value, 56 | jacocoCoverallsBuildNumber.value, 57 | jacocoCoverallsBranch.value, 58 | jacocoCoverallsPullRequest.value, 59 | jacocoCoverallsRepoToken.value 60 | ) 61 | 62 | ReportUtils.generateReport( 63 | jacocoReportDirectory.value, 64 | jacocoDataFile.value, 65 | jacocoReportSettings.value.withFormats(coverallsFormat), 66 | coveredSources.value, 67 | classesToCover.value, 68 | jacocoSourceSettings.value, 69 | streams.value, 70 | checkCoverage = false 71 | ) 72 | } 73 | }.value, 74 | jacocoCoveralls := (jacocoCoveralls dependsOn jacocoCoverallsGenerateReport).value, 75 | jacocoCoverallsServiceName := "travis-ci", 76 | jacocoCoverallsJobId := sys.env.get("TRAVIS_JOB_ID"), 77 | jacocoCoverallsBuildNumber := sys.env.get("TRAVIS_JOB_NUMBER"), 78 | jacocoCoverallsBranch := sys.env.get("TRAVIS_BRANCH"), 79 | jacocoCoverallsPullRequest := { 80 | sys.env.get("TRAVIS_PULL_REQUEST") match { 81 | case Some("false") => None 82 | case Some(pr) => Some(pr) 83 | case _ => None 84 | } 85 | }, 86 | jacocoCoverallsRepoToken := None 87 | ) 88 | } 89 | -------------------------------------------------------------------------------- /src/main/scala/com/github/sbt/jacoco/data/ExecutionDataUtils.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of sbt-jacoco. 3 | * 4 | * Copyright (c) Joachim Hofer & contributors 5 | * All rights reserved. 6 | * 7 | * This program and the accompanying materials 8 | * are made available under the terms of the Eclipse Public License v1.0 9 | * which accompanies this distribution, and is available at 10 | * http://www.eclipse.org/legal/epl-v10.html 11 | */ 12 | 13 | package com.github.sbt.jacoco.data 14 | 15 | import java.io.FileOutputStream 16 | 17 | import org.jacoco.core.data.ExecutionDataWriter 18 | import org.jacoco.core.tools.ExecFileLoader 19 | import resource._ 20 | import sbt.Keys.TaskStreams 21 | import sbt._ 22 | 23 | object ExecutionDataUtils { 24 | def saveRuntimeData(data: ProjectData, destination: File, forked: Boolean, streams: TaskStreams): Unit = { 25 | if (!forked) { 26 | streams.log.debug(s"writing execution data to $destination") 27 | IO.createDirectory(destination.getParentFile) 28 | 29 | for { 30 | os <- managed(new FileOutputStream(destination)) 31 | } { 32 | val executionDataWriter = new ExecutionDataWriter(os) 33 | data.data.collect(executionDataWriter, executionDataWriter, true) 34 | } 35 | } 36 | } 37 | 38 | def mergeExecutionData(sources: Seq[File], destination: File, streams: TaskStreams): Unit = { 39 | val files = sources.filter(_.exists()) 40 | streams.log.debug(s"Found data files: ${files.map(_.absolutePath).mkString(", ")}") 41 | 42 | val loader = new ExecFileLoader() 43 | files.foreach(loader.load) 44 | 45 | streams.log.debug(s"Writing merged data to: ${destination.getAbsolutePath}") 46 | 47 | IO.createDirectory(destination.getParentFile) 48 | 49 | for { 50 | os <- managed(new FileOutputStream(destination)) 51 | } { 52 | val dataWriter = new ExecutionDataWriter(os) 53 | loader.getSessionInfoStore.accept(dataWriter) 54 | loader.getExecutionDataStore.accept(dataWriter) 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/main/scala/com/github/sbt/jacoco/data/InstrumentationUtils.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of sbt-jacoco. 3 | * 4 | * Copyright (c) Joachim Hofer & contributors 5 | * All rights reserved. 6 | * 7 | * This program and the accompanying materials 8 | * are made available under the terms of the Eclipse Public License v1.0 9 | * which accompanies this distribution, and is available at 10 | * http://www.eclipse.org/legal/epl-v10.html 11 | */ 12 | 13 | package com.github.sbt.jacoco.data 14 | 15 | import java.io.FileInputStream 16 | 17 | import org.jacoco.core.instr.Instrumenter 18 | import org.jacoco.core.runtime.OfflineInstrumentationAccessGenerator 19 | import resource._ 20 | import sbt.Keys._ 21 | import sbt._ 22 | 23 | object InstrumentationUtils { 24 | 25 | def instrumentClasses( 26 | products: Seq[File], 27 | classFiles: Seq[File], 28 | classpath: Classpath, 29 | destDirectory: File, 30 | updateReport: UpdateReport, 31 | forked: Boolean, 32 | projectData: ProjectData, 33 | streams: TaskStreams 34 | ): Seq[Attributed[File]] = { 35 | 36 | streams.log.info(s"Instrumenting ${classFiles.size} classes to $destDirectory") 37 | streams.log.debug(s"instrumenting products: ${products.mkString(",")}") 38 | 39 | val (jacocoAgent, instrumenter) = if (forked) { 40 | val agentJars = updateReport.select(module = moduleFilter(name = "org.jacoco.agent")) 41 | 42 | val tmp = IO.temporaryDirectory 43 | val jacocoAgent = agentJars 44 | .flatMap(IO.unzip(_, tmp, (_: String).endsWith(".jar"))) 45 | .map(Attributed.blank) 46 | 47 | streams.log.debug(s"Found jacoco agent: $jacocoAgent") 48 | (jacocoAgent, new Instrumenter(new OfflineInstrumentationAccessGenerator)) 49 | } else { 50 | projectData.runtime.shutdown() 51 | projectData.runtime.startup(projectData.data) 52 | (Nil, new Instrumenter(projectData.runtime)) 53 | } 54 | 55 | val rebaseClassFiles = Path.rebase(products, destDirectory) 56 | 57 | for { 58 | classFile <- classFiles 59 | classStream <- managed(new FileInputStream(classFile)) 60 | } { 61 | streams.log.debug(s"instrumenting $classFile") 62 | val instrumentedClass = instrumenter.instrument(classStream, classFile.name) 63 | IO.write(rebaseClassFiles(classFile).get, instrumentedClass) 64 | } 65 | 66 | jacocoAgent ++ (Attributed.blank(destDirectory) +: classpath) 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/main/scala/com/github/sbt/jacoco/data/ProjectData.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of sbt-jacoco. 3 | * 4 | * Copyright (c) Joachim Hofer & contributors 5 | * All rights reserved. 6 | * 7 | * This program and the accompanying materials 8 | * are made available under the terms of the Eclipse Public License v1.0 9 | * which accompanies this distribution, and is available at 10 | * http://www.eclipse.org/legal/epl-v10.html 11 | */ 12 | 13 | package com.github.sbt.jacoco.data 14 | 15 | import org.jacoco.core.runtime.{IRuntime, LoggerRuntime, RuntimeData} 16 | 17 | import scala.collection.concurrent.TrieMap 18 | 19 | object ProjectData { 20 | private val data: TrieMap[String, ProjectData] = TrieMap() 21 | 22 | def apply(projectId: String): ProjectData = { 23 | data.getOrElseUpdate(projectId, ProjectData(new RuntimeData(), new LoggerRuntime())) 24 | } 25 | } 26 | 27 | case class ProjectData(data: RuntimeData, runtime: IRuntime) 28 | -------------------------------------------------------------------------------- /src/main/scala/com/github/sbt/jacoco/filter/AccessorDetector.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of sbt-jacoco. 3 | * 4 | * Copyright (c) Joachim Hofer & contributors 5 | * All rights reserved. 6 | * 7 | * This program and the accompanying materials 8 | * are made available under the terms of the Eclipse Public License v1.0 9 | * which accompanies this distribution, and is available at 10 | * http://www.eclipse.org/legal/epl-v10.html 11 | */ 12 | 13 | package com.github.sbt.jacoco.filter 14 | 15 | import org.objectweb.asm.Opcodes._ 16 | import org.objectweb.asm.tree._ 17 | 18 | import scala.collection.JavaConverters._ 19 | 20 | /** Detects accessor methods that do nothing other than load and return a field */ 21 | object AccessorDetector { 22 | def isAccessor(node: MethodNode): Boolean = { 23 | (node.instructions.size() < 10) && { 24 | val insn = node.instructions.iterator().asScala.toList 25 | 26 | val filtered = insn.filter { 27 | case _: LabelNode | _: LineNumberNode => false 28 | case _ => true 29 | } 30 | filtered.map(_.getOpcode) match { 31 | case ALOAD :: GETFIELD :: (IRETURN | LRETURN | FRETURN | DRETURN | ARETURN | RETURN) :: Nil => true 32 | case _ => false 33 | } 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/main/scala/com/github/sbt/jacoco/filter/FilteringClassAnalyzer.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of sbt-jacoco. 3 | * 4 | * Copyright (c) Joachim Hofer & contributors 5 | * All rights reserved. 6 | * 7 | * This program and the accompanying materials 8 | * are made available under the terms of the Eclipse Public License v1.0 9 | * which accompanies this distribution, and is available at 10 | * http://www.eclipse.org/legal/epl-v10.html 11 | */ 12 | 13 | package com.github.sbt.jacoco.filter 14 | 15 | import com.github.sbt.jacoco.filter.AccessorDetector._ 16 | import com.github.sbt.jacoco.filter.ScalaForwarderDetector._ 17 | import com.github.sbt.jacoco.filter.ScalaSyntheticMethod._ 18 | import org.jacoco.core.analysis.{Analyzer, ICoverageVisitor, IMethodCoverage} 19 | import org.jacoco.core.data.ExecutionDataStore 20 | import org.jacoco.core.internal.analysis.{ClassAnalyzer, ClassCoverageImpl, MethodAnalyzer, StringPool} 21 | import org.jacoco.core.internal.data.CRC64 22 | import org.jacoco.core.internal.flow.{ClassProbesAdapter, MethodProbesVisitor} 23 | import org.jacoco.core.internal.instr.InstrSupport 24 | import org.objectweb.asm._ 25 | import org.objectweb.asm.tree.{ClassNode, MethodNode} 26 | 27 | import scala.collection.JavaConverters._ 28 | import scala.collection.mutable 29 | 30 | /** 31 | * Filters coverage results from Scala synthetic methods: 32 | * - trait forwarders 33 | * - case class toString / equals / apply / unapply 34 | * 35 | * These are identified by the heuristic that they have the same line number as a constructor, or the same line as other 36 | * one-line methods if we are in a module class. 37 | * 38 | * This filtering should really happen in Jacoco core, but the API for this is not available and scheduled for Q1 2014. 39 | * 40 | * See [[https://github.com/jacoco/jacoco/wiki/FilteringOptions]] and [[https://github.com/jacoco/jacoco/issues/139]] 41 | * for more discussion of the JaCoCo roadmap. 42 | * 43 | * These filters are based on [[https://github.com/timezra/jacoco/commit/b6146ebed8b8e7507ec634ee565fe03f3e940fdd]], but 44 | * extended to correctly exclude synthetics in module classes. 45 | */ 46 | private final class FilteringClassAnalyzer( 47 | classCoverage: ClassCoverageImpl, 48 | classNode: ClassNode, 49 | probes: Array[Boolean], 50 | stringPool: StringPool, 51 | coverageVisitor: ICoverageVisitor 52 | ) extends ClassAnalyzer(classCoverage, probes, stringPool) { 53 | 54 | private val coverages = mutable.Buffer[IMethodCoverage]() 55 | 56 | override def visitMethod( 57 | access: Int, 58 | name: String, 59 | desc: String, 60 | signature: String, 61 | exceptions: Array[String] 62 | ): MethodProbesVisitor = { 63 | InstrSupport.assertNotInstrumented(name, classCoverage.getName) 64 | if ((access & Opcodes.ACC_SYNTHETIC) != 0) { 65 | null // scalastyle:ignore null 66 | } else { 67 | super.visitMethod(access, name, desc, signature, exceptions) 68 | } 69 | } 70 | 71 | override def visitEnd(): Unit = { 72 | try visitFiltered() 73 | finally { 74 | super.visitEnd() 75 | coverageVisitor.visitCoverage(classCoverage) 76 | } 77 | } 78 | 79 | private val isModuleClass = classCoverage.getName.endsWith("$") 80 | 81 | private val methods: Seq[MethodNode] = classNode.methods.asScala 82 | 83 | private def visitFiltered(): Unit = { 84 | for { 85 | mc <- coverages 86 | methodNode = methods.find(m => m.name == mc.getName && m.desc == mc.getDesc).get 87 | if !ignore(mc, methodNode) 88 | } classCoverage.addMethod(mc) 89 | } 90 | 91 | private def ignore(mc: IMethodCoverage, node: MethodNode): Boolean = { 92 | 93 | def isModuleStaticInit = isModuleClass && node.name == "" 94 | 95 | ( 96 | // equals/hashCode/unapply et al 97 | isSyntheticMethod(classCoverage.getName, node.name, mc.getFirstLine, mc.getLastLine) 98 | // static init, `otherwise `case class Foo` reports uncovered code if `object Foo` is not accessed 99 | || isModuleStaticInit 100 | || isScalaForwarder(classCoverage.getName, node) 101 | || isAccessor(node) 102 | ) 103 | } 104 | } 105 | 106 | final class FilteringAnalyzer(executionData: ExecutionDataStore, coverageVisitor: ICoverageVisitor) 107 | extends Analyzer(executionData, coverageVisitor) { 108 | 109 | private def analyzerError(location: String, cause: Exception): java.io.IOException = { 110 | val ex = new java.io.IOException(String.format("Error while analyzing %s.", location)) 111 | ex.initCause(cause) 112 | ex 113 | } 114 | 115 | override def analyzeClass(buffer: Array[Byte], location: String): Unit = { 116 | try { 117 | val reader = InstrSupport.classReaderFor(buffer) 118 | if ((reader.getAccess() & Opcodes.ACC_MODULE) != 0) { 119 | return 120 | } 121 | if ((reader.getAccess() & Opcodes.ACC_SYNTHETIC) != 0) { 122 | return 123 | } 124 | val classNode = new ClassNode() 125 | reader.accept(classNode, 0) 126 | val visitor = createFilteringVisitor(CRC64.classId(buffer), reader.getClassName, classNode) 127 | reader.accept(visitor, 0) 128 | } catch { 129 | case cause: RuntimeException => throw analyzerError(location, cause) 130 | } 131 | } 132 | 133 | // override def analyzeClass(reader: ClassReader): Unit = { 134 | // val classNode = new ClassNode() 135 | // reader.accept(classNode, 0) 136 | // val visitor = createFilteringVisitor(CRC64.classId(reader.b), reader.getClassName, classNode) 137 | // reader.accept(visitor, 0) 138 | // } 139 | 140 | private def createFilteringVisitor(classid: Long, className: String, classNode: ClassNode): ClassVisitor = { 141 | val data = Option(executionData.get(classid)) 142 | val noMatch = data.isEmpty || executionData.contains(className) 143 | val probes = data.map(_.getProbes).orNull 144 | val classCoverageAnalyzer = new ClassCoverageImpl(className, classid, noMatch) 145 | val analyzer = 146 | new FilteringClassAnalyzer(classCoverageAnalyzer, classNode, probes, new StringPool, coverageVisitor) 147 | new ClassProbesAdapter(analyzer, false) 148 | } 149 | } 150 | -------------------------------------------------------------------------------- /src/main/scala/com/github/sbt/jacoco/filter/ScalaForwarderDetector.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of sbt-jacoco. 3 | * 4 | * Copyright (c) Joachim Hofer & contributors 5 | * All rights reserved. 6 | * 7 | * This program and the accompanying materials 8 | * are made available under the terms of the Eclipse Public License v1.0 9 | * which accompanies this distribution, and is available at 10 | * http://www.eclipse.org/legal/epl-v10.html 11 | */ 12 | 13 | package com.github.sbt.jacoco.filter 14 | 15 | import org.objectweb.asm.Opcodes._ 16 | import org.objectweb.asm.tree.{JumpInsnNode, MethodInsnNode, MethodNode} 17 | 18 | import scala.collection.JavaConverters._ 19 | 20 | /** 21 | * Detects forwarder methods added by Scala 22 | * - classes and objects that mix in traits have a forwarder to the method body in the trait implementation class 23 | * - classes contains static forwarders to methods in the companion object (for convenient Java interop) 24 | * - methods in (boxed) value classes forward to the method body in the companion object 25 | * - implicit classes creates a factory method beside the class. 26 | * - lazy vals have an accessor that forwards to `\$lzycompute`, which is the method with the interesting code. 27 | */ 28 | object ScalaForwarderDetector { 29 | val LazyComputeSuffix: String = "$lzycompute" 30 | def isScalaForwarder(className: String, node: MethodNode): Boolean = { 31 | if (node.instructions.size() <= 100) { 32 | val insn = node.instructions.iterator().asScala.toList 33 | val hasJump = insn.exists { 34 | case _: JumpInsnNode => true 35 | case _ => false 36 | } 37 | val hasForwarderCall = insn.exists { 38 | case minsn: MethodInsnNode => 39 | isScalaForwarder(className, node.name, minsn.getOpcode, minsn.owner, minsn.name, minsn.desc, hasJump) 40 | case _ => false 41 | } 42 | hasForwarderCall 43 | } else { 44 | false 45 | } 46 | } 47 | 48 | def isScalaForwarder( 49 | className: String, 50 | methodName: String, 51 | opcode: Int, 52 | calledMethodOwner: String, 53 | calledMethodName: String, 54 | desc: String, 55 | hasJump: Boolean 56 | ): Boolean = { 57 | val callingCompanionModule = calledMethodOwner == (className + "$") 58 | val callingImplClass = calledMethodOwner.endsWith("$class") 59 | val callingImplicitClass = calledMethodOwner.endsWith("$" + methodName) || calledMethodOwner == methodName 60 | val extensionName = methodName + "$extension" 61 | 62 | val staticForwarder = opcode == INVOKEVIRTUAL && callingCompanionModule && calledMethodName == methodName 63 | val traitForwarder = opcode == INVOKESTATIC && callingImplClass && calledMethodName == methodName 64 | val extensionMethodForwarder = opcode == INVOKEVIRTUAL && 65 | callingCompanionModule && 66 | calledMethodName == extensionName 67 | val implicitClassFactory = opcode == INVOKESPECIAL && callingImplicitClass && calledMethodName == "" 68 | val lazyAccessor = opcode == INVOKESPECIAL && calledMethodName.endsWith(LazyComputeSuffix) 69 | val forwards = ( 70 | (staticForwarder || traitForwarder || extensionMethodForwarder || implicitClassFactory) && 71 | !hasJump // second condition a sanity check 72 | || lazyAccessor 73 | ) 74 | forwards 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/main/scala/com/github/sbt/jacoco/filter/ScalaSyntheticMethod.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of sbt-jacoco. 3 | * 4 | * Copyright (c) Joachim Hofer & contributors 5 | * All rights reserved. 6 | * 7 | * This program and the accompanying materials 8 | * are made available under the terms of the Eclipse Public License v1.0 9 | * which accompanies this distribution, and is available at 10 | * http://www.eclipse.org/legal/epl-v10.html 11 | */ 12 | 13 | package com.github.sbt.jacoco.filter 14 | 15 | object ScalaSyntheticMethod { 16 | def isSyntheticMethod(owner: String, name: String, firstLine: Int, lastLine: Int): Boolean = { 17 | val isModuleClass = owner.endsWith("$") 18 | val isOneLiner = firstLine == lastLine 19 | isOneLiner && ( 20 | (isModuleClass && isSyntheticObjectMethodName(name)) 21 | || isSyntheticInstanceMethodName(name) 22 | ) 23 | } 24 | 25 | private def isSyntheticInstanceMethodName(name: String): Boolean = isCaseInstanceMethod(name) 26 | private def isSyntheticObjectMethodName(name: String): Boolean = 27 | isCaseCompanionMethod(name) || isAnyValCompanionMethod(name) 28 | 29 | private def isCaseInstanceMethod(name: String) = name match { 30 | case "canEqual" | "copy" | "equals" | "hashCode" | "productPrefix" | "productArity" | "productElement" | 31 | "productIterator" | "toString" => 32 | true 33 | case _ if name.startsWith("copy$default") => true 34 | case _ => false 35 | } 36 | private def isCaseCompanionMethod(name: String) = name match { 37 | case "apply" | "unapply" | "unapplySeq" | "readResolve" => true 38 | case _ => false 39 | } 40 | private def isAnyValCompanionMethod(name: String) = name match { 41 | case "equals$extension" | "hashCode$extension" => true 42 | case _ => false 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/main/scala/com/github/sbt/jacoco/package.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of sbt-jacoco. 3 | * 4 | * Copyright (c) Joachim Hofer & contributors 5 | * All rights reserved. 6 | * 7 | * This program and the accompanying materials 8 | * are made available under the terms of the Eclipse Public License v1.0 9 | * which accompanies this distribution, and is available at 10 | * http://www.eclipse.org/legal/epl-v10.html 11 | */ 12 | 13 | package com.github.sbt 14 | 15 | import com.github.sbt.jacoco.data.ProjectData 16 | import sbt.ResolvedProject 17 | 18 | package object jacoco { 19 | private[jacoco] def projectData(project: ResolvedProject): ProjectData = { 20 | ProjectData(project.id) 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/main/scala/com/github/sbt/jacoco/report/DirectoriesSourceFileLocator.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of sbt-jacoco. 3 | * 4 | * Copyright (c) Joachim Hofer & contributors 5 | * All rights reserved. 6 | * 7 | * This program and the accompanying materials 8 | * are made available under the terms of the Eclipse Public License v1.0 9 | * which accompanies this distribution, and is available at 10 | * http://www.eclipse.org/legal/epl-v10.html 11 | */ 12 | 13 | package com.github.sbt.jacoco.report 14 | 15 | import java.io.{File, Reader} 16 | 17 | import org.jacoco.report.{DirectorySourceFileLocator, ISourceFileLocator} 18 | 19 | class DirectoriesSourceFileLocator(directories: Seq[File], sourceSettings: JacocoSourceSettings) 20 | extends ISourceFileLocator { 21 | 22 | override def getSourceFile(packageName: String, fileName: String): Reader = { 23 | def findInDirectory(dir: File) = Option(dirSourceLocator(dir).getSourceFile(packageName, fileName)) 24 | def dirSourceLocator(dir: File) = 25 | new DirectorySourceFileLocator(dir, sourceSettings.fileEncoding, sourceSettings.tabWidth) 26 | 27 | (directories flatMap findInDirectory).headOption.orNull 28 | } 29 | 30 | override def getTabWidth: Int = sourceSettings.tabWidth 31 | } 32 | -------------------------------------------------------------------------------- /src/main/scala/com/github/sbt/jacoco/report/JacocoReportFormats.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of sbt-jacoco. 3 | * 4 | * Copyright (c) Joachim Hofer & contributors 5 | * All rights reserved. 6 | * 7 | * This program and the accompanying materials 8 | * are made available under the terms of the Eclipse Public License v1.0 9 | * which accompanies this distribution, and is available at 10 | * http://www.eclipse.org/legal/epl-v10.html 11 | */ 12 | 13 | package com.github.sbt.jacoco.report 14 | 15 | import com.github.sbt.jacoco.report.formats.{CSVReportFormat, HTMLReportFormat, ScalaHTMLReportFormat, XMLReportFormat} 16 | 17 | object JacocoReportFormats { 18 | 19 | /** HTML report containing detailed metrics at instruction level and embedded source code. */ 20 | val ScalaHTML = new ScalaHTMLReportFormat() 21 | 22 | /** 23 | * HTML report containing detailed metrics at instruction level and embedded source code. 24 | * 25 | * '''Note:''' does not support Scala language constructs. 26 | */ 27 | val HTML = new HTMLReportFormat() 28 | 29 | /** 30 | * XML report containing detailed metrics at instruction level. 31 | * 32 | * '''Note:''' does not support Scala language constructs. 33 | */ 34 | val XML = new XMLReportFormat() 35 | 36 | /** 37 | * CSV report containing metrics at class level only. 38 | * 39 | * '''Note:''' does not support Scala language constructs. 40 | */ 41 | val CSV = new CSVReportFormat() 42 | } 43 | -------------------------------------------------------------------------------- /src/main/scala/com/github/sbt/jacoco/report/JacocoReportSettings.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of sbt-jacoco. 3 | * 4 | * Copyright (c) Joachim Hofer & contributors 5 | * All rights reserved. 6 | * 7 | * This program and the accompanying materials 8 | * are made available under the terms of the Eclipse Public License v1.0 9 | * which accompanies this distribution, and is available at 10 | * http://www.eclipse.org/legal/epl-v10.html 11 | */ 12 | 13 | package com.github.sbt.jacoco.report 14 | 15 | import com.github.sbt.jacoco.report.formats.JacocoReportFormat 16 | 17 | case class JacocoReportSettings( 18 | title: String = "Jacoco Coverage Report", 19 | subDirectory: Option[String] = None, 20 | thresholds: JacocoThresholds = JacocoThresholds(), 21 | formats: Seq[JacocoReportFormat] = Seq(JacocoReportFormats.ScalaHTML), 22 | fileEncoding: String = "utf-8" 23 | ) { 24 | 25 | def withTitle(title: String): JacocoReportSettings = { 26 | copy(title = title) 27 | } 28 | 29 | def withSubDirectory(subDirectory: String): JacocoReportSettings = { 30 | copy(subDirectory = Some(subDirectory)) 31 | } 32 | 33 | def withThresholds(thresholds: JacocoThresholds): JacocoReportSettings = { 34 | copy(thresholds = thresholds) 35 | } 36 | 37 | def withFormats(formats: JacocoReportFormat*): JacocoReportSettings = { 38 | copy(formats = formats) 39 | } 40 | 41 | def withFileEncoding(fileEncoding: String): JacocoReportSettings = { 42 | copy(fileEncoding = fileEncoding) 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/main/scala/com/github/sbt/jacoco/report/JacocoSourceSettings.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of sbt-jacoco. 3 | * 4 | * Copyright (c) Joachim Hofer & contributors 5 | * All rights reserved. 6 | * 7 | * This program and the accompanying materials 8 | * are made available under the terms of the Eclipse Public License v1.0 9 | * which accompanies this distribution, and is available at 10 | * http://www.eclipse.org/legal/epl-v10.html 11 | */ 12 | 13 | package com.github.sbt.jacoco.report 14 | 15 | case class JacocoSourceSettings(tabWidth: Int = 2, fileEncoding: String = "utf-8") 16 | -------------------------------------------------------------------------------- /src/main/scala/com/github/sbt/jacoco/report/JacocoThresholds.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of sbt-jacoco. 3 | * 4 | * Copyright (c) Joachim Hofer & contributors 5 | * All rights reserved. 6 | * 7 | * This program and the accompanying materials 8 | * are made available under the terms of the Eclipse Public License v1.0 9 | * which accompanies this distribution, and is available at 10 | * http://www.eclipse.org/legal/epl-v10.html 11 | */ 12 | 13 | package com.github.sbt.jacoco.report 14 | 15 | case class JacocoThresholds( 16 | instruction: Double = 0, 17 | method: Double = 0, 18 | branch: Double = 0, 19 | complexity: Double = 0, 20 | line: Double = 0, 21 | clazz: Double = 0 22 | ) 23 | -------------------------------------------------------------------------------- /src/main/scala/com/github/sbt/jacoco/report/Report.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of sbt-jacoco. 3 | * 4 | * Copyright (c) Joachim Hofer & contributors 5 | * All rights reserved. 6 | * 7 | * This program and the accompanying materials 8 | * are made available under the terms of the Eclipse Public License v1.0 9 | * which accompanies this distribution, and is available at 10 | * http://www.eclipse.org/legal/epl-v10.html 11 | */ 12 | 13 | package com.github.sbt.jacoco.report 14 | 15 | import java.io.File 16 | import java.text.DecimalFormat 17 | 18 | import org.jacoco.core.analysis._ 19 | import org.jacoco.core.data._ 20 | import org.jacoco.core.tools.ExecFileLoader 21 | import com.github.sbt.jacoco.filter.FilteringAnalyzer 22 | import com.github.sbt.jacoco.report.formats.JacocoReportFormat 23 | import sbt.Keys._ 24 | 25 | class Report( 26 | executionDataFiles: Seq[File], 27 | classDirectories: Seq[File], 28 | sourceDirectories: Seq[File], 29 | sourceSettings: JacocoSourceSettings, 30 | reportSettings: JacocoReportSettings, 31 | reportDirectory: File, 32 | streams: TaskStreams, 33 | checkCoverage: Boolean 34 | ) { 35 | 36 | private val percentageFormat = new DecimalFormat("#.##") 37 | 38 | def generate(): Unit = { 39 | val (executionDataStore, sessionInfoStore) = loadExecutionData 40 | val bundleCoverage = analyzeStructure(executionDataStore, sessionInfoStore) 41 | 42 | reportSettings.formats.foreach(createReport(_, bundleCoverage, executionDataStore, sessionInfoStore)) 43 | 44 | if (checkCoverage && !checkCoverage(bundleCoverage)) { 45 | sys.error("Required coverage is not met") 46 | } 47 | } 48 | 49 | def checkCoverage(bundle: IBundleCoverage): Boolean = { 50 | val sb = StringBuilder.newBuilder 51 | 52 | sb ++= "\n------- " 53 | sb ++= reportSettings.title 54 | sb ++= " -------\n\n" 55 | 56 | val checkResult = checkCounter("Lines", bundle.getLineCounter, reportSettings.thresholds.line, sb) :: 57 | checkCounter("Instructions", bundle.getInstructionCounter, reportSettings.thresholds.instruction, sb) :: 58 | checkCounter("Branches", bundle.getBranchCounter, reportSettings.thresholds.branch, sb) :: 59 | checkCounter("Methods", bundle.getMethodCounter, reportSettings.thresholds.method, sb) :: 60 | checkCounter("Complexity", bundle.getComplexityCounter, reportSettings.thresholds.complexity, sb) :: 61 | checkCounter("Class", bundle.getClassCounter, reportSettings.thresholds.clazz, sb) :: 62 | Nil 63 | 64 | sb ++= "\nCheck " 65 | sb ++= reportDirectory.getAbsolutePath 66 | sb ++= " for detailed report\n " 67 | streams.log.info(sb.toString()) 68 | 69 | !(checkResult contains false) 70 | } 71 | 72 | private[jacoco] def checkCounter( 73 | unit: String, 74 | c: ICounter, 75 | required: Double, 76 | summaryBuilder: StringBuilder 77 | ): Boolean = { 78 | 79 | val missedCount = c.getMissedCount 80 | val totalCount = c.getTotalCount 81 | val coveredRatio = if (c.getCoveredRatio.isNaN) 0 else c.getCoveredRatio 82 | val ratioPercent = coveredRatio * 100 83 | val success = ratioPercent >= required 84 | val sign = if (success) ">=" else "<" 85 | val status = if (success) "OK" else "NOK" 86 | val formattedRatio = percentageFormat.format(ratioPercent) 87 | 88 | summaryBuilder ++= unit 89 | summaryBuilder ++= ": " 90 | summaryBuilder ++= formattedRatio 91 | summaryBuilder ++= "% (" 92 | summaryBuilder ++= sign 93 | summaryBuilder ++= " required " 94 | summaryBuilder ++= required.toString 95 | summaryBuilder ++= "%) covered, " 96 | summaryBuilder ++= missedCount.toString 97 | summaryBuilder ++= " of " 98 | summaryBuilder ++= totalCount.toString 99 | summaryBuilder ++= " missed, " 100 | summaryBuilder ++= status 101 | summaryBuilder ++= "\n" 102 | 103 | success 104 | } 105 | 106 | private def loadExecutionData = { 107 | val loader = new ExecFileLoader 108 | executionDataFiles foreach { f => 109 | // it is possible that there's no test at all, thus no executionDataFile 110 | if (f.exists) loader.load(f) 111 | } 112 | 113 | (loader.getExecutionDataStore, loader.getSessionInfoStore) 114 | } 115 | 116 | private def analyzeStructure(executionDataStore: ExecutionDataStore, sessionInfoStore: SessionInfoStore) = { 117 | val coverageBuilder = new CoverageBuilder 118 | val analyzer = new FilteringAnalyzer(executionDataStore, coverageBuilder) 119 | 120 | classDirectories.foreach(analyzer.analyzeAll) 121 | 122 | coverageBuilder.getBundle(reportSettings.title) 123 | } 124 | 125 | private def createReport( 126 | reportFormat: JacocoReportFormat, 127 | bundleCoverage: IBundleCoverage, 128 | executionDataStore: ExecutionDataStore, 129 | sessionInfoStore: SessionInfoStore 130 | ): Unit = { 131 | 132 | val visitor = reportFormat.createVisitor(reportDirectory, reportSettings.fileEncoding) 133 | 134 | visitor.visitInfo(sessionInfoStore.getInfos, executionDataStore.getContents) 135 | visitor.visitBundle(bundleCoverage, new DirectoriesSourceFileLocator(sourceDirectories, sourceSettings)) 136 | 137 | visitor.visitEnd() 138 | } 139 | } 140 | -------------------------------------------------------------------------------- /src/main/scala/com/github/sbt/jacoco/report/ReportUtils.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of sbt-jacoco. 3 | * 4 | * Copyright (c) Joachim Hofer & contributors 5 | * All rights reserved. 6 | * 7 | * This program and the accompanying materials 8 | * are made available under the terms of the Eclipse Public License v1.0 9 | * which accompanies this distribution, and is available at 10 | * http://www.eclipse.org/legal/epl-v10.html 11 | */ 12 | 13 | package com.github.sbt.jacoco.report 14 | 15 | import sbt.Keys._ 16 | import sbt._ 17 | 18 | object ReportUtils { 19 | def generateReport( 20 | destinationDirectory: File, 21 | executionData: File, 22 | reportSettings: JacocoReportSettings, 23 | sourceDirectories: Seq[File], 24 | classDirectories: Seq[File], 25 | sourceSettings: JacocoSourceSettings, 26 | streams: TaskStreams, 27 | checkCoverage: Boolean = true 28 | ): Unit = { 29 | 30 | val report = new Report( 31 | reportDirectory = destinationDirectory, 32 | executionDataFiles = Seq(executionData), 33 | classDirectories = classDirectories, 34 | sourceDirectories = sourceDirectories, 35 | sourceSettings = sourceSettings, 36 | reportSettings = reportSettings, 37 | streams = streams, 38 | checkCoverage = checkCoverage 39 | ) 40 | 41 | report.generate() 42 | } 43 | 44 | def generateAggregateReport( 45 | destinationDirectory: File, 46 | executionDataFiles: Seq[File], 47 | reportSettings: JacocoReportSettings, 48 | sourceDirectories: Seq[File], 49 | classDirectories: Seq[File], 50 | sourceSettings: JacocoSourceSettings, 51 | streams: TaskStreams, 52 | checkCoverage: Boolean = true 53 | ): Unit = { 54 | 55 | val report = new Report( 56 | reportDirectory = destinationDirectory, 57 | executionDataFiles = executionDataFiles, 58 | classDirectories = classDirectories, 59 | sourceDirectories = sourceDirectories, 60 | sourceSettings = sourceSettings, 61 | reportSettings = reportSettings, 62 | streams = streams, 63 | checkCoverage = checkCoverage 64 | ) 65 | 66 | report.generate() 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/main/scala/com/github/sbt/jacoco/report/formats/CSVReportFormat.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of sbt-jacoco. 3 | * 4 | * Copyright (c) Joachim Hofer & contributors 5 | * All rights reserved. 6 | * 7 | * This program and the accompanying materials 8 | * are made available under the terms of the Eclipse Public License v1.0 9 | * which accompanies this distribution, and is available at 10 | * http://www.eclipse.org/legal/epl-v10.html 11 | */ 12 | 13 | package com.github.sbt.jacoco.report.formats 14 | 15 | import java.io.{File, FileOutputStream} 16 | 17 | import org.jacoco.report.IReportVisitor 18 | import org.jacoco.report.csv.CSVFormatter 19 | import sbt._ 20 | 21 | class CSVReportFormat extends JacocoReportFormat { 22 | override def createVisitor(directory: File, encoding: String): IReportVisitor = { 23 | IO.createDirectory(directory) 24 | 25 | val formatter = new CSVFormatter() 26 | formatter.setOutputEncoding(encoding) 27 | formatter.createVisitor(new FileOutputStream(new File(directory, "jacoco.csv"))) 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/main/scala/com/github/sbt/jacoco/report/formats/HTMLReportFormat.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of sbt-jacoco. 3 | * 4 | * Copyright (c) Joachim Hofer & contributors 5 | * All rights reserved. 6 | * 7 | * This program and the accompanying materials 8 | * are made available under the terms of the Eclipse Public License v1.0 9 | * which accompanies this distribution, and is available at 10 | * http://www.eclipse.org/legal/epl-v10.html 11 | */ 12 | 13 | package com.github.sbt.jacoco.report.formats 14 | 15 | import java.io.File 16 | 17 | import org.jacoco.report.html.HTMLFormatter 18 | import org.jacoco.report.{FileMultiReportOutput, IReportVisitor} 19 | import sbt._ 20 | 21 | class HTMLReportFormat extends JacocoReportFormat { 22 | override def createVisitor(directory: File, encoding: String): IReportVisitor = { 23 | IO.createDirectory(directory) 24 | 25 | val formatter = new HTMLFormatter() 26 | formatter.setOutputEncoding(encoding) 27 | formatter.createVisitor(new FileMultiReportOutput(new File(directory, "html"))) 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/main/scala/com/github/sbt/jacoco/report/formats/JacocoReportFormat.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of sbt-jacoco. 3 | * 4 | * Copyright (c) Joachim Hofer & contributors 5 | * All rights reserved. 6 | * 7 | * This program and the accompanying materials 8 | * are made available under the terms of the Eclipse Public License v1.0 9 | * which accompanies this distribution, and is available at 10 | * http://www.eclipse.org/legal/epl-v10.html 11 | */ 12 | 13 | package com.github.sbt.jacoco.report.formats 14 | 15 | import java.io.File 16 | 17 | import org.jacoco.report.IReportVisitor 18 | 19 | trait JacocoReportFormat { 20 | def createVisitor(destDirectory: File, encoding: String): IReportVisitor 21 | } 22 | -------------------------------------------------------------------------------- /src/main/scala/com/github/sbt/jacoco/report/formats/ScalaHTMLReportFormat.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of sbt-jacoco. 3 | * 4 | * Copyright (c) Joachim Hofer & contributors 5 | * All rights reserved. 6 | * 7 | * This program and the accompanying materials 8 | * are made available under the terms of the Eclipse Public License v1.0 9 | * which accompanies this distribution, and is available at 10 | * http://www.eclipse.org/legal/epl-v10.html 11 | */ 12 | 13 | package com.github.sbt.jacoco.report.formats 14 | 15 | import java.io.File 16 | 17 | import org.jacoco.core.analysis.ICoverageNode 18 | import org.jacoco.report.html.HTMLFormatter 19 | import org.jacoco.report.internal.html.resources.Styles 20 | import org.jacoco.report.internal.html.table._ 21 | import org.jacoco.report.{FileMultiReportOutput, IReportVisitor} 22 | import sbt._ 23 | 24 | class ScalaHTMLReportFormat(withBranchCoverage: Boolean = true) extends JacocoReportFormat { 25 | override def createVisitor(directory: File, encoding: String): IReportVisitor = { 26 | IO.createDirectory(directory) 27 | 28 | val formatter = new ScalaHtmlFormatter(withBranchCoverage) 29 | formatter.setOutputEncoding(encoding) 30 | formatter.createVisitor(new FileMultiReportOutput(new File(directory, "html"))) 31 | } 32 | 33 | def withoutBranches: ScalaHTMLReportFormat = new ScalaHTMLReportFormat(withBranchCoverage = false) 34 | } 35 | 36 | /** 37 | * Omits displaying instruction and branch coverage in the coverage tables, as Scala generates null checks which make 38 | * these too noisy. 39 | * 40 | * TODO: Find a way to remove them from the annotated source code reports, too. 41 | */ 42 | class ScalaHtmlFormatter(withBranchCoverage: Boolean) extends HTMLFormatter { 43 | private val table: Table = createTable 44 | 45 | setLanguageNames(new ScalaLanguageNames) 46 | 47 | override def getTable: Table = table 48 | 49 | private def createTable: Table = { 50 | val t: Table = new Table 51 | t.add("Element", null, new LabelColumn, false) // scalastyle:ignore null 52 | t.add("Missed Lines", Styles.BAR, new BarColumn(ICoverageNode.CounterEntity.LINE, getLocale), true) 53 | t.add("Total Lines", Styles.CTR1, CounterColumn.newTotal(ICoverageNode.CounterEntity.LINE, getLocale), false) 54 | t.add("Cov.", Styles.CTR2, new PercentageColumn(ICoverageNode.CounterEntity.LINE, getLocale), false) 55 | if (withBranchCoverage) { 56 | t.add("Missed Branches", Styles.BAR, new BarColumn(ICoverageNode.CounterEntity.BRANCH, getLocale), false) 57 | t.add("Cov.", Styles.CTR2, new PercentageColumn(ICoverageNode.CounterEntity.BRANCH, getLocale), false) 58 | } 59 | addMissedTotalColumns(t, "Methods", ICoverageNode.CounterEntity.METHOD) 60 | addMissedTotalColumns(t, "Classes", ICoverageNode.CounterEntity.CLASS) 61 | 62 | t 63 | } 64 | 65 | private def addMissedTotalColumns(table: Table, label: String, entity: ICoverageNode.CounterEntity): Unit = { 66 | table.add("Missed", Styles.CTR1, CounterColumn.newMissed(entity, getLocale), false) 67 | table.add(label, Styles.CTR2, CounterColumn.newTotal(entity, getLocale), false) 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/main/scala/com/github/sbt/jacoco/report/formats/ScalaLanguageNames.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of sbt-jacoco. 3 | * 4 | * Copyright (c) Joachim Hofer & contributors 5 | * All rights reserved. 6 | * 7 | * This program and the accompanying materials 8 | * are made available under the terms of the Eclipse Public License v1.0 9 | * which accompanies this distribution, and is available at 10 | * http://www.eclipse.org/legal/epl-v10.html 11 | */ 12 | 13 | package com.github.sbt.jacoco.report.formats 14 | 15 | import org.jacoco.report.JavaNames 16 | import com.github.sbt.jacoco.filter.ScalaForwarderDetector 17 | 18 | import scala.reflect.NameTransformer._ 19 | 20 | private[formats] class ScalaLanguageNames extends JavaNames { 21 | override def getPackageName(vmname: String): String = 22 | super.getPackageName(decode(vmname)) 23 | 24 | override def getClassName( 25 | vmname: String, 26 | vmsignature: String, 27 | vmsuperclass: String, 28 | vminterfaces: Array[String] 29 | ): String = { 30 | if (vmname.contains("anonfun$")) { 31 | vmname.split("""anonfun\$""").toList match { 32 | case List(pre, post) => 33 | getClassName(cleanClassName(pre)) + " anonfun$" + post 34 | case _ => 35 | getClassName(cleanClassName(vmname)) 36 | } 37 | } else if (vmname.contains("$anon$")) { 38 | vminterfaces.map(getClassName).mkString("new " + getClassName(vmsuperclass), " with ", "{ ... }") 39 | } else { 40 | getClassName(cleanClassName(vmname)) 41 | } 42 | } 43 | 44 | override def getQualifiedClassName(vmname: String): String = 45 | super.getQualifiedClassName(cleanClassName(vmname)) 46 | 47 | override def getMethodName(vmclassname: String, vmmethodname: String, vmdesc: String, vmsignature: String): String = 48 | super.getMethodName(vmclassname, getMethodName(vmmethodname), vmdesc, vmsignature) 49 | 50 | override def getQualifiedMethodName( 51 | vmclassname: String, 52 | vmmethodname: String, 53 | vmdesc: String, 54 | vmsignature: String 55 | ): String = 56 | super.getQualifiedMethodName(vmclassname, getMethodName(vmmethodname), vmdesc, vmsignature) 57 | 58 | private def cleanClassName(name: String) = { 59 | decode(name.stripSuffix("$class")) 60 | } 61 | 62 | private def getClassName(vmname: String) = { 63 | val pos: Int = vmname.lastIndexOf('/') 64 | val name: String = if (pos == -1) vmname else vmname.substring(pos + 1) 65 | cleanClassName( 66 | if (name.endsWith("$$")) { 67 | name.dropRight(2).replace('$', '.') // ambiguous, we could be an inner class of the object or the class. 68 | } else if (name.endsWith("$")) { 69 | name.dropRight(1).replace('$', '.') + " (object)" 70 | } else { 71 | name.replace('$', '.') 72 | } 73 | ) 74 | } 75 | 76 | private def getMethodName(vmname: String) = { 77 | val pos: Int = vmname.lastIndexOf("$$") 78 | val name: String = if (pos == -1) vmname else vmname.substring(pos + 2) 79 | decode(name.stripSuffix(ScalaForwarderDetector.LazyComputeSuffix)) 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/main/scala/com/github/sbt/jacoco/report/formats/XMLReportFormat.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of sbt-jacoco. 3 | * 4 | * Copyright (c) Joachim Hofer & contributors 5 | * All rights reserved. 6 | * 7 | * This program and the accompanying materials 8 | * are made available under the terms of the Eclipse Public License v1.0 9 | * which accompanies this distribution, and is available at 10 | * http://www.eclipse.org/legal/epl-v10.html 11 | */ 12 | 13 | package com.github.sbt.jacoco.report.formats 14 | 15 | import java.io.{File, FileOutputStream} 16 | 17 | import org.jacoco.report.IReportVisitor 18 | import org.jacoco.report.xml.XMLFormatter 19 | import sbt._ 20 | 21 | class XMLReportFormat extends JacocoReportFormat { 22 | override def createVisitor(directory: File, encoding: String): IReportVisitor = { 23 | IO.createDirectory(directory) 24 | 25 | val formatter = new XMLFormatter() 26 | formatter.setOutputEncoding(encoding) 27 | formatter.createVisitor(new FileOutputStream(new File(directory, "jacoco.xml"))) 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/sbt-test/sbt-jacoco/csv-report/build.sbt: -------------------------------------------------------------------------------- 1 | name := "jacocoTest" 2 | organization := "com.navetas" 3 | 4 | scalaVersion := "2.12.17" 5 | scalacOptions ++= Seq("-deprecation", "-optimize", "-unchecked", "-Xlint", "-language:_") 6 | 7 | libraryDependencies += "org.scalatest" %% "scalatest" % "3.2.19" % "test" 8 | 9 | Test / jacocoReportSettings := JacocoReportSettings() 10 | .withFormats( 11 | JacocoReportFormats.CSV 12 | ) 13 | -------------------------------------------------------------------------------- /src/sbt-test/sbt-jacoco/csv-report/project/plugins.sbt: -------------------------------------------------------------------------------- 1 | { 2 | val pluginVersion = System.getProperty("plugin.version") 3 | if (pluginVersion == null) 4 | throw new RuntimeException("""|The system property 'plugin.version' is not defined. 5 | |Specify this property using the parameter -Dplugin.version=""".stripMargin) 6 | else addSbtPlugin("com.github.sbt" % "sbt-jacoco" % pluginVersion) 7 | } 8 | -------------------------------------------------------------------------------- /src/sbt-test/sbt-jacoco/csv-report/src/main/scala/jacocotest/TestClass.scala: -------------------------------------------------------------------------------- 1 | package jacocotest 2 | 3 | case class TestClass(val x: Int) { 4 | def double(): Int = x * 2 5 | 6 | def triple(): Int = x * 3 7 | } 8 | -------------------------------------------------------------------------------- /src/sbt-test/sbt-jacoco/csv-report/src/test/scala/jacocotest/TestClassTests.scala: -------------------------------------------------------------------------------- 1 | package jacocotest 2 | 3 | import org.scalatest.flatspec._ 4 | 5 | class TestClassTests extends AnyFlatSpec { 6 | "TestClass" should "double a number correctly" in { 7 | assert(new TestClass(2).double === 4) 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/sbt-test/sbt-jacoco/csv-report/test: -------------------------------------------------------------------------------- 1 | > clean 2 | 3 | # coverage should pass 4 | > jacoco 5 | 6 | # instrumented classes 7 | $ exists target/scala-2.12/jacoco/instrumented-classes 8 | 9 | # results 10 | $ exists target/scala-2.12/jacoco/data/jacoco.exec 11 | 12 | # report formats 13 | $ absent target/scala-2.12/jacoco/report/html 14 | $ absent target/scala-2.12/jacoco/report/jacoco.xml 15 | $ exists target/scala-2.12/jacoco/report/jacoco.csv -------------------------------------------------------------------------------- /src/sbt-test/sbt-jacoco/forked-integration/build.sbt: -------------------------------------------------------------------------------- 1 | libraryDependencies += "org.scalatest" %% "scalatest" % "3.2.19" % "test,it" 2 | 3 | val root = project.in(file(".")).configs(IntegrationTest) 4 | scalaVersion := "2.12.17" 5 | 6 | enablePlugins(JacocoItPlugin) 7 | 8 | Test / fork := true 9 | IntegrationTest / fork := true 10 | -------------------------------------------------------------------------------- /src/sbt-test/sbt-jacoco/forked-integration/project/plugins.sbt: -------------------------------------------------------------------------------- 1 | { 2 | val pluginVersion = System.getProperty("plugin.version") 3 | if (pluginVersion == null) 4 | throw new RuntimeException("""|The system property 'plugin.version' is not defined. 5 | |Specify this property using the parameter -Dplugin.version=""".stripMargin) 6 | else addSbtPlugin("com.github.sbt" % "sbt-jacoco" % pluginVersion) 7 | } 8 | -------------------------------------------------------------------------------- /src/sbt-test/sbt-jacoco/forked-integration/src/it/scala/IntegrationTest.scala: -------------------------------------------------------------------------------- 1 | package example 2 | 3 | import org.scalatest.funsuite._ 4 | 5 | class UnitTest extends AnyFunSuite { 6 | test("forIntegrationTests") { 7 | assert(TestSubject.forIntegrationTests == 4) 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/sbt-test/sbt-jacoco/forked-integration/src/main/scala/TestSubject.scala: -------------------------------------------------------------------------------- 1 | package example 2 | 3 | object TestSubject { 4 | def forUnitTests = 1 + 1 5 | def forIntegrationTests = 2 + 2 6 | } 7 | -------------------------------------------------------------------------------- /src/sbt-test/sbt-jacoco/forked-integration/src/test/scala/UnitTest.scala: -------------------------------------------------------------------------------- 1 | package example 2 | 3 | import org.scalatest.funsuite._ 4 | 5 | class UnitTest extends AnyFunSuite { 6 | test("forUnitTests") { 7 | assert(TestSubject.forUnitTests == 2) 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/sbt-test/sbt-jacoco/forked-integration/test: -------------------------------------------------------------------------------- 1 | > clean 2 | 3 | # coverage should pass 4 | > jacoco 5 | > it:jacoco 6 | 7 | # instrumented classes - common to both tests 8 | $ exists target/scala-2.12/jacoco/instrumented-classes 9 | 10 | # unit tests 11 | $ exists target/scala-2.12/jacoco/data/jacoco.exec 12 | $ exists target/scala-2.12/jacoco/report/test/html/index.html 13 | 14 | # integration tests 15 | $ exists target/scala-2.12/jacoco/data/jacoco-it.exec 16 | $ exists target/scala-2.12/jacoco/data/jacoco-merged.exec 17 | $ exists target/scala-2.12/jacoco/report/it/html/index.html 18 | 19 | # combined html report 20 | $ exists target/scala-2.12/jacoco/report/merged/html/index.html -------------------------------------------------------------------------------- /src/sbt-test/sbt-jacoco/forked/build.sbt: -------------------------------------------------------------------------------- 1 | name := "jacocoTest" 2 | organization := "com.navetas" 3 | 4 | scalaVersion := "2.12.17" 5 | scalacOptions ++= Seq("-deprecation", "-optimize", "-unchecked", "-Xlint", "-language:_") 6 | 7 | libraryDependencies += "org.scalatest" %% "scalatest" % "3.2.19" % "test" 8 | 9 | Test / fork := true 10 | -------------------------------------------------------------------------------- /src/sbt-test/sbt-jacoco/forked/project/plugins.sbt: -------------------------------------------------------------------------------- 1 | { 2 | val pluginVersion = System.getProperty("plugin.version") 3 | if (pluginVersion == null) 4 | throw new RuntimeException("""|The system property 'plugin.version' is not defined. 5 | |Specify this property using the parameter -Dplugin.version=""".stripMargin) 6 | else addSbtPlugin("com.github.sbt" % "sbt-jacoco" % pluginVersion) 7 | } 8 | -------------------------------------------------------------------------------- /src/sbt-test/sbt-jacoco/forked/src/main/scala/jacocotest/TestClass.scala: -------------------------------------------------------------------------------- 1 | package jacocotest 2 | 3 | case class TestClass(val x: Int) { 4 | def double(): Int = x * 2 5 | 6 | def triple(): Int = x * 3 7 | } 8 | -------------------------------------------------------------------------------- /src/sbt-test/sbt-jacoco/forked/src/test/scala/jacocotest/TestClassTests.scala: -------------------------------------------------------------------------------- 1 | package jacocotest 2 | 3 | import org.scalatest.flatspec._ 4 | 5 | class TestClassTests extends AnyFlatSpec { 6 | "TestClass" should "double a number correctly" in { 7 | assert(new TestClass(2).double === 4) 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/sbt-test/sbt-jacoco/forked/test: -------------------------------------------------------------------------------- 1 | > clean 2 | 3 | # coverage should pass 4 | > jacoco 5 | 6 | # instrumented classes 7 | $ exists target/scala-2.12/jacoco/instrumented-classes 8 | 9 | # results 10 | $ exists target/scala-2.12/jacoco/data/jacoco.exec 11 | 12 | # html report 13 | $ exists target/scala-2.12/jacoco/report/html/index.html -------------------------------------------------------------------------------- /src/sbt-test/sbt-jacoco/integration-test-no-merge/build.sbt: -------------------------------------------------------------------------------- 1 | libraryDependencies += "org.scalatest" %% "scalatest" % "3.2.19" % "test,it" 2 | 3 | val root = project.in(file(".")).configs(IntegrationTest) 4 | scalaVersion := "2.12.17" 5 | 6 | enablePlugins(JacocoItPlugin) 7 | 8 | IntegrationTest / jacocoAutoMerge := false 9 | -------------------------------------------------------------------------------- /src/sbt-test/sbt-jacoco/integration-test-no-merge/project/plugins.sbt: -------------------------------------------------------------------------------- 1 | { 2 | val pluginVersion = System.getProperty("plugin.version") 3 | if (pluginVersion == null) 4 | throw new RuntimeException("""|The system property 'plugin.version' is not defined. 5 | |Specify this property using the parameter -Dplugin.version=""".stripMargin) 6 | else addSbtPlugin("com.github.sbt" % "sbt-jacoco" % pluginVersion) 7 | } 8 | -------------------------------------------------------------------------------- /src/sbt-test/sbt-jacoco/integration-test-no-merge/src/it/scala/IntegrationTest.scala: -------------------------------------------------------------------------------- 1 | package example 2 | 3 | import org.scalatest.funsuite._ 4 | 5 | class UnitTest extends AnyFunSuite { 6 | test("forIntegrationTests") { 7 | assert(TestSubject.forIntegrationTests == 4) 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/sbt-test/sbt-jacoco/integration-test-no-merge/src/main/scala/TestSubject.scala: -------------------------------------------------------------------------------- 1 | package example 2 | 3 | object TestSubject { 4 | def forUnitTests = 1 + 1 5 | def forIntegrationTests = 2 + 2 6 | } 7 | -------------------------------------------------------------------------------- /src/sbt-test/sbt-jacoco/integration-test-no-merge/src/test/scala/UnitTest.scala: -------------------------------------------------------------------------------- 1 | package example 2 | 3 | import org.scalatest.funsuite._ 4 | 5 | class UnitTest extends AnyFunSuite { 6 | test("forUnitTests") { 7 | assert(TestSubject.forUnitTests == 2) 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/sbt-test/sbt-jacoco/integration-test-no-merge/test: -------------------------------------------------------------------------------- 1 | > clean 2 | 3 | # coverage should pass 4 | > jacoco 5 | > it:jacoco 6 | 7 | # instrumented classes - common to both tests 8 | $ exists target/scala-2.12/jacoco/instrumented-classes 9 | 10 | # unit tests 11 | $ exists target/scala-2.12/jacoco/data/jacoco.exec 12 | $ exists target/scala-2.12/jacoco/report/test/html/index.html 13 | 14 | # integration tests 15 | $ exists target/scala-2.12/jacoco/data/jacoco-it.exec 16 | $ exists target/scala-2.12/jacoco/report/it/html/index.html 17 | 18 | # should not have a merged report 19 | $ absent target/scala-2.12/jacoco/data/jacoco-merged.exec 20 | $ absent target/scala-2.12/jacoco/report/merged 21 | 22 | # manually trigger report merge 23 | > it:jacocoMergedReport 24 | $ exists target/scala-2.12/jacoco/data/jacoco-merged.exec 25 | $ exists target/scala-2.12/jacoco/report/merged/html/index.html -------------------------------------------------------------------------------- /src/sbt-test/sbt-jacoco/integration-test/build.sbt: -------------------------------------------------------------------------------- 1 | libraryDependencies += "org.scalatest" %% "scalatest" % "3.2.19" % "test,it" 2 | 3 | val root = project.in(file(".")).configs(IntegrationTest) 4 | scalaVersion := "2.12.17" 5 | 6 | enablePlugins(JacocoItPlugin) 7 | -------------------------------------------------------------------------------- /src/sbt-test/sbt-jacoco/integration-test/project/plugins.sbt: -------------------------------------------------------------------------------- 1 | { 2 | val pluginVersion = System.getProperty("plugin.version") 3 | if (pluginVersion == null) 4 | throw new RuntimeException("""|The system property 'plugin.version' is not defined. 5 | |Specify this property using the parameter -Dplugin.version=""".stripMargin) 6 | else addSbtPlugin("com.github.sbt" % "sbt-jacoco" % pluginVersion) 7 | } 8 | -------------------------------------------------------------------------------- /src/sbt-test/sbt-jacoco/integration-test/src/it/scala/IntegrationTest.scala: -------------------------------------------------------------------------------- 1 | package example 2 | 3 | import org.scalatest.funsuite._ 4 | 5 | class UnitTest extends AnyFunSuite { 6 | test("forIntegrationTests") { 7 | assert(TestSubject.forIntegrationTests == 4) 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/sbt-test/sbt-jacoco/integration-test/src/main/scala/TestSubject.scala: -------------------------------------------------------------------------------- 1 | package example 2 | 3 | object TestSubject { 4 | def forUnitTests = 1 + 1 5 | def forIntegrationTests = 2 + 2 6 | } 7 | -------------------------------------------------------------------------------- /src/sbt-test/sbt-jacoco/integration-test/src/test/scala/UnitTest.scala: -------------------------------------------------------------------------------- 1 | package example 2 | 3 | import org.scalatest.funsuite._ 4 | 5 | class UnitTest extends AnyFunSuite { 6 | test("forUnitTests") { 7 | assert(TestSubject.forUnitTests == 2) 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/sbt-test/sbt-jacoco/integration-test/test: -------------------------------------------------------------------------------- 1 | > clean 2 | 3 | # coverage should pass 4 | > jacoco 5 | > it:jacoco 6 | 7 | # instrumented classes - common to both tests 8 | $ exists target/scala-2.12/jacoco/instrumented-classes 9 | 10 | # unit tests 11 | $ exists target/scala-2.12/jacoco/data/jacoco.exec 12 | $ exists target/scala-2.12/jacoco/report/test/html/index.html 13 | 14 | # integration tests 15 | $ exists target/scala-2.12/jacoco/data/jacoco-it.exec 16 | $ exists target/scala-2.12/jacoco/data/jacoco-merged.exec 17 | $ exists target/scala-2.12/jacoco/report/it/html/index.html 18 | 19 | # combined html report 20 | $ exists target/scala-2.12/jacoco/report/merged/html/index.html -------------------------------------------------------------------------------- /src/sbt-test/sbt-jacoco/multi-build/build.sbt: -------------------------------------------------------------------------------- 1 | organization := "com.example" 2 | 3 | scalaVersion := "2.12.17" 4 | 5 | libraryDependencies ++= Seq( 6 | "org.scalatest" %% "scalatest" % "3.2.19" % "test" 7 | ) 8 | 9 | lazy val common = project 10 | .in(file("common")) 11 | 12 | lazy val extras = project 13 | .in(file("extras")) 14 | 15 | lazy val root = project 16 | .in(file(".")) 17 | .aggregate( 18 | common, 19 | extras 20 | ) 21 | .settings( 22 | publish := {}, 23 | publishLocal := {}, 24 | test := {}, 25 | testOnly := {} 26 | ) 27 | -------------------------------------------------------------------------------- /src/sbt-test/sbt-jacoco/multi-build/common/src/main/scala/jacocotest/common/Greeter.scala: -------------------------------------------------------------------------------- 1 | package jacocotest.common 2 | 3 | class Greeter { 4 | def greet(name: String): String = s"Hello, $name" 5 | } 6 | -------------------------------------------------------------------------------- /src/sbt-test/sbt-jacoco/multi-build/common/src/test/scala/jacocotest/common/GreeterSpec.scala: -------------------------------------------------------------------------------- 1 | package jacocotest.common 2 | 3 | import org.scalatest.flatspec._ 4 | import org.scalatest.matchers.should.Matchers 5 | 6 | class GreeterSpec extends AnyFlatSpec with Matchers { 7 | val greeter = new Greeter() 8 | 9 | "greet" should "greet bob" in { 10 | greeter.greet("bob") shouldBe "Hello, bob" 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/sbt-test/sbt-jacoco/multi-build/extras/src/main/scala/jacocotest/extra/Money.scala: -------------------------------------------------------------------------------- 1 | package jacocotest.extra 2 | 3 | import java.util.{Currency, Locale} 4 | 5 | case class Money(amount: BigDecimal, currency: Currency) { 6 | override def toString: String = { 7 | "%.2f %s".format(amount, currency.getSymbol(Locale.forLanguageTag("en-GB"))) 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/sbt-test/sbt-jacoco/multi-build/extras/src/test/scala/jacocotest/extra/MoneySpec.scala: -------------------------------------------------------------------------------- 1 | package jacocotest.extra 2 | 3 | import java.util.Currency 4 | import org.scalatest.flatspec._ 5 | import org.scalatest.matchers.should.Matchers 6 | 7 | class MoneySpec extends AnyFlatSpec with Matchers { 8 | "toString" should "include currency" in { 9 | Money(BigDecimal(5), Currency.getInstance("GBP")).toString shouldBe "5.00 £" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/sbt-test/sbt-jacoco/multi-build/project/SettingsPlugin.scala: -------------------------------------------------------------------------------- 1 | import sbt._ 2 | import sbt.Keys._ 3 | import sbt.plugins.JvmPlugin 4 | 5 | object SettingsPlugin extends AutoPlugin { 6 | override def trigger: PluginTrigger = AllRequirements 7 | override def requires: Plugins = JvmPlugin 8 | 9 | override def projectSettings: Seq[Setting[_]] = Seq( 10 | scalaVersion := (LocalRootProject / scalaVersion).value, 11 | libraryDependencies ++= (LocalRootProject / libraryDependencies).value 12 | ) 13 | } 14 | -------------------------------------------------------------------------------- /src/sbt-test/sbt-jacoco/multi-build/project/plugins.sbt: -------------------------------------------------------------------------------- 1 | { 2 | val pluginVersion = System.getProperty("plugin.version") 3 | if (pluginVersion == null) 4 | throw new RuntimeException("""|The system property 'plugin.version' is not defined. 5 | |Specify this property using the parameter -Dplugin.version=""".stripMargin) 6 | else addSbtPlugin("com.github.sbt" % "sbt-jacoco" % pluginVersion) 7 | } 8 | -------------------------------------------------------------------------------- /src/sbt-test/sbt-jacoco/multi-build/test: -------------------------------------------------------------------------------- 1 | > clean 2 | 3 | # coverage should pass 4 | > jacoco 5 | 6 | # project results 7 | $ exists common/target/scala-2.12/jacoco/data/jacoco.exec 8 | $ exists common/target/scala-2.12/jacoco/report/html/index.html 9 | $ exists extras/target/scala-2.12/jacoco/data/jacoco.exec 10 | $ exists extras/target/scala-2.12/jacoco/report/html/index.html 11 | 12 | $ absent target/scala-2.12/jacoco/report/aggregate 13 | 14 | > clean 15 | > jacocoAggregate 16 | 17 | # project results 18 | $ exists common/target/scala-2.12/jacoco/data/jacoco.exec 19 | $ exists extras/target/scala-2.12/jacoco/data/jacoco.exec 20 | 21 | # aggregated results 22 | $ exists target/scala-2.12/jacoco/report/aggregate/html/index.html 23 | -------------------------------------------------------------------------------- /src/sbt-test/sbt-jacoco/report-settings/build.sbt: -------------------------------------------------------------------------------- 1 | name := "jacocoTest" 2 | organization := "com.navetas" 3 | 4 | scalaVersion := "2.12.17" 5 | scalacOptions ++= Seq("-deprecation", "-optimize", "-unchecked", "-Xlint", "-language:_") 6 | 7 | libraryDependencies += "org.scalatest" %% "scalatest" % "3.2.19" % "test" 8 | 9 | Test / jacocoReportSettings := JacocoReportSettings() 10 | .withFormats( 11 | JacocoReportFormats.ScalaHTML.withoutBranches, 12 | JacocoReportFormats.XML, 13 | JacocoReportFormats.CSV 14 | ) 15 | -------------------------------------------------------------------------------- /src/sbt-test/sbt-jacoco/report-settings/project/plugins.sbt: -------------------------------------------------------------------------------- 1 | { 2 | val pluginVersion = System.getProperty("plugin.version") 3 | if (pluginVersion == null) 4 | throw new RuntimeException("""|The system property 'plugin.version' is not defined. 5 | |Specify this property using the parameter -Dplugin.version=""".stripMargin) 6 | else addSbtPlugin("com.github.sbt" % "sbt-jacoco" % pluginVersion) 7 | } 8 | -------------------------------------------------------------------------------- /src/sbt-test/sbt-jacoco/report-settings/src/main/scala/jacocotest/TestClass.scala: -------------------------------------------------------------------------------- 1 | package jacocotest 2 | 3 | case class TestClass(val x: Int) { 4 | def double(): Int = x * 2 5 | 6 | def triple(): Int = x * 3 7 | } 8 | -------------------------------------------------------------------------------- /src/sbt-test/sbt-jacoco/report-settings/src/test/scala/jacocotest/TestClassTests.scala: -------------------------------------------------------------------------------- 1 | package jacocotest 2 | 3 | import org.scalatest.flatspec._ 4 | 5 | class TestClassTests extends AnyFlatSpec { 6 | "TestClass" should "double a number correctly" in { 7 | assert(new TestClass(2).double === 4) 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/sbt-test/sbt-jacoco/report-settings/test: -------------------------------------------------------------------------------- 1 | > clean 2 | 3 | # coverage should pass 4 | > jacoco 5 | 6 | # instrumented classes 7 | $ exists target/scala-2.12/jacoco/instrumented-classes 8 | 9 | # results 10 | $ exists target/scala-2.12/jacoco/data/jacoco.exec 11 | 12 | # report formats 13 | $ exists target/scala-2.12/jacoco/report/html/index.html 14 | $ exists target/scala-2.12/jacoco/report/jacoco.xml 15 | $ exists target/scala-2.12/jacoco/report/jacoco.csv -------------------------------------------------------------------------------- /src/sbt-test/sbt-jacoco/simple-scoped/build.sbt: -------------------------------------------------------------------------------- 1 | name := "jacocoTest" 2 | organization := "com.navetas" 3 | 4 | scalaVersion := "2.12.17" 5 | scalacOptions ++= Seq("-deprecation", "-optimize", "-unchecked", "-Xlint", "-language:_") 6 | 7 | libraryDependencies += "org.scalatest" %% "scalatest" % "3.2.19" % "test" 8 | 9 | Test / jacocoReportSettings := JacocoReportSettings() 10 | .withThresholds( 11 | JacocoThresholds(instruction = 100, method = 100, branch = 100, complexity = 100, line = 100, clazz = 100) 12 | ) 13 | -------------------------------------------------------------------------------- /src/sbt-test/sbt-jacoco/simple-scoped/project/plugins.sbt: -------------------------------------------------------------------------------- 1 | { 2 | val pluginVersion = System.getProperty("plugin.version") 3 | if (pluginVersion == null) 4 | throw new RuntimeException("""|The system property 'plugin.version' is not defined. 5 | |Specify this property using the parameter -Dplugin.version=""".stripMargin) 6 | else addSbtPlugin("com.github.sbt" % "sbt-jacoco" % pluginVersion) 7 | } 8 | -------------------------------------------------------------------------------- /src/sbt-test/sbt-jacoco/simple-scoped/src/main/scala/jacocotest/TestClass.scala: -------------------------------------------------------------------------------- 1 | package jacocotest 2 | 3 | case class TestClass(val x: Int) { 4 | def double(): Int = x * 2 5 | 6 | def triple(): Int = x * 3 7 | } 8 | -------------------------------------------------------------------------------- /src/sbt-test/sbt-jacoco/simple-scoped/src/test/scala/jacocotest/TestClassTests.scala: -------------------------------------------------------------------------------- 1 | package jacocotest 2 | 3 | import org.scalatest.flatspec._ 4 | 5 | class TestClassTests extends AnyFlatSpec { 6 | "TestClass" should "double a number correctly" in { 7 | assert(new TestClass(2).double === 4) 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/sbt-test/sbt-jacoco/simple-scoped/test: -------------------------------------------------------------------------------- 1 | > clean 2 | 3 | # coverage should fail 4 | -> jacoco 5 | 6 | # instrumented classes 7 | $ exists target/scala-2.12/jacoco/instrumented-classes 8 | 9 | # results 10 | $ exists target/scala-2.12/jacoco/data/jacoco.exec 11 | 12 | # html report 13 | $ exists target/scala-2.12/jacoco/report/html/index.html -------------------------------------------------------------------------------- /src/sbt-test/sbt-jacoco/simple/build.sbt: -------------------------------------------------------------------------------- 1 | name := "jacocoTest" 2 | organization := "com.navetas" 3 | 4 | scalaVersion := "2.12.17" 5 | scalacOptions ++= Seq("-deprecation", "-optimize", "-unchecked", "-Xlint", "-language:_") 6 | 7 | libraryDependencies += "org.scalatest" %% "scalatest" % "3.2.19" % "test" 8 | 9 | jacocoReportSettings := JacocoReportSettings() 10 | .withThresholds( 11 | JacocoThresholds(instruction = 100, method = 100, branch = 100, complexity = 100, line = 100, clazz = 100) 12 | ) 13 | -------------------------------------------------------------------------------- /src/sbt-test/sbt-jacoco/simple/project/plugins.sbt: -------------------------------------------------------------------------------- 1 | { 2 | val pluginVersion = System.getProperty("plugin.version") 3 | if (pluginVersion == null) 4 | throw new RuntimeException("""|The system property 'plugin.version' is not defined. 5 | |Specify this property using the parameter -Dplugin.version=""".stripMargin) 6 | else addSbtPlugin("com.github.sbt" % "sbt-jacoco" % pluginVersion) 7 | } 8 | -------------------------------------------------------------------------------- /src/sbt-test/sbt-jacoco/simple/src/main/scala/jacocotest/TestClass.scala: -------------------------------------------------------------------------------- 1 | package jacocotest 2 | 3 | case class TestClass(val x: Int) { 4 | def double(): Int = x * 2 5 | 6 | def triple(): Int = x * 3 7 | } 8 | -------------------------------------------------------------------------------- /src/sbt-test/sbt-jacoco/simple/src/test/scala/jacocotest/TestClassTests.scala: -------------------------------------------------------------------------------- 1 | package jacocotest 2 | 3 | import org.scalatest.flatspec._ 4 | 5 | class TestClassTests extends AnyFlatSpec { 6 | "TestClass" should "double a number correctly" in { 7 | assert(new TestClass(2).double === 4) 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/sbt-test/sbt-jacoco/simple/test: -------------------------------------------------------------------------------- 1 | > clean 2 | 3 | # coverage should fail 4 | -> jacoco 5 | 6 | # instrumented classes 7 | $ exists target/scala-2.12/jacoco/instrumented-classes 8 | 9 | # results 10 | $ exists target/scala-2.12/jacoco/data/jacoco.exec 11 | 12 | # html report 13 | $ exists target/scala-2.12/jacoco/report/html/index.html -------------------------------------------------------------------------------- /src/sbt-test/sbt-jacoco/source-settings/build.sbt: -------------------------------------------------------------------------------- 1 | name := "jacocoTest" 2 | organization := "com.navetas" 3 | 4 | scalaVersion := "2.12.17" 5 | scalacOptions ++= Seq("-deprecation", "-optimize", "-unchecked", "-Xlint", "-language:_") 6 | 7 | libraryDependencies += "org.scalatest" %% "scalatest" % "3.2.19" % "test" 8 | 9 | jacocoSourceSettings := JacocoSourceSettings(tabWidth = 4, fileEncoding = "ISO-8859-1") 10 | -------------------------------------------------------------------------------- /src/sbt-test/sbt-jacoco/source-settings/project/plugins.sbt: -------------------------------------------------------------------------------- 1 | { 2 | val pluginVersion = System.getProperty("plugin.version") 3 | if (pluginVersion == null) 4 | throw new RuntimeException("""|The system property 'plugin.version' is not defined. 5 | |Specify this property using the parameter -Dplugin.version=""".stripMargin) 6 | else addSbtPlugin("com.github.sbt" % "sbt-jacoco" % pluginVersion) 7 | } 8 | -------------------------------------------------------------------------------- /src/sbt-test/sbt-jacoco/source-settings/src/main/scala/jacocotest/TestClass.scala: -------------------------------------------------------------------------------- 1 | package jacocotest 2 | 3 | case class TestClass(val x: Int) { 4 | def double(): Int = x * 2 5 | 6 | def triple(): Int = x * 3 7 | } 8 | -------------------------------------------------------------------------------- /src/sbt-test/sbt-jacoco/source-settings/src/test/scala/jacocotest/TestClassTests.scala: -------------------------------------------------------------------------------- 1 | package jacocotest 2 | 3 | import org.scalatest.flatspec._ 4 | 5 | class TestClassTests extends AnyFlatSpec { 6 | "TestClass" should "double a number correctly" in { 7 | assert(new TestClass(2).double === 4) 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/sbt-test/sbt-jacoco/source-settings/test: -------------------------------------------------------------------------------- 1 | > clean 2 | 3 | # coverage should pass 4 | > jacoco 5 | 6 | # instrumented classes 7 | $ exists target/scala-2.12/jacoco/instrumented-classes 8 | 9 | # results 10 | $ exists target/scala-2.12/jacoco/data/jacoco.exec 11 | 12 | # html report 13 | $ exists target/scala-2.12/jacoco/report/html/index.html -------------------------------------------------------------------------------- /src/sbt-test/sbt-jacoco/unmanaged-resources/build.sbt: -------------------------------------------------------------------------------- 1 | name := "jacocoTest" 2 | organization := "com.navetas" 3 | 4 | scalaVersion := "2.12.17" 5 | scalacOptions ++= Seq("-deprecation", "-optimize", "-unchecked", "-Xlint", "-language:_") 6 | 7 | libraryDependencies += "org.scalatest" %% "scalatest" % "3.2.19" % "test" 8 | 9 | Compile / unmanagedResourceDirectories += sourceDirectory.value / "unmanaged" 10 | -------------------------------------------------------------------------------- /src/sbt-test/sbt-jacoco/unmanaged-resources/project/plugins.sbt: -------------------------------------------------------------------------------- 1 | { 2 | val pluginVersion = System.getProperty("plugin.version") 3 | if (pluginVersion == null) 4 | throw new RuntimeException("""|The system property 'plugin.version' is not defined. 5 | |Specify this property using the parameter -Dplugin.version=""".stripMargin) 6 | else addSbtPlugin("com.github.sbt" % "sbt-jacoco" % pluginVersion) 7 | } 8 | -------------------------------------------------------------------------------- /src/sbt-test/sbt-jacoco/unmanaged-resources/src/main/scala/jacocotest/PropertyUtils.scala: -------------------------------------------------------------------------------- 1 | package jacocotest 2 | 3 | import java.{util => ju} 4 | 5 | object PropertyUtils { 6 | private val props = new ju.Properties() 7 | props.load(getClass.getResourceAsStream("/info.properties")) 8 | 9 | def getFoo(): Int = { 10 | props.getProperty("foo").toInt 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/sbt-test/sbt-jacoco/unmanaged-resources/src/test/scala/jacocotest/PropertyUtilsSpec.scala: -------------------------------------------------------------------------------- 1 | package jacocotest 2 | 3 | import org.scalatest.flatspec._ 4 | import org.scalatest.matchers.should.Matchers 5 | 6 | class PropertyUtilsSpec extends AnyFlatSpec with Matchers { 7 | "PropertyUtils" should "load foo" in { 8 | PropertyUtils.getFoo() shouldBe 182 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/sbt-test/sbt-jacoco/unmanaged-resources/src/unmanaged/info.properties: -------------------------------------------------------------------------------- 1 | foo=182 -------------------------------------------------------------------------------- /src/sbt-test/sbt-jacoco/unmanaged-resources/test: -------------------------------------------------------------------------------- 1 | > clean 2 | 3 | # coverage should pass 4 | > jacoco 5 | 6 | # instrumented classes 7 | $ exists target/scala-2.12/jacoco/instrumented-classes 8 | 9 | # results 10 | $ exists target/scala-2.12/jacoco/data/jacoco.exec 11 | 12 | # html report 13 | $ exists target/scala-2.12/jacoco/report/html/index.html -------------------------------------------------------------------------------- /src/sbt-test/sbt-jacoco/xml-report/build.sbt: -------------------------------------------------------------------------------- 1 | name := "jacocoTest" 2 | organization := "com.navetas" 3 | 4 | scalaVersion := "2.12.17" 5 | scalacOptions ++= Seq("-deprecation", "-optimize", "-unchecked", "-Xlint", "-language:_") 6 | 7 | libraryDependencies += "org.scalatest" %% "scalatest" % "3.2.19" % "test" 8 | 9 | Test / jacocoReportSettings := JacocoReportSettings() 10 | .withFormats( 11 | JacocoReportFormats.XML 12 | ) 13 | -------------------------------------------------------------------------------- /src/sbt-test/sbt-jacoco/xml-report/project/plugins.sbt: -------------------------------------------------------------------------------- 1 | { 2 | val pluginVersion = System.getProperty("plugin.version") 3 | if (pluginVersion == null) 4 | throw new RuntimeException("""|The system property 'plugin.version' is not defined. 5 | |Specify this property using the parameter -Dplugin.version=""".stripMargin) 6 | else addSbtPlugin("com.github.sbt" % "sbt-jacoco" % pluginVersion) 7 | } 8 | -------------------------------------------------------------------------------- /src/sbt-test/sbt-jacoco/xml-report/src/main/scala/jacocotest/TestClass.scala: -------------------------------------------------------------------------------- 1 | package jacocotest 2 | 3 | case class TestClass(val x: Int) { 4 | def double(): Int = x * 2 5 | 6 | def triple(): Int = x * 3 7 | } 8 | -------------------------------------------------------------------------------- /src/sbt-test/sbt-jacoco/xml-report/src/test/scala/jacocotest/TestClassTests.scala: -------------------------------------------------------------------------------- 1 | package jacocotest 2 | 3 | import org.scalatest.flatspec._ 4 | 5 | class TestClassTests extends AnyFlatSpec { 6 | "TestClass" should "double a number correctly" in { 7 | assert(new TestClass(2).double === 4) 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/sbt-test/sbt-jacoco/xml-report/test: -------------------------------------------------------------------------------- 1 | > clean 2 | 3 | # coverage should pass 4 | > jacoco 5 | 6 | # instrumented classes 7 | $ exists target/scala-2.12/jacoco/instrumented-classes 8 | 9 | # results 10 | $ exists target/scala-2.12/jacoco/data/jacoco.exec 11 | 12 | # report formats 13 | $ absent target/scala-2.12/jacoco/report/html 14 | $ exists target/scala-2.12/jacoco/report/jacoco.xml 15 | $ absent target/scala-2.12/jacoco/report/jacoco.csv -------------------------------------------------------------------------------- /src/test/scala-sbt-0.13/com/github/sbt/jacoco/TestCounters.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of sbt-jacoco. 3 | * 4 | * Copyright (c) Joachim Hofer & contributors 5 | * All rights reserved. 6 | * 7 | * This program and the accompanying materials 8 | * are made available under the terms of the Eclipse Public License v1.0 9 | * which accompanies this distribution, and is available at 10 | * http://www.eclipse.org/legal/epl-v10.html 11 | */ 12 | 13 | package com.github.sbt.jacoco 14 | 15 | import java.io.File 16 | 17 | import org.jacoco.core.analysis.{IBundleCoverage, ICounter} 18 | import org.mockito.Mockito.{mock, when} 19 | import com.github.sbt.jacoco.report.{JacocoReportSettings, JacocoSourceSettings, JacocoThresholds, Report} 20 | import sbt.Keys.TaskStreams 21 | import sbt.Logger 22 | 23 | class TestCounters { 24 | private val mockStreams = mock(classOf[TaskStreams]) 25 | private val mockICounter = mock(classOf[ICounter]) 26 | private val mockBundle = mock(classOf[IBundleCoverage]) 27 | private val mockLog = mock(classOf[Logger]) 28 | 29 | private val report = 30 | new Report( 31 | streams = mockStreams, 32 | sourceSettings = JacocoSourceSettings(), 33 | reportSettings = JacocoReportSettings( 34 | thresholds = 35 | JacocoThresholds(instruction = 35, method = 40, branch = 30, complexity = 35, line = 50, clazz = 40) 36 | ), 37 | reportDirectory = new File("."), 38 | executionDataFiles = Nil, 39 | classDirectories = Nil, 40 | sourceDirectories = Nil, 41 | checkCoverage = true 42 | ) 43 | 44 | when[Logger](mockStreams.log).thenReturn(mockLog) 45 | when(mockBundle.getLineCounter).thenReturn(mockICounter) 46 | when(mockBundle.getInstructionCounter).thenReturn(mockICounter) 47 | when(mockBundle.getBranchCounter).thenReturn(mockICounter) 48 | when(mockBundle.getMethodCounter).thenReturn(mockICounter) 49 | when(mockBundle.getComplexityCounter).thenReturn(mockICounter) 50 | when(mockBundle.getClassCounter).thenReturn(mockICounter) 51 | 52 | def stubCounters(missedCount: Int, totalCount: Int, coveredRatio: Double): Unit = { 53 | when(mockICounter.getMissedCount).thenReturn(missedCount) 54 | when(mockICounter.getTotalCount).thenReturn(totalCount) 55 | when(mockICounter.getCoveredRatio).thenReturn(coveredRatio) 56 | } 57 | 58 | def stubCounters(missedCount: Seq[Int], totalCount: Seq[Int], coveredRatio: Seq[Double]): Unit = { 59 | missedCount.foldLeft(when(mockICounter.getMissedCount)) { (acc, c) => 60 | acc.thenReturn(c) 61 | } 62 | totalCount.foldLeft(when(mockICounter.getTotalCount)) { (acc, c) => 63 | acc.thenReturn(c) 64 | } 65 | coveredRatio.foldLeft(when(mockICounter.getCoveredRatio)) { (acc, c) => 66 | acc.thenReturn(c) 67 | } 68 | } 69 | 70 | def checkCounter(required: Double): Boolean = { 71 | report.checkCounter("foo", mockICounter, required, StringBuilder.newBuilder) 72 | } 73 | 74 | def checkBundle(): Boolean = { 75 | report.checkCoverage(mockBundle) 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/test/scala-sbt-1.0/com/github/sbt/jacoco/TestCounters.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of sbt-jacoco. 3 | * 4 | * Copyright (c) Joachim Hofer & contributors 5 | * All rights reserved. 6 | * 7 | * This program and the accompanying materials 8 | * are made available under the terms of the Eclipse Public License v1.0 9 | * which accompanies this distribution, and is available at 10 | * http://www.eclipse.org/legal/epl-v10.html 11 | */ 12 | 13 | package com.github.sbt.jacoco 14 | 15 | import java.io.File 16 | 17 | import org.jacoco.core.analysis.{IBundleCoverage, ICounter} 18 | import org.mockito.Mockito.{mock, when} 19 | import com.github.sbt.jacoco.report.{JacocoReportSettings, JacocoSourceSettings, JacocoThresholds, Report} 20 | import sbt.Keys.TaskStreams 21 | import sbt.internal.util.ManagedLogger 22 | 23 | class TestCounters { 24 | private val mockStreams = mock(classOf[TaskStreams]) 25 | private val mockICounter = mock(classOf[ICounter]) 26 | private val mockBundle = mock(classOf[IBundleCoverage]) 27 | private val mockLog = mock(classOf[ManagedLogger]) 28 | 29 | private val report = 30 | new Report( 31 | streams = mockStreams, 32 | sourceSettings = JacocoSourceSettings(), 33 | reportSettings = JacocoReportSettings( 34 | thresholds = 35 | JacocoThresholds(instruction = 35, method = 40, branch = 30, complexity = 35, line = 50, clazz = 40) 36 | ), 37 | reportDirectory = new File("."), 38 | executionDataFiles = Nil, 39 | classDirectories = Nil, 40 | sourceDirectories = Nil, 41 | checkCoverage = true 42 | ) 43 | 44 | when[ManagedLogger](mockStreams.log).thenReturn(mockLog) 45 | when(mockBundle.getLineCounter).thenReturn(mockICounter) 46 | when(mockBundle.getInstructionCounter).thenReturn(mockICounter) 47 | when(mockBundle.getBranchCounter).thenReturn(mockICounter) 48 | when(mockBundle.getMethodCounter).thenReturn(mockICounter) 49 | when(mockBundle.getComplexityCounter).thenReturn(mockICounter) 50 | when(mockBundle.getClassCounter).thenReturn(mockICounter) 51 | 52 | def stubCounters(missedCount: Int, totalCount: Int, coveredRatio: Double): Unit = { 53 | when(mockICounter.getMissedCount).thenReturn(missedCount) 54 | when(mockICounter.getTotalCount).thenReturn(totalCount) 55 | when(mockICounter.getCoveredRatio).thenReturn(coveredRatio) 56 | } 57 | 58 | def stubCounters(missedCount: Seq[Int], totalCount: Seq[Int], coveredRatio: Seq[Double]): Unit = { 59 | missedCount.foldLeft(when(mockICounter.getMissedCount)) { (acc, c) => 60 | acc.thenReturn(c) 61 | } 62 | totalCount.foldLeft(when(mockICounter.getTotalCount)) { (acc, c) => 63 | acc.thenReturn(c) 64 | } 65 | coveredRatio.foldLeft(when(mockICounter.getCoveredRatio)) { (acc, c) => 66 | acc.thenReturn(c) 67 | } 68 | } 69 | 70 | def checkCounter(required: Double): Boolean = { 71 | report.checkCounter("foo", mockICounter, required, StringBuilder.newBuilder) 72 | } 73 | 74 | def checkBundle(): Boolean = { 75 | report.checkCoverage(mockBundle) 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/test/scala/com/github/sbt/jacoco/ReportSpec.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of sbt-jacoco. 3 | * 4 | * Copyright (c) Joachim Hofer & contributors 5 | * All rights reserved. 6 | * 7 | * This program and the accompanying materials 8 | * are made available under the terms of the Eclipse Public License v1.0 9 | * which accompanies this distribution, and is available at 10 | * http://www.eclipse.org/legal/epl-v10.html 11 | */ 12 | 13 | package com.github.sbt.jacoco 14 | 15 | import org.scalatest.flatspec.AnyFlatSpec 16 | import org.scalatest.matchers.should.Matchers 17 | 18 | class ReportSpec extends AnyFlatSpec with Matchers { 19 | val testCounters = new TestCounters() 20 | 21 | "Report.checkCounter" should "return false if coverage is less than required" in { 22 | testCounters.stubCounters(30, 40, 0.25) 23 | testCounters.checkCounter(50) shouldBe false 24 | } 25 | 26 | it should "return true if coverage is more than required" in { 27 | testCounters.stubCounters(10, 40, 0.75) 28 | testCounters.checkCounter(50) shouldBe true 29 | } 30 | 31 | it should "if coverage is equals to required" in { 32 | testCounters.stubCounters(20, 40, 0.50) 33 | testCounters.checkCounter(50) shouldBe true 34 | } 35 | 36 | it should "return true if required coverage is 0 and ratio is NaN" in { 37 | testCounters.stubCounters(0, 0, Double.NaN) 38 | testCounters.checkCounter(0) shouldBe true 39 | } 40 | 41 | it should "return true if required coverage is 0 and ratio is 0" in { 42 | testCounters.stubCounters(40, 0, 0) 43 | testCounters.checkCounter(0) shouldBe true 44 | } 45 | 46 | "Report.checkCoverage" should "return true if all required coverage metrics are met" in { 47 | testCounters.stubCounters(10, 40, 0.75) 48 | testCounters.checkBundle() shouldBe true 49 | } 50 | 51 | it should "return false if at least one metric is not met" in { 52 | testCounters.stubCounters(Seq(10, 30), Seq(40), Seq(0.75, 0.25)) 53 | testCounters.checkBundle() shouldBe false 54 | } 55 | } 56 | --------------------------------------------------------------------------------