diff --git a/compiler/tests-mutes/tc-integration/build.gradle.kts b/compiler/tests-mutes/tc-integration/build.gradle.kts index 83e6146e740..b214c5a8c10 100644 --- a/compiler/tests-mutes/tc-integration/build.gradle.kts +++ b/compiler/tests-mutes/tc-integration/build.gradle.kts @@ -5,10 +5,12 @@ plugins { } dependencies { - api(kotlinStdlib()) + implementation(kotlinStdlib("jdk8")) implementation(project(":compiler:tests-mutes")) - implementation("khttp:khttp:1.0.0") - implementation("com.fasterxml.jackson.module:jackson-module-kotlin:2.11.0") + implementation(libs.ktor.client.cio) + implementation(libs.ktor.client.content.negotiation) + implementation(libs.ktor.serialization.jackson) + implementation(libs.jackson.module.kotlin) } sourceSets { diff --git a/compiler/tests-mutes/tc-integration/src/org/jetbrains/kotlin/test/mutes/MutedTestsSync.kt b/compiler/tests-mutes/tc-integration/src/org/jetbrains/kotlin/test/mutes/MutedTestsSync.kt index f70c11e6293..141247a37ec 100644 --- a/compiler/tests-mutes/tc-integration/src/org/jetbrains/kotlin/test/mutes/MutedTestsSync.kt +++ b/compiler/tests-mutes/tc-integration/src/org/jetbrains/kotlin/test/mutes/MutedTestsSync.kt @@ -5,9 +5,10 @@ package org.jetbrains.kotlin.test.mutes +import kotlinx.coroutines.runBlocking import java.io.File -fun main() { +suspend fun main() { syncMutedTestsOnTeamCityWithDatabase() } @@ -16,16 +17,16 @@ fun main() { * * Purpose: possibility to run flaky tests on teamcity that will not affect on build status */ -fun syncMutedTestsOnTeamCityWithDatabase() { +suspend fun syncMutedTestsOnTeamCityWithDatabase() { val remotelyMutedTests = RemotelyMutedTests() val locallyMutedTests = LocallyMutedTests() syncMutedTests(remotelyMutedTests.projectTests, locallyMutedTests.projectTests) } -private fun syncMutedTests( +private suspend fun syncMutedTests( remotelyMutedTests: Map, - locallyMutedTests: Map + locallyMutedTests: Map, ) { val deleteList = remotelyMutedTests - locallyMutedTests.keys val uploadList = locallyMutedTests - remotelyMutedTests.keys @@ -40,7 +41,7 @@ private const val MUTES_PACKAGE_NAME = "org.jetbrains.kotlin.test.mutes" internal val projectId = getMandatoryProperty("$MUTES_PACKAGE_NAME.tests.project.id") class RemotelyMutedTests { - private val tests = getMutedTestsOnTeamcityForRootProject(projectId) + private val tests = runBlocking { getMutedTestsOnTeamcityForRootProject(projectId) } val projectTests = getTestsJson(projectId) private fun getTestsJson(scopeId: String): Map { return filterMutedTestsByScope(tests, scopeId) diff --git a/compiler/tests-mutes/tc-integration/src/org/jetbrains/kotlin/test/mutes/TeamCityInteraction.kt b/compiler/tests-mutes/tc-integration/src/org/jetbrains/kotlin/test/mutes/TeamCityInteraction.kt index cd783d462d2..2ed04393c3e 100644 --- a/compiler/tests-mutes/tc-integration/src/org/jetbrains/kotlin/test/mutes/TeamCityInteraction.kt +++ b/compiler/tests-mutes/tc-integration/src/org/jetbrains/kotlin/test/mutes/TeamCityInteraction.kt @@ -2,19 +2,37 @@ package org.jetbrains.kotlin.test.mutes import com.fasterxml.jackson.databind.JsonNode import com.fasterxml.jackson.module.kotlin.treeToValue -import khttp.responses.Response -import khttp.structures.authorization.Authorization +import io.ktor.client.* +import io.ktor.client.plugins.* +import io.ktor.client.plugins.contentnegotiation.* +import io.ktor.client.request.* +import io.ktor.client.statement.* +import io.ktor.http.* +import io.ktor.serialization.jackson.* +import kotlin.time.DurationUnit +import kotlin.time.toDuration internal const val TAG = "[MUTED-BY-CSVFILE]" private val buildServerUrl = getMandatoryProperty("org.jetbrains.kotlin.test.mutes.teamcity.server.url") -private val headers = mapOf("Content-type" to "application/json", "Accept" to "application/json") -private val authUser = object : Authorization { - override val header = "Authorization" to "Bearer ${getMandatoryProperty("org.jetbrains.kotlin.test.mutes.teamcity.server.token")}" +private val token = getMandatoryProperty("org.jetbrains.kotlin.test.mutes.teamcity.server.token") +private val REQUEST_TIMEOUT = 120.toDuration(DurationUnit.SECONDS) + +private val httpClient = HttpClient { + install(HttpTimeout) + install(ContentNegotiation) { + jackson() + } } -private const val REQUEST_TIMEOUT_SEC = 120.0 +private fun HttpRequestBuilder.applyCommonProperties() { + headers { + bearerAuth(token) + append(HttpHeaders.Accept, "application/json") + } + timeout { requestTimeoutMillis = REQUEST_TIMEOUT.inWholeMilliseconds } +} -internal fun getMutedTestsOnTeamcityForRootProject(rootScopeId: String): List { +internal suspend fun getMutedTestsOnTeamcityForRootProject(rootScopeId: String): List { val requestHref = "/app/rest/mutes" val requestParams = mapOf( "locator" to "project:(id:$rootScopeId)", @@ -29,16 +47,23 @@ internal fun getMutedTestsOnTeamcityForRootProject(rootScopeId: String): List(it) } } -private fun traverseAll( +private suspend fun traverseAll( @Suppress("SameParameterValue") requestHref: String, - requestParams: Map + requestParams: Map, ): List { val jsonResponses = mutableListOf() - fun request(url: String, params: Map): String { - val currentResponse = khttp.get(url, headers, params, auth = authUser, timeout = REQUEST_TIMEOUT_SEC) + suspend fun request(url: String, params: Map): String { + val currentResponse = httpClient.get(url) { + applyCommonProperties() + url { + for (entry in params) { + parameters.append(entry.key, entry.value) + } + } + } checkResponseAndLog(currentResponse) - val currentJsonResponse = jsonObjectMapper.readTree(currentResponse.text) + val currentJsonResponse = jsonObjectMapper.readTree(currentResponse.bodyAsText()) jsonResponses.add(currentJsonResponse) return currentJsonResponse.get("nextHref")?.textValue() ?: "" } @@ -51,27 +76,22 @@ private fun traverseAll( return jsonResponses } -internal fun uploadMutedTests(uploadMap: Map) { +internal suspend fun uploadMutedTests(uploadMap: Map) { for ((_, muteTestJson) in uploadMap) { - val response = khttp.post( - "$buildServerUrl/app/rest/mutes", - headers = headers, - data = jsonObjectMapper.writeValueAsString(muteTestJson), - auth = authUser, - timeout = REQUEST_TIMEOUT_SEC - ) + val response = httpClient.post("$buildServerUrl/app/rest/mutes") { + applyCommonProperties() + contentType(ContentType.Application.Json) + setBody(muteTestJson) + } checkResponseAndLog(response) } } -internal fun deleteMutedTests(deleteMap: Map) { +internal suspend fun deleteMutedTests(deleteMap: Map) { for ((_, muteTestJson) in deleteMap) { - val response = khttp.delete( - "$buildServerUrl/app/rest/mutes/id:${muteTestJson.id}", - headers = headers, - auth = authUser, - timeout = REQUEST_TIMEOUT_SEC - ) + val response = httpClient.delete("$buildServerUrl/app/rest/mutes/id:${muteTestJson.id}") { + applyCommonProperties() + } try { checkResponseAndLog(response) } catch (e: Exception) { @@ -80,13 +100,13 @@ internal fun deleteMutedTests(deleteMap: Map) { } } -private fun checkResponseAndLog(response: Response) { - val isResponseBad = response.connection.responseCode !in 200..299 +private suspend fun checkResponseAndLog(response: HttpResponse) { + val isResponseBad = response.status.value !in 200..299 if (isResponseBad) { throw Exception( "${response.request.method}-request to ${response.request.url} failed:\n" + - "${response.text}\n" + - "${response.request.data ?: ""}" + "${response.bodyAsText()}\n" + + "${response.request.content}" ) } } diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index f1b36d5d277..17921da1614 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -16,6 +16,7 @@ kotlinx-metadataJvm = "0.6.2" # Forcing gson version because of https://github.com/google/gson/pull/1991 gson = { strictly = "2.8.9" } # should be in sync with version.properties jetbrains-ideaExt = "1.0.1" +jackson = "2.15.2" [libraries] dexMemberList = { module = "com.jakewharton.dex:dex-member-list", version.ref = "dexMemberList" } @@ -31,6 +32,9 @@ spdx-gradlePlugin = { module = "org.spdx:spdx-gradle-plugin", version.ref = "spd proguard-gradlePlugin = { module = "net.sf.proguard:proguard-gradle", version.ref = "proguard" } ktor-client-core = { module = "io.ktor:ktor-client-core", version.ref = "ktor" } ktor-client-cio = { module = "io.ktor:ktor-client-cio", version.ref = "ktor" } +ktor-client-content-negotiation = { module = "io.ktor:ktor-client-content-negotiation", version.ref = "ktor" } +ktor-serialization-jackson = { module = "io.ktor:ktor-serialization-jackson", version.ref = "ktor" } +jackson-module-kotlin = { module = "com.fasterxml.jackson.module:jackson-module-kotlin", version.ref = "jackson" } gson = { module = "com.google.code.gson:gson", version.ref = "gson" } jetbrains-ideaExt-gradlePlugin = { module = "gradle.plugin.org.jetbrains.gradle.plugin.idea-ext:gradle-idea-ext", version.ref = "jetbrains-ideaExt" } jdom2 = { module = "org.jdom:jdom2", version.ref = "jdom2" }