// Copyright 2019 The Nomulus Authors. All Rights Reserved. // // 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 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. apply plugin: 'java' apply plugin: 'net.ltgt.errorprone' apply plugin: 'checkstyle' apply plugin: 'jacoco' // Checkstyle should run as part of the testing task tasks.test.dependsOn tasks.checkstyleMain tasks.test.dependsOn tasks.checkstyleTest // Generate per-subproject reports in build/reports/jacoco directory. // TODO(weiminyu): publish periodical reports to well known location. // TODO(weiminyu): investigate incremental coverage change calculation and alert. tasks.test.finalizedBy jacocoTestReport // Exclude test-only dependencies from release artifacts. // TLDR: Project dependency should be on runtimeClasspath. A custom // configuration 'deploy_jar' is used as a proxy to expose runtimeClasspath // to other projects. It is also convenient to exclude test-only jars from // runtimeClasspath. // // Here is the context: // - Some dependencies have test-only jars on their 'compile' classpaths. // - As a result, they appear in our 'implementation', 'runtimeClasspath' and // 'default' configurations, among others, and end up in our release war // files. // - Since these jars are needed for our own tests, we can only exclude them // from runtimeClasspath. Excluding them from 'compile' or 'runtimeOnly' would // also exclude them from testCompileClasspath and testRuntimeClasspath, // resulting in test failures. // - If configuration is not specified, project dependency uses the 'default' // configuration, which always has the same content as 'runtimeOnly'. // - When release is involved, 'runtimeClasspath' is actually the right // configuration to use. The 'runtimeOnly' configuration does not include // 'runtimeOnly' dependencies, while 'runtimeClasspath' does. // - 'runtimeClasspath' cannot be referenced directly by another project. // We use a custom configuration 'deploy_jar' as a proxy. // TODO(weiminyu): Fix all project dependencies to use deploy_jar configurations { deploy_jar.extendsFrom runtimeClasspath all.findAll { it.name in ['runtimeClasspath', 'compileClasspath'] }.each { // JUnit is from org.apache.beam:beam-runners-google-cloud-dataflow-java, // and json-simple. it.exclude group: 'junit' // Mockito is from org.apache.beam:beam-runners-google-cloud-dataflow-java // See https://issues.apache.org/jira/browse/BEAM-8862 it.exclude group: 'org.mockito', module: 'mockito-core' } all.each { // log4j has high-profile security vulnerabilities. It's a transitive // dependency used by some Apache Beam packages. Excluding it does not // impact our troubleshooting needs. it.exclude group: 'org.apache.logging.log4j' } } dependencies { // compatibility with Java 8 errorprone("com.google.errorprone:error_prone_core:2.3.4") } test { useJUnitPlatform() } // Sets up integration test with a registry environment. The target environment // is passed by the 'test.gcp_integration.env' property. Test runner must have // been authorized to access the corresponding GCP project, e.g., by running // 'gcloud auth application-default login' or by downloading a credential file // and assign the path to it to the GOOGLE_APPLICATION_CREDENTIALS environment // variable. // // A typical use case is to run tests from desktop that accesses Cloud resources. tasks.withType(Test).configureEach { def gcp_integration_env_property = 'test.gcp_integration.env' if (project.hasProperty(gcp_integration_env_property)) { String targetEnv = project.property(gcp_integration_env_property) if (targetEnv in ['sandbox', 'production']) { throw new RuntimeException("Integration test with production or sandbox not allowed.") } systemProperty gcp_integration_env_property, targetEnv } // This environment variable along with testcontainers 1.15.2 works around // a race condition introduced in 1.15.0. This can be removed once httpclient5 // becomes the default transport type in testcontainers, which may happen // in 1.16.x. // See https://github.com/testcontainers/testcontainers-java/issues/3531 // for more information. environment('TESTCONTAINERS_TRANSPORT_TYPE', 'httpclient5') } tasks.withType(JavaCompile).configureEach { // The -Werror flag causes Intellij to fail on deprecated api use. // Allow IDE user to turn off this flag by specifying a Gradle VM // option from inside the IDE. if (System.getProperty('no_werror') != 'true' && // The core project throws an warning about Gradle annotation processor // not compatible with Java source version > 8. Re-assess once the // warning is fixed. getProject().name != 'core') { options.compilerArgs << "-Werror" } options.errorprone.disableWarningsInGeneratedCode = true options.errorprone.errorproneArgumentProviders.add([ asArguments: { return ['-XepExcludedPaths:.*/build/generated/.*'] }] as CommandLineArgumentProvider) // Disable features currently incompatible with Java 12 if ((JavaVersion.current().majorVersion as Integer) > 11) { // This check is broken in Java 12. // See https://github.com/google/error-prone/issues/1257 options.errorprone.errorproneArgs=['-Xep:Finally:OFF'] } } compileJava { options.encoding = "UTF-8" } compileTestJava { options.encoding = "UTF-8" } // To check or fix file formats, run the following commands from this directory: // - Check: ./gradlew spotlessCheck // - Format in place: ./gradlew spotlessApply spotless { format 'misc', { clearSteps() target '**/*.gradle' targetExclude '**/cloudbuild-caches/**' trimTrailingWhitespace() indentWithSpaces(2) endWithNewline() } } jacocoTestReport { // Use coverage data from all tests tasks, not just the one named 'test'. getExecutionData().setFrom(fileTree(buildDir).include("/jacoco/*.exec")) } // Initialize for coverage minimum determination for each sub project. task initCoverageMinimums { // Use current coverage ratio of each subproject as placeholder value. // Long-term plan is to calculate incremental coverage on the fly. rootProject.ext.coverageMinimums = [ 'core' : 0.6, 'proxy' : 0.52, 'util' : 0.57 ].asImmutable() rootProject.ext.getMinCoverage = { key -> if (rootProject.coverageMinimums.containsKey(key)) { return rootProject.coverageMinimums.get(key) } return 0.0 } } // Alerts for coverage violation. Note that, // - This task is FYI only and needs to be invoked explicitly. // - This task does not address incremental coverage. jacocoTestCoverageVerification { dependsOn initCoverageMinimums violationRules { rule { // Each limit consists of the following properties: // - An 'element' type: BUNDLE (default), PACKAGE, CLASS, SOURCEFILE, or METHOD. // - A 'counter' type: INSTRUCTION (default), LINE, BRANCH, COMPLEXITY, METHOD, or CLASS // - A 'value' type: TOTALCOUNT, COVEREDCOUNT, MISSEDCOUNT, COVEREDRATIO (default), // or MISSEDRATIO // - The 'minimum' threshold, given as a fraction or a percentage (including '%') limit { minimum = rootProject.getMinCoverage(project.getName()) } } } }