diff --git a/scripts/assets/maven_dependencies.textproto b/scripts/assets/maven_dependencies.textproto index 841c9c94fa0..44a07d86bb4 100644 --- a/scripts/assets/maven_dependencies.textproto +++ b/scripts/assets/maven_dependencies.textproto @@ -1050,8 +1050,19 @@ maven_dependency { } } maven_dependency { - artifact_name: "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.4.1" - artifact_version: "1.4.1" + artifact_name: "org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.4.3" + artifact_version: "1.4.3" + license { + license_name: "The Apache Software License, Version 2.0" + original_link: "https://www.apache.org/licenses/LICENSE-2.0.txt" + scrapable_link { + url: "https://www.apache.org/licenses/LICENSE-2.0.txt" + } + } +} +maven_dependency { + artifact_name: "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.4.3" + artifact_version: "1.4.3" license { license_name: "The Apache Software License, Version 2.0" original_link: "https://www.apache.org/licenses/LICENSE-2.0.txt" diff --git a/scripts/src/java/org/oppia/android/scripts/build/TransformAndroidManifest.kt b/scripts/src/java/org/oppia/android/scripts/build/TransformAndroidManifest.kt index 4fde91e42eb..96095ee2925 100644 --- a/scripts/src/java/org/oppia/android/scripts/build/TransformAndroidManifest.kt +++ b/scripts/src/java/org/oppia/android/scripts/build/TransformAndroidManifest.kt @@ -1,6 +1,8 @@ package org.oppia.android.scripts.build +import org.oppia.android.scripts.common.CommandExecutorImpl import org.oppia.android.scripts.common.GitClient +import org.oppia.android.scripts.common.ScriptBackgroundCoroutineDispatcher import org.w3c.dom.Document import org.w3c.dom.NodeList import java.io.File @@ -62,21 +64,24 @@ fun main(args: Array) { check(args.size >= 9) { USAGE_STRING } val repoRoot = File(args[0]).also { if (!it.exists()) error("File doesn't exist: ${args[0]}") } - TransformAndroidManifest( - repoRoot = repoRoot, - sourceManifestFile = File(args[1]).also { - if (!it.exists()) { - error("File doesn't exist: ${args[1]}") - } - }, - outputManifestFile = File(args[2]), - buildFlavor = args[3], - majorVersion = args[4].toIntOrNull() ?: error(USAGE_STRING), - minorVersion = args[5].toIntOrNull() ?: error(USAGE_STRING), - versionCode = args[6].toIntOrNull() ?: error(USAGE_STRING), - relativelyQualifiedApplicationClass = args[7], - baseDevelopBranchReference = args[8] - ).generateAndOutputNewManifest() + ScriptBackgroundCoroutineDispatcher().use { scriptBgDispatcher -> + TransformAndroidManifest( + repoRoot = repoRoot, + sourceManifestFile = File(args[1]).also { + if (!it.exists()) { + error("File doesn't exist: ${args[1]}") + } + }, + outputManifestFile = File(args[2]), + buildFlavor = args[3], + majorVersion = args[4].toIntOrNull() ?: error(USAGE_STRING), + minorVersion = args[5].toIntOrNull() ?: error(USAGE_STRING), + versionCode = args[6].toIntOrNull() ?: error(USAGE_STRING), + relativelyQualifiedApplicationClass = args[7], + baseDevelopBranchReference = args[8], + scriptBgDispatcher + ).generateAndOutputNewManifest() + } } private class TransformAndroidManifest( @@ -88,11 +93,11 @@ private class TransformAndroidManifest( private val minorVersion: Int, private val versionCode: Int, private val relativelyQualifiedApplicationClass: String, - private val baseDevelopBranchReference: String + private val baseDevelopBranchReference: String, + private val scriptBgDispatcher: ScriptBackgroundCoroutineDispatcher ) { - private val gitClient by lazy { - GitClient(repoRoot, baseDevelopBranchReference) - } + private val commandExecutor by lazy { CommandExecutorImpl(scriptBgDispatcher) } + private val gitClient by lazy { GitClient(repoRoot, baseDevelopBranchReference, commandExecutor) } private val documentBuilderFactory by lazy { DocumentBuilderFactory.newInstance() } private val transformerFactory by lazy { TransformerFactory.newInstance() } diff --git a/scripts/src/java/org/oppia/android/scripts/ci/ComputeAffectedTests.kt b/scripts/src/java/org/oppia/android/scripts/ci/ComputeAffectedTests.kt index 5437874f191..ec82cb18e8b 100644 --- a/scripts/src/java/org/oppia/android/scripts/ci/ComputeAffectedTests.kt +++ b/scripts/src/java/org/oppia/android/scripts/ci/ComputeAffectedTests.kt @@ -5,6 +5,7 @@ import org.oppia.android.scripts.common.CommandExecutor import org.oppia.android.scripts.common.CommandExecutorImpl import org.oppia.android.scripts.common.GitClient import org.oppia.android.scripts.common.ProtoStringEncoder.Companion.toCompressedBase64 +import org.oppia.android.scripts.common.ScriptBackgroundCoroutineDispatcher import org.oppia.android.scripts.proto.AffectedTestsBucket import java.io.File import java.util.Locale @@ -59,9 +60,10 @@ fun main(args: Array) { " '$computeAllTestsValue'" ) } - ComputeAffectedTests().compute( - pathToRoot, pathToOutputFile, baseDevelopBranchReference, computeAllTestsSetting - ) + ScriptBackgroundCoroutineDispatcher().use { scriptBgDispatcher -> + ComputeAffectedTests(scriptBgDispatcher) + .compute(pathToRoot, pathToOutputFile, baseDevelopBranchReference, computeAllTestsSetting) + } } // Needed since the codebase isn't yet using Kotlin 1.5, so this function isn't available. @@ -75,10 +77,11 @@ private fun String.toBooleanStrictOrNull(): Boolean? { /** Utility used to compute affected test targets. */ class ComputeAffectedTests( - private val maxTestCountPerLargeShard: Int = MAX_TEST_COUNT_PER_LARGE_SHARD, - private val maxTestCountPerMediumShard: Int = MAX_TEST_COUNT_PER_MEDIUM_SHARD, - private val maxTestCountPerSmallShard: Int = MAX_TEST_COUNT_PER_SMALL_SHARD, - private val commandExecutor: CommandExecutor = CommandExecutorImpl() + private val scriptBgDispatcher: ScriptBackgroundCoroutineDispatcher, + val maxTestCountPerLargeShard: Int = MAX_TEST_COUNT_PER_LARGE_SHARD, + val maxTestCountPerMediumShard: Int = MAX_TEST_COUNT_PER_MEDIUM_SHARD, + val maxTestCountPerSmallShard: Int = MAX_TEST_COUNT_PER_SMALL_SHARD, + val commandExecutor: CommandExecutor = CommandExecutorImpl(scriptBgDispatcher) ) { private companion object { private const val GENERIC_TEST_BUCKET_NAME = "generic" @@ -108,7 +111,7 @@ class ComputeAffectedTests( println("Running from directory root: $rootDirectory") - val gitClient = GitClient(rootDirectory, baseDevelopBranchReference) + val gitClient = GitClient(rootDirectory, baseDevelopBranchReference, commandExecutor) val bazelClient = BazelClient(rootDirectory, commandExecutor) println("Current branch: ${gitClient.currentBranch}") println("Most recent common commit: ${gitClient.branchMergeBase}") diff --git a/scripts/src/java/org/oppia/android/scripts/common/BUILD.bazel b/scripts/src/java/org/oppia/android/scripts/common/BUILD.bazel index 3a94254e9fc..6b26dd4f49f 100644 --- a/scripts/src/java/org/oppia/android/scripts/common/BUILD.bazel +++ b/scripts/src/java/org/oppia/android/scripts/common/BUILD.bazel @@ -36,6 +36,10 @@ kt_jvm_library( "CommandResult.kt", ], visibility = ["//scripts:oppia_script_library_visibility"], + deps = [ + ":script_background_coroutine_dispatcher", + "//third_party:org_jetbrains_kotlinx_kotlinx-coroutines-core", + ], ) kt_jvm_library( @@ -57,3 +61,12 @@ kt_jvm_library( "//third_party:org_jetbrains_kotlin_kotlin-stdlib-jdk8_jar", ], ) + +kt_jvm_library( + name = "script_background_coroutine_dispatcher", + srcs = ["ScriptBackgroundCoroutineDispatcher.kt"], + visibility = ["//scripts:oppia_script_library_visibility"], + deps = [ + "//third_party:org_jetbrains_kotlinx_kotlinx-coroutines-core", + ], +) diff --git a/scripts/src/java/org/oppia/android/scripts/common/BazelClient.kt b/scripts/src/java/org/oppia/android/scripts/common/BazelClient.kt index b6071d86b76..7d5806a5869 100644 --- a/scripts/src/java/org/oppia/android/scripts/common/BazelClient.kt +++ b/scripts/src/java/org/oppia/android/scripts/common/BazelClient.kt @@ -10,7 +10,7 @@ import java.util.Locale */ class BazelClient( private val rootDirectory: File, - private val commandExecutor: CommandExecutor = CommandExecutorImpl() + private val commandExecutor: CommandExecutor ) { /** Returns all Bazel test targets in the workspace. */ fun retrieveAllTestTargets(): List { diff --git a/scripts/src/java/org/oppia/android/scripts/common/CommandExecutorImpl.kt b/scripts/src/java/org/oppia/android/scripts/common/CommandExecutorImpl.kt index 44817344a8f..8431c534496 100644 --- a/scripts/src/java/org/oppia/android/scripts/common/CommandExecutorImpl.kt +++ b/scripts/src/java/org/oppia/android/scripts/common/CommandExecutorImpl.kt @@ -1,5 +1,8 @@ package org.oppia.android.scripts.common +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.async +import kotlinx.coroutines.runBlocking import java.io.File import java.util.concurrent.TimeUnit @@ -11,6 +14,7 @@ const val WAIT_PROCESS_TIMEOUT_MS = 60_000L /** Default implementation of [CommandExecutor]. */ class CommandExecutorImpl( + private val scriptBgDispatcher: ScriptBackgroundCoroutineDispatcher, private val processTimeout: Long = WAIT_PROCESS_TIMEOUT_MS, private val processTimeoutUnit: TimeUnit = TimeUnit.MILLISECONDS ) : CommandExecutor { @@ -29,7 +33,11 @@ class CommandExecutorImpl( .directory(workingDir) .redirectErrorStream(includeErrorOutput) .start() - val finished = process.waitFor(processTimeout, processTimeoutUnit) + val finished = runBlocking { + CoroutineScope(scriptBgDispatcher).async { + process.waitFor(processTimeout, processTimeoutUnit) + }.await() + } check(finished) { "Process did not finish within the expected timeout" } return CommandResult( process.exitValue(), diff --git a/scripts/src/java/org/oppia/android/scripts/common/GitClient.kt b/scripts/src/java/org/oppia/android/scripts/common/GitClient.kt index 797d345bbf2..dfda3889b8f 100644 --- a/scripts/src/java/org/oppia/android/scripts/common/GitClient.kt +++ b/scripts/src/java/org/oppia/android/scripts/common/GitClient.kt @@ -9,10 +9,9 @@ import java.io.File */ class GitClient( private val workingDirectory: File, - private val baseDevelopBranchReference: String + private val baseDevelopBranchReference: String, + private val commandExecutor: CommandExecutor ) { - private val commandExecutor by lazy { CommandExecutorImpl() } - /** The commit hash of the HEAD of the local Git repository. */ val currentCommit: String by lazy { retrieveCurrentCommit() } diff --git a/scripts/src/java/org/oppia/android/scripts/common/ScriptBackgroundCoroutineDispatcher.kt b/scripts/src/java/org/oppia/android/scripts/common/ScriptBackgroundCoroutineDispatcher.kt new file mode 100644 index 00000000000..52bb21c0a0f --- /dev/null +++ b/scripts/src/java/org/oppia/android/scripts/common/ScriptBackgroundCoroutineDispatcher.kt @@ -0,0 +1,86 @@ +package org.oppia.android.scripts.common + +import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.Runnable +import kotlinx.coroutines.asCoroutineDispatcher +import java.io.Closeable +import java.util.concurrent.ExecutorService +import java.util.concurrent.Executors +import java.util.concurrent.TimeUnit +import kotlin.coroutines.CoroutineContext + +/** + * A [CoroutineDispatcher] that's [Closeable] and particularly tailored to be easily used in scripts + * that need to perform parallel tasks for expensive IO. It's highly recommended to exclusively use + * this dispatcher over any others, and to ensure that [close] is called at the end of the script to + * avoid any potential threads hanging (causing the script to not actually close). + * + * Note that the dispatcher attempts to finish any ongoing tasks when [close] is called, but it will + * reject new tasks from being scheduled and it will force terminate if any pending tasks at the + * time of closing don't end within the configured [closeTimeout] provided. + * + * A simple example for using this dispatcher: + * ```kotlin + * ScriptBackgroundCoroutineDispatcher().use { scriptBgDispatcher -> + * val deferred = CoroutineScope(scriptBgDispatcher).async { + * // Expensive task... + * } + * // IMPORTANT: The operation must be observed before use{} ends, otherwise the dispatcher will + * // close and terminate any pending tasks. + * runBlocking { deferred.await() } + * } + * ``` + * + * A more complex example for I/O operations: + * ```kotlin + * ScriptBackgroundCoroutineDispatcher().use { scriptBgDispatcher -> + * val deferred = CoroutineScope(scriptBgDispatcher).async { + * withContext(Dispatchers.IO) { + * // Perform I/O using Kotlin's highly parallelized I/O dispatcher, but wait for the result + * // using the background script dispatcher (since execution could continue if other I/O + * // operations need to be kicked off, or if other work can be done alongside the I/O). + * } + * } + * // IMPORTANT: The operation must be observed before use{} ends, otherwise the dispatcher will + * // close and terminate any pending tasks. + * runBlocking { deferred.await() } + * } + * ``` + * + * @property closeTimeout the amount of time, in [closeTimeoutUnit] units, that should be waited + * when [close]ing this dispatcher before force-ending ongoing tasks + * @property closeTimeoutUnit the unit of time used for [closeTimeout] + */ +class ScriptBackgroundCoroutineDispatcher( + private val closeTimeout: Long = 5, + private val closeTimeoutUnit: TimeUnit = TimeUnit.SECONDS +) : CoroutineDispatcher(), Closeable { + private val threadPool by lazy { Executors.newCachedThreadPool() } + private val coroutineDispatcher by lazy { threadPool.asCoroutineDispatcher() } + + override fun dispatch(context: CoroutineContext, block: Runnable) { + coroutineDispatcher.dispatch(context, block) + } + + override fun close() { + threadPool.tryShutdownFully(timeout = closeTimeout, unit = closeTimeoutUnit) + coroutineDispatcher.close() + } + + private companion object { + private fun ExecutorService.tryShutdownFully(timeout: Long, unit: TimeUnit) { + // Try to fully shutdown the executor service per https://stackoverflow.com/a/33690603 and + // https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/ExecutorService.html. + shutdown() + try { + if (!awaitTermination(timeout, unit)) { + shutdownNow() + check(awaitTermination(timeout, unit)) { "ExecutorService didn't fully shutdown: $this." } + } + } catch (e: InterruptedException) { + shutdownNow() + Thread.currentThread().interrupt() + } + } + } +} diff --git a/scripts/src/java/org/oppia/android/scripts/license/MavenDependenciesListCheck.kt b/scripts/src/java/org/oppia/android/scripts/license/MavenDependenciesListCheck.kt index 1632c3c9068..d8f178c7b21 100644 --- a/scripts/src/java/org/oppia/android/scripts/license/MavenDependenciesListCheck.kt +++ b/scripts/src/java/org/oppia/android/scripts/license/MavenDependenciesListCheck.kt @@ -3,6 +3,7 @@ package org.oppia.android.scripts.license import com.google.protobuf.TextFormat import org.oppia.android.scripts.common.CommandExecutor import org.oppia.android.scripts.common.CommandExecutorImpl +import org.oppia.android.scripts.common.ScriptBackgroundCoroutineDispatcher import org.oppia.android.scripts.proto.MavenDependency /** @@ -24,7 +25,9 @@ import org.oppia.android.scripts.proto.MavenDependency * third_party/maven_install.json scripts/assets/maven_dependencies.pb */ fun main(args: Array) { - MavenDependenciesListCheck(LicenseFetcherImpl()).main(args) + ScriptBackgroundCoroutineDispatcher().use { scriptBgDispatcher -> + MavenDependenciesListCheck(LicenseFetcherImpl(), scriptBgDispatcher).main(args) + } } /** @@ -33,7 +36,8 @@ fun main(args: Array) { */ class MavenDependenciesListCheck( private val licenseFetcher: LicenseFetcher, - private val commandExecutor: CommandExecutor = CommandExecutorImpl() + scriptBgDispatcher: ScriptBackgroundCoroutineDispatcher, + private val commandExecutor: CommandExecutor = CommandExecutorImpl(scriptBgDispatcher) ) { /** diff --git a/scripts/src/java/org/oppia/android/scripts/license/MavenDependenciesRetriever.kt b/scripts/src/java/org/oppia/android/scripts/license/MavenDependenciesRetriever.kt index 3b67d51a751..e37c188bac5 100644 --- a/scripts/src/java/org/oppia/android/scripts/license/MavenDependenciesRetriever.kt +++ b/scripts/src/java/org/oppia/android/scripts/license/MavenDependenciesRetriever.kt @@ -5,7 +5,6 @@ import com.squareup.moshi.Moshi import com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory import org.oppia.android.scripts.common.BazelClient import org.oppia.android.scripts.common.CommandExecutor -import org.oppia.android.scripts.common.CommandExecutorImpl import org.oppia.android.scripts.maven.model.MavenListDependency import org.oppia.android.scripts.maven.model.MavenListDependencyTree import org.oppia.android.scripts.proto.License @@ -24,7 +23,7 @@ private const val MAVEN_PREFIX = "@maven//:" class MavenDependenciesRetriever( private val rootPath: String, private val licenseFetcher: LicenseFetcher, - private val commandExecutor: CommandExecutor = CommandExecutorImpl() + private val commandExecutor: CommandExecutor ) { private val bazelClient by lazy { diff --git a/scripts/src/java/org/oppia/android/scripts/maven/GenerateMavenDependenciesList.kt b/scripts/src/java/org/oppia/android/scripts/maven/GenerateMavenDependenciesList.kt index 8829a55a475..36b28f3228c 100644 --- a/scripts/src/java/org/oppia/android/scripts/maven/GenerateMavenDependenciesList.kt +++ b/scripts/src/java/org/oppia/android/scripts/maven/GenerateMavenDependenciesList.kt @@ -2,6 +2,7 @@ package org.oppia.android.scripts.maven import org.oppia.android.scripts.common.CommandExecutor import org.oppia.android.scripts.common.CommandExecutorImpl +import org.oppia.android.scripts.common.ScriptBackgroundCoroutineDispatcher import org.oppia.android.scripts.license.LicenseFetcher import org.oppia.android.scripts.license.LicenseFetcherImpl import org.oppia.android.scripts.license.MavenDependenciesRetriever @@ -27,13 +28,16 @@ import org.oppia.android.scripts.proto.MavenDependencyList * scripts/assets/maven_dependencies.pb */ fun main(args: Array) { - GenerateMavenDependenciesList(LicenseFetcherImpl()).main(args) + ScriptBackgroundCoroutineDispatcher().use { scriptBgDispatcher -> + GenerateMavenDependenciesList(LicenseFetcherImpl(), scriptBgDispatcher).main(args) + } } /** Wrapper class to pass dependencies to be utilized by the the main method. */ class GenerateMavenDependenciesList( private val licenseFetcher: LicenseFetcher, - private val commandExecutor: CommandExecutor = CommandExecutorImpl() + scriptBgDispatcher: ScriptBackgroundCoroutineDispatcher, + private val commandExecutor: CommandExecutor = CommandExecutorImpl(scriptBgDispatcher) ) { /** * Compiles a list of third-party maven dependencies along with their license links on @@ -48,45 +52,46 @@ class GenerateMavenDependenciesList( val pathToMavenDependenciesTextProto = "$pathToRoot/${args[2]}" val pathToMavenDependenciesPb = args[3] - val MavenDependenciesRetriever = MavenDependenciesRetriever(pathToRoot, licenseFetcher) + val mavenDependenciesRetriever = + MavenDependenciesRetriever(pathToRoot, licenseFetcher, commandExecutor) val bazelQueryDepsList = - MavenDependenciesRetriever.retrieveThirdPartyMavenDependenciesList() - val mavenInstallDepsList = MavenDependenciesRetriever.getDependencyListFromMavenInstall( + mavenDependenciesRetriever.retrieveThirdPartyMavenDependenciesList() + val mavenInstallDepsList = mavenDependenciesRetriever.getDependencyListFromMavenInstall( pathToMavenInstallJson, bazelQueryDepsList ) val dependenciesListFromPom = - MavenDependenciesRetriever + mavenDependenciesRetriever .retrieveDependencyListFromPom(mavenInstallDepsList) .mavenDependencyList val dependenciesListFromTextProto = - MavenDependenciesRetriever.retrieveMavenDependencyList(pathToMavenDependenciesPb) + mavenDependenciesRetriever.retrieveMavenDependencyList(pathToMavenDependenciesPb) - val updatedDependenciesList = MavenDependenciesRetriever.addChangesFromTextProto( + val updatedDependenciesList = mavenDependenciesRetriever.addChangesFromTextProto( dependenciesListFromPom, dependenciesListFromTextProto ) val manuallyUpdatedLicenses = - MavenDependenciesRetriever.retrieveManuallyUpdatedLicensesSet(updatedDependenciesList) + mavenDependenciesRetriever.retrieveManuallyUpdatedLicensesSet(updatedDependenciesList) - val finalDependenciesList = MavenDependenciesRetriever.updateMavenDependenciesList( + val finalDependenciesList = mavenDependenciesRetriever.updateMavenDependenciesList( updatedDependenciesList, manuallyUpdatedLicenses ) - MavenDependenciesRetriever.writeTextProto( + mavenDependenciesRetriever.writeTextProto( pathToMavenDependenciesTextProto, MavenDependencyList.newBuilder().addAllMavenDependency(finalDependenciesList).build() ) val licensesToBeFixed = - MavenDependenciesRetriever.getAllBrokenLicenses(finalDependenciesList) + mavenDependenciesRetriever.getAllBrokenLicenses(finalDependenciesList) if (licensesToBeFixed.isNotEmpty()) { - val licenseToDependencyMap = MavenDependenciesRetriever + val licenseToDependencyMap = mavenDependenciesRetriever .findFirstDependenciesWithBrokenLicenses( finalDependenciesList, licensesToBeFixed @@ -95,34 +100,34 @@ class GenerateMavenDependenciesList( """ Some licenses do not have their 'original_link' verified. To verify a license link, click on the original link of the license and check if the link points to any valid license or - not. If the link does not point to a valid license (e.g - https://fabric.io/terms), set + not. If the link does not point to a valid license (e.g. - https://fabric.io/terms), set the 'is_original_link_invalid' field of the license to 'true'. - - e.g - + + e.g. - license { license_name: "Terms of Service for Firebase Services" original_link: "https://fabric.io/terms" is_original_link_invalid: true } - - If the link does point to a valid license then choose the most appropriate category for + + If the link does point to a valid license then choose the most appropriate category for the link: - + 1. scrapable_link: If the license text is plain text and the URL mentioned can be scraped - directly from the original_link of the license. e.g - + directly from the original_link of the license. e.g. - https://www.apache.org/licenses/LICENSE-2.0.txt - - 2. extracted_copy_link: If the license text is plain text but it can not be scraped - directly from the original_link of the license. e.g - + + 2. extracted_copy_link: If the license text is plain text but it can not be scraped + directly from the original_link of the license. e.g. - https://www.opensource.org/licenses/bsd-license - + 3. direct_link_only: If the license text is not plain text, it's best to display only the - link of the license. e.g - https://developer.android.com/studio/terms.html - + link of the license. e.g. - https://developer.android.com/studio/terms.html + After identifying the category of the license, modify the license to include one of the - above mentioned 'url'. - - e.g - + above mentioned 'url'. + + e.g. - license { license_name: "The Apache Software License, Version 2.0" original_link: "https://www.apache.org/licenses/LICENSE-2.0.txt" @@ -130,9 +135,9 @@ class GenerateMavenDependenciesList( url: "https://www.apache.org/licenses/LICENSE-2.0.txt" } } - - Please verify the license link(s) for the following license(s) manually in - maven_dependencies.textproto. Note that only first dependency that contains the license + + Please verify the license link(s) for the following license(s) manually in + maven_dependencies.textproto. Note that only first dependency that contains the license needs to be updated and also re-run the script to update the license details at all places. """.trimIndent() ) @@ -150,21 +155,21 @@ class GenerateMavenDependenciesList( } val dependenciesWithoutAnyLinks = - MavenDependenciesRetriever.getDependenciesThatNeedIntervention(finalDependenciesList) + mavenDependenciesRetriever.getDependenciesThatNeedIntervention(finalDependenciesList) if (dependenciesWithoutAnyLinks.isNotEmpty()) { println( """ - Please remove all the invalid links (if any) from maven_dependencies.textproto for the + Please remove all the invalid links (if any) from maven_dependencies.textproto for the below mentioned dependencies and provide the valid license links manually. - e.g - - + e.g. - + maven_dependency { artifact_name: "com.google.guava:failureaccess:1.0.1" artifact_version: "1.0.1" } - + ***** changes to ***** - + maven_dependency { artifact_name: "com.google.guava:failureaccess:1.0.1" artifact_version: "1.0.1" @@ -175,7 +180,7 @@ class GenerateMavenDependenciesList( } } } - + Dependencies with invalid or no license links: """.trimIndent() + "\n" ) diff --git a/scripts/src/javatests/org/oppia/android/scripts/build/TransformAndroidManifestTest.kt b/scripts/src/javatests/org/oppia/android/scripts/build/TransformAndroidManifestTest.kt index 5d197b04615..d614ef5a1db 100644 --- a/scripts/src/javatests/org/oppia/android/scripts/build/TransformAndroidManifestTest.kt +++ b/scripts/src/javatests/org/oppia/android/scripts/build/TransformAndroidManifestTest.kt @@ -1,11 +1,13 @@ package org.oppia.android.scripts.build import com.google.common.truth.Truth.assertThat +import org.junit.After import org.junit.Before import org.junit.Rule import org.junit.Test import org.junit.rules.TemporaryFolder import org.oppia.android.scripts.common.CommandExecutorImpl +import org.oppia.android.scripts.common.ScriptBackgroundCoroutineDispatcher import org.oppia.android.scripts.testing.TestGitRepository import org.oppia.android.testing.assertThrows import java.io.File @@ -54,16 +56,20 @@ class TransformAndroidManifestTest { private val VERSION_CODE = "23" private val APPLICATION_RELATIVE_QUALIFIED_CLASS = ".example.CustomApplication" - @Rule - @JvmField - var tempFolder = TemporaryFolder() + @field:[Rule JvmField] val tempFolder = TemporaryFolder() - private val commandExecutor by lazy { CommandExecutorImpl() } + private val scriptBgDispatcher by lazy { ScriptBackgroundCoroutineDispatcher() } + private val commandExecutor by lazy { CommandExecutorImpl(scriptBgDispatcher) } private lateinit var testGitRepository: TestGitRepository @Before fun setUp() { - testGitRepository = TestGitRepository(tempFolder, CommandExecutorImpl()) + testGitRepository = TestGitRepository(tempFolder, commandExecutor) + } + + @After + fun tearDown() { + scriptBgDispatcher.close() } @Test diff --git a/scripts/src/javatests/org/oppia/android/scripts/ci/ComputeAffectedTestsTest.kt b/scripts/src/javatests/org/oppia/android/scripts/ci/ComputeAffectedTestsTest.kt index 2bf59212f66..4d8542e1e5b 100644 --- a/scripts/src/javatests/org/oppia/android/scripts/ci/ComputeAffectedTestsTest.kt +++ b/scripts/src/javatests/org/oppia/android/scripts/ci/ComputeAffectedTestsTest.kt @@ -6,8 +6,10 @@ import org.junit.Before import org.junit.Rule import org.junit.Test import org.junit.rules.TemporaryFolder +import org.oppia.android.scripts.common.CommandExecutor import org.oppia.android.scripts.common.CommandExecutorImpl import org.oppia.android.scripts.common.ProtoStringEncoder.Companion.mergeFromCompressedBase64 +import org.oppia.android.scripts.common.ScriptBackgroundCoroutineDispatcher import org.oppia.android.scripts.proto.AffectedTestsBucket import org.oppia.android.scripts.testing.TestBazelWorkspace import org.oppia.android.scripts.testing.TestGitRepository @@ -31,8 +33,9 @@ import java.util.concurrent.TimeUnit class ComputeAffectedTestsTest { @field:[Rule JvmField] val tempFolder = TemporaryFolder() - private val commandExecutor by lazy { initiazeCommandExecutorWithLongProcessWaitTime() } + private val scriptBgDispatcher by lazy { ScriptBackgroundCoroutineDispatcher() } + private lateinit var commandExecutor: CommandExecutor private lateinit var testBazelWorkspace: TestBazelWorkspace private lateinit var testGitRepository: TestGitRepository private lateinit var pendingOutputStream: ByteArrayOutputStream @@ -40,8 +43,9 @@ class ComputeAffectedTestsTest { @Before fun setUp() { + commandExecutor = initializeCommandExecutorWithLongProcessWaitTime() testBazelWorkspace = TestBazelWorkspace(tempFolder) - testGitRepository = TestGitRepository(tempFolder, CommandExecutorImpl()) + testGitRepository = TestGitRepository(tempFolder, commandExecutor) // Redirect script output for testing purposes. pendingOutputStream = ByteArrayOutputStream() @@ -58,6 +62,8 @@ class ComputeAffectedTestsTest { // and to help manually verify the expect git state at the end of each test. println("git status (at end of test):") println(testGitRepository.status(checkForGitRepository = false)) + + scriptBgDispatcher.close() } @Test @@ -722,6 +728,7 @@ class ComputeAffectedTestsTest { // Note that main() can't be used since the shard counts need to be overwritten. Dagger would // be a nicer means to do this, but it's not set up currently for scripts. ComputeAffectedTests( + scriptBgDispatcher, maxTestCountPerLargeShard = maxTestCountPerLargeShard, maxTestCountPerMediumShard = maxTestCountPerMediumShard, maxTestCountPerSmallShard = maxTestCountPerSmallShard, @@ -864,7 +871,9 @@ class ComputeAffectedTestsTest { testGitRepository.commit(message = "Modified library $name") } - private fun initiazeCommandExecutorWithLongProcessWaitTime(): CommandExecutorImpl { - return CommandExecutorImpl(processTimeout = 5, processTimeoutUnit = TimeUnit.MINUTES) + private fun initializeCommandExecutorWithLongProcessWaitTime(): CommandExecutorImpl { + return CommandExecutorImpl( + scriptBgDispatcher, processTimeout = 5, processTimeoutUnit = TimeUnit.MINUTES + ) } } diff --git a/scripts/src/javatests/org/oppia/android/scripts/common/BUILD.bazel b/scripts/src/javatests/org/oppia/android/scripts/common/BUILD.bazel index bceb5cfe864..bb2009d98bd 100644 --- a/scripts/src/javatests/org/oppia/android/scripts/common/BUILD.bazel +++ b/scripts/src/javatests/org/oppia/android/scripts/common/BUILD.bazel @@ -61,3 +61,14 @@ kt_jvm_test( "//third_party:org_jetbrains_kotlin_kotlin-test-junit", ], ) + +kt_jvm_test( + name = "ScriptBackgroundCoroutineDispatcherTest", + srcs = ["ScriptBackgroundCoroutineDispatcherTest.kt"], + deps = [ + "//scripts/src/java/org/oppia/android/scripts/common:script_background_coroutine_dispatcher", + "//testing:assertion_helpers", + "//third_party:org_jetbrains_kotlin_kotlin-test-junit", + "//third_party:org_mockito_mockito-core", + ], +) diff --git a/scripts/src/javatests/org/oppia/android/scripts/common/BazelClientTest.kt b/scripts/src/javatests/org/oppia/android/scripts/common/BazelClientTest.kt index 2109155025f..57b2f26964a 100644 --- a/scripts/src/javatests/org/oppia/android/scripts/common/BazelClientTest.kt +++ b/scripts/src/javatests/org/oppia/android/scripts/common/BazelClientTest.kt @@ -1,6 +1,7 @@ package org.oppia.android.scripts.common import com.google.common.truth.Truth.assertThat +import org.junit.After import org.junit.Before import org.junit.Rule import org.junit.Test @@ -28,10 +29,17 @@ class BazelClientTest { @field:[Rule JvmField] val tempFolder = TemporaryFolder() @field:[Rule JvmField] val mockitoRule: MockitoRule = MockitoJUnit.rule() + private val scriptBgDispatcher by lazy { ScriptBackgroundCoroutineDispatcher() } + private val commandExecutor by lazy { CommandExecutorImpl(scriptBgDispatcher) } + private val longCommandExecutor by lazy { initializeCommandExecutorWithLongProcessWaitTime() } + private lateinit var testBazelWorkspace: TestBazelWorkspace + @Mock lateinit var mockCommandExecutor: CommandExecutor - private val commandExecutor by lazy { initiazeCommandExecutorWithLongProcessWaitTime() } - private lateinit var testBazelWorkspace: TestBazelWorkspace + @After + fun tearDown() { + scriptBgDispatcher.close() + } @Before fun setUp() { @@ -335,7 +343,7 @@ class BazelClientTest { artifactName = "com.android.support:support-annotations:28.0.0", buildFile = thirdPartyBuild ) - val bazelClient = BazelClient(tempFolder.root, commandExecutor) + val bazelClient = BazelClient(tempFolder.root, longCommandExecutor) val thirdPartyDependenciesList = bazelClient.retrieveThirdPartyMavenDepsListForBinary("//:test_oppia") @@ -344,7 +352,7 @@ class BazelClientTest { } @Test - fun testRetrieveMavenDepsList_binaryDependsOnArtifactNotViaThirdParty_doesNotreturnArtifact() { + fun testRetrieveMavenDepsList_binaryDependsOnArtifactNotViaThirdParty_doesNotReturnArtifact() { testBazelWorkspace.initEmptyWorkspace() testBazelWorkspace.setUpWorkspaceForRulesJvmExternal( listOf("com.android.support:support-annotations:28.0.0") @@ -365,7 +373,7 @@ class BazelClientTest { artifactName = "com.android.support:support-annotations:28.0.0", buildFile = testBazelWorkspace.rootBuildFile ) - val bazelClient = BazelClient(tempFolder.root, commandExecutor) + val bazelClient = BazelClient(tempFolder.root, longCommandExecutor) val thirdPartyDependenciesList = bazelClient.retrieveThirdPartyMavenDepsListForBinary("//:test_oppia") @@ -457,8 +465,10 @@ class BazelClientTest { return secondNewFile } - private fun initiazeCommandExecutorWithLongProcessWaitTime(): CommandExecutorImpl { - return CommandExecutorImpl(processTimeout = 5, processTimeoutUnit = TimeUnit.MINUTES) + private fun initializeCommandExecutorWithLongProcessWaitTime(): CommandExecutorImpl { + return CommandExecutorImpl( + scriptBgDispatcher, processTimeout = 5, processTimeoutUnit = TimeUnit.MINUTES + ) } private fun updateBuildFileToUseCustomJvmTestRule(bazelFile: File, buildFile: File) { diff --git a/scripts/src/javatests/org/oppia/android/scripts/common/CommandExecutorImplTest.kt b/scripts/src/javatests/org/oppia/android/scripts/common/CommandExecutorImplTest.kt index 78dc945dff6..140c27bfe1d 100644 --- a/scripts/src/javatests/org/oppia/android/scripts/common/CommandExecutorImplTest.kt +++ b/scripts/src/javatests/org/oppia/android/scripts/common/CommandExecutorImplTest.kt @@ -1,6 +1,7 @@ package org.oppia.android.scripts.common import com.google.common.truth.Truth.assertThat +import org.junit.After import org.junit.Rule import org.junit.Test import org.junit.rules.TemporaryFolder @@ -19,13 +20,18 @@ import java.util.concurrent.TimeUnit // Function name: test names are conventionally named with underscores. @Suppress("FunctionName") class CommandExecutorImplTest { - @Rule - @JvmField - var tempFolder = TemporaryFolder() + @field:[Rule JvmField] val tempFolder = TemporaryFolder() + + private val scriptBgDispatcher by lazy { ScriptBackgroundCoroutineDispatcher() } + + @After + fun tearDown() { + scriptBgDispatcher.close() + } @Test fun testExecute_echo_oneArgument_succeedsWithOutput() { - val commandExecutor = CommandExecutorImpl() + val commandExecutor = CommandExecutorImpl(scriptBgDispatcher) val result = commandExecutor.executeCommand(tempFolder.root, "echo", "value") @@ -35,7 +41,7 @@ class CommandExecutorImplTest { @Test fun testExecute_echo_invalidDirectory_throwsException() { - val commandExecutor = CommandExecutorImpl() + val commandExecutor = CommandExecutorImpl(scriptBgDispatcher) val exception = assertThrows() { commandExecutor.executeCommand(File("invaliddirectory"), "echo", "value") @@ -47,7 +53,7 @@ class CommandExecutorImplTest { @Test fun testExecute_echo_largeOutput_insufficientTimeout_throwsException() { val commandExecutor = CommandExecutorImpl( - processTimeout = 0L, processTimeoutUnit = TimeUnit.MILLISECONDS + scriptBgDispatcher, processTimeout = 0L, processTimeoutUnit = TimeUnit.MILLISECONDS ) // Produce a large output so that echo takes a bit longer to reduce the likelihood of this test @@ -63,7 +69,7 @@ class CommandExecutorImplTest { @Test fun testExecute_nonexistentCommand_throwsException() { - val commandExecutor = CommandExecutorImpl() + val commandExecutor = CommandExecutorImpl(scriptBgDispatcher) val exception = assertThrows() { commandExecutor.executeCommand(tempFolder.root, "commanddoesnotexist") @@ -74,7 +80,7 @@ class CommandExecutorImplTest { @Test fun testExecute_echo_multipleArguments_succeedsWithOutput() { - val commandExecutor = CommandExecutorImpl() + val commandExecutor = CommandExecutorImpl(scriptBgDispatcher) val result = commandExecutor.executeCommand(tempFolder.root, "echo", "first", "second", "third") @@ -84,7 +90,7 @@ class CommandExecutorImplTest { @Test fun testExecute_echo_multipleArguments_resultHasCorrectCommand() { - val commandExecutor = CommandExecutorImpl() + val commandExecutor = CommandExecutorImpl(scriptBgDispatcher) val result = commandExecutor.executeCommand(tempFolder.root, "echo", "first", "second", "third") @@ -93,7 +99,7 @@ class CommandExecutorImplTest { @Test fun testExecute_defaultErrorOutput_rmdir_failed_failsWithCombinedOutput() { - val commandExecutor = CommandExecutorImpl() + val commandExecutor = CommandExecutorImpl(scriptBgDispatcher) val result = commandExecutor.executeCommand(tempFolder.root, "rmdir", "filethatdoesnotexist") @@ -105,7 +111,7 @@ class CommandExecutorImplTest { @Test fun testExecute_splitErrorOutput_rmdir_failed_failsWithErrorOutput() { - val commandExecutor = CommandExecutorImpl() + val commandExecutor = CommandExecutorImpl(scriptBgDispatcher) val result = commandExecutor.executeCommand( @@ -121,7 +127,7 @@ class CommandExecutorImplTest { @Test fun testExecute_removeDirectoryInLocalDirectory_succeeds() { val newFolder = tempFolder.newFolder("newfolder") - val commandExecutor = CommandExecutorImpl() + val commandExecutor = CommandExecutorImpl(scriptBgDispatcher) val result = commandExecutor.executeCommand(tempFolder.root, "rmdir", "./newfolder") @@ -135,7 +141,7 @@ class CommandExecutorImplTest { fun testExecute_removeUnknownDirectoryInOtherDirectory_fails() { val newFolder = tempFolder.newFolder("newfolder") val alternateRoot = tempFolder.newFolder("alternateroot") - val commandExecutor = CommandExecutorImpl() + val commandExecutor = CommandExecutorImpl(scriptBgDispatcher) val result = commandExecutor.executeCommand(alternateRoot, "rmdir", "./newfolder") diff --git a/scripts/src/javatests/org/oppia/android/scripts/common/GitClientTest.kt b/scripts/src/javatests/org/oppia/android/scripts/common/GitClientTest.kt index c3126339eca..2741900296b 100644 --- a/scripts/src/javatests/org/oppia/android/scripts/common/GitClientTest.kt +++ b/scripts/src/javatests/org/oppia/android/scripts/common/GitClientTest.kt @@ -20,12 +20,11 @@ import java.lang.IllegalStateException // Function name: test names are conventionally named with underscores. @Suppress("FunctionName") class GitClientTest { - @Rule - @JvmField - var tempFolder = TemporaryFolder() + @field:[Rule JvmField] val tempFolder = TemporaryFolder() private lateinit var testGitRepository: TestGitRepository - private val commandExecutor by lazy { CommandExecutorImpl() } + private val scriptBgDispatcher by lazy { ScriptBackgroundCoroutineDispatcher() } + private val commandExecutor by lazy { CommandExecutorImpl(scriptBgDispatcher) } @Before fun setUp() { @@ -38,11 +37,12 @@ class GitClientTest { // and to help manually verify the expected git state at the end of each test. println("git status (at end of test):") println(testGitRepository.status(checkForGitRepository = false)) + scriptBgDispatcher.close() } @Test fun testCurrentCommit_forNonRepository_throwsException() { - val gitClient = GitClient(tempFolder.root, "develop") + val gitClient = GitClient(tempFolder.root, "develop", commandExecutor) val exception = assertThrows() { gitClient.currentCommit } @@ -55,7 +55,7 @@ class GitClientTest { initializeRepoWithDevelopBranch() val developHash = getMostRecentCommitOnCurrentBranch() - val gitClient = GitClient(tempFolder.root, "develop") + val gitClient = GitClient(tempFolder.root, "develop", commandExecutor) val currentCommit = gitClient.currentCommit assertThat(currentCommit).isEqualTo(developHash) @@ -69,7 +69,7 @@ class GitClientTest { testGitRepository.commit(message = "Test empty commit", allowEmpty = true) val featureBranchHash = getMostRecentCommitOnCurrentBranch() - val gitClient = GitClient(tempFolder.root, "develop") + val gitClient = GitClient(tempFolder.root, "develop", commandExecutor) val currentCommit = gitClient.currentCommit assertThat(currentCommit).isNotEqualTo(developHash) @@ -78,7 +78,7 @@ class GitClientTest { @Test fun testCurrentBranch_forNonRepository_throwsException() { - val gitClient = GitClient(tempFolder.root, "develop") + val gitClient = GitClient(tempFolder.root, "develop", commandExecutor) val exception = assertThrows() { gitClient.currentBranch } @@ -90,7 +90,7 @@ class GitClientTest { fun testCurrentBranch_forValidRepository_returnsCorrectBranch() { initializeRepoWithDevelopBranch() - val gitClient = GitClient(tempFolder.root, "develop") + val gitClient = GitClient(tempFolder.root, "develop", commandExecutor) val currentBranch = gitClient.currentBranch assertThat(currentBranch).isEqualTo("develop") @@ -101,7 +101,7 @@ class GitClientTest { initializeRepoWithDevelopBranch() testGitRepository.checkoutNewBranch("introduce-feature") - val gitClient = GitClient(tempFolder.root, "develop") + val gitClient = GitClient(tempFolder.root, "develop", commandExecutor) val currentBranch = gitClient.currentBranch assertThat(currentBranch).isEqualTo("introduce-feature") @@ -112,7 +112,7 @@ class GitClientTest { initializeRepoWithDevelopBranch() val developHash = getMostRecentCommitOnCurrentBranch() - val gitClient = GitClient(tempFolder.root, "develop") + val gitClient = GitClient(tempFolder.root, "develop", commandExecutor) val mergeBase = gitClient.branchMergeBase assertThat(mergeBase).isEqualTo(developHash) @@ -124,7 +124,7 @@ class GitClientTest { val developHash = getMostRecentCommitOnCurrentBranch() testGitRepository.checkoutNewBranch("introduce-feature") - val gitClient = GitClient(tempFolder.root, "develop") + val gitClient = GitClient(tempFolder.root, "develop", commandExecutor) val mergeBase = gitClient.branchMergeBase assertThat(mergeBase).isEqualTo(developHash) @@ -137,7 +137,7 @@ class GitClientTest { testGitRepository.checkoutNewBranch("introduce-feature") commitNewFile("example_file") - val gitClient = GitClient(tempFolder.root, "develop") + val gitClient = GitClient(tempFolder.root, "develop", commandExecutor) val mergeBase = gitClient.branchMergeBase // The merge base is the latest common hash between this & the develop branch. @@ -149,7 +149,7 @@ class GitClientTest { initializeRepoWithDevelopBranch() testGitRepository.checkoutNewBranch("introduce-feature") - val gitClient = GitClient(tempFolder.root, "develop") + val gitClient = GitClient(tempFolder.root, "develop", commandExecutor) val changedFiles = gitClient.changedFiles assertThat(changedFiles).isEmpty() @@ -161,7 +161,7 @@ class GitClientTest { testGitRepository.checkoutNewBranch("introduce-feature") createNewFile("example_file") - val gitClient = GitClient(tempFolder.root, "develop") + val gitClient = GitClient(tempFolder.root, "develop", commandExecutor) val changedFiles = gitClient.changedFiles assertThat(changedFiles).containsExactly("example_file") @@ -173,7 +173,7 @@ class GitClientTest { testGitRepository.checkoutNewBranch("introduce-feature") stageNewFile("example_file") - val gitClient = GitClient(tempFolder.root, "develop") + val gitClient = GitClient(tempFolder.root, "develop", commandExecutor) val changedFiles = gitClient.changedFiles assertThat(changedFiles).containsExactly("example_file") @@ -185,7 +185,7 @@ class GitClientTest { testGitRepository.checkoutNewBranch("introduce-feature") commitNewFile("example_file") - val gitClient = GitClient(tempFolder.root, "develop") + val gitClient = GitClient(tempFolder.root, "develop", commandExecutor) val changedFiles = gitClient.changedFiles assertThat(changedFiles).containsExactly("example_file") @@ -197,7 +197,7 @@ class GitClientTest { commitNewFile("develop_file") testGitRepository.checkoutNewBranch("introduce-feature") - val gitClient = GitClient(tempFolder.root, "develop") + val gitClient = GitClient(tempFolder.root, "develop", commandExecutor) val changedFiles = gitClient.changedFiles // Committed files to the develop branch are not included since they aren't part of the feature @@ -212,7 +212,7 @@ class GitClientTest { commitNewFile("example_file") modifyFile("example_file") - val gitClient = GitClient(tempFolder.root, "develop") + val gitClient = GitClient(tempFolder.root, "develop", commandExecutor) val changedFiles = gitClient.changedFiles assertThat(changedFiles).containsExactly("example_file") @@ -225,7 +225,7 @@ class GitClientTest { testGitRepository.checkoutNewBranch("introduce-feature") deleteFile("develop_file") - val gitClient = GitClient(tempFolder.root, "develop") + val gitClient = GitClient(tempFolder.root, "develop", commandExecutor) val changedFiles = gitClient.changedFiles assertThat(changedFiles).containsExactly("develop_file") @@ -244,7 +244,7 @@ class GitClientTest { modifyFile("develop_branch_file_changed_not_staged") deleteFile("develop_branch_file_removed") - val gitClient = GitClient(tempFolder.root, "develop") + val gitClient = GitClient(tempFolder.root, "develop", commandExecutor) val changedFiles = gitClient.changedFiles assertThat(changedFiles).containsExactly( diff --git a/scripts/src/javatests/org/oppia/android/scripts/common/ScriptBackgroundCoroutineDispatcherTest.kt b/scripts/src/javatests/org/oppia/android/scripts/common/ScriptBackgroundCoroutineDispatcherTest.kt new file mode 100644 index 00000000000..c1d4f98314b --- /dev/null +++ b/scripts/src/javatests/org/oppia/android/scripts/common/ScriptBackgroundCoroutineDispatcherTest.kt @@ -0,0 +1,81 @@ +package org.oppia.android.scripts.common + +import kotlinx.coroutines.CancellationException +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.async +import kotlinx.coroutines.channels.Channel +import kotlinx.coroutines.delay +import kotlinx.coroutines.runBlocking +import kotlinx.coroutines.withContext +import org.junit.Rule +import org.junit.Test +import org.mockito.Mock +import org.mockito.Mockito.verify +import org.mockito.Mockito.verifyNoMoreInteractions +import org.mockito.junit.MockitoJUnit +import org.mockito.junit.MockitoRule +import org.oppia.android.testing.assertThrows +import java.util.concurrent.TimeUnit + +/** Tests for [ScriptBackgroundCoroutineDispatcher]. */ +// FunctionName: test names are conventionally named with underscores. +@Suppress("FunctionName") +class ScriptBackgroundCoroutineDispatcherTest { + @field:[Rule JvmField] val mockitoRule: MockitoRule = MockitoJUnit.rule() + + @Mock lateinit var mockRunnable: Runnable + + @Test + fun testDispatchTask_taskIsRun() { + val dispatcher = ScriptBackgroundCoroutineDispatcher() + + runBlocking { withContext(dispatcher) { mockRunnable.run() } } + + verify(mockRunnable).run() + } + + @Test + fun testClose_noExceptionThrown() { + val dispatcher = ScriptBackgroundCoroutineDispatcher() + + dispatcher.close() + + // The verification is that no exception is thrown (otherwise the test should fail). + } + + @Test + fun testDispatch_afterClosing_throwsException() { + val dispatcher = ScriptBackgroundCoroutineDispatcher() + dispatcher.close() + + // The task should fail to schedule since the dispatcher has been closed. + assertThrows() { + runBlocking { withContext(dispatcher) { mockRunnable.run() } } + } + } + + @Test + fun testClose_pendingTaskLongerThanCloseTimeout_taskIsNotRun() { + val dispatcher = + ScriptBackgroundCoroutineDispatcher( + closeTimeout = 50L, closeTimeoutUnit = TimeUnit.MILLISECONDS + ) + val taskStartedChannel = Channel() + // Schedule a task but make sure that the attempt to close the dispatcher happens exactly + // between the task starting and ending (to verify close timeout flows). + @Suppress("DeferredResultUnused") + CoroutineScope(dispatcher).async { + taskStartedChannel.send(true) + delay(1_000L) + mockRunnable.run() + } + runBlocking { taskStartedChannel.receive() } + + dispatcher.close() + // This slows down the test, but provides assurance that the task was definitely cancelled. + runBlocking { delay(2_000L) } + + // The task should not have run since it was cancelled, but no exception will be thrown. + verifyNoMoreInteractions(mockRunnable) + } +} diff --git a/scripts/src/javatests/org/oppia/android/scripts/license/MavenDependenciesListCheckTest.kt b/scripts/src/javatests/org/oppia/android/scripts/license/MavenDependenciesListCheckTest.kt index 5fdc807e3db..6ecefd7ddce 100644 --- a/scripts/src/javatests/org/oppia/android/scripts/license/MavenDependenciesListCheckTest.kt +++ b/scripts/src/javatests/org/oppia/android/scripts/license/MavenDependenciesListCheckTest.kt @@ -10,6 +10,7 @@ import org.mockito.kotlin.doReturn import org.mockito.kotlin.eq import org.mockito.kotlin.mock import org.oppia.android.scripts.common.CommandExecutorImpl +import org.oppia.android.scripts.common.ScriptBackgroundCoroutineDispatcher import org.oppia.android.scripts.proto.DirectLinkOnly import org.oppia.android.scripts.proto.ExtractedCopyLink import org.oppia.android.scripts.proto.License @@ -79,16 +80,16 @@ class MavenDependenciesListCheckTest { private val UNAVAILABLE_OR_INVALID_LICENSE_LINKS_FAILURE = "License links are invalid or not available for some dependencies" + @field:[Rule JvmField] val tempFolder = TemporaryFolder() + private val outContent: ByteArrayOutputStream = ByteArrayOutputStream() private val originalOut: PrintStream = System.out + private val scriptBgDispatcher by lazy { ScriptBackgroundCoroutineDispatcher() } private val mockLicenseFetcher by lazy { initializeLicenseFetcher() } private val commandExecutor by lazy { initializeCommandExecutorWithLongProcessWaitTime() } - private lateinit var testBazelWorkspace: TestBazelWorkspace - @Rule - @JvmField - var tempFolder = TemporaryFolder() + private lateinit var testBazelWorkspace: TestBazelWorkspace @Before fun setUp() { @@ -101,6 +102,7 @@ class MavenDependenciesListCheckTest { @After fun restoreStreams() { System.setOut(originalOut) + scriptBgDispatcher.close() } @Test @@ -113,6 +115,7 @@ class MavenDependenciesListCheckTest { val exception = assertThrows() { MavenDependenciesListCheck( mockLicenseFetcher, + scriptBgDispatcher, commandExecutor ).main( arrayOf( @@ -135,14 +138,14 @@ class MavenDependenciesListCheckTest { license_name: "The Apache License, Version 2.0" original_link: "https://www.apache.org/licenses/LICENSE-2.0.txt" } - + artifact_name: "com.google.firebase:firebase-analytics:17.5.0" artifact_version: "17.5.0" license { license_name: "Android Software Development Kit License" original_link: "https://developer.android.com/studio/terms.html" } - + Refer to https://github.com/oppia/oppia-android/wiki/Updating-Maven-Dependencies for more details. """.trimIndent() + "\n" ) @@ -189,6 +192,7 @@ class MavenDependenciesListCheckTest { val exception = assertThrows() { MavenDependenciesListCheck( mockLicenseFetcher, + scriptBgDispatcher, commandExecutor ).main( arrayOf( @@ -211,7 +215,7 @@ class MavenDependenciesListCheckTest { license_name: "Android Software Development Kit License" original_link: "https://developer.android.com/studio/terms.html" } - + Refer to https://github.com/oppia/oppia-android/wiki/Updating-Maven-Dependencies for more details. """.trimIndent() + "\n" ) @@ -253,6 +257,7 @@ class MavenDependenciesListCheckTest { val exception = assertThrows() { MavenDependenciesListCheck( mockLicenseFetcher, + scriptBgDispatcher, commandExecutor ).main( arrayOf( @@ -275,14 +280,14 @@ class MavenDependenciesListCheckTest { license_name: "The Apache License, Version 2.0" original_link: "https://www.apache.org/licenses/LICENSE-2.0.txt" } - + artifact_name: "$FIREBASE_ANALYTICS_DEP" artifact_version: "$FIREBASE_ANALYTICS_VERSION" license { license_name: "Android Software Development Kit License" original_link: "https://developer.android.com/studio/terms.html" } - + Refer to https://github.com/oppia/oppia-android/wiki/Updating-Maven-Dependencies for more details. """.trimIndent() + "\n" ) @@ -329,6 +334,7 @@ class MavenDependenciesListCheckTest { val exception = assertThrows() { MavenDependenciesListCheck( mockLicenseFetcher, + scriptBgDispatcher, commandExecutor ).main( arrayOf( @@ -408,6 +414,7 @@ class MavenDependenciesListCheckTest { val exception = assertThrows() { MavenDependenciesListCheck( mockLicenseFetcher, + scriptBgDispatcher, commandExecutor ).main( arrayOf( @@ -430,7 +437,7 @@ class MavenDependenciesListCheckTest { license_name: "The Apache License, Version 2.0" original_link: "https://www.apache.org/licenses/LICENSE-2.0.txt" } - + artifact_name: "$FIREBASE_ANALYTICS_DEP" artifact_version: "$FIREBASE_ANALYTICS_VERSION" license { @@ -484,6 +491,7 @@ class MavenDependenciesListCheckTest { val exception = assertThrows() { MavenDependenciesListCheck( mockLicenseFetcher, + scriptBgDispatcher, commandExecutor ).main( arrayOf( @@ -508,14 +516,14 @@ class MavenDependenciesListCheckTest { } Missing dependencies that need to be added: - + artifact_name: "$FIREBASE_ANALYTICS_DEP" artifact_version: "$FIREBASE_ANALYTICS_VERSION" license { license_name: "Android Software Development Kit License" original_link: "https://developer.android.com/studio/terms.html" } - + Refer to https://github.com/oppia/oppia-android/wiki/Updating-Maven-Dependencies for more details. """.trimIndent() + "\n" ) @@ -562,6 +570,7 @@ class MavenDependenciesListCheckTest { val exception = assertThrows() { MavenDependenciesListCheck( mockLicenseFetcher, + scriptBgDispatcher, commandExecutor ).main( arrayOf( @@ -586,14 +595,14 @@ class MavenDependenciesListCheckTest { } Missing dependencies that need to be added: - + artifact_name: "$FIREBASE_ANALYTICS_UPGRADED_DEP" artifact_version: "$FIREBASE_ANALYTICS_UPGRADED_VERSION" license { license_name: "Android Software Development Kit License" original_link: "https://developer.android.com/studio/terms.html" } - + Refer to https://github.com/oppia/oppia-android/wiki/Updating-Maven-Dependencies for more details. """.trimIndent() + "\n" ) @@ -640,6 +649,7 @@ class MavenDependenciesListCheckTest { val exception = assertThrows() { MavenDependenciesListCheck( mockLicenseFetcher, + scriptBgDispatcher, commandExecutor ).main( arrayOf( @@ -651,7 +661,7 @@ class MavenDependenciesListCheckTest { } assertThat(exception).hasMessageThat().contains(MISSING_AND_REDUNDANT_DEPENDENCIES_FAILURE) assertThat(outContent.toString()).isEqualTo( - """ + """ Errors were encountered. Please run script GenerateMavenDependenciesList.kt to fix. Redundant dependencies that need to be removed: @@ -664,14 +674,14 @@ class MavenDependenciesListCheckTest { } Missing dependencies that need to be added: - + artifact_name: "$FIREBASE_ANALYTICS_DEP" artifact_version: "$FIREBASE_ANALYTICS_VERSION" license { license_name: "Android Software Development Kit License" original_link: "https://developer.android.com/studio/terms.html" } - + Refer to https://github.com/oppia/oppia-android/wiki/Updating-Maven-Dependencies for more details. """.trimIndent() + "\n" ) @@ -720,6 +730,7 @@ class MavenDependenciesListCheckTest { MavenDependenciesListCheck( mockLicenseFetcher, + scriptBgDispatcher, commandExecutor ).main( arrayOf( @@ -773,6 +784,7 @@ class MavenDependenciesListCheckTest { val exception = assertThrows() { MavenDependenciesListCheck( mockLicenseFetcher, + scriptBgDispatcher, commandExecutor ).main( arrayOf( @@ -822,6 +834,7 @@ class MavenDependenciesListCheckTest { val exception = assertThrows() { MavenDependenciesListCheck( mockLicenseFetcher, + scriptBgDispatcher, commandExecutor ).main( arrayOf( @@ -877,6 +890,7 @@ class MavenDependenciesListCheckTest { val exception = assertThrows() { MavenDependenciesListCheck( mockLicenseFetcher, + scriptBgDispatcher, commandExecutor ).main( arrayOf( @@ -1018,13 +1032,15 @@ class MavenDependenciesListCheckTest { } ] } - } + } """.trimIndent() ) } private fun initializeCommandExecutorWithLongProcessWaitTime(): CommandExecutorImpl { - return CommandExecutorImpl(processTimeout = 5, processTimeoutUnit = TimeUnit.MINUTES) + return CommandExecutorImpl( + scriptBgDispatcher, processTimeout = 5, processTimeoutUnit = TimeUnit.MINUTES + ) } /** Returns a mock for the [LicenseFetcher]. */ diff --git a/scripts/src/javatests/org/oppia/android/scripts/license/MavenDependenciesRetrieverTest.kt b/scripts/src/javatests/org/oppia/android/scripts/license/MavenDependenciesRetrieverTest.kt index 5dd8baec8b3..311bfe5f446 100644 --- a/scripts/src/javatests/org/oppia/android/scripts/license/MavenDependenciesRetrieverTest.kt +++ b/scripts/src/javatests/org/oppia/android/scripts/license/MavenDependenciesRetrieverTest.kt @@ -11,6 +11,7 @@ import org.mockito.kotlin.doReturn import org.mockito.kotlin.eq import org.mockito.kotlin.mock import org.oppia.android.scripts.common.CommandExecutorImpl +import org.oppia.android.scripts.common.ScriptBackgroundCoroutineDispatcher import org.oppia.android.scripts.maven.model.MavenListDependency import org.oppia.android.scripts.proto.DirectLinkOnly import org.oppia.android.scripts.proto.ExtractedCopyLink @@ -81,12 +82,11 @@ class MavenDependenciesRetrieverTest { private val mavenDependenciesRetriever by lazy { initializeMavenDependenciesRetriever() } + private val scriptBgDispatcher by lazy { ScriptBackgroundCoroutineDispatcher() } private lateinit var testBazelWorkspace: TestBazelWorkspace - @Rule - @JvmField - var tempFolder = TemporaryFolder() + @field:[Rule JvmField] val tempFolder = TemporaryFolder() @Before fun setUp() { @@ -99,6 +99,7 @@ class MavenDependenciesRetrieverTest { @After fun restoreStreams() { System.setOut(originalOut) + scriptBgDispatcher.close() } @Test @@ -1220,13 +1221,15 @@ class MavenDependenciesRetrieverTest { } ] } - } + } """.trimIndent() ) } private fun initializeCommandExecutorWithLongProcessWaitTime(): CommandExecutorImpl { - return CommandExecutorImpl(processTimeout = 5, processTimeoutUnit = TimeUnit.MINUTES) + return CommandExecutorImpl( + scriptBgDispatcher, processTimeout = 5, processTimeoutUnit = TimeUnit.MINUTES + ) } /** Returns a mock for the [LicenseFetcher]. */ diff --git a/scripts/src/javatests/org/oppia/android/scripts/maven/GenerateMavenDependenciesListTest.kt b/scripts/src/javatests/org/oppia/android/scripts/maven/GenerateMavenDependenciesListTest.kt index 4507d76a5dc..96e3a92ad64 100644 --- a/scripts/src/javatests/org/oppia/android/scripts/maven/GenerateMavenDependenciesListTest.kt +++ b/scripts/src/javatests/org/oppia/android/scripts/maven/GenerateMavenDependenciesListTest.kt @@ -11,6 +11,7 @@ import org.mockito.kotlin.doReturn import org.mockito.kotlin.eq import org.mockito.kotlin.mock import org.oppia.android.scripts.common.CommandExecutorImpl +import org.oppia.android.scripts.common.ScriptBackgroundCoroutineDispatcher import org.oppia.android.scripts.license.LicenseFetcher import org.oppia.android.scripts.proto.DirectLinkOnly import org.oppia.android.scripts.proto.ExtractedCopyLink @@ -64,17 +65,16 @@ class GenerateMavenDependenciesListTest { private val SCRIPT_PASSED_MESSAGE = "Script executed succesfully: maven_dependencies.textproto updated successfully." + @field:[Rule JvmField] val tempFolder = TemporaryFolder() + private val outContent: ByteArrayOutputStream = ByteArrayOutputStream() private val originalOut: PrintStream = System.out + private val scriptBgDispatcher by lazy { ScriptBackgroundCoroutineDispatcher() } private val mockLicenseFetcher by lazy { initializeLicenseFetcher() } private val commandExecutor by lazy { initializeCommandExecutorWithLongProcessWaitTime() } private lateinit var testBazelWorkspace: TestBazelWorkspace - @Rule - @JvmField - var tempFolder = TemporaryFolder() - @Before fun setUp() { tempFolder.newFolder("scripts", "assets") @@ -86,6 +86,7 @@ class GenerateMavenDependenciesListTest { @After fun restoreStreams() { System.setOut(originalOut) + scriptBgDispatcher.close() } @Test @@ -99,6 +100,7 @@ class GenerateMavenDependenciesListTest { val exception = assertThrows() { GenerateMavenDependenciesList( mockLicenseFetcher, + scriptBgDispatcher, commandExecutor ).main( arrayOf( @@ -183,6 +185,7 @@ class GenerateMavenDependenciesListTest { val exception = assertThrows() { GenerateMavenDependenciesList( mockLicenseFetcher, + scriptBgDispatcher, commandExecutor ).main( arrayOf( @@ -207,6 +210,7 @@ class GenerateMavenDependenciesListTest { val exception = assertThrows() { GenerateMavenDependenciesList( mockLicenseFetcher, + scriptBgDispatcher, commandExecutor ).main( arrayOf( @@ -249,6 +253,7 @@ class GenerateMavenDependenciesListTest { val exception = assertThrows() { GenerateMavenDependenciesList( mockLicenseFetcher, + scriptBgDispatcher, commandExecutor ).main( arrayOf( @@ -316,6 +321,7 @@ class GenerateMavenDependenciesListTest { val exception = assertThrows() { GenerateMavenDependenciesList( mockLicenseFetcher, + scriptBgDispatcher, commandExecutor ).main( arrayOf( @@ -358,6 +364,7 @@ class GenerateMavenDependenciesListTest { val exception = assertThrows() { GenerateMavenDependenciesList( mockLicenseFetcher, + scriptBgDispatcher, commandExecutor ).main( arrayOf( @@ -426,6 +433,7 @@ class GenerateMavenDependenciesListTest { GenerateMavenDependenciesList( mockLicenseFetcher, + scriptBgDispatcher, commandExecutor ).main( arrayOf( @@ -519,6 +527,7 @@ class GenerateMavenDependenciesListTest { GenerateMavenDependenciesList( mockLicenseFetcher, + scriptBgDispatcher, commandExecutor ).main( arrayOf( @@ -617,6 +626,7 @@ class GenerateMavenDependenciesListTest { val exception = assertThrows() { GenerateMavenDependenciesList( mockLicenseFetcher, + scriptBgDispatcher, commandExecutor ).main( arrayOf( @@ -680,6 +690,7 @@ class GenerateMavenDependenciesListTest { GenerateMavenDependenciesList( mockLicenseFetcher, + scriptBgDispatcher, commandExecutor ).main( arrayOf( @@ -793,6 +804,7 @@ class GenerateMavenDependenciesListTest { val exception = assertThrows() { GenerateMavenDependenciesList( mockLicenseFetcher, + scriptBgDispatcher, commandExecutor ).main( arrayOf( @@ -918,6 +930,7 @@ class GenerateMavenDependenciesListTest { val exception = assertThrows() { GenerateMavenDependenciesList( mockLicenseFetcher, + scriptBgDispatcher, commandExecutor ).main( arrayOf( @@ -1031,6 +1044,7 @@ class GenerateMavenDependenciesListTest { val exception = assertThrows() { GenerateMavenDependenciesList( mockLicenseFetcher, + scriptBgDispatcher, commandExecutor ).main( arrayOf( @@ -1150,6 +1164,7 @@ class GenerateMavenDependenciesListTest { GenerateMavenDependenciesList( mockLicenseFetcher, + scriptBgDispatcher, commandExecutor ).main( arrayOf( @@ -1394,13 +1409,15 @@ class GenerateMavenDependenciesListTest { } ] } - } + } """.trimIndent() ) } private fun initializeCommandExecutorWithLongProcessWaitTime(): CommandExecutorImpl { - return CommandExecutorImpl(processTimeout = 5, processTimeoutUnit = TimeUnit.MINUTES) + return CommandExecutorImpl( + scriptBgDispatcher, processTimeout = 5, processTimeoutUnit = TimeUnit.MINUTES + ) } /** Returns a mock for the [LicenseFetcher]. */ diff --git a/scripts/src/javatests/org/oppia/android/scripts/testing/TestGitRepositoryTest.kt b/scripts/src/javatests/org/oppia/android/scripts/testing/TestGitRepositoryTest.kt index 453f17da044..0782a6c5512 100644 --- a/scripts/src/javatests/org/oppia/android/scripts/testing/TestGitRepositoryTest.kt +++ b/scripts/src/javatests/org/oppia/android/scripts/testing/TestGitRepositoryTest.kt @@ -1,12 +1,14 @@ package org.oppia.android.scripts.testing import com.google.common.truth.Truth.assertThat +import org.junit.After import org.junit.Rule import org.junit.Test import org.junit.rules.TemporaryFolder import org.oppia.android.scripts.common.CommandExecutor import org.oppia.android.scripts.common.CommandExecutorImpl import org.oppia.android.scripts.common.CommandResult +import org.oppia.android.scripts.common.ScriptBackgroundCoroutineDispatcher import org.oppia.android.testing.assertThrows import java.io.File import java.util.UUID @@ -31,11 +33,15 @@ import java.util.UUID // Function name: test names are conventionally named with underscores. @Suppress("FunctionName") class TestGitRepositoryTest { - @Rule - @JvmField - var tempFolder = TemporaryFolder() + @field:[Rule JvmField] val tempFolder = TemporaryFolder() - private val commandExecutorInterceptor by lazy { CommandExecutorInterceptor() } + private val scriptBgDispatcher by lazy { ScriptBackgroundCoroutineDispatcher() } + private val commandExecutorInterceptor by lazy { CommandExecutorInterceptor(scriptBgDispatcher) } + + @After + fun tearDown() { + scriptBgDispatcher.close() + } @Test fun testCreateTestUtility_doesNotImmediatelyCreateAnyFiles() { @@ -52,7 +58,7 @@ class TestGitRepositoryTest { testGitRepository.init() - assertThat(tempFolder.root.list().toList()).containsExactly(".git") + assertThat(tempFolder.root.list()?.toList()).containsExactly(".git") } @Test @@ -494,9 +500,11 @@ class TestGitRepositoryTest { return file } - private class CommandExecutorInterceptor : CommandExecutor { + private class CommandExecutorInterceptor( + scriptBgDispatcher: ScriptBackgroundCoroutineDispatcher + ) : CommandExecutor { private val commandResults = mutableListOf() - private val realCommandExecutor by lazy { CommandExecutorImpl() } + private val realCommandExecutor by lazy { CommandExecutorImpl(scriptBgDispatcher) } override fun executeCommand( workingDir: File, diff --git a/third_party/maven_install.json b/third_party/maven_install.json index f154369ddb7..453ad22c4d0 100644 --- a/third_party/maven_install.json +++ b/third_party/maven_install.json @@ -1,8 +1,8 @@ { "dependency_tree": { "__AUTOGENERATED_FILE_DO_NOT_MODIFY_THIS_FILE_MANUALLY": "THERE_IS_NO_DATA_ONLY_ZUUL", - "__INPUT_ARTIFACTS_HASH": 577686317, - "__RESOLVED_ARTIFACTS_HASH": 445130734, + "__INPUT_ARTIFACTS_HASH": 928649579, + "__RESOLVED_ARTIFACTS_HASH": -1902356738, "conflict_resolution": { "androidx.constraintlayout:constraintlayout:1.1.3": "androidx.constraintlayout:constraintlayout:2.0.1", "androidx.core:core:1.0.1": "androidx.core:core:1.3.1", @@ -1783,12 +1783,12 @@ { "coord": "androidx.lifecycle:lifecycle-livedata-ktx:2.2.0", "dependencies": [ - "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.4.1", "androidx.lifecycle:lifecycle-common:2.2.0", "androidx.annotation:annotation:1.1.0", "androidx.lifecycle:lifecycle-livedata:aar:2.2.0", "androidx.lifecycle:lifecycle-livedata-core:aar:2.2.0", "androidx.lifecycle:lifecycle-livedata-core-ktx:aar:2.2.0", + "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.4.3", "androidx.arch.core:core-common:2.1.0", "org.jetbrains.kotlin:kotlin-stdlib:1.5.0", "androidx.arch.core:core-runtime:aar:2.1.0" @@ -1797,7 +1797,7 @@ "androidx.lifecycle:lifecycle-livedata:aar:2.2.0", "androidx.lifecycle:lifecycle-livedata-core-ktx:aar:2.2.0", "org.jetbrains.kotlin:kotlin-stdlib:1.5.0", - "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.4.1" + "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.4.3" ], "file": "v1/https/maven.google.com/androidx/lifecycle/lifecycle-livedata-ktx/2.2.0/lifecycle-livedata-ktx-2.2.0.aar", "mirror_urls": [ @@ -1813,12 +1813,12 @@ { "coord": "androidx.lifecycle:lifecycle-livedata-ktx:jar:sources:2.2.0", "dependencies": [ - "org.jetbrains.kotlinx:kotlinx-coroutines-core:jar:sources:1.4.1", "androidx.arch.core:core-runtime:aar:sources:2.1.0", "org.jetbrains.kotlin:kotlin-stdlib:jar:sources:1.5.0", "androidx.lifecycle:lifecycle-livedata-core:aar:sources:2.2.0", "androidx.lifecycle:lifecycle-livedata:aar:sources:2.2.0", "androidx.lifecycle:lifecycle-livedata-core-ktx:aar:sources:2.2.0", + "org.jetbrains.kotlinx:kotlinx-coroutines-core:jar:sources:1.4.3", "androidx.annotation:annotation:jar:sources:1.1.0", "androidx.arch.core:core-common:jar:sources:2.1.0", "androidx.lifecycle:lifecycle-common:jar:sources:2.2.0" @@ -1827,7 +1827,7 @@ "androidx.lifecycle:lifecycle-livedata:aar:sources:2.2.0", "androidx.lifecycle:lifecycle-livedata-core-ktx:aar:sources:2.2.0", "org.jetbrains.kotlin:kotlin-stdlib:jar:sources:1.5.0", - "org.jetbrains.kotlinx:kotlinx-coroutines-core:jar:sources:1.4.1" + "org.jetbrains.kotlinx:kotlinx-coroutines-core:jar:sources:1.4.3" ], "file": "v1/https/maven.google.com/androidx/lifecycle/lifecycle-livedata-ktx/2.2.0/lifecycle-livedata-ktx-2.2.0-sources.jar", "mirror_urls": [ @@ -9860,13 +9860,12 @@ { "coord": "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.4.1", "dependencies": [ - "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.4.1", - "org.jetbrains.kotlin:kotlin-stdlib-common:1.5.0", - "org.jetbrains.kotlin:kotlin-stdlib:1.5.0" + "org.jetbrains.kotlin:kotlin-stdlib:1.5.0", + "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.4.3" ], "directDependencies": [ "org.jetbrains.kotlin:kotlin-stdlib:1.5.0", - "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.4.1" + "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.4.3" ], "file": "v1/https/repo1.maven.org/maven2/org/jetbrains/kotlinx/kotlinx-coroutines-android/1.4.1/kotlinx-coroutines-android-1.4.1.jar", "mirror_urls": [ @@ -9883,12 +9882,11 @@ "coord": "org.jetbrains.kotlinx:kotlinx-coroutines-android:jar:sources:1.4.1", "dependencies": [ "org.jetbrains.kotlin:kotlin-stdlib:jar:sources:1.5.0", - "org.jetbrains.kotlinx:kotlinx-coroutines-core:jar:sources:1.4.1", - "org.jetbrains.kotlin:kotlin-stdlib-common:jar:sources:1.5.0" + "org.jetbrains.kotlinx:kotlinx-coroutines-core:jar:sources:1.4.3" ], "directDependencies": [ "org.jetbrains.kotlin:kotlin-stdlib:jar:sources:1.5.0", - "org.jetbrains.kotlinx:kotlinx-coroutines-core:jar:sources:1.4.1" + "org.jetbrains.kotlinx:kotlinx-coroutines-core:jar:sources:1.4.3" ], "file": "v1/https/repo1.maven.org/maven2/org/jetbrains/kotlinx/kotlinx-coroutines-android/1.4.1/kotlinx-coroutines-android-1.4.1-sources.jar", "mirror_urls": [ @@ -9902,7 +9900,7 @@ "url": "https://repo1.maven.org/maven2/org/jetbrains/kotlinx/kotlinx-coroutines-android/1.4.1/kotlinx-coroutines-android-1.4.1-sources.jar" }, { - "coord": "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.4.1", + "coord": "org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.4.3", "dependencies": [ "org.jetbrains.kotlin:kotlin-stdlib-common:1.5.0", "org.jetbrains.kotlin:kotlin-stdlib:1.5.0" @@ -9911,19 +9909,19 @@ "org.jetbrains.kotlin:kotlin-stdlib:1.5.0", "org.jetbrains.kotlin:kotlin-stdlib-common:1.5.0" ], - "file": "v1/https/repo1.maven.org/maven2/org/jetbrains/kotlinx/kotlinx-coroutines-core/1.4.1/kotlinx-coroutines-core-1.4.1.jar", + "file": "v1/https/repo1.maven.org/maven2/org/jetbrains/kotlinx/kotlinx-coroutines-core-jvm/1.4.3/kotlinx-coroutines-core-jvm-1.4.3.jar", "mirror_urls": [ - "https://maven.google.com/org/jetbrains/kotlinx/kotlinx-coroutines-core/1.4.1/kotlinx-coroutines-core-1.4.1.jar", - "https://repo1.maven.org/maven2/org/jetbrains/kotlinx/kotlinx-coroutines-core/1.4.1/kotlinx-coroutines-core-1.4.1.jar", - "https://maven.fabric.io/public/org/jetbrains/kotlinx/kotlinx-coroutines-core/1.4.1/kotlinx-coroutines-core-1.4.1.jar", - "https://maven.google.com/org/jetbrains/kotlinx/kotlinx-coroutines-core/1.4.1/kotlinx-coroutines-core-1.4.1.jar", - "https://repo1.maven.org/maven2/org/jetbrains/kotlinx/kotlinx-coroutines-core/1.4.1/kotlinx-coroutines-core-1.4.1.jar" + "https://maven.google.com/org/jetbrains/kotlinx/kotlinx-coroutines-core-jvm/1.4.3/kotlinx-coroutines-core-jvm-1.4.3.jar", + "https://repo1.maven.org/maven2/org/jetbrains/kotlinx/kotlinx-coroutines-core-jvm/1.4.3/kotlinx-coroutines-core-jvm-1.4.3.jar", + "https://maven.fabric.io/public/org/jetbrains/kotlinx/kotlinx-coroutines-core-jvm/1.4.3/kotlinx-coroutines-core-jvm-1.4.3.jar", + "https://maven.google.com/org/jetbrains/kotlinx/kotlinx-coroutines-core-jvm/1.4.3/kotlinx-coroutines-core-jvm-1.4.3.jar", + "https://repo1.maven.org/maven2/org/jetbrains/kotlinx/kotlinx-coroutines-core-jvm/1.4.3/kotlinx-coroutines-core-jvm-1.4.3.jar" ], - "sha256": "6d2f87764b6638f27aff12ed380db4b63c9d46ba55dc32683a650598fa5a3e22", - "url": "https://repo1.maven.org/maven2/org/jetbrains/kotlinx/kotlinx-coroutines-core/1.4.1/kotlinx-coroutines-core-1.4.1.jar" + "sha256": "f7be08ddf86bd88020da7b78adbf44228799cca54d5c0c4396d850bc66725163", + "url": "https://repo1.maven.org/maven2/org/jetbrains/kotlinx/kotlinx-coroutines-core-jvm/1.4.3/kotlinx-coroutines-core-jvm-1.4.3.jar" }, { - "coord": "org.jetbrains.kotlinx:kotlinx-coroutines-core:jar:sources:1.4.1", + "coord": "org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:jar:sources:1.4.3", "dependencies": [ "org.jetbrains.kotlin:kotlin-stdlib:jar:sources:1.5.0", "org.jetbrains.kotlin:kotlin-stdlib-common:jar:sources:1.5.0" @@ -9932,16 +9930,58 @@ "org.jetbrains.kotlin:kotlin-stdlib:jar:sources:1.5.0", "org.jetbrains.kotlin:kotlin-stdlib-common:jar:sources:1.5.0" ], - "file": "v1/https/repo1.maven.org/maven2/org/jetbrains/kotlinx/kotlinx-coroutines-core/1.4.1/kotlinx-coroutines-core-1.4.1-sources.jar", + "file": "v1/https/repo1.maven.org/maven2/org/jetbrains/kotlinx/kotlinx-coroutines-core-jvm/1.4.3/kotlinx-coroutines-core-jvm-1.4.3-sources.jar", + "mirror_urls": [ + "https://maven.google.com/org/jetbrains/kotlinx/kotlinx-coroutines-core-jvm/1.4.3/kotlinx-coroutines-core-jvm-1.4.3-sources.jar", + "https://repo1.maven.org/maven2/org/jetbrains/kotlinx/kotlinx-coroutines-core-jvm/1.4.3/kotlinx-coroutines-core-jvm-1.4.3-sources.jar", + "https://maven.fabric.io/public/org/jetbrains/kotlinx/kotlinx-coroutines-core-jvm/1.4.3/kotlinx-coroutines-core-jvm-1.4.3-sources.jar", + "https://maven.google.com/org/jetbrains/kotlinx/kotlinx-coroutines-core-jvm/1.4.3/kotlinx-coroutines-core-jvm-1.4.3-sources.jar", + "https://repo1.maven.org/maven2/org/jetbrains/kotlinx/kotlinx-coroutines-core-jvm/1.4.3/kotlinx-coroutines-core-jvm-1.4.3-sources.jar" + ], + "sha256": "6f1a3f8be952a3c7c003cb32eca36a92a7afc4affea6cd8b769dd23d7f14bad2", + "url": "https://repo1.maven.org/maven2/org/jetbrains/kotlinx/kotlinx-coroutines-core-jvm/1.4.3/kotlinx-coroutines-core-jvm-1.4.3-sources.jar" + }, + { + "coord": "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.4.3", + "dependencies": [ + "org.jetbrains.kotlin:kotlin-stdlib-common:1.5.0", + "org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.4.3", + "org.jetbrains.kotlin:kotlin-stdlib:1.5.0" + ], + "directDependencies": [ + "org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.4.3" + ], + "file": "v1/https/repo1.maven.org/maven2/org/jetbrains/kotlinx/kotlinx-coroutines-core/1.4.3/kotlinx-coroutines-core-1.4.3.jar", + "mirror_urls": [ + "https://maven.google.com/org/jetbrains/kotlinx/kotlinx-coroutines-core/1.4.3/kotlinx-coroutines-core-1.4.3.jar", + "https://repo1.maven.org/maven2/org/jetbrains/kotlinx/kotlinx-coroutines-core/1.4.3/kotlinx-coroutines-core-1.4.3.jar", + "https://maven.fabric.io/public/org/jetbrains/kotlinx/kotlinx-coroutines-core/1.4.3/kotlinx-coroutines-core-1.4.3.jar", + "https://maven.google.com/org/jetbrains/kotlinx/kotlinx-coroutines-core/1.4.3/kotlinx-coroutines-core-1.4.3.jar", + "https://repo1.maven.org/maven2/org/jetbrains/kotlinx/kotlinx-coroutines-core/1.4.3/kotlinx-coroutines-core-1.4.3.jar" + ], + "sha256": "de487d57b156e4e237abbc9cf7fff8777b2495aff6caa8bc4e9cf6ec859f0224", + "url": "https://repo1.maven.org/maven2/org/jetbrains/kotlinx/kotlinx-coroutines-core/1.4.3/kotlinx-coroutines-core-1.4.3.jar" + }, + { + "coord": "org.jetbrains.kotlinx:kotlinx-coroutines-core:jar:sources:1.4.3", + "dependencies": [ + "org.jetbrains.kotlin:kotlin-stdlib:jar:sources:1.5.0", + "org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:jar:sources:1.4.3", + "org.jetbrains.kotlin:kotlin-stdlib-common:jar:sources:1.5.0" + ], + "directDependencies": [ + "org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:jar:sources:1.4.3" + ], + "file": "v1/https/repo1.maven.org/maven2/org/jetbrains/kotlinx/kotlinx-coroutines-core/1.4.3/kotlinx-coroutines-core-1.4.3-sources.jar", "mirror_urls": [ - "https://maven.google.com/org/jetbrains/kotlinx/kotlinx-coroutines-core/1.4.1/kotlinx-coroutines-core-1.4.1-sources.jar", - "https://repo1.maven.org/maven2/org/jetbrains/kotlinx/kotlinx-coroutines-core/1.4.1/kotlinx-coroutines-core-1.4.1-sources.jar", - "https://maven.fabric.io/public/org/jetbrains/kotlinx/kotlinx-coroutines-core/1.4.1/kotlinx-coroutines-core-1.4.1-sources.jar", - "https://maven.google.com/org/jetbrains/kotlinx/kotlinx-coroutines-core/1.4.1/kotlinx-coroutines-core-1.4.1-sources.jar", - "https://repo1.maven.org/maven2/org/jetbrains/kotlinx/kotlinx-coroutines-core/1.4.1/kotlinx-coroutines-core-1.4.1-sources.jar" + "https://maven.google.com/org/jetbrains/kotlinx/kotlinx-coroutines-core/1.4.3/kotlinx-coroutines-core-1.4.3-sources.jar", + "https://repo1.maven.org/maven2/org/jetbrains/kotlinx/kotlinx-coroutines-core/1.4.3/kotlinx-coroutines-core-1.4.3-sources.jar", + "https://maven.fabric.io/public/org/jetbrains/kotlinx/kotlinx-coroutines-core/1.4.3/kotlinx-coroutines-core-1.4.3-sources.jar", + "https://maven.google.com/org/jetbrains/kotlinx/kotlinx-coroutines-core/1.4.3/kotlinx-coroutines-core-1.4.3-sources.jar", + "https://repo1.maven.org/maven2/org/jetbrains/kotlinx/kotlinx-coroutines-core/1.4.3/kotlinx-coroutines-core-1.4.3-sources.jar" ], - "sha256": "bb339efebc2d9141401f1aa43a035abe929210e362cfff13d03c6b7b11dc0469", - "url": "https://repo1.maven.org/maven2/org/jetbrains/kotlinx/kotlinx-coroutines-core/1.4.1/kotlinx-coroutines-core-1.4.1-sources.jar" + "sha256": "315d99f5b340ceaba2cb898eff7e6414fd615b27e3f093b84ac13ac31c2f7dc0", + "url": "https://repo1.maven.org/maven2/org/jetbrains/kotlinx/kotlinx-coroutines-core/1.4.3/kotlinx-coroutines-core-1.4.3-sources.jar" }, { "coord": "org.jetbrains.kotlinx:kotlinx-coroutines-debug:1.2.2", @@ -9976,13 +10016,13 @@ { "coord": "org.jetbrains.kotlinx:kotlinx-coroutines-test:1.2.2", "dependencies": [ - "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.4.1", "org.jetbrains.kotlinx:kotlinx-coroutines-debug:1.2.2", - "org.jetbrains.kotlin:kotlin-stdlib:1.5.0" + "org.jetbrains.kotlin:kotlin-stdlib:1.5.0", + "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.4.3" ], "directDependencies": [ "org.jetbrains.kotlin:kotlin-stdlib:1.5.0", - "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.4.1", + "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.4.3", "org.jetbrains.kotlinx:kotlinx-coroutines-debug:1.2.2" ], "file": "v1/https/repo1.maven.org/maven2/org/jetbrains/kotlinx/kotlinx-coroutines-test/1.2.2/kotlinx-coroutines-test-1.2.2.jar", @@ -10000,12 +10040,12 @@ "coord": "org.jetbrains.kotlinx:kotlinx-coroutines-test:jar:sources:1.2.2", "dependencies": [ "org.jetbrains.kotlin:kotlin-stdlib:jar:sources:1.5.0", - "org.jetbrains.kotlinx:kotlinx-coroutines-core:jar:sources:1.4.1", + "org.jetbrains.kotlinx:kotlinx-coroutines-core:jar:sources:1.4.3", "org.jetbrains.kotlinx:kotlinx-coroutines-debug:jar:sources:1.2.2" ], "directDependencies": [ "org.jetbrains.kotlin:kotlin-stdlib:jar:sources:1.5.0", - "org.jetbrains.kotlinx:kotlinx-coroutines-core:jar:sources:1.4.1", + "org.jetbrains.kotlinx:kotlinx-coroutines-core:jar:sources:1.4.3", "org.jetbrains.kotlinx:kotlinx-coroutines-debug:jar:sources:1.2.2" ], "file": "v1/https/repo1.maven.org/maven2/org/jetbrains/kotlinx/kotlinx-coroutines-test/1.2.2/kotlinx-coroutines-test-1.2.2-sources.jar", diff --git a/third_party/versions.bzl b/third_party/versions.bzl index f62c87f8f15..499494bea41 100644 --- a/third_party/versions.bzl +++ b/third_party/versions.bzl @@ -79,7 +79,7 @@ MAVEN_PRODUCTION_DEPENDENCY_VERSIONS = { "org.checkerframework:checker-qual": "3.13.0", "org.jetbrains.kotlin:kotlin-stdlib-jdk8:jar": "1.3.72", "org.jetbrains.kotlinx:kotlinx-coroutines-android": "1.4.1", - "org.jetbrains.kotlinx:kotlinx-coroutines-core": "1.4.1", + "org.jetbrains.kotlinx:kotlinx-coroutines-core": "1.4.3", "org.jetbrains:annotations:jar": "13.0", } diff --git a/wiki/Updating-Maven-Dependencies.md b/wiki/Updating-Maven-Dependencies.md index 6e0653f844b..353de7a5d7c 100644 --- a/wiki/Updating-Maven-Dependencies.md +++ b/wiki/Updating-Maven-Dependencies.md @@ -18,7 +18,7 @@ cd ~/opensource/oppia-android The above command ensures that the terminal points to the root directory `oppia-android` repository. Note that if you have configured a different path to the `oppia-android` repository then you should modify the above command accordingly ( `cd ~/` ). #### Running `GenerateMavenDependenciesList.kt` script -After the terminal points to the Oppia-android repository, run the bazel run command to execute the Kotlin script. +After the terminal points to the Oppia-android repository, run the bazel run command to execute the Kotlin script. ``` bazel run //scripts:generate_maven_dependencies_list -- $(pwd) third_party/maven_install.json scripts/assets/maven_dependencies.textproto scripts/assets/maven_dependencies.pb ``` @@ -26,19 +26,19 @@ bazel run //scripts:generate_maven_dependencies_list -- $(pwd) third_party/maven ## Handling Exception: `Too few arguments passed` If after running the script the exception message says: **Too few arguments passed**, then please ensure that you copied the command correctly from [here](https://github.com/oppia/oppia-android/wiki/Updating-Maven-Dependencies#running-generatemavendependencieslistkt-script). The script accepts 4 parameters to be passed to run successfully: -1. **_path_to_directory_root_**: directory path to the root of the Oppia Android repository, e.g - `home//opensource/oppia-android` -2. **_path_to_maven_install_json_**: relative path to the maven_install.json file, e.g - `third_party/maven_install.json` -3. **_path_to_maven_dependencies_textproto_**: relative path to the maven_dependencies.textproto, e.g - `scripts/assets/maven_dependencies.textproto` -4. **_path_to_maven_dependencies_pb_**: relative path to the maven_dependencies.pb file, e.g - `scripts/assets/maven_dependencies.pb` +1. **_path_to_directory_root_**: directory path to the root of the Oppia Android repository, e.g. - `home//opensource/oppia-android` +2. **_path_to_maven_install_json_**: relative path to the maven_install.json file, e.g. - `third_party/maven_install.json` +3. **_path_to_maven_dependencies_textproto_**: relative path to the maven_dependencies.textproto, e.g. - `scripts/assets/maven_dependencies.textproto` +4. **_path_to_maven_dependencies_pb_**: relative path to the maven_dependencies.pb file, e.g. - `scripts/assets/maven_dependencies.pb` ## Handling Exception: `Licenses details are not completed` The script can take about a minute to execute, and if the script fails with the exception: `Licenses details are not completed`, you will need to do some manual work in `maven_dependencies.textproto`. -The script would call out specific dependencies that need to be updated manually, e.g - +The script would call out specific dependencies that need to be updated manually, e.g. - ``` -Please verify the license link(s) for the following license(s) manually in -maven_dependencies.textproto, note that only the first dependency that contains the license +Please verify the license link(s) for the following license(s) manually in +maven_dependencies.textproto, note that only the first dependency that contains the license needs to be updated and also re-run the script to update the license details at all places: license_name: Android Software Development Kit License @@ -67,14 +67,14 @@ maven_dependency { ### Categorizing the license link If the link does point to a valid license then choose the most appropriate category for the link: -1. scrapable_link: If the license text is plain text and the URL mentioned can be scraped directly from the original_link of the license. - e.g - https://www.apache.org/licenses/LICENSE-2.0.txt +1. scrapable_link: If the license text is plain text and the URL mentioned can be scraped directly from the original_link of the license. + e.g. - https://www.apache.org/licenses/LICENSE-2.0.txt 2. extracted_copy_link: If the license text is plain text but can not be scraped directly from the original_link of the license. - e.g - https://opensource.org/license/bsd-3-clause + e.g. - https://opensource.org/license/bsd-3-clause 3. direct_link_only: If the license text is not plain text, it's best to display only the link of the license. - e.g - https://developer.android.com/studio/terms.html + e.g. - https://developer.android.com/studio/terms.html -After identifying the category of the license, modify the license to include one of the above-mentioned 'url'. e.g - +After identifying the category of the license, modify the license to include one of the above-mentioned 'url'. e.g. - ``` license { license_name: "The Apache Software License, Version 2.0" @@ -86,7 +86,7 @@ license { ``` Also, if the license falls in the `extracted_copy_link` category, then go to [Oppia-android-licenses](https://github.com/oppia/oppia-android-licenses) and find if there exists a copy of the license already in the repository. If there exists a copy of the license, perform the following steps to get the link for the license that can be scraped easily. -1. Click on the appropriate license file. +1. Click on the appropriate license file. 2. Now click on the raw button positioned in the left of the edit and delete button. 3. Copy the URL from the browser and mention it at the appropriate place. @@ -95,7 +95,7 @@ If the license does not exist in the Oppia-android-licenses repository, then coo -After modifying `maven_dependencies.textproto` for all the called out licenses in the console output, re-run the script and see if any other error occurs. +After modifying `maven_dependencies.textproto` for all the called out licenses in the console output, re-run the script and see if any other error occurs. ## Handling Exception: `License links are invalid or not available for some dependencies` @@ -122,7 +122,7 @@ maven_dependency { To fix the error, consider the above example. For the first maven_dependency: "io.fabric.sdk.android:fabric:1.4.7", the original_link is invalid, and hence we need to find a valid link for this dependency. Please coordinate with the Oppia Android team and find a valid link for this dependency. Once you have a valid link for this license then categorize it as mentioned [here](https://github.com/oppia/oppia-android/wiki/Updating-Maven-Dependencies#categorizing-the-license-link). -For the second maven_dependency: "com.google.guava:failureaccess:1.0.1", you need to find a license by coordinating with the Oppia Android team and then specify it under the artifact_version field of the dependency. e.g - +For the second maven_dependency: "com.google.guava:failureaccess:1.0.1", you need to find a license by coordinating with the Oppia Android team and then specify it under the artifact_version field of the dependency. e.g. - ``` maven_dependency {