diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml new file mode 100644 index 0000000..a8189eb --- /dev/null +++ b/.github/workflows/gradle.yml @@ -0,0 +1,38 @@ +# This workflow will build a Java project with Gradle +# For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-gradle + +name: Java CI with Gradle + +on: + push: + branches: [ master ] + pull_request: + branches: [ master ] + +jobs: + build: + strategy: + fail-fast: false + matrix: + include: + - name: 'ignore warnings' + additional-check-args: '' + continue-on-error: false + - name: 'fail on warning' + additional-check-args: '--warning-mode=fail -PfailOnWarning' + continue-on-error: true + + runs-on: ubuntu-latest + continue-on-error: ${{ matrix.continue-on-error }} + + steps: + - uses: actions/checkout@v2 + - name: Set up JDK 11 + uses: actions/setup-java@v2 + with: + java-version: '11' + distribution: 'adopt' + - name: Grant execute permission for gradlew + run: chmod +x gradlew + - name: Build with Gradle + run: ./gradlew --info --stacktrace check ${{ matrix.additional-check-args }} diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 1e5da93..0000000 --- a/.travis.yml +++ /dev/null @@ -1,3 +0,0 @@ -language: groovy -script: - - "./gradlew --info --stacktrace check" \ No newline at end of file diff --git a/README.md b/README.md index b44ec2e..f5fd7b1 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -[![Build Status](https://travis-ci.org/scoverage/gradle-scoverage.png?branch=master)](https://travis-ci.org/scoverage/gradle-scoverage) +[![Java CI with Gradle](https://github.com/scoverage/gradle-scoverage/actions/workflows/gradle.yml/badge.svg)](https://github.com/scoverage/gradle-scoverage/actions/workflows/gradle.yml) gradle-scoverage ================ @@ -43,7 +43,7 @@ You can find instructions on how to apply the plugin at http://plugins.gradle.or The plugin exposes multiple options that can be configured by setting them in an `scoverage` block within the project's build script. These options are as follows: -* `scoverageVersion = ` (default `"1.4.8`): The version of the scoverage scalac plugin. This (gradle) plugin +* `scoverageVersion = ` (default `"2.1.1`): The version of the scoverage scalac plugin. This (gradle) plugin should be compatible with all 1+ versions. * `scoverageScalaVersion = ` (default `detected`): The scala version of the scoverage scalac plugin. This @@ -64,6 +64,10 @@ required for the validation to pass (otherwise `checkScoverage` will fail the bu `checkScoverage` task. For more information on the different types, please refer to the documentation of the scalac plugin (https://github.com/scoverage/scalac-scoverage-plugin). +* `excludedFiles = ` (default `not set`): Comma separated list of regexes for files to exclude from coverage. + +* `excludedPackages = ` (default `not set`): Comma separated list of regexes for packages, classes and modules to exclude from coverage. + #### Multiple check tasks It is possible to configure multiple checks; for instance, one check for a statement rate and another for a branch rate: @@ -103,18 +107,15 @@ scoverage { coverageType = CoverageType.Statement } } -``` - -### Run without normal compilation - -By default, running any of the plugin tasks will compile the code both using "normal" compilation (`compileScala`) -and using the scoverage scalac plugin (`compileScoverageScala`). - -In cases where you only wish to generate reports / validate coverage, but are not interested in publishing the code, -it is possible to only compile the code with the scoverage scalac plugin, thus reducing build times significantly. -In order to do so, simply add the arguments `-x compileScala` to the gradle execution. -For example: `gradle reportScoverage -x compileScala`. +``` +In case you use the Kotlin DSL, the following snippet can give you pointers for configuring Scoverage: +```kotlin +scoverage { + minimumRate.set(BigDecimal("0.80")) + excludedPackages.set(listOf("com.example.scala.demo")) +} +``` ### Compatibility with Consistent Versions Plugin @@ -122,6 +123,18 @@ In order for the plugin to work alongside [Palantir's consistent versions plugin the Scala version must be manually configured (via `scoverageScalaVersion`); otherwise, the plugin will attempt to resolve the compilation classpath, which is prohibited by the versions plugin. +Migration to 8.x +---------------- + +* Requires scoverage 2.0 +* Adds support for Scala 3 +* Drops support for disabling normal compilation + +Migration to 7.x +---------------- + +* Running without normal compilation is now made with `-PscoverageCompileOnly` instead of `-x compileScala`. + Migration to 5.x ---------------- @@ -178,4 +191,4 @@ task aggregateScoverage(type: org.scoverage.ScoverageAggregate) checkScoverage { reportDir = file("$buildDir/scoverage-aggregate") } -``` \ No newline at end of file +``` diff --git a/build.gradle b/build.gradle index 2ccabbb..85cff81 100644 --- a/build.gradle +++ b/build.gradle @@ -1,17 +1,17 @@ plugins { id 'java-gradle-plugin' - id "com.gradle.plugin-publish" version "0.15.0" + id "com.gradle.plugin-publish" version "1.1.0" id "org.jetbrains.gradle.plugin.idea-ext" version "1.0" } repositories { - jcenter() + mavenCentral() } group 'org.scoverage' description = 'gradle-scoverage is a Gradle plugin for calculating code coverage using Scoverage' if (project.version == 'unspecified') { - version = '3.0.0-SNAPSHOT' + version = '8.0.0-SNAPSHOT' } ext { website = 'http://scoverage.org' @@ -22,31 +22,32 @@ ext { } gradlePlugin { + website = project.ext.website + vcsUrl = project.ext.vcsUrl + description = project.description plugins { gradleScoverage { id = 'org.scoverage' implementationClass = 'org.scoverage.ScoveragePlugin' displayName = 'Gradle Scoverage plugin' + description = 'gradle-scoverage is a Gradle plugin for calculating code coverage using Scoverage' + tags.set(['coverage', 'scala', 'scoverage']) } } } -pluginBundle { - website = project.website - vcsUrl = ext.vcsUrl - description = project.description - tags = ['coverage', 'scala', 'scoverage'] -} - apply plugin: 'maven-publish' apply plugin: 'groovy' -sourceCompatibility = '1.8' -targetCompatibility = '1.8' - +java { + sourceCompatibility = JavaVersion.VERSION_1_8 + targetCompatibility = JavaVersion.VERSION_1_8 +} dependencies { - compileOnly "org.scoverage:scalac-scoverage-plugin_2.13:1.4.2" + compileOnly 'org.scoverage:scalac-scoverage-plugin_2.13.14:2.1.1' + compileOnly 'org.scoverage:scalac-scoverage-reporter_2.13:2.1.1' + implementation group: 'commons-io', name: 'commons-io', version: '2.6' testImplementation 'junit:junit:4.12' @@ -84,7 +85,10 @@ task crossScalaVersionTest(type: Test) { classpath = sourceSets.crossScalaVersionTest.runtimeClasspath forkEvery = 1 // crucial to run every test in its own JVM - testLogging.showStandardStreams = true + testLogging { + events 'passed', 'failed', 'skipped' + showStandardStreams = System.env.CI == 'true' + } mustRunAfter test } @@ -96,7 +100,12 @@ task functionalTest(type: Test) { testClassesDirs = sourceSets.functionalTest.output classpath = sourceSets.functionalTest.runtimeClasspath - testLogging.showStandardStreams = true + testLogging { + events 'passed', 'failed', 'skipped' + showStandardStreams = System.env.CI == 'true' + } + + systemProperty 'failOnWarning', project.hasProperty('failOnWarning') mustRunAfter crossScalaVersionTest } @@ -108,12 +117,7 @@ gradlePlugin { task groovydocJar(type: Jar, dependsOn: groovydoc) { from "$buildDir/docs/groovydoc" - classifier 'groovydoc' -} - -task sourcesJar(type: Jar) { - from sourceSets.main.allSource - classifier 'sources' + archiveClassifier.set('groovydoc') } def propOrDefault(String property) { @@ -171,7 +175,6 @@ publishing { } from components.java artifact groovydocJar - artifact sourcesJar } } } @@ -181,6 +184,7 @@ if (project.properties.containsKey('signing.keyId')) { signing { sign publishing.publications.mavenJava } + project.tasks.publishMavenJavaPublicationToMavenRepository.inputs.files(project.tasks.signMavenJavaPublication) } // see https://stackoverflow.com/questions/44679007 @@ -200,4 +204,4 @@ idea.project.settings { taskTriggers { beforeBuild fixIdeaPluginClasspath, pluginUnderTestMetadata } -} \ No newline at end of file +} diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 5c2d1cf..7454180 100644 Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 69a9715..7dbd4a7 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.11.1-bin.zip +distributionSha256Sum=f397b287023acdba1e9f6fc5ea72d22dd63669d59ed4a289a29b1a76eee151c6 zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew index b0d6d0a..1b6c787 100755 --- a/gradlew +++ b/gradlew @@ -1,13 +1,13 @@ -#!/usr/bin/env sh +#!/bin/sh # -# Copyright 2015 the original author or authors. +# Copyright © 2015-2021 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # -# http://www.apache.org/licenses/LICENSE-2.0 +# https://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, @@ -17,78 +17,113 @@ # ############################################################################## -## -## Gradle start up script for UN*X -## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# ############################################################################## # Attempt to set APP_HOME + # Resolve links: $0 may be a link -PRG="$0" -# Need this for relative symlinks. -while [ -h "$PRG" ] ; do - ls=`ls -ld "$PRG"` - link=`expr "$ls" : '.*-> \(.*\)$'` - if expr "$link" : '/.*' > /dev/null; then - PRG="$link" - else - PRG=`dirname "$PRG"`"/$link" - fi +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac done -SAVED="`pwd`" -cd "`dirname \"$PRG\"`/" >/dev/null -APP_HOME="`pwd -P`" -cd "$SAVED" >/dev/null + +APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit APP_NAME="Gradle" -APP_BASE_NAME=`basename "$0"` +APP_BASE_NAME=${0##*/} # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' # Use the maximum available, or set MAX_FD != -1 to use that value. -MAX_FD="maximum" +MAX_FD=maximum warn () { echo "$*" -} +} >&2 die () { echo echo "$*" echo exit 1 -} +} >&2 # OS specific support (must be 'true' or 'false'). cygwin=false msys=false darwin=false nonstop=false -case "`uname`" in - CYGWIN* ) - cygwin=true - ;; - Darwin* ) - darwin=true - ;; - MINGW* ) - msys=true - ;; - NONSTOP* ) - nonstop=true - ;; +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; esac CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + # Determine the Java command to use to start the JVM. if [ -n "$JAVA_HOME" ] ; then if [ -x "$JAVA_HOME/jre/sh/java" ] ; then # IBM's JDK on AIX uses strange locations for the executables - JAVACMD="$JAVA_HOME/jre/sh/java" + JAVACMD=$JAVA_HOME/jre/sh/java else - JAVACMD="$JAVA_HOME/bin/java" + JAVACMD=$JAVA_HOME/bin/java fi if [ ! -x "$JAVACMD" ] ; then die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME @@ -97,7 +132,7 @@ Please set the JAVA_HOME variable in your environment to match the location of your Java installation." fi else - JAVACMD="java" + JAVACMD=java which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. Please set the JAVA_HOME variable in your environment to match the @@ -105,84 +140,95 @@ location of your Java installation." fi # Increase the maximum file descriptors if we can. -if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then - MAX_FD_LIMIT=`ulimit -H -n` - if [ $? -eq 0 ] ; then - if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then - MAX_FD="$MAX_FD_LIMIT" - fi - ulimit -n $MAX_FD - if [ $? -ne 0 ] ; then - warn "Could not set maximum file descriptor limit: $MAX_FD" - fi - else - warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" - fi +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac fi -# For Darwin, add options to specify how the application appears in the dock -if $darwin; then - GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" -fi +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) -# For Cygwin, switch paths to Windows format before running java -if $cygwin ; then - APP_HOME=`cygpath --path --mixed "$APP_HOME"` - CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` - JAVACMD=`cygpath --unix "$JAVACMD"` - - # We build the pattern for arguments to be converted via cygpath - ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` - SEP="" - for dir in $ROOTDIRSRAW ; do - ROOTDIRS="$ROOTDIRS$SEP$dir" - SEP="|" - done - OURCYGPATTERN="(^($ROOTDIRS))" - # Add a user-defined pattern to the cygpath arguments - if [ "$GRADLE_CYGPATTERN" != "" ] ; then - OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" - fi # Now convert the arguments - kludge to limit ourselves to /bin/sh - i=0 - for arg in "$@" ; do - CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` - CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option - - if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition - eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` - else - eval `echo args$i`="\"$arg\"" + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) fi - i=$((i+1)) + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg done - case $i in - (0) set -- ;; - (1) set -- "$args0" ;; - (2) set -- "$args0" "$args1" ;; - (3) set -- "$args0" "$args1" "$args2" ;; - (4) set -- "$args0" "$args1" "$args2" "$args3" ;; - (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; - (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; - (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; - (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; - (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; - esac fi -# Escape application args -save () { - for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done - echo " " -} -APP_ARGS=$(save "$@") +# Collect all arguments for the java command; +# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of +# shell script including quotes and variable substitutions, so put them in +# double quotes to make sure that they get re-expanded; and +# * put everything else in single quotes, so that it's not re-expanded. -# Collect all arguments for the java command, following the shell quoting and substitution rules -eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" -# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong -if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then - cd "$(dirname "$0")" -fi +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat index 15e1ee3..ac1b06f 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -5,7 +5,7 @@ @rem you may not use this file except in compliance with the License. @rem You may obtain a copy of the License at @rem -@rem http://www.apache.org/licenses/LICENSE-2.0 +@rem https://www.apache.org/licenses/LICENSE-2.0 @rem @rem Unless required by applicable law or agreed to in writing, software @rem distributed under the License is distributed on an "AS IS" BASIS, @@ -29,6 +29,9 @@ if "%DIRNAME%" == "" set DIRNAME=. set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" @@ -37,7 +40,7 @@ if defined JAVA_HOME goto findJavaFromJavaHome set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 -if "%ERRORLEVEL%" == "0" goto init +if "%ERRORLEVEL%" == "0" goto execute echo. echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. @@ -51,7 +54,7 @@ goto fail set JAVA_HOME=%JAVA_HOME:"=% set JAVA_EXE=%JAVA_HOME%/bin/java.exe -if exist "%JAVA_EXE%" goto init +if exist "%JAVA_EXE%" goto execute echo. echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% @@ -61,28 +64,14 @@ echo location of your Java installation. goto fail -:init -@rem Get command-line arguments, handling Windows variants - -if not "%OS%" == "Windows_NT" goto win9xME_args - -:win9xME_args -@rem Slurp the command line arguments. -set CMD_LINE_ARGS= -set _SKIP=2 - -:win9xME_args_slurp -if "x%~1" == "x" goto execute - -set CMD_LINE_ARGS=%* - :execute @rem Setup the command line set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + @rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* :end @rem End local scope for the variables with windows NT shell diff --git a/src/crossScalaVersionTest/java/org/scoverage/Scala32Test.java b/src/crossScalaVersionTest/java/org/scoverage/Scala32Test.java new file mode 100644 index 0000000..d657410 --- /dev/null +++ b/src/crossScalaVersionTest/java/org/scoverage/Scala32Test.java @@ -0,0 +1,6 @@ +package org.scoverage; + +public class Scala32Test extends ScalaVersionTest { + + public Scala32Test() { super("3_2"); } +} diff --git a/src/crossScalaVersionTest/java/org/scoverage/ScalaCrossVersionAggregationTest.java b/src/crossScalaVersionTest/java/org/scoverage/ScalaCrossVersionAggregationTest.java index 62850aa..a7b7e5b 100644 --- a/src/crossScalaVersionTest/java/org/scoverage/ScalaCrossVersionAggregationTest.java +++ b/src/crossScalaVersionTest/java/org/scoverage/ScalaCrossVersionAggregationTest.java @@ -2,13 +2,6 @@ import org.junit.Assert; import org.junit.Test; -import org.junit.jupiter.api.Tag; -import org.junit.runner.RunWith; -import org.junit.runners.Suite; -import org.scoverage.ScoverageFunctionalTest; -import org.scoverage.ScoveragePlugin; - -import java.io.File; public class ScalaCrossVersionAggregationTest extends ScoverageFunctionalTest { @@ -37,7 +30,7 @@ public void checkAndAggregateAll() throws Exception { private void assertAggregationFilesExist() { Assert.assertTrue(resolve(reportDir(), "index.html").exists()); - Assert.assertTrue(resolve(reportDir(), "2_12/src/main/scala/org/hello/World2_12.scala.html").exists()); - Assert.assertTrue(resolve(reportDir(), "2_13/src/main/scala/org/hello/World2_13.scala.html").exists()); + Assert.assertTrue(resolve(reportDir(), "org/hello/World2_12.scala.html").exists()); + Assert.assertTrue(resolve(reportDir(), "org/hello/World2_13.scala.html").exists()); } } diff --git a/src/crossScalaVersionTest/java/org/scoverage/ScalaVersionTest.java b/src/crossScalaVersionTest/java/org/scoverage/ScalaVersionTest.java index 55a15c8..657ca8e 100644 --- a/src/crossScalaVersionTest/java/org/scoverage/ScalaVersionTest.java +++ b/src/crossScalaVersionTest/java/org/scoverage/ScalaVersionTest.java @@ -27,6 +27,6 @@ public void report() throws Exception { File reportDir = reportDir(projectDir().toPath().resolve(scalaVersion).toFile()); Assert.assertTrue(resolve(reportDir, "index.html").exists()); - Assert.assertTrue(resolve(reportDir, "src/main/scala/org/hello/World" + scalaVersion + ".scala.html").exists()); + Assert.assertTrue(resolve(reportDir, "org/hello/World" + scalaVersion + ".scala.html").exists()); } -} \ No newline at end of file +} diff --git a/src/crossScalaVersionTest/resources/projects/scala-multi-module-cross-version/2_12/build.gradle b/src/crossScalaVersionTest/resources/projects/scala-multi-module-cross-version/2_12/build.gradle index 88c5533..a510442 100644 --- a/src/crossScalaVersionTest/resources/projects/scala-multi-module-cross-version/2_12/build.gradle +++ b/src/crossScalaVersionTest/resources/projects/scala-multi-module-cross-version/2_12/build.gradle @@ -1,4 +1,4 @@ dependencies { - implementation group: 'org.scala-lang', name: 'scala-library', version: "2.12.8" + implementation group: 'org.scala-lang', name: 'scala-library', version: "2.12.17" testImplementation group: 'org.scalatest', name: "scalatest_2.12", version: scalatestVersion } diff --git a/src/crossScalaVersionTest/resources/projects/scala-multi-module-cross-version/2_13/build.gradle b/src/crossScalaVersionTest/resources/projects/scala-multi-module-cross-version/2_13/build.gradle index 4820211..ea57742 100644 --- a/src/crossScalaVersionTest/resources/projects/scala-multi-module-cross-version/2_13/build.gradle +++ b/src/crossScalaVersionTest/resources/projects/scala-multi-module-cross-version/2_13/build.gradle @@ -1,4 +1,4 @@ dependencies { - implementation group: 'org.scala-lang', name: 'scala-library', version: "2.13.1" + implementation group: 'org.scala-lang', name: 'scala-library', version: "2.13.10" testImplementation group: 'org.scalatest', name: "scalatest_2.13", version: scalatestVersion } diff --git a/src/crossScalaVersionTest/resources/projects/scala-multi-module-cross-version/3_2/build.gradle b/src/crossScalaVersionTest/resources/projects/scala-multi-module-cross-version/3_2/build.gradle new file mode 100644 index 0000000..93cb66d --- /dev/null +++ b/src/crossScalaVersionTest/resources/projects/scala-multi-module-cross-version/3_2/build.gradle @@ -0,0 +1,5 @@ +dependencies { + implementation 'org.scala-lang:scala3-library_3:3.4.2' + testImplementation 'org.scalatest:scalatest_3:3.2.16' + testImplementation "org.scalatestplus:junit-4-13_3:3.2.16.0" +} \ No newline at end of file diff --git a/src/crossScalaVersionTest/resources/projects/scala-multi-module-cross-version/3_2/src/main/scala/org/hello/World3_2.scala b/src/crossScalaVersionTest/resources/projects/scala-multi-module-cross-version/3_2/src/main/scala/org/hello/World3_2.scala new file mode 100644 index 0000000..2e2530b --- /dev/null +++ b/src/crossScalaVersionTest/resources/projects/scala-multi-module-cross-version/3_2/src/main/scala/org/hello/World3_2.scala @@ -0,0 +1,14 @@ +package org.hello + +class World3_2 { + + // Scala 3 enum to force Scala 3 (Dotty) compiler + enum Num(val value: String): + case Three extends Num("3") + case Two extends Num("2") + + def foo(): String = { + val s = Num.Three.value + Num.Two.value + s + } +} \ No newline at end of file diff --git a/src/crossScalaVersionTest/resources/projects/scala-multi-module-cross-version/3_2/src/test/scala/org/hello/World3_2Suite.scala b/src/crossScalaVersionTest/resources/projects/scala-multi-module-cross-version/3_2/src/test/scala/org/hello/World3_2Suite.scala new file mode 100644 index 0000000..4582dc8 --- /dev/null +++ b/src/crossScalaVersionTest/resources/projects/scala-multi-module-cross-version/3_2/src/test/scala/org/hello/World3_2Suite.scala @@ -0,0 +1,13 @@ +package org.hello + +import org.junit.runner.RunWith +import org.scalatest.funsuite.AnyFunSuite +import org.scalatestplus.junit.JUnitRunner + +@RunWith(classOf[JUnitRunner]) +class World3_2Suite extends AnyFunSuite { + + test("foo") { + new World3_2().foo() + } +} \ No newline at end of file diff --git a/src/crossScalaVersionTest/resources/projects/scala-multi-module-cross-version/build.gradle b/src/crossScalaVersionTest/resources/projects/scala-multi-module-cross-version/build.gradle index f9d1535..38e7b9f 100644 --- a/src/crossScalaVersionTest/resources/projects/scala-multi-module-cross-version/build.gradle +++ b/src/crossScalaVersionTest/resources/projects/scala-multi-module-cross-version/build.gradle @@ -4,7 +4,7 @@ plugins { allprojects { repositories { - jcenter() + mavenCentral() } } diff --git a/src/crossScalaVersionTest/resources/projects/scala-multi-module-cross-version/settings.gradle b/src/crossScalaVersionTest/resources/projects/scala-multi-module-cross-version/settings.gradle index f294f6a..9123650 100644 --- a/src/crossScalaVersionTest/resources/projects/scala-multi-module-cross-version/settings.gradle +++ b/src/crossScalaVersionTest/resources/projects/scala-multi-module-cross-version/settings.gradle @@ -1 +1 @@ -include '2_12', '2_13' \ No newline at end of file +include '2_12', '2_13', '3_2' \ No newline at end of file diff --git a/src/functionalTest/java/org/scoverage/CompositeBuildTest.java b/src/functionalTest/java/org/scoverage/CompositeBuildTest.java index 98a9ee8..4bd4bb7 100644 --- a/src/functionalTest/java/org/scoverage/CompositeBuildTest.java +++ b/src/functionalTest/java/org/scoverage/CompositeBuildTest.java @@ -18,14 +18,12 @@ public CompositeBuildTest() { super("composite-build"); } - @Ignore @Test public void buildComposite() { runComposite("clean", "build"); } - @Ignore @Test public void reportComposite() { @@ -34,7 +32,7 @@ public void reportComposite() { private AssertableBuildResult runComposite(String... arguments) { - List fullArguments = new ArrayList(); + List fullArguments = new ArrayList<>(); fullArguments.add("-p"); fullArguments.add("proj1"); fullArguments.add("--include-build"); diff --git a/src/functionalTest/java/org/scoverage/ScalaJavaAnnotationProcessorTest.java b/src/functionalTest/java/org/scoverage/ScalaJavaAnnotationProcessorTest.java new file mode 100644 index 0000000..c4c2e38 --- /dev/null +++ b/src/functionalTest/java/org/scoverage/ScalaJavaAnnotationProcessorTest.java @@ -0,0 +1,56 @@ +package org.scoverage; + +import org.gradle.testkit.runner.TaskOutcome; +import org.junit.Assert; +import org.junit.Test; + +import java.io.File; + +public class ScalaJavaAnnotationProcessorTest extends ScoverageFunctionalTest { + + public ScalaJavaAnnotationProcessorTest() { + super("scala-java-annotation-processor"); + } + + @Test + public void checkAndAggregateScoverage() throws Exception { + + AssertableBuildResult result = run("clean", ScoveragePlugin.getCHECK_NAME(), + ScoveragePlugin.getAGGREGATE_NAME()); + + result.assertTaskSkipped("java_only:" + ScoveragePlugin.getCOMPILE_NAME()); + + result.assertTaskSkipped(ScoveragePlugin.getREPORT_NAME()); + result.assertTaskSucceeded("mixed_scala_java:" + ScoveragePlugin.getREPORT_NAME()); + result.assertTaskSkipped("java_only:" + ScoveragePlugin.getREPORT_NAME()); + + result.assertTaskSucceeded(ScoveragePlugin.getCHECK_NAME()); + result.assertTaskSucceeded("mixed_scala_java:" + ScoveragePlugin.getCHECK_NAME()); + result.assertTaskSkipped("java_only:" + ScoveragePlugin.getCHECK_NAME()); + + result.assertTaskSucceeded(ScoveragePlugin.getAGGREGATE_NAME()); + + assertAllReportFilesExist(); + assertCoverage(100.0); + } + + private void assertAllReportFilesExist() { + + Assert.assertTrue(resolve(reportDir(), "index.html").exists()); + + assertMixedScalaJavaReportFilesExist(); + assertAggregationFilesExist(); + } + + private void assertAggregationFilesExist() { + + Assert.assertTrue(resolve(reportDir(), "org/hello/WorldScala.scala.html").exists()); + } + + private void assertMixedScalaJavaReportFilesExist() { + + File reportDir = reportDir(projectDir().toPath().resolve("mixed_scala_java").toFile()); + Assert.assertTrue(resolve(reportDir, "index.html").exists()); + Assert.assertTrue(resolve(reportDir, "org/hello/WorldScala.scala.html").exists()); + } +} diff --git a/src/functionalTest/java/org/scoverage/ScalaJavaMultiModuleTest.java b/src/functionalTest/java/org/scoverage/ScalaJavaMultiModuleTest.java index 6f094bf..2b09933 100644 --- a/src/functionalTest/java/org/scoverage/ScalaJavaMultiModuleTest.java +++ b/src/functionalTest/java/org/scoverage/ScalaJavaMultiModuleTest.java @@ -1,6 +1,5 @@ package org.scoverage; -import org.gradle.testkit.runner.TaskOutcome; import org.junit.Assert; import org.junit.Test; @@ -47,21 +46,21 @@ private void assertAllReportFilesExist() { private void assertAggregationFilesExist() { - Assert.assertTrue(resolve(reportDir(), "scala_only/src/main/scala/org/hello/WorldScalaOnly.scala.html").exists()); - Assert.assertTrue(resolve(reportDir(), "mixed_scala_java/src/main/scala/org/hello/WorldScala.scala.html").exists()); + Assert.assertTrue(resolve(reportDir(), "org/hello/WorldScalaOnly.scala.html").exists()); + Assert.assertTrue(resolve(reportDir(), "org/hello/WorldScala.scala.html").exists()); } private void assertScalaOnlyReportFilesExist() { File reportDir = reportDir(projectDir().toPath().resolve("scala_only").toFile()); Assert.assertTrue(resolve(reportDir, "index.html").exists()); - Assert.assertTrue(resolve(reportDir, "src/main/scala/org/hello/WorldScalaOnly.scala.html").exists()); + Assert.assertTrue(resolve(reportDir, "org/hello/WorldScalaOnly.scala.html").exists()); } private void assertMixedScalaJavaReportFilesExist() { File reportDir = reportDir(projectDir().toPath().resolve("mixed_scala_java").toFile()); Assert.assertTrue(resolve(reportDir, "index.html").exists()); - Assert.assertTrue(resolve(reportDir, "src/main/scala/org/hello/WorldScala.scala.html").exists()); + Assert.assertTrue(resolve(reportDir, "org/hello/WorldScala.scala.html").exists()); } } diff --git a/src/functionalTest/java/org/scoverage/ScalaMultiModuleTest.java b/src/functionalTest/java/org/scoverage/ScalaMultiModuleTest.java index 6442b1b..fa85c16 100644 --- a/src/functionalTest/java/org/scoverage/ScalaMultiModuleTest.java +++ b/src/functionalTest/java/org/scoverage/ScalaMultiModuleTest.java @@ -1,6 +1,7 @@ package org.scoverage; import org.junit.Assert; +import org.junit.Ignore; import org.junit.Test; import java.io.File; @@ -22,6 +23,17 @@ public void reportScoverage() { result.assertTaskExists("common:" + ScoveragePlugin.getREPORT_NAME()); } + @Test + public void reportScoverageParallel() { + + AssertableBuildResult result = dryRun("clean", ScoveragePlugin.getREPORT_NAME(), "--parallel"); + + result.assertTaskExists(ScoveragePlugin.getREPORT_NAME()); + result.assertTaskExists("a:" + ScoveragePlugin.getREPORT_NAME()); + result.assertTaskExists("b:" + ScoveragePlugin.getREPORT_NAME()); + result.assertTaskExists("common:" + ScoveragePlugin.getREPORT_NAME()); + } + @Test public void reportScoverageOnlyRoot() { @@ -49,10 +61,11 @@ public void reportScoverageOnlyA() { } @Test + @Ignore public void reportScoverageOnlyAWithoutNormalCompilation() { AssertableBuildResult result = run("clean", ":a:" + ScoveragePlugin.getREPORT_NAME(), - "-x", "compileScala"); + "-P" + ScoveragePlugin.getSCOVERAGE_COMPILE_ONLY_PROPERTY()); result.assertTaskSkipped("compileScala"); result.assertTaskSkipped("a:compileScala"); @@ -181,12 +194,13 @@ public void checkScoverageWithoutCoverageInA() throws Exception { } @Test + @Ignore public void checkScoverageWithoutNormalCompilationAndWithoutCoverageInCommon() throws Exception { AssertableBuildResult result = runAndFail("clean", ":a:test", ":common:test", "--tests", "org.hello.common.TestNothingCommonSuite", - "-x", "compileScala", + "-P" + ScoveragePlugin.getSCOVERAGE_COMPILE_ONLY_PROPERTY(), ScoveragePlugin.getCHECK_NAME()); result.assertTaskFailed("common:" + ScoveragePlugin.getCHECK_NAME()); @@ -243,10 +257,11 @@ public void checkAndAggregateScoverageWithoutCoverageInAll() throws Exception { } @Test + @Ignore public void aggregateScoverageWithoutNormalCompilation() throws Exception { AssertableBuildResult result = run("clean", ScoveragePlugin.getAGGREGATE_NAME(), - "-x", "compileScala"); + "-P" + ScoveragePlugin.getSCOVERAGE_COMPILE_ONLY_PROPERTY()); result.assertTaskSkipped("compileScala"); result.assertTaskSkipped("a:compileScala"); @@ -286,35 +301,35 @@ private void assertAllReportFilesExist() { private void assertAggregationFilesExist() { - Assert.assertTrue(resolve(reportDir(), "a/src/main/scala/org/hello/a/WorldA.scala.html").exists()); - Assert.assertTrue(resolve(reportDir(), "b/src/main/scala/org/hello/b/WorldB.scala.html").exists()); - Assert.assertTrue(resolve(reportDir(), "common/src/main/scala/org/hello/common/WorldCommon.scala.html").exists()); + Assert.assertTrue(resolve(reportDir(), "org/hello/a/WorldA.scala.html").exists()); + Assert.assertTrue(resolve(reportDir(), "org/hello/b/WorldB.scala.html").exists()); + Assert.assertTrue(resolve(reportDir(), "org/hello/common/WorldCommon.scala.html").exists()); } private void assertRootReportFilesExist() { Assert.assertTrue(resolve(reportDir(), "index.html").exists()); - Assert.assertTrue(resolve(reportDir(), "src/main/scala/org/hello/World.scala.html").exists()); + Assert.assertTrue(resolve(reportDir(), "org/hello/World.scala.html").exists()); } private void assertAReportFilesExist() { File reportDir = reportDir(projectDir().toPath().resolve("a").toFile()); Assert.assertTrue(resolve(reportDir, "index.html").exists()); - Assert.assertTrue(resolve(reportDir, "src/main/scala/org/hello/a/WorldA.scala.html").exists()); + Assert.assertTrue(resolve(reportDir, "org/hello/a/WorldA.scala.html").exists()); } private void assertBReportFilesExist() { File reportDir = reportDir(projectDir().toPath().resolve("b").toFile()); Assert.assertTrue(resolve(reportDir, "index.html").exists()); - Assert.assertTrue(resolve(reportDir, "src/main/scala/org/hello/b/WorldB.scala.html").exists()); + Assert.assertTrue(resolve(reportDir, "org/hello/b/WorldB.scala.html").exists()); } private void assertCommonReportFilesExist() { File reportDir = reportDir(projectDir().toPath().resolve("common").toFile()); Assert.assertTrue(resolve(reportDir, "index.html").exists()); - Assert.assertTrue(resolve(reportDir, "src/main/scala/org/hello/common/WorldCommon.scala.html").exists()); + Assert.assertTrue(resolve(reportDir, "org/hello/common/WorldCommon.scala.html").exists()); } } diff --git a/src/functionalTest/java/org/scoverage/ScalaMultiModuleWithMultipleTestTasksTest.java b/src/functionalTest/java/org/scoverage/ScalaMultiModuleWithMultipleTestTasksTest.java index 1905a1c..8d9982b 100644 --- a/src/functionalTest/java/org/scoverage/ScalaMultiModuleWithMultipleTestTasksTest.java +++ b/src/functionalTest/java/org/scoverage/ScalaMultiModuleWithMultipleTestTasksTest.java @@ -1,6 +1,7 @@ package org.scoverage; import org.junit.Assert; +import org.junit.Ignore; import org.junit.Test; @@ -188,12 +189,13 @@ public void checkScoverageWithoutCoverageInA() throws Exception { } @Test + @Ignore public void checkScoverageWithoutNormalCompilationAndWithoutCoverageInCommon() throws Exception { AssertableBuildResult result = runAndFail("clean", ":a:test", ":common:test", "--tests", "org.hello.common.TestNothingCommonSuite", - "-x", "compileScala", + "-P" + ScoveragePlugin.getSCOVERAGE_COMPILE_ONLY_PROPERTY(), ScoveragePlugin.getCHECK_NAME()); result.assertTaskFailed("common:" + ScoveragePlugin.getCHECK_NAME()); @@ -251,10 +253,11 @@ public void checkAndAggregateScoverageWithoutCoverageInAll() throws Exception { } @Test + @Ignore public void aggregateScoverageWithoutNormalCompilation() throws Exception { AssertableBuildResult result = run("clean", ScoveragePlugin.getAGGREGATE_NAME(), - "-x", "compileScala"); + "-P" + ScoveragePlugin.getSCOVERAGE_COMPILE_ONLY_PROPERTY()); result.assertTaskSkipped("compileScala"); result.assertTaskSkipped("a:compileScala"); @@ -294,35 +297,35 @@ private void assertAllReportFilesExist() { private void assertAggregationFilesExist() { - Assert.assertTrue(resolve(reportDir(), "a/src/main/scala/org/hello/a/WorldA.scala.html").exists()); - Assert.assertTrue(resolve(reportDir(), "b/src/main/scala/org/hello/b/WorldB.scala.html").exists()); - Assert.assertTrue(resolve(reportDir(), "common/src/main/scala/org/hello/common/WorldCommon.scala.html").exists()); + Assert.assertTrue(resolve(reportDir(), "org/hello/a/WorldA.scala.html").exists()); + Assert.assertTrue(resolve(reportDir(), "org/hello/b/WorldB.scala.html").exists()); + Assert.assertTrue(resolve(reportDir(), "org/hello/common/WorldCommon.scala.html").exists()); } private void assertRootReportFilesExist() { Assert.assertTrue(resolve(reportDir(), "index.html").exists()); - Assert.assertTrue(resolve(reportDir(), "src/main/scala/org/hello/World.scala.html").exists()); + Assert.assertTrue(resolve(reportDir(), "org/hello/World.scala.html").exists()); } private void assertAReportFilesExist() { File reportDir = reportDir(projectDir().toPath().resolve("a").toFile()); Assert.assertTrue(resolve(reportDir, "index.html").exists()); - Assert.assertTrue(resolve(reportDir, "src/main/scala/org/hello/a/WorldA.scala.html").exists()); + Assert.assertTrue(resolve(reportDir, "org/hello/a/WorldA.scala.html").exists()); } private void assertBReportFilesExist() { File reportDir = reportDir(projectDir().toPath().resolve("b").toFile()); Assert.assertTrue(resolve(reportDir, "index.html").exists()); - Assert.assertTrue(resolve(reportDir, "src/main/scala/org/hello/b/WorldB.scala.html").exists()); + Assert.assertTrue(resolve(reportDir, "org/hello/b/WorldB.scala.html").exists()); } private void assertCommonReportFilesExist() { File reportDir = reportDir(projectDir().toPath().resolve("common").toFile()); Assert.assertTrue(resolve(reportDir, "index.html").exists()); - Assert.assertTrue(resolve(reportDir, "src/main/scala/org/hello/common/WorldCommon.scala.html").exists()); + Assert.assertTrue(resolve(reportDir, "org/hello/common/WorldCommon.scala.html").exists()); } } diff --git a/src/functionalTest/java/org/scoverage/ScalaSingleModuleTest.java b/src/functionalTest/java/org/scoverage/ScalaSingleModuleTest.java index f93e184..dd74b13 100644 --- a/src/functionalTest/java/org/scoverage/ScalaSingleModuleTest.java +++ b/src/functionalTest/java/org/scoverage/ScalaSingleModuleTest.java @@ -1,14 +1,22 @@ package org.scoverage; import org.junit.Assert; +import org.junit.Ignore; import org.junit.Test; +import java.util.List; + public class ScalaSingleModuleTest extends ScoverageFunctionalTest { public ScalaSingleModuleTest() { super("scala-single-module"); } + @Override + protected List getVersionAgruments() { + return ScalaVersionArguments.version2; + } + @Test public void test() { @@ -91,7 +99,7 @@ public void reportScoverageWithExcludedClasses() throws Exception { result.assertTaskDoesntExist(ScoveragePlugin.getAGGREGATE_NAME()); Assert.assertTrue(resolve(reportDir(), "index.html").exists()); - Assert.assertFalse(resolve(reportDir(), "src/main/scala/org/hello/World.scala.html").exists()); + Assert.assertFalse(resolve(reportDir(), "org/hello/World.scala.html").exists()); assertCoverage(100.0); // coverage is 100 since no classes are covered // compiled class should exist in the default classes directory, but not in scoverage @@ -100,10 +108,11 @@ public void reportScoverageWithExcludedClasses() throws Exception { } @Test + @Ignore public void reportScoverageWithoutNormalCompilation() throws Exception { AssertableBuildResult result = run("clean", ScoveragePlugin.getREPORT_NAME(), - "-x", "compileScala"); + "-P" + ScoveragePlugin.getSCOVERAGE_COMPILE_ONLY_PROPERTY()); result.assertTaskSkipped("compileScala"); result.assertTaskSucceeded(ScoveragePlugin.getCOMPILE_NAME()); @@ -122,10 +131,10 @@ public void reportScoverageWithoutNormalCompilation() throws Exception { public void reportScoverageWithoutNormalCompilationAndWithExcludedClasses() throws Exception { AssertableBuildResult result = run("clean", ScoveragePlugin.getREPORT_NAME(), - "-PexcludedFile=.*", "-x", "compileScala"); + "-PexcludedFile=.*", "-P" + ScoveragePlugin.getSCOVERAGE_COMPILE_ONLY_PROPERTY()); Assert.assertTrue(resolve(reportDir(), "index.html").exists()); - Assert.assertFalse(resolve(reportDir(), "src/main/scala/org/hello/World.scala.html").exists()); + Assert.assertFalse(resolve(reportDir(), "org/hello/World.scala.html").exists()); assertCoverage(100.0); // coverage is 100 since no classes are covered // compiled class should exist in the default classes directory, but not in scoverage @@ -133,9 +142,9 @@ public void reportScoverageWithoutNormalCompilationAndWithExcludedClasses() thro Assert.assertFalse(resolve(buildDir(), "classes/scala/scoverage/org/hello/World.class").exists()); } - private void assertReportFilesExist() { + protected void assertReportFilesExist() { Assert.assertTrue(resolve(reportDir(), "index.html").exists()); - Assert.assertTrue(resolve(reportDir(), "src/main/scala/org/hello/World.scala.html").exists()); + Assert.assertTrue(resolve(reportDir(), "org/hello/World.scala.html").exists()); } -} \ No newline at end of file +} diff --git a/src/functionalTest/java/org/scoverage/ScalaSingleModuleTestScala3.java b/src/functionalTest/java/org/scoverage/ScalaSingleModuleTestScala3.java new file mode 100644 index 0000000..38bf371 --- /dev/null +++ b/src/functionalTest/java/org/scoverage/ScalaSingleModuleTestScala3.java @@ -0,0 +1,57 @@ +package org.scoverage; + +import org.junit.Assert; + +import java.util.List; + +public class ScalaSingleModuleTestScala3 extends ScalaSingleModuleTest { + + @Override + protected List getVersionAgruments() { + return ScalaVersionArguments.version3; + } + + @Override + public void checkScoverage() throws Exception { + AssertableBuildResult result = run("clean", ScoveragePlugin.getCHECK_NAME()); + + result.assertTaskSucceeded(ScoveragePlugin.getCOMPILE_NAME()); + result.assertTaskSucceeded(ScoveragePlugin.getREPORT_NAME()); + result.assertTaskSucceeded(ScoveragePlugin.getCHECK_NAME()); + result.assertTaskDoesntExist(ScoveragePlugin.getAGGREGATE_NAME()); + + assertReportFilesExist(); + assertCoverage(66.67); + } + + @Override + public void reportScoverageWithExcludedClasses() throws Exception { + AssertableBuildResult result = run("clean", ScoveragePlugin.getREPORT_NAME(), + "-PexcludedFile=.*"); + + result.assertTaskSucceeded(ScoveragePlugin.getCOMPILE_NAME()); + result.assertTaskSucceeded(ScoveragePlugin.getREPORT_NAME()); + result.assertTaskDoesntExist(ScoveragePlugin.getCHECK_NAME()); + result.assertTaskDoesntExist(ScoveragePlugin.getAGGREGATE_NAME()); + + Assert.assertTrue(resolve(reportDir(), "index.html").exists()); + Assert.assertFalse(resolve(reportDir(), "org/hello/World.scala.html").exists()); + assertCoverage(100.0); // coverage is 100 since no classes are covered + + // compiled class should exist in the default classes directory, but not in scoverage + Assert.assertTrue(resolve(buildDir(), "classes/scala/main/org/hello/World.class").exists()); + } + + @Override + public void reportScoverageWithoutNormalCompilationAndWithExcludedClasses() throws Exception { + AssertableBuildResult result = run("clean", ScoveragePlugin.getREPORT_NAME(), + "-PexcludedFile=.*", "-P" + ScoveragePlugin.getSCOVERAGE_COMPILE_ONLY_PROPERTY()); + + Assert.assertTrue(resolve(reportDir(), "index.html").exists()); + Assert.assertFalse(resolve(reportDir(), "org/hello/World.scala.html").exists()); + assertCoverage(100.0); // coverage is 100 since no classes are covered + + // compiled class should exist in the default classes directory, but not in scoverage + Assert.assertTrue(resolve(buildDir(), "classes/scala/main/org/hello/World.class").exists()); + } +} diff --git a/src/functionalTest/java/org/scoverage/ScalaSingleModuleWithDependencyManagerTest.java b/src/functionalTest/java/org/scoverage/ScalaSingleModuleWithDependencyManagerTest.java index e7f701a..3ce3b63 100644 --- a/src/functionalTest/java/org/scoverage/ScalaSingleModuleWithDependencyManagerTest.java +++ b/src/functionalTest/java/org/scoverage/ScalaSingleModuleWithDependencyManagerTest.java @@ -26,6 +26,6 @@ public void checkScoverage() throws Exception { private void assertReportFilesExist() { Assert.assertTrue(resolve(reportDir(), "index.html").exists()); - Assert.assertTrue(resolve(reportDir(), "src/main/scala/org/hello/World.scala.html").exists()); + Assert.assertTrue(resolve(reportDir(), "org/hello/World.scala.html").exists()); } -} \ No newline at end of file +} diff --git a/src/functionalTest/java/org/scoverage/ScalaSingleModuleWithMultipleTestTasksTest.java b/src/functionalTest/java/org/scoverage/ScalaSingleModuleWithMultipleTestTasksTest.java index 3d09d61..6b485b6 100644 --- a/src/functionalTest/java/org/scoverage/ScalaSingleModuleWithMultipleTestTasksTest.java +++ b/src/functionalTest/java/org/scoverage/ScalaSingleModuleWithMultipleTestTasksTest.java @@ -1,6 +1,7 @@ package org.scoverage; import org.junit.Assert; +import org.junit.Ignore; import org.junit.Test; public class ScalaSingleModuleWithMultipleTestTasksTest extends ScoverageFunctionalTest { @@ -109,7 +110,7 @@ public void reportScoverageWithExcludedClasses() throws Exception { result.assertTaskDoesntExist(ScoveragePlugin.getAGGREGATE_NAME()); Assert.assertTrue(resolve(reportDir(), "index.html").exists()); - Assert.assertFalse(resolve(reportDir(), "src/main/scala/org/hello/World.scala.html").exists()); + Assert.assertFalse(resolve(reportDir(), "org/hello/World.scala.html").exists()); assertCoverage(100.0); // coverage is 100 since no classes are covered // compiled class should exist in the default classes directory, but not in scoverage @@ -118,10 +119,11 @@ public void reportScoverageWithExcludedClasses() throws Exception { } @Test + @Ignore public void reportScoverageWithoutNormalCompilation() throws Exception { AssertableBuildResult result = run("clean", ScoveragePlugin.getREPORT_NAME(), - "-x", "compileScala"); + "-P" + ScoveragePlugin.getSCOVERAGE_COMPILE_ONLY_PROPERTY()); result.assertTaskSkipped("compileScala"); result.assertTaskSucceeded(ScoveragePlugin.getCOMPILE_NAME()); @@ -140,10 +142,10 @@ public void reportScoverageWithoutNormalCompilation() throws Exception { public void reportScoverageWithoutNormalCompilationAndWithExcludedClasses() throws Exception { AssertableBuildResult result = run("clean", ScoveragePlugin.getREPORT_NAME(), - "-PexcludedFile=.*", "-x", "compileScala"); + "-PexcludedFile=.*", "-P" + ScoveragePlugin.getSCOVERAGE_COMPILE_ONLY_PROPERTY()); Assert.assertTrue(resolve(reportDir(), "index.html").exists()); - Assert.assertFalse(resolve(reportDir(), "src/main/scala/org/hello/World.scala.html").exists()); + Assert.assertFalse(resolve(reportDir(), "org/hello/World.scala.html").exists()); assertCoverage(100.0); // coverage is 100 since no classes are covered // compiled class should exist in the default classes directory, but not in scoverage @@ -155,6 +157,6 @@ public void reportScoverageWithoutNormalCompilationAndWithExcludedClasses() thro private void assertReportFilesExist() { Assert.assertTrue(resolve(reportDir(), "index.html").exists()); - Assert.assertTrue(resolve(reportDir(), "src/main/scala/org/hello/World.scala.html").exists()); + Assert.assertTrue(resolve(reportDir(), "org/hello/World.scala.html").exists()); } } diff --git a/src/functionalTest/java/org/scoverage/ScalaVersionArguments.java b/src/functionalTest/java/org/scoverage/ScalaVersionArguments.java new file mode 100644 index 0000000..e2fbb1c --- /dev/null +++ b/src/functionalTest/java/org/scoverage/ScalaVersionArguments.java @@ -0,0 +1,33 @@ +package org.scoverage; + +import java.util.Arrays; +import java.util.List; + +public interface ScalaVersionArguments { + List version2WithLegacyScalatest = Arrays.asList( + "-PscalaVersionMajor=2", + "-PscalaVersionMinor=13", + "-PscalaVersionBuild=14", + "-PjunitVersion=5.3.2", + "-PjunitPlatformVersion=1.3.2", + "-PscalatestVersion=3.0.8" + ); + + List version2 = Arrays.asList( + "-PscalaVersionMajor=2", + "-PscalaVersionMinor=13", + "-PscalaVersionBuild=14", + "-PjunitVersion=5.3.2", + "-PjunitPlatformVersion=1.3.2", + "-PscalatestVersion=3.2.16" + ); + + List version3 = Arrays.asList( + "-PscalaVersionMajor=3", + "-PscalaVersionMinor=4", + "-PscalaVersionBuild=2", + "-PjunitVersion=5.3.2", + "-PjunitPlatformVersion=1.3.2", + "-PscalatestVersion=3.2.16" + ); +} diff --git a/src/functionalTest/java/org/scoverage/ScoverageFunctionalTest.java b/src/functionalTest/java/org/scoverage/ScoverageFunctionalTest.java index c1bd0b1..c1992d8 100644 --- a/src/functionalTest/java/org/scoverage/ScoverageFunctionalTest.java +++ b/src/functionalTest/java/org/scoverage/ScoverageFunctionalTest.java @@ -1,7 +1,7 @@ package org.scoverage; import groovy.util.Node; -import groovy.util.XmlParser; +import groovy.xml.XmlParser; import org.gradle.testkit.runner.BuildResult; import org.gradle.testkit.runner.BuildTask; import org.gradle.testkit.runner.GradleRunner; @@ -11,8 +11,6 @@ import java.io.File; import java.io.IOException; -import java.text.NumberFormat; -import java.text.ParseException; import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -87,7 +85,7 @@ protected AssertableBuildResult runAndFail(String... arguments) { protected AssertableBuildResult dryRun(String... arguments) { - List withDryArgument = new ArrayList(Arrays.asList(arguments)); + List withDryArgument = new ArrayList<>(Arrays.asList(arguments)); withDryArgument.add("--dry-run"); return run(withDryArgument.toArray(new String[]{})); } @@ -108,25 +106,28 @@ protected File resolve(File file, String relativePath) { return file.toPath().resolve(relativePath).toFile(); } - private Double coverage(File reportDir, CoverageType coverageType) throws IOException, SAXException, ParseException { + private Double coverage(File reportDir, CoverageType coverageType) throws IOException, SAXException, NumberFormatException { File reportFile = reportDir.toPath().resolve(coverageType.getFileName()).toFile(); Node xml = parser.parse(reportFile); Object attribute = xml.attribute(coverageType.getParamName()); - double rawValue = NumberFormat.getInstance().parse(attribute.toString()).doubleValue(); + double rawValue = Double.parseDouble(attribute.toString()); return coverageType.normalize(rawValue) * 100.0; } + protected List getVersionAgruments() { + return ScalaVersionArguments.version2WithLegacyScalatest; + } + private void configureArguments(String... arguments) { - List fullArguments = new ArrayList(); + List fullArguments = new ArrayList<>(getVersionAgruments()); - fullArguments.add("-PscalaVersionMajor=2"); - fullArguments.add("-PscalaVersionMinor=12"); - fullArguments.add("-PscalaVersionBuild=8"); - fullArguments.add("-PjunitVersion=5.3.2"); - fullArguments.add("-PjunitPlatformVersion=1.3.2"); - fullArguments.add("-PscalatestVersion=3.0.8"); + if (Boolean.parseBoolean(System.getProperty("failOnWarning"))) { + fullArguments.add("--warning-mode=fail"); + } else { + fullArguments.add("--warning-mode=all"); + } fullArguments.addAll(Arrays.asList(arguments)); runner.withArguments(fullArguments); diff --git a/src/functionalTest/resources/projects/composite-build/proj1/build.gradle b/src/functionalTest/resources/projects/composite-build/proj1/build.gradle index 897dc59..88cbf46 100644 --- a/src/functionalTest/resources/projects/composite-build/proj1/build.gradle +++ b/src/functionalTest/resources/projects/composite-build/proj1/build.gradle @@ -3,7 +3,7 @@ plugins { } repositories { - jcenter() + mavenCentral() } description = 'a single-module Scala project taking part in a composite build (1)' diff --git a/src/functionalTest/resources/projects/composite-build/proj2/build.gradle b/src/functionalTest/resources/projects/composite-build/proj2/build.gradle index 99bf400..8ea2bff 100644 --- a/src/functionalTest/resources/projects/composite-build/proj2/build.gradle +++ b/src/functionalTest/resources/projects/composite-build/proj2/build.gradle @@ -3,7 +3,7 @@ plugins { } repositories { - jcenter() + mavenCentral() } description = 'a single-module Scala project taking part in a composite build (2)' diff --git a/src/functionalTest/resources/projects/detect-scala-library/compile/build.gradle b/src/functionalTest/resources/projects/detect-scala-library/compile/build.gradle index be9936b..50a45ca 100644 --- a/src/functionalTest/resources/projects/detect-scala-library/compile/build.gradle +++ b/src/functionalTest/resources/projects/detect-scala-library/compile/build.gradle @@ -3,7 +3,7 @@ plugins { } repositories { - jcenter() + mavenCentral() } description = 'defines scala library using the "implementation" configuration' diff --git a/src/functionalTest/resources/projects/detect-scala-library/compileOnly/build.gradle b/src/functionalTest/resources/projects/detect-scala-library/compileOnly/build.gradle index 4dd36e8..8444413 100644 --- a/src/functionalTest/resources/projects/detect-scala-library/compileOnly/build.gradle +++ b/src/functionalTest/resources/projects/detect-scala-library/compileOnly/build.gradle @@ -3,7 +3,7 @@ plugins { } repositories { - jcenter() + mavenCentral() } description = 'defines scala library using the "compileOnly" configuration' diff --git a/src/functionalTest/resources/projects/detect-scala-library/dependency-management/build.gradle b/src/functionalTest/resources/projects/detect-scala-library/dependency-management/build.gradle index c4de0c2..0598896 100644 --- a/src/functionalTest/resources/projects/detect-scala-library/dependency-management/build.gradle +++ b/src/functionalTest/resources/projects/detect-scala-library/dependency-management/build.gradle @@ -1,10 +1,10 @@ plugins { - id 'io.spring.dependency-management' version "1.0.4.RELEASE" + id 'io.spring.dependency-management' version "1.1.5" id 'org.scoverage' } repositories { - jcenter() + mavenCentral() } description = 'defines scala library using the "implementation" configuration and the dependency-management plugin' diff --git a/src/functionalTest/resources/projects/detect-scala-library/gradle-consistent-versions/build.gradle b/src/functionalTest/resources/projects/detect-scala-library/gradle-consistent-versions/build.gradle index 9e4d81d..1b0f2f4 100644 --- a/src/functionalTest/resources/projects/detect-scala-library/gradle-consistent-versions/build.gradle +++ b/src/functionalTest/resources/projects/detect-scala-library/gradle-consistent-versions/build.gradle @@ -4,7 +4,7 @@ plugins { } repositories { - jcenter() + mavenCentral() } description = 'defines scala library using the "implementation" configuration and the gradle-consistent-versions plugin' diff --git a/src/functionalTest/resources/projects/detect-scala-library/implementation/build.gradle b/src/functionalTest/resources/projects/detect-scala-library/implementation/build.gradle index be9936b..50a45ca 100644 --- a/src/functionalTest/resources/projects/detect-scala-library/implementation/build.gradle +++ b/src/functionalTest/resources/projects/detect-scala-library/implementation/build.gradle @@ -3,7 +3,7 @@ plugins { } repositories { - jcenter() + mavenCentral() } description = 'defines scala library using the "implementation" configuration' diff --git a/src/functionalTest/resources/projects/multi-module-plugin-not-configured-for-scala/build.gradle b/src/functionalTest/resources/projects/multi-module-plugin-not-configured-for-scala/build.gradle index ba236a7..7fd12a9 100644 --- a/src/functionalTest/resources/projects/multi-module-plugin-not-configured-for-scala/build.gradle +++ b/src/functionalTest/resources/projects/multi-module-plugin-not-configured-for-scala/build.gradle @@ -6,7 +6,7 @@ description = 'a multi-module Scala and Java project that defines scoverage only allprojects { repositories { - jcenter() + mavenCentral() } } diff --git a/src/functionalTest/resources/projects/multiple-check-tasks/multiple-checks/build.gradle b/src/functionalTest/resources/projects/multiple-check-tasks/multiple-checks/build.gradle index 4c0a9fb..ba61184 100644 --- a/src/functionalTest/resources/projects/multiple-check-tasks/multiple-checks/build.gradle +++ b/src/functionalTest/resources/projects/multiple-check-tasks/multiple-checks/build.gradle @@ -3,7 +3,7 @@ plugins { } repositories { - jcenter() + mavenCentral() } description = 'a single-module Scala project that has multiple check configurations' diff --git a/src/functionalTest/resources/projects/multiple-check-tasks/no-check/build.gradle b/src/functionalTest/resources/projects/multiple-check-tasks/no-check/build.gradle index 3ad1fea..e9f2522 100644 --- a/src/functionalTest/resources/projects/multiple-check-tasks/no-check/build.gradle +++ b/src/functionalTest/resources/projects/multiple-check-tasks/no-check/build.gradle @@ -3,7 +3,7 @@ plugins { } repositories { - jcenter() + mavenCentral() } description = 'a single-module Scala project with no check configured' diff --git a/src/functionalTest/resources/projects/multiple-check-tasks/old-and-new-syntax/build.gradle b/src/functionalTest/resources/projects/multiple-check-tasks/old-and-new-syntax/build.gradle index 3e69976..1b5137e 100644 --- a/src/functionalTest/resources/projects/multiple-check-tasks/old-and-new-syntax/build.gradle +++ b/src/functionalTest/resources/projects/multiple-check-tasks/old-and-new-syntax/build.gradle @@ -3,7 +3,7 @@ plugins { } repositories { - jcenter() + mavenCentral() } description = 'a single-module Scala project that has multiple check configurations - some new syntax, some old' diff --git a/src/functionalTest/resources/projects/multiple-check-tasks/single-check-new-syntax/build.gradle b/src/functionalTest/resources/projects/multiple-check-tasks/single-check-new-syntax/build.gradle index 8be7d9a..c3b22ec 100644 --- a/src/functionalTest/resources/projects/multiple-check-tasks/single-check-new-syntax/build.gradle +++ b/src/functionalTest/resources/projects/multiple-check-tasks/single-check-new-syntax/build.gradle @@ -3,7 +3,7 @@ plugins { } repositories { - jcenter() + mavenCentral() } description = 'a single-module Scala project that has a single check configurations (with the new syntax)' diff --git a/src/functionalTest/resources/projects/multiple-check-tasks/single-check-old-syntax/build.gradle b/src/functionalTest/resources/projects/multiple-check-tasks/single-check-old-syntax/build.gradle index 719e14e..8d7c020 100644 --- a/src/functionalTest/resources/projects/multiple-check-tasks/single-check-old-syntax/build.gradle +++ b/src/functionalTest/resources/projects/multiple-check-tasks/single-check-old-syntax/build.gradle @@ -3,7 +3,7 @@ plugins { } repositories { - jcenter() + mavenCentral() } description = 'a single-module Scala project that has a single check configurations (with the old syntax)' diff --git a/src/functionalTest/resources/projects/scala-java-annotation-processor/build.gradle b/src/functionalTest/resources/projects/scala-java-annotation-processor/build.gradle new file mode 100644 index 0000000..c67b12d --- /dev/null +++ b/src/functionalTest/resources/projects/scala-java-annotation-processor/build.gradle @@ -0,0 +1,50 @@ +plugins { + id 'org.scoverage' apply false +} + +description = 'a multi-module Scala and Java project using a Java annotation processor' + +allprojects { + repositories { + mavenCentral() + } +} + +def lombokVersion = '1.18.20' + +subprojects { + apply plugin: 'java' + + dependencies { + testImplementation group: 'org.junit.platform', name: 'junit-platform-runner', version: junitPlatformVersion + + compileOnly "org.projectlombok:lombok:$lombokVersion" + annotationProcessor "org.projectlombok:lombok:$lombokVersion" + + testCompileOnly "org.projectlombok:lombok:$lombokVersion" + testAnnotationProcessor "org.projectlombok:lombok:$lombokVersion" + } + + test { + useJUnitPlatform() + } +} + +/* +Because the Scala compiler doesn't support annotation processors, Java files using a Java annotation +processor MUST be compiled by the Java compiler. So we can't use the same +configuration as in `scala-java-multi-module` here. + +// A common practice in mixed java/scala modules to make Java code able to import Scala code +ext.configureSources = { set, name -> + set.scala.srcDir("src/$name/java") + set.java.srcDirs = [] +} +configureSources(sourceSets.main, 'main') +configureSources(sourceSets.test, 'test') +*/ + +apply plugin: 'org.scoverage' +scoverage { + minimumRate = 0.5 +} diff --git a/src/functionalTest/resources/projects/scala-java-annotation-processor/java_only/build.gradle b/src/functionalTest/resources/projects/scala-java-annotation-processor/java_only/build.gradle new file mode 100644 index 0000000..69fe4c7 --- /dev/null +++ b/src/functionalTest/resources/projects/scala-java-annotation-processor/java_only/build.gradle @@ -0,0 +1,3 @@ +dependencies { + testRuntimeOnly group: 'org.junit.vintage', name: 'junit-vintage-engine', version: junitVersion +} diff --git a/src/functionalTest/resources/projects/scala-java-annotation-processor/java_only/src/main/java/org/hello/WorldJavaOnly.java b/src/functionalTest/resources/projects/scala-java-annotation-processor/java_only/src/main/java/org/hello/WorldJavaOnly.java new file mode 100644 index 0000000..27f643b --- /dev/null +++ b/src/functionalTest/resources/projects/scala-java-annotation-processor/java_only/src/main/java/org/hello/WorldJavaOnly.java @@ -0,0 +1,8 @@ +package org.hello; + +import lombok.Value; + +@Value +public class WorldJavaOnly { + private final String foo = "java_only"; +} diff --git a/src/functionalTest/resources/projects/scala-java-annotation-processor/java_only/src/test/java/org/hello/WorldJavaOnlyTest.java b/src/functionalTest/resources/projects/scala-java-annotation-processor/java_only/src/test/java/org/hello/WorldJavaOnlyTest.java new file mode 100644 index 0000000..9a029b2 --- /dev/null +++ b/src/functionalTest/resources/projects/scala-java-annotation-processor/java_only/src/test/java/org/hello/WorldJavaOnlyTest.java @@ -0,0 +1,11 @@ +package org.hello; + +import org.junit.Test; + +public class WorldJavaOnlyTest { + + @Test + public void foo() { + new WorldJavaOnly().foo(); + } +} diff --git a/src/functionalTest/resources/projects/scala-java-annotation-processor/lombok.config b/src/functionalTest/resources/projects/scala-java-annotation-processor/lombok.config new file mode 100644 index 0000000..3d2c462 --- /dev/null +++ b/src/functionalTest/resources/projects/scala-java-annotation-processor/lombok.config @@ -0,0 +1 @@ +lombok.accessors.fluent=true diff --git a/src/functionalTest/resources/projects/scala-java-annotation-processor/mixed_scala_java/build.gradle b/src/functionalTest/resources/projects/scala-java-annotation-processor/mixed_scala_java/build.gradle new file mode 100644 index 0000000..c0021f0 --- /dev/null +++ b/src/functionalTest/resources/projects/scala-java-annotation-processor/mixed_scala_java/build.gradle @@ -0,0 +1,14 @@ +apply plugin: 'scala' + +dependencies { + implementation group: 'org.scala-lang', name: 'scala-library', version: "${scalaVersionMajor}.${scalaVersionMinor}.${scalaVersionBuild}" + + testRuntimeOnly group: 'org.junit.vintage', name: 'junit-vintage-engine', version: junitVersion + + testImplementation group: 'org.scalatest', name: "scalatest_${scalaVersionMajor}.${scalaVersionMinor}", version: scalatestVersion +} + +apply plugin: 'org.scoverage' +scoverage { + minimumRate = 0.5 +} diff --git a/src/functionalTest/resources/projects/scala-java-annotation-processor/mixed_scala_java/src/main/java/org/hello/WorldJava.java b/src/functionalTest/resources/projects/scala-java-annotation-processor/mixed_scala_java/src/main/java/org/hello/WorldJava.java new file mode 100644 index 0000000..0bcf847 --- /dev/null +++ b/src/functionalTest/resources/projects/scala-java-annotation-processor/mixed_scala_java/src/main/java/org/hello/WorldJava.java @@ -0,0 +1,8 @@ +package org.hello; + +import lombok.Value; + +@Value +public class WorldJava { + private final String foo = "java"; +} diff --git a/src/functionalTest/resources/projects/scala-java-annotation-processor/mixed_scala_java/src/main/scala/org/hello/WorldScala.scala b/src/functionalTest/resources/projects/scala-java-annotation-processor/mixed_scala_java/src/main/scala/org/hello/WorldScala.scala new file mode 100644 index 0000000..3e46057 --- /dev/null +++ b/src/functionalTest/resources/projects/scala-java-annotation-processor/mixed_scala_java/src/main/scala/org/hello/WorldScala.scala @@ -0,0 +1,7 @@ +package org.hello + +class WorldScala { + private val worldJava = new WorldJava() + + def foo() = worldJava.foo() +} diff --git a/src/functionalTest/resources/projects/scala-java-annotation-processor/mixed_scala_java/src/test/java/org/hello/WorldJavaTest.java b/src/functionalTest/resources/projects/scala-java-annotation-processor/mixed_scala_java/src/test/java/org/hello/WorldJavaTest.java new file mode 100644 index 0000000..316815c --- /dev/null +++ b/src/functionalTest/resources/projects/scala-java-annotation-processor/mixed_scala_java/src/test/java/org/hello/WorldJavaTest.java @@ -0,0 +1,11 @@ +package org.hello; + +import org.junit.Test; + +public class WorldJavaTest { + + @Test + public void foo() { + new WorldJava().foo(); + } +} diff --git a/src/functionalTest/resources/projects/scala-java-annotation-processor/mixed_scala_java/src/test/scala/org/hello/WorldScalaSuite.scala b/src/functionalTest/resources/projects/scala-java-annotation-processor/mixed_scala_java/src/test/scala/org/hello/WorldScalaSuite.scala new file mode 100644 index 0000000..08edf75 --- /dev/null +++ b/src/functionalTest/resources/projects/scala-java-annotation-processor/mixed_scala_java/src/test/scala/org/hello/WorldScalaSuite.scala @@ -0,0 +1,13 @@ +package org.hello + +import org.junit.runner.RunWith +import org.scalatest.FunSuite +import org.scalatest.junit.JUnitRunner + +@RunWith(classOf[JUnitRunner]) +class WorldScalaSuite extends FunSuite { + + test("foo") { + new WorldScala().foo() + } +} diff --git a/src/functionalTest/resources/projects/scala-java-annotation-processor/settings.gradle b/src/functionalTest/resources/projects/scala-java-annotation-processor/settings.gradle new file mode 100644 index 0000000..57567e8 --- /dev/null +++ b/src/functionalTest/resources/projects/scala-java-annotation-processor/settings.gradle @@ -0,0 +1 @@ +include 'java_only', 'mixed_scala_java' diff --git a/src/functionalTest/resources/projects/scala-java-multi-module/build.gradle b/src/functionalTest/resources/projects/scala-java-multi-module/build.gradle index 40e08b7..fa88c6f 100644 --- a/src/functionalTest/resources/projects/scala-java-multi-module/build.gradle +++ b/src/functionalTest/resources/projects/scala-java-multi-module/build.gradle @@ -6,7 +6,7 @@ description = 'a multi-module Scala and Java project that builds successfully wi allprojects { repositories { - jcenter() + mavenCentral() } } diff --git a/src/functionalTest/resources/projects/scala-multi-module-multiple-test-tasks/build.gradle b/src/functionalTest/resources/projects/scala-multi-module-multiple-test-tasks/build.gradle index 7bf8c92..27fca57 100644 --- a/src/functionalTest/resources/projects/scala-multi-module-multiple-test-tasks/build.gradle +++ b/src/functionalTest/resources/projects/scala-multi-module-multiple-test-tasks/build.gradle @@ -4,7 +4,7 @@ plugins { allprojects { repositories { - jcenter() + mavenCentral() } } @@ -25,36 +25,35 @@ allprojects { testImplementation group: 'org.scalatest', name: "scalatest_${scalaVersionMajor}.${scalaVersionMinor}", version: scalatestVersion } - test { - useJUnitPlatform() - maxParallelForks = 1 - } - - configurations { - intTestImplementation.extendsFrom testImplementation - intTestRuntimeOnly.extendsFrom testRuntimeOnly - } - sourceSets { - intTest { - resources.srcDir file('src/intTest/resources') - scala { - compileClasspath += main.output + test.output - runtimeClasspath += main.output + test.output - srcDir file("${projectDir}/src/intTest/scala") + testing { + suites { + configureEach { + useJUnit() + targets.configureEach { + testTask.configure { + maxParallelForks = 1 + } + } + } + intTest(JvmTestSuite) { + testType = TestSuiteType.INTEGRATION_TEST + // dependencies { ... } does not appear to work as advertised? + sources { + scala { + compileClasspath += sourceSets.test.compileClasspath + sourceSets.main.output + sourceSets.test.output + runtimeClasspath += sourceSets.test.runtimeClasspath + } + } + targets.configureEach { + testTask.configure{ + outputs.upToDateWhen { false } + mustRunAfter(test) + } + } } } } - - - task intTest(type: Test) { - testClassesDirs = sourceSets.intTest.output.classesDirs - classpath = sourceSets.intTest.runtimeClasspath - outputs.upToDateWhen { false } - - maxParallelForks = 1 - } - check.dependsOn(intTest) - intTest.mustRunAfter(test) + check.dependsOn(testing.suites.intTest) scoverage { minimumRate = 0.5 diff --git a/src/functionalTest/resources/projects/scala-multi-module-with-partial-scoverage-use/build.gradle b/src/functionalTest/resources/projects/scala-multi-module-with-partial-scoverage-use/build.gradle index 4205b6a..512b324 100644 --- a/src/functionalTest/resources/projects/scala-multi-module-with-partial-scoverage-use/build.gradle +++ b/src/functionalTest/resources/projects/scala-multi-module-with-partial-scoverage-use/build.gradle @@ -6,7 +6,7 @@ description = 'a multi-module Scala project that builds successfully and has mod allprojects { p -> repositories { - jcenter() + mavenCentral() } apply plugin: 'java' diff --git a/src/functionalTest/resources/projects/scala-multi-module/build.gradle b/src/functionalTest/resources/projects/scala-multi-module/build.gradle index 9235b85..0177c34 100644 --- a/src/functionalTest/resources/projects/scala-multi-module/build.gradle +++ b/src/functionalTest/resources/projects/scala-multi-module/build.gradle @@ -6,7 +6,7 @@ description = 'a multi-module Scala project that builds successfully with 100% c allprojects { p -> repositories { - jcenter() + mavenCentral() } if (p.name != 'dependencies') { diff --git a/src/functionalTest/resources/projects/scala-single-module-dependency-manager/build.gradle b/src/functionalTest/resources/projects/scala-single-module-dependency-manager/build.gradle index 84d3e1c..d972fb4 100644 --- a/src/functionalTest/resources/projects/scala-single-module-dependency-manager/build.gradle +++ b/src/functionalTest/resources/projects/scala-single-module-dependency-manager/build.gradle @@ -1,10 +1,10 @@ plugins { - id 'io.spring.dependency-management' version "1.0.4.RELEASE" + id 'io.spring.dependency-management' version "1.1.5" id 'org.scoverage' } repositories { - jcenter() + mavenCentral() } description = 'a single-module Scala project with dependency manager that builds successfully with 100% coverage' diff --git a/src/functionalTest/resources/projects/scala-single-module-multiple-test-tasks/build.gradle b/src/functionalTest/resources/projects/scala-single-module-multiple-test-tasks/build.gradle index fcf174b..1f4b316 100644 --- a/src/functionalTest/resources/projects/scala-single-module-multiple-test-tasks/build.gradle +++ b/src/functionalTest/resources/projects/scala-single-module-multiple-test-tasks/build.gradle @@ -1,10 +1,11 @@ plugins { - id 'io.spring.dependency-management' version "1.0.4.RELEASE" + id 'io.spring.dependency-management' version "1.1.5" id 'org.scoverage' + id 'jvm-test-suite' } repositories { - jcenter() + mavenCentral() } description = 'a single-module Scala project with dependency manager that builds successfully with 100% coverage' @@ -27,39 +28,35 @@ dependencies { testImplementation group: 'org.scalatest', name: "scalatest_${scalaVersionMajor}.${scalaVersionMinor}", version: scalatestVersion } -test { - useJUnitPlatform() -} - - -configurations { - intTestImplementation.extendsFrom testImplementation - intTestRuntimeOnly.extendsFrom testRuntimeOnly -} -sourceSets { - intTest { - resources.srcDir file('src/intTest/resources') - scala { - compileClasspath += main.output + test.output - runtimeClasspath += main.output + test.output - srcDir file("${projectDir}/src/intTest/scala") +testing { + suites { + configureEach { + useJUnit() + targets.configureEach { + testTask.configure { + maxParallelForks = 1 + } + } + } + intTest(JvmTestSuite) { + testType = TestSuiteType.INTEGRATION_TEST + // dependencies { ... } does not appear to work as advertised? + sources { + scala { + compileClasspath += sourceSets.test.compileClasspath + sourceSets.main.output + sourceSets.test.output + runtimeClasspath += sourceSets.test.runtimeClasspath + } + } + targets.configureEach { + testTask.configure{ + outputs.upToDateWhen { false } + mustRunAfter(test) + } + } } } } - -test { - maxParallelForks = 1 -} - -task intTest(type: Test) { - testClassesDirs = sourceSets.intTest.output.classesDirs - classpath = sourceSets.intTest.runtimeClasspath - outputs.upToDateWhen { false } - - maxParallelForks = 1 -} -check.dependsOn(intTest) -intTest.mustRunAfter(test) +check.dependsOn(testing.suites.intTest) scoverage { minimumRate = 0.6 diff --git a/src/functionalTest/resources/projects/scala-single-module/build.gradle b/src/functionalTest/resources/projects/scala-single-module/build.gradle index e96efe1..fd9d9f2 100644 --- a/src/functionalTest/resources/projects/scala-single-module/build.gradle +++ b/src/functionalTest/resources/projects/scala-single-module/build.gradle @@ -3,7 +3,7 @@ plugins { } repositories { - jcenter() + mavenCentral() } description = 'a single-module Scala project that builds successfully with 50% coverage' @@ -12,12 +12,20 @@ apply plugin: 'java' apply plugin: 'scala' dependencies { - implementation group: 'org.scala-lang', name: 'scala-library', version: "${scalaVersionMajor}.${scalaVersionMinor}.${scalaVersionBuild}" + if (project.getProperties().get("scalaVersionMajor").equals("2")) { + implementation group: 'org.scala-lang', name: 'scala-library', version: "${scalaVersionMajor}.${scalaVersionMinor}.${scalaVersionBuild}" + + testImplementation group: 'org.scalatest', name: 'scalatest_2.13', version: scalatestVersion + testImplementation group: 'org.scalatestplus', name: 'junit-4-13_2.13', version: "${scalatestVersion}.0" + } else { + implementation group: 'org.scala-lang', name: 'scala3-library_3', version: "${scalaVersionMajor}.${scalaVersionMinor}.${scalaVersionBuild}" + + testImplementation group: 'org.scalatest', name: 'scalatest_3', version: scalatestVersion + testImplementation group: 'org.scalatestplus', name: 'junit-4-13_3', version: "${scalatestVersion}.0" + } testRuntimeOnly group: 'org.junit.vintage', name: 'junit-vintage-engine', version: junitVersion testImplementation group: 'org.junit.platform', name: 'junit-platform-runner', version: junitPlatformVersion - - testImplementation group: 'org.scalatest', name: "scalatest_${scalaVersionMajor}.${scalaVersionMinor}", version: scalatestVersion } test { @@ -27,6 +35,8 @@ test { scoverage { minimumRate = 0.3 coverageType = org.scoverage.CoverageType.Line + // verify that debug mode works + coverageDebug = true } if (hasProperty("excludedFile")) { diff --git a/src/functionalTest/resources/projects/scala-single-module/src/test/scala/org/hello/TestNothingSuite.scala b/src/functionalTest/resources/projects/scala-single-module/src/test/scala/org/hello/TestNothingSuite.scala index 1ac25b5..76de321 100644 --- a/src/functionalTest/resources/projects/scala-single-module/src/test/scala/org/hello/TestNothingSuite.scala +++ b/src/functionalTest/resources/projects/scala-single-module/src/test/scala/org/hello/TestNothingSuite.scala @@ -1,11 +1,11 @@ package org.hello import org.junit.runner.RunWith -import org.scalatest.FunSuite -import org.scalatest.junit.JUnitRunner +import org.scalatest.funsuite._ +import org.scalatestplus.junit.JUnitRunner @RunWith(classOf[JUnitRunner]) -class TestNothingSuite extends FunSuite { +class TestNothingSuite extends AnyFunSuite { test("nothing") { } diff --git a/src/functionalTest/resources/projects/scala-single-module/src/test/scala/org/hello/WorldSuite.scala b/src/functionalTest/resources/projects/scala-single-module/src/test/scala/org/hello/WorldSuite.scala index 7281a12..477f27a 100644 --- a/src/functionalTest/resources/projects/scala-single-module/src/test/scala/org/hello/WorldSuite.scala +++ b/src/functionalTest/resources/projects/scala-single-module/src/test/scala/org/hello/WorldSuite.scala @@ -1,11 +1,11 @@ package org.hello import org.junit.runner.RunWith -import org.scalatest.FunSuite -import org.scalatest.junit.JUnitRunner +import org.scalatest.funsuite._ +import org.scalatestplus.junit.JUnitRunner @RunWith(classOf[JUnitRunner]) -class WorldSuite extends FunSuite { +class WorldSuite extends AnyFunSuite { test("foo") { new World().foo() diff --git a/src/main/groovy/org/scoverage/CoverageChecker.groovy b/src/main/groovy/org/scoverage/CoverageChecker.groovy index d647f12..ff240ba 100644 --- a/src/main/groovy/org/scoverage/CoverageChecker.groovy +++ b/src/main/groovy/org/scoverage/CoverageChecker.groovy @@ -1,12 +1,12 @@ package org.scoverage - +import groovy.xml.XmlParser import org.gradle.api.GradleException import org.gradle.api.logging.Logger import org.gradle.internal.impldep.com.google.common.annotations.VisibleForTesting import java.text.DecimalFormat -import java.text.NumberFormat +import java.text.DecimalFormatSymbols /** * Handles different types of coverage Scoverage can measure. @@ -56,30 +56,21 @@ class CoverageChecker { } public void checkLineCoverage(File reportDir, CoverageType coverageType, double minimumRate) throws GradleException { - NumberFormat defaultNf = NumberFormat.getInstance(Locale.getDefault()) - checkLineCoverage(reportDir, coverageType, minimumRate, defaultNf) - } - - public void checkLineCoverage(File reportDir, CoverageType coverageType, double minimumRate, NumberFormat nf) throws GradleException { logger.info("Checking coverage. Type: {}. Minimum rate: {}", coverageType, minimumRate) XmlParser parser = new XmlParser() parser.setFeature('http://apache.org/xml/features/disallow-doctype-decl', false) parser.setFeature('http://apache.org/xml/features/nonvalidating/load-external-dtd', false) - DecimalFormat df = new DecimalFormat("#.##") - try { File reportFile = new File(reportDir, coverageType.fileName) Node xml = parser.parse(reportFile) - Double coverageValue = nf.parse(xml.attribute(coverageType.paramName) as String).doubleValue() + Double coverageValue = (xml.attribute(coverageType.paramName) as String).toDouble() Double overallRate = coverageType.normalize(coverageValue) def difference = (minimumRate - overallRate) if (difference > 1e-7) { - String is = df.format(overallRate * 100) - String needed = df.format(minimumRate * 100) - throw new GradleException(errorMsg(is, needed, coverageType)) + throw new GradleException(errorMsg(overallRate, minimumRate, coverageType)) } } catch (FileNotFoundException fnfe) { throw new GradleException(fileNotFoundErrorMsg(coverageType), fnfe) @@ -87,7 +78,10 @@ class CoverageChecker { } @VisibleForTesting - protected static String errorMsg(String actual, String expected, CoverageType type) { + protected static String errorMsg(double overallRate, double minimumRate, CoverageType type) { + DecimalFormat df = new DecimalFormat("#.##", DecimalFormatSymbols.getInstance(Locale.US)) + String actual = df.format(overallRate * 100) + String expected = df.format(minimumRate * 100) "Only $actual% of project is covered by tests instead of $expected% (coverageType: $type)" } diff --git a/src/main/groovy/org/scoverage/ScalaVersion.groovy b/src/main/groovy/org/scoverage/ScalaVersion.groovy new file mode 100644 index 0000000..7d76dc4 --- /dev/null +++ b/src/main/groovy/org/scoverage/ScalaVersion.groovy @@ -0,0 +1,31 @@ +package org.scoverage + +class ScalaVersion { + final String primaryVersion + final Optional secondaryVersion + final Integer majorVersion + final String scalacScoverageVersion + final String scalacScoveragePluginVersion + final String scalacScoverageRuntimeVersion + + ScalaVersion(primaryVersion) { + this(primaryVersion, Optional.empty()) + } + + ScalaVersion(String primaryVersion, Optional secondaryVersion) { + this.primaryVersion = primaryVersion + this.secondaryVersion = secondaryVersion + + this.majorVersion = primaryVersion.substring(0, primaryVersion.indexOf('.')).toInteger() + this.scalacScoverageVersion = this.majorVersion < 3 + ? primaryVersion.substring(0, primaryVersion.lastIndexOf('.')) + : this.majorVersion.toString() + this.scalacScoveragePluginVersion = secondaryVersion.orElse(primaryVersion) + this.scalacScoverageRuntimeVersion = scalacScoveragePluginVersion.substring(0, scalacScoveragePluginVersion.lastIndexOf('.')) + } + + @Override + String toString() { + return majorVersion < 3 ? primaryVersion : "$primaryVersion (${secondaryVersion.get()})" + } +} diff --git a/src/main/groovy/org/scoverage/ScoverageAggregate.groovy b/src/main/groovy/org/scoverage/ScoverageAggregate.groovy index b445e29..f39ff98 100644 --- a/src/main/groovy/org/scoverage/ScoverageAggregate.groovy +++ b/src/main/groovy/org/scoverage/ScoverageAggregate.groovy @@ -1,19 +1,28 @@ package org.scoverage import org.gradle.api.DefaultTask +import org.gradle.api.file.FileCollection import org.gradle.api.provider.ListProperty import org.gradle.api.provider.Property import org.gradle.api.tasks.Input +import org.gradle.api.tasks.InputFiles import org.gradle.api.tasks.Nested import org.gradle.api.tasks.OutputDirectory +import org.gradle.api.tasks.PathSensitive import org.gradle.api.tasks.TaskAction -import scoverage.report.CoverageAggregator +import scoverage.reporter.CoverageAggregator + +import static org.gradle.api.tasks.PathSensitivity.RELATIVE class ScoverageAggregate extends DefaultTask { @Nested ScoverageRunner runner + @InputFiles + @PathSensitive(RELATIVE) + final Property sources = project.objects.property(FileCollection) + @OutputDirectory final Property reportDir = project.objects.property(File) @@ -48,11 +57,12 @@ class ScoverageAggregate extends DefaultTask { def dirs = [] dirs.addAll(dirsToAggregateFrom.get()) - def coverage = CoverageAggregator.aggregate(dirs.unique() as File[]) + def sourceRoot = getProject().getRootDir() + def coverage = CoverageAggregator.aggregate(dirs.unique() as File[], sourceRoot) if (coverage.nonEmpty()) { new ScoverageWriter(project.logger).write( - project.projectDir, + sources.get().getFiles(), reportDir.get(), coverage.get(), sourceEncoding.get(), diff --git a/src/main/groovy/org/scoverage/ScoverageExtension.groovy b/src/main/groovy/org/scoverage/ScoverageExtension.groovy index e106306..d2b5119 100644 --- a/src/main/groovy/org/scoverage/ScoverageExtension.groovy +++ b/src/main/groovy/org/scoverage/ScoverageExtension.groovy @@ -28,8 +28,6 @@ class ScoverageExtension { final Property dataDir /** a directory to write final output to */ final Property reportDir - /** sources to highlight */ - final Property sources /** range positioning for highlighting */ final Property highlighting /** regex for each excluded package */ @@ -57,13 +55,10 @@ class ScoverageExtension { project.plugins.apply(ScalaPlugin.class) scoverageVersion = project.objects.property(String) - scoverageVersion.set('1.4.8') + scoverageVersion.set('2.1.1') scoverageScalaVersion = project.objects.property(String) - sources = project.objects.property(File) - sources.set(project.projectDir) - dataDir = project.objects.property(File) dataDir.set(new File(project.buildDir, 'scoverage')) diff --git a/src/main/groovy/org/scoverage/ScoveragePlugin.groovy b/src/main/groovy/org/scoverage/ScoveragePlugin.groovy index 063ba04..a6a33d2 100644 --- a/src/main/groovy/org/scoverage/ScoveragePlugin.groovy +++ b/src/main/groovy/org/scoverage/ScoveragePlugin.groovy @@ -9,7 +9,6 @@ import org.gradle.api.plugins.PluginAware import org.gradle.api.plugins.scala.ScalaPlugin import org.gradle.api.tasks.SourceSet import org.gradle.api.tasks.TaskProvider -import org.gradle.api.tasks.scala.ScalaCompile import org.gradle.api.tasks.testing.Test import java.nio.file.Files @@ -24,11 +23,13 @@ class ScoveragePlugin implements Plugin { static final String CHECK_NAME = 'checkScoverage' static final String COMPILE_NAME = 'compileScoverageScala' static final String AGGREGATE_NAME = 'aggregateScoverage' - static final String DEFAULT_SCALA_VERSION = '2.13.6' + static final String DEFAULT_SCALA_VERSION = '2.13.14' + static final String SCOVERAGE_COMPILE_ONLY_PROPERTY = 'scoverageCompileOnly'; static final String DEFAULT_REPORT_DIR = 'reports' + File.separatorChar + 'scoverage' - private final ConcurrentHashMap> taskDependencies = new ConcurrentHashMap<>(); + private final ConcurrentHashMap> crossProjectTaskDependencies = new ConcurrentHashMap<>() + private final ConcurrentHashMap> sameProjectTaskDependencies = new ConcurrentHashMap<>() @Override void apply(PluginAware pluginAware) { @@ -57,18 +58,26 @@ class ScoveragePlugin implements Plugin { visible = false transitive = true description = 'Scoverage dependencies' + canBeResolved = true + canBeConsumed = false } project.afterEvaluate { - def scalaFullVersion = resolveScalaVersion(project) - def scalaBinaryVersion = scalaFullVersion.substring(0, scalaFullVersion.lastIndexOf('.')) + def scalaVersion = resolveScalaVersions(project) + def scoverageVersion = project.extensions.scoverage.scoverageVersion.get() + project.logger.info("Using scoverage scalac plugin $scoverageVersion for scala $scalaVersion") - project.logger.info("Using scoverage scalac plugin $scoverageVersion for scala $scalaFullVersion") + def scalacScoverageVersion = scalaVersion.scalacScoverageVersion + def scalacScoveragePluginVersion = scalaVersion.scalacScoveragePluginVersion + def scalacScoverageRuntimeVersion = scalaVersion.scalacScoverageRuntimeVersion project.dependencies { - scoverage("org.scoverage:scalac-scoverage-plugin_$scalaFullVersion:$scoverageVersion") - scoverage("org.scoverage:scalac-scoverage-runtime_$scalaBinaryVersion:$scoverageVersion") + scoverage("org.scoverage:scalac-scoverage-domain_$scalacScoverageVersion:$scoverageVersion") + scoverage("org.scoverage:scalac-scoverage-reporter_$scalacScoverageVersion:$scoverageVersion") + scoverage("org.scoverage:scalac-scoverage-serializer_$scalacScoverageVersion:$scoverageVersion") + scoverage("org.scoverage:scalac-scoverage-runtime_$scalacScoverageRuntimeVersion:$scoverageVersion") + scoverage("org.scoverage:scalac-scoverage-plugin_$scalacScoveragePluginVersion:$scoverageVersion") } } } @@ -87,6 +96,7 @@ class ScoveragePlugin implements Plugin { java.source(originalSourceSet.java) scala.source(originalSourceSet.scala) + annotationProcessorPath += originalSourceSet.annotationProcessorPath + project.configurations.scoverage compileClasspath += originalSourceSet.compileClasspath + project.configurations.scoverage runtimeClasspath = it.output + project.configurations.scoverage + originalSourceSet.runtimeClasspath } @@ -96,7 +106,6 @@ class ScoveragePlugin implements Plugin { def compileTask = project.tasks[instrumentedSourceSet.getCompileTaskName("scala")] compileTask.mustRunAfter(originalCompileTask) - originalJarTask.mustRunAfter(compileTask) def globalReportTask = project.tasks.register(REPORT_NAME, ScoverageAggregate) def globalCheckTask = project.tasks.register(CHECK_NAME) @@ -118,12 +127,12 @@ class ScoveragePlugin implements Plugin { def taskReportDir = project.file("${project.buildDir}/reports/scoverage${testTask.name.capitalize()}") project.tasks.create(reportTaskName, ScoverageReport) { - dependsOn compileTask, testTask + dependsOn originalJarTask, compileTask, testTask onlyIf { extension.dataDir.get().list() } group = 'verification' runner = scoverageRunner reportDir = taskReportDir - sources = extension.sources + sources = originalSourceSet.scala.getSourceDirectories() dataDir = extension.dataDir sourceEncoding.set(detectedSourceEncoding) coverageOutputCobertura = extension.coverageOutputCobertura @@ -142,6 +151,7 @@ class ScoveragePlugin implements Plugin { group = 'verification' runner = scoverageRunner reportDir = extension.reportDir + sources = originalSourceSet.scala.getSourceDirectories() dirsToAggregateFrom = dataDirs sourceEncoding.set(detectedSourceEncoding) deleteReportsOnAggregation = false @@ -153,56 +163,107 @@ class ScoveragePlugin implements Plugin { configureCheckTask(project, extension, globalCheckTask, globalReportTask) - // make this project's scoverage compilation depend on scoverage compilation of any other project - // which this project depends on its normal compilation - // (essential when running without normal compilation on multi-module projects with inner dependencies) - def originalCompilationDependencies = recursiveDependenciesOf(compileTask).findAll { - it instanceof ScalaCompile - } - originalCompilationDependencies.each { - def dependencyProjectCompileTask = it.project.tasks.findByName(COMPILE_NAME) - def dependencyProjectReportTask = it.project.tasks.findByName(REPORT_NAME) - if (dependencyProjectCompileTask != null) { - compileTask.dependsOn(dependencyProjectCompileTask) - // we don't want this project's tests to affect the other project's report - testTasks.each { - it.mustRunAfter(dependencyProjectReportTask) - } - } - } - compileTask.configure { List parameters = [] List existingParameters = scalaCompileOptions.additionalParameters if (existingParameters) { parameters.addAll(existingParameters) } - parameters.add("-P:scoverage:dataDir:${extension.dataDir.get().absolutePath}".toString()) - if (extension.excludedPackages.get()) { - def packages = extension.excludedPackages.get().join(';') - parameters.add("-P:scoverage:excludedPackages:$packages".toString()) - } - if (extension.excludedFiles.get()) { - def packages = extension.excludedFiles.get().join(';') - parameters.add("-P:scoverage:excludedFiles:$packages".toString()) - } - if (extension.highlighting.get()) { - parameters.add('-Yrangepos') + + def scalaVersion = resolveScalaVersions(project) + if (scalaVersion.majorVersion < 3) { + parameters.add("-P:scoverage:dataDir:${extension.dataDir.get().absolutePath}".toString()) + parameters.add("-P:scoverage:sourceRoot:${extension.project.getRootDir().absolutePath}".toString()) + if (extension.excludedPackages.get()) { + def packages = extension.excludedPackages.get().join(';') + parameters.add("-P:scoverage:excludedPackages:$packages".toString()) + } + if (extension.excludedFiles.get()) { + def packages = extension.excludedFiles.get().join(';') + parameters.add("-P:scoverage:excludedFiles:$packages".toString()) + } + if (extension.highlighting.get()) { + parameters.add('-Yrangepos') + } + scalaCompileOptions.additionalParameters = parameters + // the compile task creates a store of measured statements + outputs.file(new File(extension.dataDir.get(), 'scoverage.coverage')) + + dependsOn project.configurations[CONFIGURATION_NAME] + doFirst { + /* + It is crucial that this would run in `doFirst`, as this resolves the (dependencies of the) + configuration, which we do not want to do at configuration time (but only at execution time). + */ + def pluginFiles = project.configurations[CONFIGURATION_NAME].findAll { + it.name.startsWith("scalac-scoverage-plugin") || + it.name.startsWith("scalac-scoverage-domain") || + it.name.startsWith("scalac-scoverage-serializer") + }.collect { + it.absolutePath + } + scalaCompileOptions.additionalParameters.add('-Xplugin:' + pluginFiles.join(File.pathSeparator)) + } + } else { + parameters.add("-sourceroot:${project.rootDir.absolutePath}".toString()) + parameters.add("-coverage-out:${extension.dataDir.get().absolutePath}".toString()) + if (extension.excludedPackages.get()) { + def packages = extension.excludedPackages.get().join(',') + parameters.add("-coverage-exclude-classlikes:$packages".toString()) + } + if (extension.excludedFiles.get()) { + def packages = extension.excludedFiles.get().join(';') + parameters.add("-coverage-exclude-files:$packages".toString()) + } + scalaCompileOptions.additionalParameters = parameters } - scalaCompileOptions.additionalParameters = parameters - // the compile task creates a store of measured statements - outputs.file(new File(extension.dataDir.get(), 'scoverage.coverage')) + } - dependsOn project.configurations[CONFIGURATION_NAME] + compileTask.configure { doFirst { - /* - It is crucial that this would run in `doFirst`, as this resolves the (dependencies of the) - configuration, which we do not want to do at configuration time (but only at execution time). - */ - def pluginFile = project.configurations[CONFIGURATION_NAME].find { - it.name.startsWith("scalac-scoverage-plugin") + destinationDirectory.get().getAsFile().deleteDir() + } + + // delete non-instrumented classes by comparing normally compiled classes to those compiled with scoverage + doLast { + project.logger.info("Deleting classes compiled by scoverage but non-instrumented (identical to normal compilation)") + def originalCompileTaskName = project.sourceSets.getByName(SourceSet.MAIN_SOURCE_SET_NAME) + .getCompileTaskName("scala") + def originalDestinationDirectory = project.tasks[originalCompileTaskName].destinationDirectory + def originalDestinationDir = originalDestinationDirectory.get().asFile + def destinationDir = destinationDirectory.get().asFile + + + def findFiles = { File dir, Closure condition = null -> + def files = [] + + if (dir.exists()) { + dir.eachFileRecurse(FILES) { f -> + if (condition == null || condition(f)) { + def relativePath = dir.relativePath(f) + files << relativePath + } + } + } + + files + } + + def isSameFile = { String relativePath -> + def fileA = new File(originalDestinationDir, relativePath) + def fileB = new File(destinationDir, relativePath) + FileUtils.contentEquals(fileA, fileB) + } + + def originalClasses = findFiles(originalDestinationDir) + def identicalInstrumentedClasses = findFiles(destinationDir, { f -> + def relativePath = destinationDir.relativePath(f) + originalClasses.contains(relativePath) && isSameFile(relativePath) + }) + + identicalInstrumentedClasses.each { f -> + Files.deleteIfExists(destinationDir.toPath().resolve(f)) } - scalaCompileOptions.additionalParameters.add('-Xplugin:' + pluginFile.absolutePath) } } @@ -227,56 +288,6 @@ class ScoveragePlugin implements Plugin { } } } - - compileTask.configure { - if (!graph.hasTask(originalCompileTask)) { - project.logger.info("Making scoverage compilation the primary compilation task (instead of compileScala)") - destinationDir = originalCompileTask.destinationDir - } else { - doFirst { - destinationDir.deleteDir() - } - - // delete non-instrumented classes by comparing normally compiled classes to those compiled with scoverage - doLast { - project.logger.info("Deleting classes compiled by scoverage but non-instrumented (identical to normal compilation)") - def originalCompileTaskName = project.sourceSets.getByName(SourceSet.MAIN_SOURCE_SET_NAME) - .getCompileTaskName("scala") - def originalDestinationDir = project.tasks[originalCompileTaskName].destinationDir - - def findFiles = { File dir, Closure condition = null -> - def files = [] - - if (dir.exists()) { - dir.eachFileRecurse(FILES) { f -> - if (condition == null || condition(f)) { - def relativePath = dir.relativePath(f) - files << relativePath - } - } - } - - files - } - - def isSameFile = { String relativePath -> - def fileA = new File(originalDestinationDir, relativePath) - def fileB = new File(destinationDir, relativePath) - FileUtils.contentEquals(fileA, fileB) - } - - def originalClasses = findFiles(originalDestinationDir) - def identicalInstrumentedClasses = findFiles(destinationDir, { f -> - def relativePath = destinationDir.relativePath(f) - originalClasses.contains(relativePath) && isSameFile(relativePath) - }) - - identicalInstrumentedClasses.each { f -> - Files.deleteIfExists(destinationDir.toPath().resolve(f)) - } - } - } - } } // define aggregation task @@ -293,6 +304,10 @@ class ScoveragePlugin implements Plugin { } } def allReportTasks = childReportTasks + globalReportTask.get() + def allSources = project.objects.fileCollection() + allReportTasks.each { + allSources = allSources.plus(it.sources.get()) + } def aggregationTask = project.tasks.create(AGGREGATE_NAME, ScoverageAggregate) { def dataDirs = allReportTasks.findResults { it.dirsToAggregateFrom.get() }.flatten() onlyIf { @@ -302,6 +317,7 @@ class ScoveragePlugin implements Plugin { group = 'verification' runner = scoverageRunner reportDir = extension.reportDir + sources = allSources sourceEncoding.set(detectedSourceEncoding) dirsToAggregateFrom = dataDirs deleteReportsOnAggregation = extension.deleteReportsOnAggregation @@ -346,40 +362,42 @@ class ScoveragePlugin implements Plugin { } } - private String resolveScalaVersion(Project project) { - + private ScalaVersion resolveScalaVersions(Project project) { def scalaVersionProperty = project.extensions.scoverage.scoverageScalaVersion if (scalaVersionProperty.isPresent()) { def configuredScalaVersion = scalaVersionProperty.get() project.logger.info("Using configured Scala version: $configuredScalaVersion") - return configuredScalaVersion + return new ScalaVersion(configuredScalaVersion) } else { project.logger.info("No Scala version configured. Detecting scala library...") def components = project.configurations.compileClasspath.incoming.resolutionResult.getAllComponents() + + def scala3Library = components.find { + it.moduleVersion.group == "org.scala-lang" && it.moduleVersion.name == "scala3-library_3" + } def scalaLibrary = components.find { it.moduleVersion.group == "org.scala-lang" && it.moduleVersion.name == "scala-library" } - if (scalaLibrary != null) { - def scalaVersion = scalaLibrary.moduleVersion.version - project.logger.info("Detected scala library in compilation classpath. Scala version: $scalaVersion") - return scalaVersion - } else { - project.logger.info("No scala library detected. Using default Scala version: $DEFAULT_SCALA_VERSION") - return DEFAULT_SCALA_VERSION + + // Scala 3 + if (scala3Library != null) { + def scala3Version = scala3Library.moduleVersion.version + def scala2Version = scalaLibrary.moduleVersion.version + project.logger.info("Detected scala 3 library in compilation classpath. Scala 3 version: $scala3Version; using Scala 2 library: $scala2Version") + return new ScalaVersion(scala3Version, Optional.of(scala2Version)) } - } - } - private Set recursiveDependenciesOf(Task task) { - if (!taskDependencies.containsKey(task)) { - def directDependencies = task.getTaskDependencies().getDependencies(task) - def nestedDependencies = directDependencies.collect { recursiveDependenciesOf(it) }.flatten() - def dependencies = directDependencies + nestedDependencies + // Scala 2 + if (scalaLibrary != null) { + def scala2Version = scalaLibrary.moduleVersion.version + project.logger.info("Detected scala library in compilation classpath. Scala version: $scala2Version") + return new ScalaVersion(scala2Version) + } - taskDependencies.put(task, dependencies) - return dependencies - } else { - return taskDependencies.get(task) + // No Scala library was found, using default Scala version + project.logger.info("No scala library detected. Using default Scala version: $DEFAULT_SCALA_VERSION") + return new ScalaVersion(DEFAULT_SCALA_VERSION) } } + } diff --git a/src/main/groovy/org/scoverage/ScoverageReport.groovy b/src/main/groovy/org/scoverage/ScoverageReport.groovy index e95f547..ef09483 100644 --- a/src/main/groovy/org/scoverage/ScoverageReport.groovy +++ b/src/main/groovy/org/scoverage/ScoverageReport.groovy @@ -1,15 +1,17 @@ package org.scoverage import org.gradle.api.DefaultTask +import org.gradle.api.file.FileCollection import org.gradle.api.provider.Property import org.gradle.api.tasks.CacheableTask import org.gradle.api.tasks.Input import org.gradle.api.tasks.InputDirectory +import org.gradle.api.tasks.InputFiles import org.gradle.api.tasks.Nested import org.gradle.api.tasks.OutputDirectory import org.gradle.api.tasks.PathSensitive import org.gradle.api.tasks.TaskAction -import scoverage.report.CoverageAggregator +import scoverage.reporter.CoverageAggregator import static org.gradle.api.tasks.PathSensitivity.RELATIVE @@ -23,9 +25,9 @@ class ScoverageReport extends DefaultTask { @PathSensitive(RELATIVE) final Property dataDir = project.objects.property(File) - @InputDirectory + @InputFiles @PathSensitive(RELATIVE) - final Property sources = project.objects.property(File) + final Property sources = project.objects.property(FileCollection) @OutputDirectory final Property reportDir = project.objects.property(File) @@ -48,13 +50,14 @@ class ScoverageReport extends DefaultTask { reportDir.get().delete() reportDir.get().mkdirs() - def coverage = CoverageAggregator.aggregate([dataDir.get()] as File[]) + def sourceRoot = getProject().getRootDir() + def coverage = CoverageAggregator.aggregate([dataDir.get()] as File[], sourceRoot) if (coverage.isEmpty()) { project.logger.info("[scoverage] Could not find coverage file, skipping...") } else { new ScoverageWriter(project.logger).write( - sources.get(), + sources.get().getFiles(), reportDir.get(), coverage.get(), sourceEncoding.get(), diff --git a/src/main/groovy/org/scoverage/ScoverageRunner.groovy b/src/main/groovy/org/scoverage/ScoverageRunner.groovy index 713b9e4..a5dd552 100644 --- a/src/main/groovy/org/scoverage/ScoverageRunner.groovy +++ b/src/main/groovy/org/scoverage/ScoverageRunner.groovy @@ -23,7 +23,7 @@ class ScoverageRunner { method.setAccessible(true) runtimeClasspath.files.each { f -> - def url = f.toURL() + def url = f.toURI().toURL() if (!cloader.getURLs().contains(url)) { method.invoke(cloader, url) } diff --git a/src/main/groovy/org/scoverage/ScoverageWriter.java b/src/main/groovy/org/scoverage/ScoverageWriter.java index 2f95a51..6131977 100644 --- a/src/main/groovy/org/scoverage/ScoverageWriter.java +++ b/src/main/groovy/org/scoverage/ScoverageWriter.java @@ -1,14 +1,23 @@ package org.scoverage; import org.gradle.api.logging.Logger; +import scala.Option; import scala.Some; -import scoverage.Constants; -import scoverage.Coverage; -import scoverage.report.CoberturaXmlWriter; -import scoverage.report.ScoverageHtmlWriter; -import scoverage.report.ScoverageXmlWriter; +import scala.collection.immutable.Seq; +import scala.collection.mutable.Buffer; +import scoverage.domain.Constants; +import scoverage.domain.Coverage; +import scoverage.reporter.CoberturaXmlWriter; +import scoverage.reporter.ScoverageHtmlWriter; +import scoverage.reporter.ScoverageXmlWriter; +import scala.collection.JavaConverters; import java.io.File; +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Set; /** * Util for generating and saving coverage files. @@ -26,7 +35,7 @@ public ScoverageWriter(Logger logger) { /** * Generates all reports from given data. * - * @param sourceDir directory with project sources + * @param sourceDirs directories with project sources * @param reportDir directory for generate reports * @param coverage coverage data * @param sourceEncoding the encoding of the source files @@ -35,21 +44,39 @@ public ScoverageWriter(Logger logger) { * @param coverageOutputHTML switch for Scoverage HTML output * @param coverageDebug switch for Scoverage Debug output */ - public void write(File sourceDir, + public void write(Set sourceDirs, File reportDir, Coverage coverage, String sourceEncoding, Boolean coverageOutputCobertura, Boolean coverageOutputXML, Boolean coverageOutputHTML, - Boolean coverageDebug) { + Boolean coverageDebug) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException, InstantiationException { logger.info("[scoverage] Generating scoverage reports..."); reportDir.mkdirs(); + Object scalaBuffer = Class.forName("scala.collection.JavaConverters") + .getMethod("asScalaBuffer", java.util.List.class) + .invoke(null, new ArrayList<>(sourceDirs)); + Object sourceDirsSeq = scalaBuffer.getClass().getMethod("toIndexedSeq").invoke(scalaBuffer); + if (coverageOutputCobertura) { - new CoberturaXmlWriter(sourceDir, reportDir).write(coverage); + Constructor cst; + try { + cst = CoberturaXmlWriter.class.getConstructor( + Class.forName("scala.collection.immutable.Seq"), + File.class, + Class.forName("scala.Option")); + } catch (NoSuchMethodException | ClassNotFoundException e) { + cst = CoberturaXmlWriter.class.getConstructor( + Class.forName("scala.collection.Seq"), + File.class, + Class.forName("scala.Option")); + } + CoberturaXmlWriter writer = cst.newInstance(sourceDirsSeq, reportDir, new Some<>(sourceEncoding)); + writer.write(coverage); logger.info("[scoverage] Written Cobertura XML report to " + reportDir.getAbsolutePath() + File.separator + @@ -57,13 +84,29 @@ public void write(File sourceDir, } if (coverageOutputXML) { - new ScoverageXmlWriter(sourceDir, reportDir, /* debug = */ false).write(coverage); + Constructor cst; + try { + cst = ScoverageXmlWriter.class.getConstructor( + Class.forName("scala.collection.immutable.Seq"), + File.class, + boolean.class, + Class.forName("scala.Option")); + } catch (NoSuchMethodException | ClassNotFoundException e) { + cst = ScoverageXmlWriter.class.getConstructor( + Class.forName("scala.collection.Seq"), + File.class, + boolean.class, + Class.forName("scala.Option")); + } + ScoverageXmlWriter writer = cst.newInstance(sourceDirsSeq, reportDir, false, new Some<>(sourceEncoding)); + writer.write(coverage); logger.info("[scoverage] Written XML report to " + reportDir.getAbsolutePath() + File.separator + Constants.XMLReportFilename()); if (coverageDebug) { - new ScoverageXmlWriter(sourceDir, reportDir, /* debug = */ true).write(coverage); + ScoverageXmlWriter writerDebug = cst.newInstance(sourceDirsSeq, reportDir, true, new Some<>(sourceEncoding)); + writerDebug.write(coverage); logger.info("[scoverage] Written XML report with debug information to " + reportDir.getAbsolutePath() + File.separator + @@ -72,7 +115,14 @@ public void write(File sourceDir, } if (coverageOutputHTML) { - new ScoverageHtmlWriter(new File[]{sourceDir}, reportDir, new Some<>(sourceEncoding)).write(coverage); + Constructor cst; + try { + cst = ScoverageHtmlWriter.class.getConstructor(Class.forName("scala.collection.immutable.Seq"), File.class, Option.class); + } catch (NoSuchMethodException | ClassNotFoundException e) { + cst = ScoverageHtmlWriter.class.getConstructor(Class.forName("scala.collection.Seq"), File.class, Option.class); + } + ScoverageHtmlWriter writer = cst.newInstance(sourceDirsSeq, reportDir, new Some<>(sourceEncoding)); + writer.write(coverage); logger.info("[scoverage] Written HTML report to " + reportDir.getAbsolutePath() + File.separator + diff --git a/src/test/groovy/org/scoverage/CoverageCheckerTest.groovy b/src/test/groovy/org/scoverage/CoverageCheckerTest.groovy index 04c695a..844968e 100644 --- a/src/test/groovy/org/scoverage/CoverageCheckerTest.groovy +++ b/src/test/groovy/org/scoverage/CoverageCheckerTest.groovy @@ -10,15 +10,13 @@ import org.junit.rules.TemporaryFolder import org.slf4j.LoggerFactory import java.nio.file.Paths -import java.text.NumberFormat +import static org.junit.Assert.assertEquals import static org.junit.Assert.assertThat import static org.junit.jupiter.api.Assertions.assertThrows class CoverageCheckerTest { - private NumberFormat numberFormat = NumberFormat.getInstance(Locale.US) - private File reportDir = Paths.get(getClass().getClassLoader().getResource("checkTask").toURI()).toFile() private CoverageChecker checker = new CoverageChecker(LoggerFactory.getLogger(CoverageCheckerTest.class)) @@ -31,7 +29,7 @@ class CoverageCheckerTest { @Test void failsWhenReportFileIsNotFound() { assertFailure(CoverageChecker.fileNotFoundErrorMsg(CoverageType.Line), { - checker.checkLineCoverage(tempDir.getRoot(), CoverageType.Line, 0.0, numberFormat) + checker.checkLineCoverage(tempDir.getRoot(), CoverageType.Line, 0.0) }) } @@ -39,60 +37,66 @@ class CoverageCheckerTest { @Test void failsWhenLineRateIsBelowTarget() { - assertFailure(CoverageChecker.errorMsg("66", "100", CoverageType.Line), { - checker.checkLineCoverage(reportDir, CoverageType.Line, 1.0, numberFormat) + assertFailure(CoverageChecker.errorMsg(0.66, 1.0, CoverageType.Line), { + checker.checkLineCoverage(reportDir, CoverageType.Line, 1.0) }) } @Test void doesNotFailWhenLineRateIsAtTarget() { - checker.checkLineCoverage(reportDir, CoverageType.Line, 0.66, numberFormat) + checker.checkLineCoverage(reportDir, CoverageType.Line, 0.66) } @Test void doesNotFailWhenLineRateIsAboveTarget() { - checker.checkLineCoverage(reportDir, CoverageType.Line, 0.6, numberFormat) + checker.checkLineCoverage(reportDir, CoverageType.Line, 0.6) } // Statement coverage @Test void failsWhenStatementRateIsBelowTarget() { - assertFailure(CoverageChecker.errorMsg(numberFormat.format(new Double(33.33)), "100", CoverageType.Statement), { - checker.checkLineCoverage(reportDir, CoverageType.Statement, 1.0, numberFormat) + assertFailure(CoverageChecker.errorMsg(0.3333, 1.0, CoverageType.Statement), { + checker.checkLineCoverage(reportDir, CoverageType.Statement, 1.0) }) } @Test void doesNotFailWhenStatementRateIsAtTarget() { - checker.checkLineCoverage(reportDir, CoverageType.Statement, 0.33, numberFormat) + checker.checkLineCoverage(reportDir, CoverageType.Statement, 0.33) } @Test void doesNotFailWhenStatementRateIsAboveTarget() { - checker.checkLineCoverage(reportDir, CoverageType.Statement, 0.3, numberFormat) + checker.checkLineCoverage(reportDir, CoverageType.Statement, 0.3) } // Branch coverage @Test void failsWhenBranchRateIsBelowTarget() { - assertFailure(CoverageChecker.errorMsg("50", "100", CoverageType.Branch), { - checker.checkLineCoverage(reportDir, CoverageType.Branch, 1.0, numberFormat) + assertFailure(CoverageChecker.errorMsg(0.5, 1.0, CoverageType.Branch), { + checker.checkLineCoverage(reportDir, CoverageType.Branch, 1.0) }) } @Test void doesNotFailWhenBranchRateIsAtTarget() { - checker.checkLineCoverage(reportDir, CoverageType.Branch, 0.5, numberFormat) + checker.checkLineCoverage(reportDir, CoverageType.Branch, 0.5) } @Test void doesNotFailWhenBranchRateIsAboveTarget() { - checker.checkLineCoverage(reportDir, CoverageType.Branch, 0.45, numberFormat) + checker.checkLineCoverage(reportDir, CoverageType.Branch, 0.45) + } + + @Test + void printsErrorMsgInEnglishWithUpTwoDecimalPointsPrecision() { + assertEquals("Only 54.32% of project is covered by tests instead of 90% (coverageType: Branch)", + CoverageChecker.errorMsg(0.54321, 0.9, CoverageType.Branch)) } - private void assertFailure(String message, Executable executable) { + private static void assertFailure(String message, Executable executable) { GradleException e = assertThrows(GradleException.class, executable) assertThat(e, new CauseMatcher(message)) }