diff --git a/buildSrc/$project.buildDir/generated/third_party_licenses/third_party_licenses.json b/buildSrc/$project.buildDir/generated/third_party_licenses/third_party_licenses.json deleted file mode 100644 index 3d5cd336b0b..00000000000 --- a/buildSrc/$project.buildDir/generated/third_party_licenses/third_party_licenses.json +++ /dev/null @@ -1 +0,0 @@ -{"customLib1":{"length":15,"start":11}} diff --git a/buildSrc/$project.buildDir/generated/third_party_licenses/third_party_licenses.txt b/buildSrc/$project.buildDir/generated/third_party_licenses/third_party_licenses.txt deleted file mode 100644 index 0faa2891f4f..00000000000 --- a/buildSrc/$project.buildDir/generated/third_party_licenses/third_party_licenses.txt +++ /dev/null @@ -1,3 +0,0 @@ -customLib1: -Test license - diff --git a/buildSrc/build.gradle b/buildSrc/build.gradle index 73118aead83..e82afcf3105 100644 --- a/buildSrc/build.gradle +++ b/buildSrc/build.gradle @@ -13,8 +13,8 @@ // limitations under the License. plugins { - id 'java-gradle-plugin' - id 'groovy' + id "org.gradle.kotlin.kotlin-dsl" version "1.2.6" + id "org.jlleitschuh.gradle.ktlint" version "9.2.1" } repositories { @@ -42,21 +42,16 @@ dependencies { implementation 'digital.wup:android-maven-publish:3.6.2' implementation 'org.jetbrains.kotlin:kotlin-gradle-plugin:1.3.20' implementation 'org.json:json:20180813' - implementation 'io.opencensus:opencensus-api:0.18.0' implementation 'io.opencensus:opencensus-exporter-stats-stackdriver:0.18.0' runtime 'io.opencensus:opencensus-impl:0.18.0' implementation group: 'com.google.code.gson', name: 'gson', version: '2.8.6' - implementation 'org.jetbrains.dokka:dokka-android-gradle-plugin:0.9.17-g008' - implementation 'com.android.tools.build:gradle:3.4.1' - testImplementation 'junit:junit:4.12' - testImplementation group: 'com.google.code.gson', name: 'gson', version: '2.8.6' - testImplementation('org.spockframework:spock-core:1.1-groovy-2.4') { - exclude group: 'org.codehaus.groovy' - } + testImplementation 'junit:junit:4.13-rc-1' + testImplementation group: 'com.google.code.gson', name: 'gson', version: '2.8.6' + testImplementation "com.google.truth:truth:1.0.1" testImplementation 'commons-io:commons-io:2.6' } diff --git a/buildSrc/src/main/groovy/com/google/firebase/gradle/plugins/ClosureUtil.java b/buildSrc/src/main/java/com/google/firebase/gradle/plugins/ClosureUtil.java similarity index 100% rename from buildSrc/src/main/groovy/com/google/firebase/gradle/plugins/ClosureUtil.java rename to buildSrc/src/main/java/com/google/firebase/gradle/plugins/ClosureUtil.java diff --git a/buildSrc/src/main/groovy/com/google/firebase/gradle/plugins/Dokka.java b/buildSrc/src/main/java/com/google/firebase/gradle/plugins/Dokka.java similarity index 100% rename from buildSrc/src/main/groovy/com/google/firebase/gradle/plugins/Dokka.java rename to buildSrc/src/main/java/com/google/firebase/gradle/plugins/Dokka.java diff --git a/buildSrc/src/main/groovy/com/google/firebase/gradle/plugins/FirebaseLibraryExtension.java b/buildSrc/src/main/java/com/google/firebase/gradle/plugins/FirebaseLibraryExtension.java similarity index 100% rename from buildSrc/src/main/groovy/com/google/firebase/gradle/plugins/FirebaseLibraryExtension.java rename to buildSrc/src/main/java/com/google/firebase/gradle/plugins/FirebaseLibraryExtension.java diff --git a/buildSrc/src/main/groovy/com/google/firebase/gradle/plugins/FirebaseLibraryPlugin.java b/buildSrc/src/main/java/com/google/firebase/gradle/plugins/FirebaseLibraryPlugin.java similarity index 100% rename from buildSrc/src/main/groovy/com/google/firebase/gradle/plugins/FirebaseLibraryPlugin.java rename to buildSrc/src/main/java/com/google/firebase/gradle/plugins/FirebaseLibraryPlugin.java diff --git a/buildSrc/src/main/groovy/com/google/firebase/gradle/plugins/FirebaseStaticAnalysis.java b/buildSrc/src/main/java/com/google/firebase/gradle/plugins/FirebaseStaticAnalysis.java similarity index 100% rename from buildSrc/src/main/groovy/com/google/firebase/gradle/plugins/FirebaseStaticAnalysis.java rename to buildSrc/src/main/java/com/google/firebase/gradle/plugins/FirebaseStaticAnalysis.java diff --git a/buildSrc/src/main/groovy/com/google/firebase/gradle/plugins/LibraryType.java b/buildSrc/src/main/java/com/google/firebase/gradle/plugins/LibraryType.java similarity index 100% rename from buildSrc/src/main/groovy/com/google/firebase/gradle/plugins/LibraryType.java rename to buildSrc/src/main/java/com/google/firebase/gradle/plugins/LibraryType.java diff --git a/buildSrc/src/main/groovy/com/google/firebase/gradle/plugins/SdkUtil.java b/buildSrc/src/main/java/com/google/firebase/gradle/plugins/SdkUtil.java similarity index 100% rename from buildSrc/src/main/groovy/com/google/firebase/gradle/plugins/SdkUtil.java rename to buildSrc/src/main/java/com/google/firebase/gradle/plugins/SdkUtil.java diff --git a/buildSrc/src/main/groovy/com/google/firebase/gradle/plugins/apiinfo/ApiInformationTask.java b/buildSrc/src/main/java/com/google/firebase/gradle/plugins/apiinfo/ApiInformationTask.java similarity index 100% rename from buildSrc/src/main/groovy/com/google/firebase/gradle/plugins/apiinfo/ApiInformationTask.java rename to buildSrc/src/main/java/com/google/firebase/gradle/plugins/apiinfo/ApiInformationTask.java diff --git a/buildSrc/src/main/groovy/com/google/firebase/gradle/plugins/apiinfo/GenerateApiTxtFileTask.java b/buildSrc/src/main/java/com/google/firebase/gradle/plugins/apiinfo/GenerateApiTxtFileTask.java similarity index 100% rename from buildSrc/src/main/groovy/com/google/firebase/gradle/plugins/apiinfo/GenerateApiTxtFileTask.java rename to buildSrc/src/main/java/com/google/firebase/gradle/plugins/apiinfo/GenerateApiTxtFileTask.java diff --git a/buildSrc/src/main/groovy/com/google/firebase/gradle/plugins/apiinfo/GenerateStubsTask.java b/buildSrc/src/main/java/com/google/firebase/gradle/plugins/apiinfo/GenerateStubsTask.java similarity index 100% rename from buildSrc/src/main/groovy/com/google/firebase/gradle/plugins/apiinfo/GenerateStubsTask.java rename to buildSrc/src/main/java/com/google/firebase/gradle/plugins/apiinfo/GenerateStubsTask.java diff --git a/buildSrc/src/main/groovy/com/google/firebase/gradle/plugins/apiinfo/GetMetalavaJarTask.java b/buildSrc/src/main/java/com/google/firebase/gradle/plugins/apiinfo/GetMetalavaJarTask.java similarity index 100% rename from buildSrc/src/main/groovy/com/google/firebase/gradle/plugins/apiinfo/GetMetalavaJarTask.java rename to buildSrc/src/main/java/com/google/firebase/gradle/plugins/apiinfo/GetMetalavaJarTask.java diff --git a/buildSrc/src/main/groovy/com/google/firebase/gradle/plugins/ci/AffectedProjectFinder.java b/buildSrc/src/main/java/com/google/firebase/gradle/plugins/ci/AffectedProjectFinder.java similarity index 100% rename from buildSrc/src/main/groovy/com/google/firebase/gradle/plugins/ci/AffectedProjectFinder.java rename to buildSrc/src/main/java/com/google/firebase/gradle/plugins/ci/AffectedProjectFinder.java diff --git a/buildSrc/src/main/groovy/com/google/firebase/gradle/plugins/ci/ContinuousIntegrationExtension.java b/buildSrc/src/main/java/com/google/firebase/gradle/plugins/ci/ContinuousIntegrationExtension.java similarity index 100% rename from buildSrc/src/main/groovy/com/google/firebase/gradle/plugins/ci/ContinuousIntegrationExtension.java rename to buildSrc/src/main/java/com/google/firebase/gradle/plugins/ci/ContinuousIntegrationExtension.java diff --git a/buildSrc/src/main/groovy/com/google/firebase/gradle/plugins/ci/ContinuousIntegrationPlugin.java b/buildSrc/src/main/java/com/google/firebase/gradle/plugins/ci/ContinuousIntegrationPlugin.java similarity index 100% rename from buildSrc/src/main/groovy/com/google/firebase/gradle/plugins/ci/ContinuousIntegrationPlugin.java rename to buildSrc/src/main/java/com/google/firebase/gradle/plugins/ci/ContinuousIntegrationPlugin.java diff --git a/buildSrc/src/main/groovy/com/google/firebase/gradle/plugins/ci/Coverage.java b/buildSrc/src/main/java/com/google/firebase/gradle/plugins/ci/Coverage.java similarity index 100% rename from buildSrc/src/main/groovy/com/google/firebase/gradle/plugins/ci/Coverage.java rename to buildSrc/src/main/java/com/google/firebase/gradle/plugins/ci/Coverage.java diff --git a/buildSrc/src/main/groovy/com/google/firebase/gradle/plugins/ci/Environment.java b/buildSrc/src/main/java/com/google/firebase/gradle/plugins/ci/Environment.java similarity index 100% rename from buildSrc/src/main/groovy/com/google/firebase/gradle/plugins/ci/Environment.java rename to buildSrc/src/main/java/com/google/firebase/gradle/plugins/ci/Environment.java diff --git a/buildSrc/src/main/groovy/com/google/firebase/gradle/plugins/ci/SmokeTestsPlugin.java b/buildSrc/src/main/java/com/google/firebase/gradle/plugins/ci/SmokeTestsPlugin.java similarity index 100% rename from buildSrc/src/main/groovy/com/google/firebase/gradle/plugins/ci/SmokeTestsPlugin.java rename to buildSrc/src/main/java/com/google/firebase/gradle/plugins/ci/SmokeTestsPlugin.java diff --git a/buildSrc/src/main/groovy/com/google/firebase/gradle/plugins/ci/device/FirebaseTestLabExtension.java b/buildSrc/src/main/java/com/google/firebase/gradle/plugins/ci/device/FirebaseTestLabExtension.java similarity index 100% rename from buildSrc/src/main/groovy/com/google/firebase/gradle/plugins/ci/device/FirebaseTestLabExtension.java rename to buildSrc/src/main/java/com/google/firebase/gradle/plugins/ci/device/FirebaseTestLabExtension.java diff --git a/buildSrc/src/main/groovy/com/google/firebase/gradle/plugins/ci/device/FirebaseTestLabPlugin.java b/buildSrc/src/main/java/com/google/firebase/gradle/plugins/ci/device/FirebaseTestLabPlugin.java similarity index 100% rename from buildSrc/src/main/groovy/com/google/firebase/gradle/plugins/ci/device/FirebaseTestLabPlugin.java rename to buildSrc/src/main/java/com/google/firebase/gradle/plugins/ci/device/FirebaseTestLabPlugin.java diff --git a/buildSrc/src/main/groovy/com/google/firebase/gradle/plugins/ci/device/FirebaseTestServer.java b/buildSrc/src/main/java/com/google/firebase/gradle/plugins/ci/device/FirebaseTestServer.java similarity index 100% rename from buildSrc/src/main/groovy/com/google/firebase/gradle/plugins/ci/device/FirebaseTestServer.java rename to buildSrc/src/main/java/com/google/firebase/gradle/plugins/ci/device/FirebaseTestServer.java diff --git a/buildSrc/src/main/groovy/com/google/firebase/gradle/plugins/ci/metrics/DrainingBuildListener.java b/buildSrc/src/main/java/com/google/firebase/gradle/plugins/ci/metrics/DrainingBuildListener.java similarity index 100% rename from buildSrc/src/main/groovy/com/google/firebase/gradle/plugins/ci/metrics/DrainingBuildListener.java rename to buildSrc/src/main/java/com/google/firebase/gradle/plugins/ci/metrics/DrainingBuildListener.java diff --git a/buildSrc/src/main/groovy/com/google/firebase/gradle/plugins/ci/metrics/MeasuringTaskExecutionListener.java b/buildSrc/src/main/java/com/google/firebase/gradle/plugins/ci/metrics/MeasuringTaskExecutionListener.java similarity index 100% rename from buildSrc/src/main/groovy/com/google/firebase/gradle/plugins/ci/metrics/MeasuringTaskExecutionListener.java rename to buildSrc/src/main/java/com/google/firebase/gradle/plugins/ci/metrics/MeasuringTaskExecutionListener.java diff --git a/buildSrc/src/main/groovy/com/google/firebase/gradle/plugins/ci/metrics/Metrics.java b/buildSrc/src/main/java/com/google/firebase/gradle/plugins/ci/metrics/Metrics.java similarity index 100% rename from buildSrc/src/main/groovy/com/google/firebase/gradle/plugins/ci/metrics/Metrics.java rename to buildSrc/src/main/java/com/google/firebase/gradle/plugins/ci/metrics/Metrics.java diff --git a/buildSrc/src/main/groovy/com/google/firebase/gradle/plugins/ci/metrics/MetricsPlugin.java b/buildSrc/src/main/java/com/google/firebase/gradle/plugins/ci/metrics/MetricsPlugin.java similarity index 100% rename from buildSrc/src/main/groovy/com/google/firebase/gradle/plugins/ci/metrics/MetricsPlugin.java rename to buildSrc/src/main/java/com/google/firebase/gradle/plugins/ci/metrics/MetricsPlugin.java diff --git a/buildSrc/src/main/groovy/com/google/firebase/gradle/plugins/ci/metrics/StackdriverMetrics.java b/buildSrc/src/main/java/com/google/firebase/gradle/plugins/ci/metrics/StackdriverMetrics.java similarity index 100% rename from buildSrc/src/main/groovy/com/google/firebase/gradle/plugins/ci/metrics/StackdriverMetrics.java rename to buildSrc/src/main/java/com/google/firebase/gradle/plugins/ci/metrics/StackdriverMetrics.java diff --git a/buildSrc/src/main/groovy/com/google/firebase/gradle/plugins/license/GenerateLicensesTask.java b/buildSrc/src/main/java/com/google/firebase/gradle/plugins/license/GenerateLicensesTask.java similarity index 100% rename from buildSrc/src/main/groovy/com/google/firebase/gradle/plugins/license/GenerateLicensesTask.java rename to buildSrc/src/main/java/com/google/firebase/gradle/plugins/license/GenerateLicensesTask.java diff --git a/buildSrc/src/main/groovy/com/google/firebase/gradle/plugins/license/LicenseResolverPlugin.java b/buildSrc/src/main/java/com/google/firebase/gradle/plugins/license/LicenseResolverPlugin.java similarity index 100% rename from buildSrc/src/main/groovy/com/google/firebase/gradle/plugins/license/LicenseResolverPlugin.java rename to buildSrc/src/main/java/com/google/firebase/gradle/plugins/license/LicenseResolverPlugin.java diff --git a/buildSrc/src/main/groovy/com/google/firebase/gradle/plugins/license/ThirdPartyLicensesExtension.java b/buildSrc/src/main/java/com/google/firebase/gradle/plugins/license/ThirdPartyLicensesExtension.java similarity index 100% rename from buildSrc/src/main/groovy/com/google/firebase/gradle/plugins/license/ThirdPartyLicensesExtension.java rename to buildSrc/src/main/java/com/google/firebase/gradle/plugins/license/ThirdPartyLicensesExtension.java diff --git a/buildSrc/src/main/groovy/com/google/firebase/gradle/plugins/publish/Mode.java b/buildSrc/src/main/java/com/google/firebase/gradle/plugins/publish/Mode.java similarity index 100% rename from buildSrc/src/main/groovy/com/google/firebase/gradle/plugins/publish/Mode.java rename to buildSrc/src/main/java/com/google/firebase/gradle/plugins/publish/Mode.java diff --git a/buildSrc/src/main/groovy/com/google/firebase/gradle/plugins/publish/Publisher.java b/buildSrc/src/main/java/com/google/firebase/gradle/plugins/publish/Publisher.java similarity index 100% rename from buildSrc/src/main/groovy/com/google/firebase/gradle/plugins/publish/Publisher.java rename to buildSrc/src/main/java/com/google/firebase/gradle/plugins/publish/Publisher.java diff --git a/buildSrc/src/main/groovy/com/google/firebase/gradle/plugins/publish/PublishingPlugin.java b/buildSrc/src/main/java/com/google/firebase/gradle/plugins/publish/PublishingPlugin.java similarity index 100% rename from buildSrc/src/main/groovy/com/google/firebase/gradle/plugins/publish/PublishingPlugin.java rename to buildSrc/src/main/java/com/google/firebase/gradle/plugins/publish/PublishingPlugin.java diff --git a/buildSrc/src/test/groovy/com/google/firebase/gradle/plugins/license/LicenseResolverPluginSpec.groovy b/buildSrc/src/test/groovy22/com/google/firebase/gradle/plugins/license/LicenseResolverPluginSpec.groovy similarity index 100% rename from buildSrc/src/test/groovy/com/google/firebase/gradle/plugins/license/LicenseResolverPluginSpec.groovy rename to buildSrc/src/test/groovy22/com/google/firebase/gradle/plugins/license/LicenseResolverPluginSpec.groovy diff --git a/buildSrc/src/test/groovy/com/google/firebase/gradle/plugins/publish/PublishingPluginSpec.groovy b/buildSrc/src/test/groovy22/com/google/firebase/gradle/plugins/publish/PublishingPluginSpec.groovy similarity index 100% rename from buildSrc/src/test/groovy/com/google/firebase/gradle/plugins/publish/PublishingPluginSpec.groovy rename to buildSrc/src/test/groovy22/com/google/firebase/gradle/plugins/publish/PublishingPluginSpec.groovy diff --git a/buildSrc/src/test/kotlin/com/google/firebase/gradle/plugins/LicenseResolverPluginTests.kt b/buildSrc/src/test/kotlin/com/google/firebase/gradle/plugins/LicenseResolverPluginTests.kt new file mode 100644 index 00000000000..766d603c121 --- /dev/null +++ b/buildSrc/src/test/kotlin/com/google/firebase/gradle/plugins/LicenseResolverPluginTests.kt @@ -0,0 +1,147 @@ +// Copyright 2020 Google LLC +// +// 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. + +package com.google.firebase.gradle.plugins + +import com.google.common.truth.Truth.assertThat +import com.google.gson.Gson +import com.google.gson.reflect.TypeToken +import java.io.File +import org.gradle.testkit.runner.BuildResult +import org.gradle.testkit.runner.GradleRunner +import org.gradle.testkit.runner.TaskOutcome +import org.gradle.testkit.runner.UnexpectedBuildFailure +import org.junit.Assert +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.junit.rules.TemporaryFolder +import org.junit.runner.RunWith +import org.junit.runners.JUnit4 + +@RunWith(JUnit4::class) +class LicenseResolverPluginTests { + + @Rule + @JvmField + val testProjectDir = TemporaryFolder() + private lateinit var buildFile: File + + val idempotentBuild: (taskName: String) -> BuildResult + get() = this::build.memoize() + + @Before + fun setup() { + buildFile = testProjectDir.newFile("build.gradle") + testProjectDir.newFolder("src", "main", "java", "com", "example") + testProjectDir.newFile("src/main/java/com/example/Foo.java").writeText("package com.example; class Foo {}") + testProjectDir.newFile("src/main/AndroidManifest.xml").writeText(MANIFEST) + + buildFile.writeText(BUILD_CONFIG) + } + + @Test + fun `Generating licenses`() { + val result = idempotentBuild("generateLicenses") + assertThat(result.task(":generateLicenses")?.outcome).isEqualTo(TaskOutcome.SUCCESS) + + val json = getLicenseJson() + val txt = getLicenseText() + + assertThat(txt).isNotEmpty() + + assertThat(json).containsKey("customLib1") + + assertThat(txt).contains("customLib1") + assertThat(txt).contains("Test license") + val (start, length) = json["customLib1"]!! + assertThat(txt.substring(start, start + length).trim()).isEqualTo("Test license") + } + + @Test + fun `License tasks throw useful exception if file URI not found`() { + buildFile.writeText(""" + plugins { + id 'com.android.library' + id 'LicenseResolverPlugin' + } + android.compileSdkVersion = 26 + + thirdPartyLicenses { + add 'customLib', "file:///${File("non_existent_path.txt").absolutePath}" + } + """) + + val thrown = Assert.assertThrows(UnexpectedBuildFailure::class.java) { + build("generateLicenses") + } + + assertThat(thrown.message).contains("License file not found") + } + + data class FileOffset(val start: Int, val length: Int) + + private fun getLicenseJson(): Map = + Gson().fromJson( + File("${testProjectDir.root}/build/generated/third_party_licenses/", + "third_party_licenses.json").readText(), + object : TypeToken>() {}.type) + + private fun getLicenseText(): String = + File("${testProjectDir.root}/build/generated/third_party_licenses/", + "third_party_licenses.txt").readText() + + private fun build(taskName: String): BuildResult = GradleRunner.create() + .withProjectDir(testProjectDir.root) + .withArguments(taskName, "--stacktrace") + .withPluginClasspath() + .build() + + companion object { + const val MANIFEST = """ + + + + """ + val BUILD_CONFIG = """ + buildscript { + repositories { + google() + jcenter() + } + } + + plugins { + id 'com.android.library' + id 'LicenseResolverPlugin' + } + + android.compileSdkVersion = 26 + + repositories { + jcenter() + google() + } + dependencies { + implementation 'com.squareup.picasso:picasso:2.71828' + implementation 'com.squareup.okhttp:okhttp:2.7.5' + } + + thirdPartyLicenses { + add 'customLib1', "file:///${File("src/test/fixtures/license.txt").absolutePath}" + } + """ + } +} diff --git a/buildSrc/src/test/kotlin/com/google/firebase/gradle/plugins/Memoization.kt b/buildSrc/src/test/kotlin/com/google/firebase/gradle/plugins/Memoization.kt new file mode 100644 index 00000000000..7d25f4f835e --- /dev/null +++ b/buildSrc/src/test/kotlin/com/google/firebase/gradle/plugins/Memoization.kt @@ -0,0 +1,24 @@ +// Copyright 2020 Google LLC +// +// 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. + +package com.google.firebase.gradle.plugins + +class Memoize1(val f: (T) -> R) : (T) -> R { + private val values = mutableMapOf() + override fun invoke(x: T): R { + return values.getOrPut(x, { f(x) }) + } +} + +fun ((T) -> R).memoize(): (T) -> R = Memoize1(this) diff --git a/buildSrc/src/test/kotlin/com/google/firebase/gradle/plugins/PublishingPluginTests.kt b/buildSrc/src/test/kotlin/com/google/firebase/gradle/plugins/PublishingPluginTests.kt new file mode 100644 index 00000000000..7492c3fa6eb --- /dev/null +++ b/buildSrc/src/test/kotlin/com/google/firebase/gradle/plugins/PublishingPluginTests.kt @@ -0,0 +1,312 @@ +// Copyright 2020 Google LLC +// +// 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. + +package com.google.firebase.gradle.plugins + +import com.google.common.truth.Truth.assertThat +import com.google.firebase.gradle.plugins.publish.Mode +import java.io.File +import org.gradle.testkit.runner.BuildResult +import org.gradle.testkit.runner.GradleRunner +import org.gradle.testkit.runner.TaskOutcome +import org.gradle.testkit.runner.UnexpectedBuildFailure +import org.junit.Assert +import org.junit.Rule +import org.junit.Test +import org.junit.rules.TemporaryFolder + +class PublishingPluginTests { + @Rule + @JvmField + val testProjectDir = TemporaryFolder() + + private val subprojects = mutableListOf() + private lateinit var rootBuildFile: File + private lateinit var rootSettingsFile: File + + @Test + fun `Publishing dependent projects succeeds`() { + val project1 = Project(name = "childProject1", version = "1.0") + val project2 = Project( + name = "childProject2", + version = "0.9", + projectDependencies = setOf(project1), + customizePom = """ +licenses { + license { + name = 'Hello' + } +} +""") + subprojectsDefined(project1, project2) + val result = publish(Mode.RELEASE, project1, project2) + assertThat(result.task(":firebasePublish")?.outcome).isEqualTo(TaskOutcome.SUCCESS) + + val pomOrNull1 = project1.getPublishedPom("${testProjectDir.root}/build/m2repository") + val pomOrNull2 = project2.getPublishedPom("${testProjectDir.root}/build/m2repository") + assertThat(pomOrNull1).isNotNull() + assertThat(pomOrNull2).isNotNull() + val pom1 = pomOrNull1!! + val pom2 = pomOrNull2!! + + assertThat(pom1.artifact.version).isEqualTo(project1.version) + assertThat(pom2.artifact.version).isEqualTo(project2.version) + assertThat(pom1.license).isEqualTo(License( + "The Apache Software License, Version 2.0", + "http://www.apache.org/licenses/LICENSE-2.0.txt")) + assertThat(pom2.license).isEqualTo(License( + "Hello", + "")) + + assertThat(pom2.dependencies).isEqualTo( + listOf(Artifact( + groupId = project1.group, + artifactId = project1.name, + version = project1.version, + type = Type.AAR, + scope = "compile"))) + } + + @Test + fun `Publish with unreleased dependency`() { + val project1 = Project(name = "childProject1", version = "1.0") + val project2 = Project( + name = "childProject2", + version = "0.9", + projectDependencies = setOf(project1)) + + subprojectsDefined(project1, project2) + val exception = Assert.assertThrows(UnexpectedBuildFailure::class.java) { + publish(Mode.RELEASE, project2) + } + assertThat(exception.message).contains("Failed to release com.example:childProject2") + } + + @Test + fun `Publish with released dependency`() { + val project1 = Project(name = "childProject1", version = "1.0", latestReleasedVersion = "0.8") + val project2 = Project( + name = "childProject2", + version = "0.9", + projectDependencies = setOf(project1)) + subprojectsDefined(project1, project2) + + val result = publish(Mode.RELEASE, project2) + assertThat(result.task(":firebasePublish")?.outcome).isEqualTo(TaskOutcome.SUCCESS) + + val pomOrNull1 = project1.getPublishedPom("${testProjectDir.root}/build/m2repository") + val pomOrNull2 = project2.getPublishedPom("${testProjectDir.root}/build/m2repository") + assertThat(pomOrNull1).isNull() + assertThat(pomOrNull2).isNotNull() + + val pom2 = pomOrNull2!! + assertThat(pom2.dependencies).isEqualTo( + listOf(Artifact( + groupId = project1.group, + artifactId = project1.name, + version = project1.latestReleasedVersion!!, + type = Type.AAR, + scope = "compile"))) + } + + @Test + fun `Publish all dependent snapshot projects succeeds`() { + val project1 = Project(name = "childProject1", version = "1.0") + val project2 = Project( + name = "childProject2", + version = "0.9", + projectDependencies = setOf(project1)) + subprojectsDefined(project1, project2) + val result = publish(Mode.SNAPSHOT, project1, project2) + assertThat(result.task(":firebasePublish")?.outcome).isEqualTo(TaskOutcome.SUCCESS) + + val pomOrNull1 = project1.getPublishedPom("${testProjectDir.root}/build/m2repository") + val pomOrNull2 = project2.getPublishedPom("${testProjectDir.root}/build/m2repository") + assertThat(pomOrNull1).isNotNull() + assertThat(pomOrNull2).isNotNull() + + val pom1 = pomOrNull1!! + val pom2 = pomOrNull2!! + + assertThat(pom1.artifact.version).isEqualTo("${project1.version}-SNAPSHOT") + assertThat(pom2.artifact.version).isEqualTo("${project2.version}-SNAPSHOT") + + assertThat(pom2.dependencies).isEqualTo( + listOf(Artifact( + groupId = project1.group, + artifactId = project1.name, + version = "${project1.version}-SNAPSHOT", + type = Type.AAR, + scope = "compile"))) + } + + @Test + fun `Publish snapshots with released dependency`() { + val project1 = Project(name = "childProject1", version = "1.0", latestReleasedVersion = "0.8") + val project2 = Project( + name = "childProject2", + version = "0.9", + projectDependencies = setOf(project1)) + subprojectsDefined(project1, project2) + + val result = publish(Mode.SNAPSHOT, project2) + assertThat(result.task(":firebasePublish")?.outcome).isEqualTo(TaskOutcome.SUCCESS) + + val pomOrNull1 = project1.getPublishedPom("${testProjectDir.root}/build/m2repository") + val pomOrNull2 = project2.getPublishedPom("${testProjectDir.root}/build/m2repository") + assertThat(pomOrNull1).isNull() + assertThat(pomOrNull2).isNotNull() + + val pom2 = pomOrNull2!! + + assertThat(pom2.artifact.version).isEqualTo("${project2.version}-SNAPSHOT") + assertThat(pom2.dependencies).isEqualTo( + listOf(Artifact( + groupId = project1.group, + artifactId = project1.name, + version = project1.latestReleasedVersion!!, + type = Type.AAR, + scope = "compile"))) + } + + @Test + fun `Publish project should also publish coreleased projects`() { + val project1 = Project(name = "childProject1", version = "1.0") + val project2 = Project( + name = "childProject2", + version = "0.9", + projectDependencies = setOf(project1), + releaseWith = project1) + subprojectsDefined(project1, project2) + + val result = publish(Mode.RELEASE, project1) + assertThat(result.task(":firebasePublish")?.outcome).isEqualTo(TaskOutcome.SUCCESS) + + val pomOrNull1 = project1.getPublishedPom("${testProjectDir.root}/build/m2repository") + val pomOrNull2 = project2.getPublishedPom("${testProjectDir.root}/build/m2repository") + assertThat(pomOrNull1).isNotNull() + assertThat(pomOrNull2).isNotNull() + + val pom1 = pomOrNull1!! + val pom2 = pomOrNull2!! + + assertThat(pom1.artifact.version).isEqualTo(project1.version) + assertThat(pom2.artifact.version).isEqualTo(project1.version) + assertThat(pom2.dependencies).isEqualTo( + listOf(Artifact( + groupId = project1.group, + artifactId = project1.name, + version = project1.version, + type = Type.AAR, + scope = "compile"))) + } + + @Test + fun `Publish project should correctly set dependency types`() { + val project1 = Project(name = "childProject1", version = "1.0", latestReleasedVersion = "0.8") + val project2 = Project( + name = "childProject2", + version = "0.9", + projectDependencies = setOf(project1), + externalDependencies = setOf( + Artifact("com.google.dagger", "dagger", "2.22"), + Artifact("com.google.dagger", "dagger-android-support", "2.22"), + Artifact("com.android.support", "multidex", "1.0.3") + )) + subprojectsDefined(project1, project2) + + val result = publish(Mode.RELEASE, project2) + assertThat(result.task(":firebasePublish")?.outcome).isEqualTo(TaskOutcome.SUCCESS) + + val pomOrNull2 = project2.getPublishedPom("${testProjectDir.root}/build/m2repository") + assertThat(pomOrNull2).isNotNull() + + val pom2 = pomOrNull2!! + assertThat(pom2.artifact.version).isEqualTo(project2.version) + assertThat(pom2.dependencies).containsExactly( + Artifact( + groupId = project1.group, + artifactId = project1.name, + version = project1.latestReleasedVersion!!, + type = Type.AAR, + scope = "compile"), + Artifact( + groupId = "com.google.dagger", + artifactId = "dagger", + version = "2.22", + type = Type.JAR, + scope = "compile"), + Artifact( + groupId = "com.google.dagger", + artifactId = "dagger-android-support", + version = "2.22", + type = Type.AAR, + scope = "compile") + ) + } + + private fun publish(mode: Mode, vararg projects: Project): BuildResult = + GradleRunner.create() + .withProjectDir(testProjectDir.root) + .withArguments( + "-PprojectsToPublish=${projects.joinToString(",") { it.name }}", + "-PpublishMode=$mode", + "firebasePublish") + .withPluginClasspath() + .build() + + private fun include(project: Project) { + testProjectDir.newFolder(project.name, "src", "main") + testProjectDir + .newFile("${project.name}/build.gradle") + .writeText(project.generateBuildFile()) + testProjectDir + .newFile("${project.name}/src/main/AndroidManifest.xml") + .writeText(MANIFEST) + subprojects.add(project) + } + + private fun subprojectsDefined(vararg projects: Project) { + rootBuildFile = testProjectDir.newFile("build.gradle") + rootSettingsFile = testProjectDir.newFile("settings.gradle") + + projects.forEach(this::include) + + rootBuildFile.writeText(ROOT_PROJECT) + rootSettingsFile.writeText(projects.joinToString("\n") { "include ':${it.name}'" }) + } + + companion object { + private const val ROOT_PROJECT = """ + buildscript { + repositories { + google() + jcenter() + maven { + url 'https://storage.googleapis.com/android-ci/mvn/' + } + } + } + plugins { + id 'PublishingPlugin' + } + """ + private const val MANIFEST = """ + + + + """ + } +} diff --git a/buildSrc/src/test/kotlin/com/google/firebase/gradle/plugins/publishing.kt b/buildSrc/src/test/kotlin/com/google/firebase/gradle/plugins/publishing.kt new file mode 100644 index 00000000000..409e2423dbf --- /dev/null +++ b/buildSrc/src/test/kotlin/com/google/firebase/gradle/plugins/publishing.kt @@ -0,0 +1,199 @@ +// Copyright 2020 Google LLC +// +// 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. + +package com.google.firebase.gradle.plugins + +import java.io.File +import javax.xml.parsers.DocumentBuilderFactory +import org.gradle.api.GradleException +import org.w3c.dom.Element +import org.w3c.dom.NodeList + +data class Project( + val name: String, + val group: String = "com.example", + val version: String = "undefined", + val latestReleasedVersion: String? = null, + val projectDependencies: Set = setOf(), + val externalDependencies: Set = setOf(), + val releaseWith: Project? = null, + val customizePom: String? = null +) { + fun generateBuildFile(): String { + return """ + plugins { + id 'firebase-library' + } + group = '$group' + version = '$version' + ${if (latestReleasedVersion != null) "ext.latestReleasedVersion = $latestReleasedVersion" else ""} + firebaseLibrary { + ${if (releaseWith != null) "releaseWith project(':${releaseWith.name}')" else ""} + ${if (customizePom != null) "customizePom {$customizePom}" else ""} + } + android.compileSdkVersion = 26 + + repositories { + google() + jcenter() + } + dependencies { + ${projectDependencies.joinToString("\n") { "implementation project(':${it.name}')" }} + ${externalDependencies.joinToString("\n") { "implementation '${it.simpleDepString}'" }} + } + """ + } + + fun getPublishedPom(rootDirectory: String): Pom? { + val v = releaseWith?.version ?: version + return File(rootDirectory).walk().asSequence() + .filter { it.isFile } + .filter { + it.path.matches(Regex(".*/${group.replace('.', '/')}/$name/$v.*/.*\\.pom$")) + } + .map(Pom::parse) + .firstOrNull() + } +} + +data class License(val name: String, val url: String) + +enum class Type { + JAR, AAR +} + +data class Artifact( + val groupId: String, + val artifactId: String, + val version: String, + val type: Type = Type.JAR, + val scope: String = "" +) { + val simpleDepString: String + get() = "$groupId:$artifactId:$version" +} + +data class Pom( + val artifact: Artifact, + val license: License = License( + name = "The Apache Software License, Version 2.0", + url = "http://www.apache.org/licenses/LICENSE-2.0.txt"), + val dependencies: List = listOf() +) { + companion object { + fun parse(file: File): Pom { + val document = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(file) + val childNodes = document.documentElement.childNodes + + var groupId: String? = null + var artifactId: String? = null + var version: String? = null + var type = Type.JAR + var license: License? = null + var deps: List = listOf() + + for (i in 0 until childNodes.length) { + val child = childNodes.item(i) + if (child !is Element) { + continue + } + when (child.tagName) { + "groupId" -> groupId = child.textContent.trim() + "artifactId" -> artifactId = child.textContent.trim() + "version" -> version = child.textContent.trim() + "packaging" -> type = Type.valueOf(child.textContent.trim().toUpperCase()) + "licenses" -> license = parseLicense(child.getElementsByTagName("license")) + "dependencies" -> deps = parseDeps(child.getElementsByTagName("dependency")) + } + } + if (groupId == null) { + throw GradleException("'' missing in pom") + } + if (artifactId == null) { + throw GradleException("'' missing in pom") + } + if (version == null) { + throw GradleException("'' missing in pom") + } + if (license == null) { + throw GradleException("'' missing in pom") + } + + return Pom(Artifact(groupId, artifactId, version, type), license, deps) + } + + private fun parseDeps(nodes: NodeList): List { + val deps = mutableListOf() + for (i in 0 until nodes.length) { + val child = nodes.item(i) + if (child !is Element) { + continue + } + deps.add(parseDep(child)) + } + return deps + } + + private fun parseDep(dependencies: Element): Artifact { + var groupId: String? = null + var artifactId: String? = null + var version: String? = null + var type = Type.JAR + var scope: String? = null + + val nodes = dependencies.childNodes + + for (i in 0 until nodes.length) { + val child = nodes.item(i) + if (child !is Element) { + continue + } + when (child.tagName) { + "groupId" -> groupId = child.textContent.trim() + "artifactId" -> artifactId = child.textContent.trim() + "version" -> version = child.textContent.trim() + "type" -> type = Type.valueOf(child.textContent.trim().toUpperCase()) + "scope" -> scope = child.textContent.trim() + } + } + if (groupId == null) { + throw GradleException("'' missing in pom") + } + if (artifactId == null) { + throw GradleException("'' missing in pom") + } + if (version == null) { + throw GradleException("'' missing in pom") + } + if (scope == null) { + throw GradleException("'' missing in pom") + } + + return Artifact(groupId, artifactId, version, type, scope) + } + + private fun parseLicense(nodes: NodeList): License? { + if (nodes.length == 0) { + return null + } + val license = nodes.item(0) as Element + val urlElements = license.getElementsByTagName("url") + val url = if (urlElements.length == 0) "" else urlElements.item(0).textContent.trim() + + val nameElements = license.getElementsByTagName("name") + val name = if (nameElements.length == 0) "" else nameElements.item(0).textContent.trim() + return License(name = name, url = url) + } + } +}