package org.infernus.idea.checkstyle.build; import org.gradle.api.Project; import org.gradle.api.artifacts.Configuration; import org.gradle.api.artifacts.ConfigurationContainer; import org.gradle.api.file.FileCollection; import org.gradle.api.plugins.JavaPlugin; import org.gradle.api.tasks.SourceSet; import org.gradle.api.tasks.SourceSetContainer; import org.gradle.api.tasks.TaskContainer; import org.gradle.language.base.plugins.LifecycleBasePlugin; import org.gradle.testing.jacoco.plugins.JacocoPluginExtension; import org.gradle.testing.jacoco.plugins.JacocoTaskExtension; import org.gradle.testing.jacoco.tasks.JacocoCoverageVerification; import org.gradle.testing.jacoco.tasks.JacocoReport; import org.gradle.testing.jacoco.tasks.JacocoReportBase; import org.gradle.testing.jacoco.tasks.rules.JacocoLimit; import org.gradle.testing.jacoco.tasks.rules.JacocoViolationRule; import java.io.File; import java.math.BigDecimal; import java.util.stream.Collectors; public class CustomSourceSetCreator { public static final String CSACCESS_SOURCESET_NAME = "csaccess"; public static final String CSACCESSTEST_SOURCESET_NAME = "csaccessTest"; public static final String JACOCO_REPORT_TASK_NAME = "jacoco" + capitalize(CSACCESS_SOURCESET_NAME) + "Report"; public static final String JACOCO_VERIFICATION_TASK_NAME = "jacoco" + capitalize(CSACCESS_SOURCESET_NAME) + "CoverageVerification"; private static final double MINIMUM_CSACCESS_COVERAGE = 0.88d; private final Project project; public CustomSourceSetCreator(final Project pProject) { project = pProject; } private static String capitalize(final String pString) { String result = pString; if (pString != null) { final int strLen = pString.length(); if (strLen > 0) { result = new StringBuilder(strLen + 1).append(Character.toTitleCase(pString.charAt(0))) .append(pString.substring(1)).toString(); } } return result; } public CustomSourceSetCreator establishCsAccessSourceSet() { final SourceSetContainer sourceSets = (SourceSetContainer) project.getProperties().get("sourceSets"); final SourceSet mainSourceSet = sourceSets.getByName(SourceSet.MAIN_SOURCE_SET_NAME); // Create the 'csaccess' source set final SourceSet csaccessSourceSet = sourceSets.create(CSACCESS_SOURCESET_NAME); csaccessSourceSet.setCompileClasspath(csaccessSourceSet.getCompileClasspath().plus(mainSourceSet.getOutput())); csaccessSourceSet.setRuntimeClasspath(csaccessSourceSet.getRuntimeClasspath().plus(mainSourceSet.getOutput())); sourceSets.add(csaccessSourceSet); // Derive all its configurations from 'main', so 'csaccess' code can see 'main' code final ConfigurationContainer configurations = project.getConfigurations(); final Configuration compileConfig = configurations.getByName(mainSourceSet.getCompileConfigurationName()); final Configuration compileOnlyConfig = configurations.getByName(mainSourceSet .getCompileOnlyConfigurationName()); final Configuration compileClasspathConfig = configurations.getByName(mainSourceSet .getCompileClasspathConfigurationName()); final Configuration runtimeConfig = configurations.getByName(mainSourceSet.getRuntimeConfigurationName()); configurations.getByName(csaccessSourceSet.getCompileConfigurationName()).extendsFrom(compileConfig); configurations.getByName(csaccessSourceSet.getCompileOnlyConfigurationName()).extendsFrom(compileOnlyConfig); configurations.getByName(csaccessSourceSet.getCompileClasspathConfigurationName()).extendsFrom (compileClasspathConfig); configurations.getByName(csaccessSourceSet.getRuntimeConfigurationName()).extendsFrom(runtimeConfig); // Wire task dependencies to match the classpath dependencies (arrow means "depends on"): // - compileTestJava -> compileCsaccessJava // - testClasses -> csaccessClasses // - jar -> csaccessClasses final TaskContainer tasks = project.getTasks(); tasks.getByName(JavaPlugin.COMPILE_TEST_JAVA_TASK_NAME).dependsOn(tasks.getByName(csaccessSourceSet .getCompileJavaTaskName())); tasks.getByName(JavaPlugin.TEST_CLASSES_TASK_NAME).dependsOn(tasks.getByName(csaccessSourceSet .getClassesTaskName())); tasks.getByName(JavaPlugin.JAR_TASK_NAME).dependsOn(tasks.getByName(csaccessSourceSet.getClassesTaskName())); return this; } public CustomSourceSetCreator establishCsAccessTestSourceSet() { final SourceSetContainer sourceSets = (SourceSetContainer) project.getProperties().get("sourceSets"); final SourceSet mainSourceSet = sourceSets.getByName(SourceSet.MAIN_SOURCE_SET_NAME); final SourceSet csaccessSourceSet = sourceSets.getByName(CSACCESS_SOURCESET_NAME); final SourceSet testSourceSet = sourceSets.getByName(SourceSet.TEST_SOURCE_SET_NAME); // Create the 'csaccess' source set final SourceSet csaccessTestSourceSet = sourceSets.create(CSACCESSTEST_SOURCESET_NAME); csaccessTestSourceSet.setCompileClasspath(csaccessTestSourceSet.getCompileClasspath(). // plus(mainSourceSet.getOutput()).plus(csaccessSourceSet.getOutput())); csaccessTestSourceSet.setRuntimeClasspath(csaccessTestSourceSet.getRuntimeClasspath(). // plus(mainSourceSet.getOutput()).plus(csaccessSourceSet.getOutput())); sourceSets.add(csaccessTestSourceSet); // Derive all its configurations from 'test' and 'csaccess' final ConfigurationContainer configurations = project.getConfigurations(); final Configuration csaccessCompileConfig = configurations.getByName( // csaccessSourceSet.getCompileConfigurationName()); final Configuration csaccessCompileOnlyConfig = configurations.getByName( // csaccessSourceSet.getCompileOnlyConfigurationName()); final Configuration csaccessCompileClasspathConfig = configurations.getByName( // csaccessSourceSet.getCompileClasspathConfigurationName()); final Configuration csaccessRuntimeConfig = configurations.getByName( // csaccessSourceSet.getRuntimeConfigurationName()); final Configuration testCompileConfig = configurations.getByName(testSourceSet.getCompileConfigurationName()); final Configuration testCompileOnlyConfig = configurations.getByName( // testSourceSet.getCompileOnlyConfigurationName()); final Configuration testCompileClasspathConfig = configurations.getByName( // testSourceSet.getCompileClasspathConfigurationName()); final Configuration testRuntimeConfig = configurations.getByName(testSourceSet.getRuntimeConfigurationName()); configurations.getByName(csaccessTestSourceSet.getCompileConfigurationName()). // extendsFrom(csaccessCompileConfig, testCompileConfig); configurations.getByName(csaccessTestSourceSet.getCompileOnlyConfigurationName()). // extendsFrom(csaccessCompileOnlyConfig, testCompileOnlyConfig); configurations.getByName(csaccessTestSourceSet.getCompileClasspathConfigurationName()). // extendsFrom(csaccessCompileClasspathConfig, testCompileClasspathConfig); configurations.getByName(csaccessTestSourceSet.getRuntimeConfigurationName()). // extendsFrom(csaccessRuntimeConfig, testRuntimeConfig); // Wire task dependencies to match the classpath dependencies (arrow means "depends on"): // - compileCsaccessTestJava -> compileCsaccessJava // - csaccessTestClasses -> csaccessClasses final TaskContainer tasks = project.getTasks(); tasks.getByName(csaccessTestSourceSet.getCompileJavaTaskName()).dependsOn(tasks.getByName(csaccessSourceSet .getCompileJavaTaskName())); tasks.getByName(csaccessTestSourceSet.getClassesTaskName()).dependsOn(tasks.getByName(csaccessSourceSet .getClassesTaskName())); return this; } public void setupCoverageVerification() { final TaskContainer tasks = project.getTasks(); // Disable JaCoCo for 'test' source set final JacocoTaskExtension jacocoTestTaskExtension = (JacocoTaskExtension) tasks.getByName( JavaPlugin.TEST_TASK_NAME).getExtensions().getByName(JacocoPluginExtension.TASK_EXTENSION_NAME); jacocoTestTaskExtension.setEnabled(false); tasks.remove(tasks.getByName("jacocoTestReport")); tasks.remove(tasks.getByName("jacocoTestCoverageVerification")); // Enable JaCoCo reporting for 'csaccess' source set final JacocoReport jacocoReportTask = tasks.create(JACOCO_REPORT_TASK_NAME, JacocoReport.class); jacocoReportTask.dependsOn(tasks.getByName(CsaccessTestTask.NAME), tasks.getByName(CsaccessTestTask.XTEST_TASK_NAME)); jacocoReportTask.setDescription("Generate exclusive JaCoCo test report on the '" + CSACCESS_SOURCESET_NAME + "' classes"); configureJacocoTask(jacocoReportTask); jacocoReportTask.getReports().getXml().setEnabled(true); jacocoReportTask.getReports().getCsv().setEnabled(false); jacocoReportTask.getReports().getHtml().setEnabled(true); // Verify minimum line coverage for 'csaccess' source set final JacocoCoverageVerification jacocoVerificationTask = tasks.create(JACOCO_VERIFICATION_TASK_NAME, JacocoCoverageVerification.class); jacocoVerificationTask.dependsOn(jacocoReportTask); jacocoVerificationTask.setDescription("Ensure that '" + CSACCESS_SOURCESET_NAME + "' test coverage does not drop below a certain level"); configureJacocoTask(jacocoVerificationTask); jacocoVerificationTask.getViolationRules().rule((final JacocoViolationRule rule) -> { rule.limit((final JacocoLimit jacocoLimit) -> { jacocoLimit.setMinimum(BigDecimal.valueOf(MINIMUM_CSACCESS_COVERAGE)); }); }); // Wire 'build' task so that it ensures coverage tasks.getByName(LifecycleBasePlugin.BUILD_TASK_NAME).dependsOn(jacocoVerificationTask); } private void configureJacocoTask(final JacocoReportBase pJacocoTask) { pJacocoTask.setGroup(LifecycleBasePlugin.VERIFICATION_GROUP); final SourceSetContainer sourceSets = (SourceSetContainer) project.getProperties().get("sourceSets"); final SourceSet csaccessSourceSet = sourceSets.getByName(CSACCESS_SOURCESET_NAME); pJacocoTask.setClassDirectories(project.files(csaccessSourceSet.getOutput().getClassesDir())); pJacocoTask.setSourceDirectories(csaccessSourceSet.getJava().getSourceDirectories()); final FileCollection execFiles = project.files(project.getTasks().withType(CsaccessTestTask.class).stream() .map((final CsaccessTestTask task) -> { // return new File(project.getBuildDir() + "/jacoco", task.getName() + ".exec"); }).collect(Collectors.toList())); pJacocoTask.setExecutionData(execFiles); } }