[Build, IGS] Implement ConsentManager
#KTI-1223 In Progress
This commit is contained in:
committed by
Space Team
parent
6804d0a0af
commit
f04f1d18c4
+59
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.build
|
||||
|
||||
import java.io.BufferedReader
|
||||
import java.io.PrintStream
|
||||
|
||||
private val isInIdeaSync
|
||||
get() = System.getProperty("idea.sync.active").toBoolean()
|
||||
|
||||
internal const val USER_CONSENT_MARKER = "# This line indicates that you have chosen to enable automatic configuration of properties."
|
||||
|
||||
internal const val USER_REFUSAL_MARKER =
|
||||
"# This line indicates that you have chosen to disable automatic configuration of properties. If you want to enable it, remove this line."
|
||||
|
||||
internal val USER_CONSENT_REQUEST = """
|
||||
|
||||
! ATTENTION REQUIRED !
|
||||
Most probably you're a developer from the Kotlin team. We are asking for your consent for automatic configuration of local.properties file
|
||||
for providing some optimizations and collecting additional debug information.
|
||||
|
||||
""".trimIndent()
|
||||
|
||||
internal const val PROMPT_REQUEST = "Do you agree with this? Please answer with 'yes' or 'no': "
|
||||
|
||||
internal class ConsentManager(
|
||||
private val modifier: LocalPropertiesModifier,
|
||||
private val input: BufferedReader = System.`in`.bufferedReader(),
|
||||
private val output: PrintStream = System.out,
|
||||
) {
|
||||
fun getUserDecision() = when {
|
||||
modifier.initiallyContains(USER_REFUSAL_MARKER) -> false
|
||||
modifier.initiallyContains(USER_CONSENT_MARKER) -> true
|
||||
isInIdeaSync -> false
|
||||
else -> null
|
||||
}
|
||||
|
||||
fun askForConsent(): Boolean {
|
||||
output.println(USER_CONSENT_REQUEST)
|
||||
while (true) {
|
||||
output.println(PROMPT_REQUEST)
|
||||
when (input.readLine()) {
|
||||
"yes" -> {
|
||||
output.println("You've given the consent")
|
||||
modifier.putLine(USER_CONSENT_MARKER)
|
||||
return true
|
||||
}
|
||||
"no" -> {
|
||||
output.println("You've refused to give the consent")
|
||||
modifier.putLine(USER_REFUSAL_MARKER)
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+12
-5
@@ -20,11 +20,13 @@ internal val SYNCED_PROPERTIES_START_LINES = """
|
||||
internal const val SYNCED_PROPERTIES_END_LINE = "# the end of automatically configured properties"
|
||||
|
||||
internal class LocalPropertiesModifier(private val localProperties: File) {
|
||||
private val manuallyConfiguredPropertiesContent by lazy {
|
||||
if (!localProperties.exists()) return@lazy ""
|
||||
private val initialUserConfiguredPropertiesContent = getUserConfiguredPropertiesContent()
|
||||
|
||||
private fun getUserConfiguredPropertiesContent(): String {
|
||||
if (!localProperties.exists()) return ""
|
||||
var insideAutomaticallyConfiguredSection = false
|
||||
// filter out the automatically configured lines
|
||||
localProperties.readLines().filter { line ->
|
||||
return localProperties.readLines().filter { line ->
|
||||
if (line == SYNCED_PROPERTIES_START_LINE) {
|
||||
insideAutomaticallyConfiguredSection = true
|
||||
}
|
||||
@@ -45,15 +47,16 @@ internal class LocalPropertiesModifier(private val localProperties: File) {
|
||||
if (localProperties.exists() && !localProperties.isFile) {
|
||||
error("$localProperties is not a file!")
|
||||
}
|
||||
val content = getUserConfiguredPropertiesContent()
|
||||
val manuallyConfiguredProperties = Properties().apply {
|
||||
StringReader(manuallyConfiguredPropertiesContent).use {
|
||||
StringReader(content).use {
|
||||
load(it)
|
||||
}
|
||||
}
|
||||
val propertiesToSetup = setupFile.properties.mapValues { PropertyValue(it.value, manuallyConfiguredProperties.containsKey(it.key)) }
|
||||
localProperties.writeText(
|
||||
"""
|
||||
|${manuallyConfiguredPropertiesContent.addSuffix("\n")}
|
||||
|${content.addSuffix("\n")}
|
||||
|$SYNCED_PROPERTIES_START_LINES
|
||||
|${propertiesToSetup.asPropertiesLines}
|
||||
|$SYNCED_PROPERTIES_END_LINE
|
||||
@@ -61,6 +64,10 @@ internal class LocalPropertiesModifier(private val localProperties: File) {
|
||||
""".trimMargin()
|
||||
)
|
||||
}
|
||||
|
||||
fun initiallyContains(line: String) = initialUserConfiguredPropertiesContent.contains(line)
|
||||
|
||||
fun putLine(line: String) = localProperties.appendText("\n$line")
|
||||
}
|
||||
|
||||
private fun String.addSuffix(suffix: String): String {
|
||||
|
||||
+76
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
* Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.build
|
||||
|
||||
import org.apache.tools.ant.filters.StringInputStream
|
||||
import org.junit.jupiter.api.DisplayName
|
||||
import org.junit.jupiter.api.io.TempDir
|
||||
import java.io.ByteArrayOutputStream
|
||||
import java.io.PrintStream
|
||||
import java.nio.file.Files
|
||||
import java.nio.file.Path
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.assertNull
|
||||
import kotlin.test.assertTrue
|
||||
|
||||
class ConsentManagerTest {
|
||||
@TempDir
|
||||
lateinit var workingDir: Path
|
||||
|
||||
private val localPropertiesFile by lazy {
|
||||
workingDir.resolve("local.properties")
|
||||
}
|
||||
|
||||
private val modifier by lazy {
|
||||
LocalPropertiesModifier(localPropertiesFile.toFile())
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("can check if there's no decision over the consent")
|
||||
fun testNoConsentRead() {
|
||||
assertNull(ConsentManager(modifier).getUserDecision())
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("can check if there's agree to the consent")
|
||||
fun testConsentAgreeRead() {
|
||||
localPropertiesFile.toFile().writeText(USER_CONSENT_MARKER)
|
||||
assertEquals(true, ConsentManager(modifier).getUserDecision())
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("can check if there's refusal to the consent")
|
||||
fun testConsentRefusalRead() {
|
||||
localPropertiesFile.toFile().writeText(USER_REFUSAL_MARKER)
|
||||
assertEquals(false, ConsentManager(modifier).getUserDecision())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testConsentRequestAgree() = testUserDecision("yes", true, USER_CONSENT_MARKER)
|
||||
|
||||
@Test
|
||||
fun testConsentRequestRefusal() = testUserDecision("no", false, USER_REFUSAL_MARKER)
|
||||
|
||||
@Test
|
||||
fun testConsentRequestAnswerAfterBadInput() = testUserDecision("\nasdasd\nno", false, USER_REFUSAL_MARKER, 3)
|
||||
|
||||
private fun testUserDecision(prompt: String, expectedDecision: Boolean, expectedLine: String, promptCount: Int = 1) {
|
||||
StringInputStream(prompt).bufferedReader().use { input ->
|
||||
val outputStream = ByteArrayOutputStream(255)
|
||||
PrintStream(outputStream).use { printStream ->
|
||||
val consentManager = ConsentManager(modifier, input, printStream)
|
||||
val userDecision = consentManager.askForConsent()
|
||||
assertEquals(expectedDecision, userDecision)
|
||||
val content = Files.readAllLines(localPropertiesFile)
|
||||
assertTrue(content.contains(expectedLine))
|
||||
val output = String(outputStream.toByteArray())
|
||||
assertContainsExactTimes(output, USER_CONSENT_REQUEST, 1)
|
||||
assertContainsExactTimes(output, PROMPT_REQUEST, promptCount)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+2
-15
@@ -205,21 +205,8 @@ class LocalPropertiesModifierTest {
|
||||
}
|
||||
|
||||
private fun assertContainsMarkersOnce(content: String) {
|
||||
assertContainsOnce(content, SYNCED_PROPERTIES_START_LINES)
|
||||
assertContainsOnce(content, SYNCED_PROPERTIES_END_LINE)
|
||||
}
|
||||
|
||||
private fun assertContainsOnce(content: String, substring: String) {
|
||||
var currentOffset = 0
|
||||
var count = 0
|
||||
var nextIndex = content.indexOf(substring, currentOffset)
|
||||
|
||||
while (nextIndex != -1 && count < 2) {
|
||||
count++
|
||||
currentOffset = nextIndex + substring.length
|
||||
nextIndex = content.indexOf(substring, currentOffset)
|
||||
}
|
||||
assertEquals(1, count)
|
||||
assertContainsExactTimes(content, SYNCED_PROPERTIES_START_LINES, 1)
|
||||
assertContainsExactTimes(content, SYNCED_PROPERTIES_END_LINE, 1)
|
||||
}
|
||||
|
||||
private fun fillInitialLocalPropertiesFile(content: Map<String, PropertyValue>) {
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.build
|
||||
|
||||
internal fun assertContainsExactTimes(content: String, substring: String, expectedCount: Int) {
|
||||
var currentOffset = 0
|
||||
var count = 0
|
||||
var nextIndex = content.indexOf(substring, currentOffset)
|
||||
|
||||
while (nextIndex != -1 && count < expectedCount + 1) {
|
||||
count++
|
||||
currentOffset = nextIndex + substring.length
|
||||
nextIndex = content.indexOf(substring, currentOffset)
|
||||
}
|
||||
assert(expectedCount == count) {
|
||||
"""
|
||||
|The content is expected to contain '$substring' exactly $expectedCount times, but was found $count times:
|
||||
|File content:
|
||||
|${content.prependIndent()}
|
||||
""".trimMargin()
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user