Skip to content

Commit 812406d

Browse files
Add the retry logic and diagnostics to DiktatSaveSmokeTest (#1477)
### What's done: * `JAVA_HOME` is now correctly set for child processes (works around compatibility problems with Java 17). * Added the retry logic for download and delete operations. * Added test timeouts of 20s. * Added debug logging. * Dropped the dependency on Apache HttpClient (not really necessary). * See #1476.
1 parent efc40ba commit 812406d

4 files changed

Lines changed: 262 additions & 68 deletions

File tree

diktat-rules/pom.xml

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@
1515
<properties>
1616
<maven.compiler.source>1.8</maven.compiler.source>
1717
<maven.compiler.target>1.8</maven.compiler.target>
18-
<apache.httpclient.version>4.5.13</apache.httpclient.version>
1918
</properties>
2019

2120
<dependencies>
@@ -94,11 +93,6 @@
9493
<groupId>com.bpodgursky</groupId>
9594
<artifactId>jbool_expressions</artifactId>
9695
</dependency>
97-
<dependency>
98-
<groupId>org.apache.httpcomponents</groupId>
99-
<artifactId>httpclient</artifactId>
100-
<version>${apache.httpclient.version}</version>
101-
</dependency>
10296
</dependencies>
10397

10498
<build>
Lines changed: 128 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,35 @@
11
package org.cqfn.diktat.ruleset.smoke
22

3+
import org.cqfn.diktat.common.utils.loggerWithKtlintConfig
34
import org.cqfn.diktat.util.SAVE_VERSION
4-
import org.apache.commons.io.FileUtils
5-
import org.apache.http.client.methods.CloseableHttpResponse
6-
import org.apache.http.client.methods.HttpGet
7-
import org.apache.http.impl.client.HttpClients
5+
import org.cqfn.diktat.util.deleteIfExistsSilently
6+
import org.cqfn.diktat.util.prependPath
7+
import org.cqfn.diktat.util.retry
8+
9+
import mu.KotlinLogging
10+
import org.assertj.core.api.Assertions.fail
11+
import org.assertj.core.api.SoftAssertions.assertSoftly
812
import org.junit.jupiter.api.AfterAll
9-
import org.junit.jupiter.api.Assertions
1013
import org.junit.jupiter.api.BeforeAll
1114
import org.junit.jupiter.api.condition.DisabledOnOs
1215
import org.junit.jupiter.api.condition.OS
13-
import java.io.File
14-
import java.io.FileOutputStream
15-
import java.nio.file.Paths
16+
17+
import java.net.URL
18+
import java.nio.file.Path
19+
import kotlin.io.path.Path
20+
import kotlin.io.path.absolute
21+
import kotlin.io.path.copyTo
22+
import kotlin.io.path.createDirectories
23+
import kotlin.io.path.div
1624
import kotlin.io.path.exists
25+
import kotlin.io.path.isSameFileAs
1726
import kotlin.io.path.listDirectoryEntries
1827
import kotlin.io.path.name
28+
import kotlin.io.path.outputStream
1929
import kotlin.io.path.pathString
30+
import kotlin.io.path.readText
31+
import kotlin.io.path.relativeTo
32+
import kotlin.system.measureNanoTime
2033

2134
@DisabledOnOs(OS.MAC)
2235
class DiktatSaveSmokeTest : DiktatSmokeTestBase() {
@@ -26,7 +39,7 @@ class DiktatSaveSmokeTest : DiktatSmokeTestBase() {
2639
expected: String,
2740
test: String,
2841
) {
29-
saveSmokeTest(config, test)
42+
saveSmokeTest(Path(config), test)
3043
}
3144

3245
/**
@@ -35,97 +48,150 @@ class DiktatSaveSmokeTest : DiktatSmokeTestBase() {
3548
*/
3649
@Suppress("TOO_LONG_FUNCTION")
3750
private fun saveSmokeTest(
38-
configFilePath: String,
51+
configFilePath: Path,
3952
testPath: String
4053
) {
41-
val processBuilder = createProcessBuilderWithCmd(testPath)
54+
assertSoftly { softly ->
55+
softly.assertThat(configFilePath).isRegularFile
4256

43-
val file = File("src/test/resources/test/smoke/tmpSave.txt")
44-
val configFile = File("src/test/resources/test/smoke/diktat-analysis.yml")
45-
val configFileFrom = File(configFilePath)
57+
val configFile = (baseDirectory / "diktat-analysis.yml").apply {
58+
parent.createDirectories()
59+
}
60+
val saveLog = (baseDirectory / "tmpSave.txt").apply {
61+
parent.createDirectories()
62+
deleteIfExistsSilently()
63+
}
4664

47-
configFile.createNewFile()
48-
file.createNewFile()
65+
try {
66+
configFilePath.copyTo(configFile, overwrite = true)
4967

50-
FileUtils.copyFile(configFileFrom, configFile)
68+
val processBuilder = createProcessBuilderWithCmd(testPath).apply {
69+
redirectErrorStream(true)
70+
redirectOutput(ProcessBuilder.Redirect.appendTo(saveLog.toFile()))
5171

52-
processBuilder.redirectErrorStream(true)
53-
processBuilder.redirectOutput(ProcessBuilder.Redirect.appendTo(file))
72+
/*
73+
* Inherit JAVA_HOME for the child process.
74+
*/
75+
val javaHome = System.getProperty("java.home")
76+
environment()["JAVA_HOME"] = javaHome
77+
prependPath(Path(javaHome) / "bin")
78+
}
5479

55-
val process = processBuilder.start()
56-
process.waitFor()
80+
val saveProcess = processBuilder.start()
81+
val saveExitCode = saveProcess.waitFor()
82+
softly.assertThat(saveExitCode).describedAs("The exit code of SAVE").isZero
5783

58-
val output = file.readLines()
59-
val saveOutput = output.joinToString("\n")
84+
softly.assertThat(saveLog).isRegularFile
6085

61-
file.delete()
86+
val saveOutput = saveLog.readText()
6287

63-
Assertions.assertTrue(
64-
saveOutput.contains("SUCCESS")
65-
)
88+
val saveCommandLine = processBuilder.command().joinToString(separator = " ")
89+
softly.assertThat(saveOutput)
90+
.describedAs("The output of \"$saveCommandLine\"")
91+
.isNotEmpty
92+
.contains("SUCCESS")
93+
} finally {
94+
configFile.deleteIfExistsSilently()
95+
saveLog.deleteIfExistsSilently()
96+
}
97+
}
6698
}
6799

100+
@Suppress("WRONG_ORDER_IN_CLASS_LIKE_STRUCTURES") // False positives
68101
companion object {
69102
private const val KTLINT_VERSION = "0.46.1"
70103

71-
private fun getSaveForCurrentOs() = when {
72-
System.getProperty("os.name").startsWith("Linux", ignoreCase = true) -> "save-$SAVE_VERSION-linuxX64.kexe"
73-
System.getProperty("os.name").startsWith("Mac", ignoreCase = true) -> "save-$SAVE_VERSION-macosX64.kexe"
74-
System.getProperty("os.name").startsWith("Windows", ignoreCase = true) -> "save-$SAVE_VERSION-mingwX64.exe"
75-
else -> ""
104+
@Suppress("EMPTY_BLOCK_STRUCTURE_ERROR")
105+
private val logger = KotlinLogging.loggerWithKtlintConfig { }
106+
private val baseDirectory = Path("src/test/resources/test/smoke").absolute()
107+
108+
private fun getSaveForCurrentOs(): String {
109+
val osName = System.getProperty("os.name")
110+
111+
return when {
112+
osName.startsWith("Linux", ignoreCase = true) -> "save-$SAVE_VERSION-linuxX64.kexe"
113+
osName.startsWith("Mac", ignoreCase = true) -> "save-$SAVE_VERSION-macosX64.kexe"
114+
osName.startsWith("Windows", ignoreCase = true) -> "save-$SAVE_VERSION-mingwX64.exe"
115+
else -> fail("SAVE doesn't support $osName (version ${System.getProperty("os.version")})")
116+
}
76117
}
77118

78-
private fun downloadFile(url: String, file: File) {
79-
val httpClient = HttpClients.createDefault()
80-
val request = HttpGet(url)
81-
httpClient.use {
82-
val response: CloseableHttpResponse = httpClient.execute(request)
83-
response.use {
84-
val fileSave = response.entity
85-
fileSave?.let {
86-
FileOutputStream(file).use { outstream -> fileSave.writeTo(outstream) }
119+
@Suppress("FLOAT_IN_ACCURATE_CALCULATIONS")
120+
private fun downloadFile(from: URL, to: Path) {
121+
logger.info {
122+
"Downloading $from to ${to.relativeTo(baseDirectory)}..."
123+
}
124+
125+
val attempts = 5
126+
127+
val lazyDefault: (Throwable) -> Unit = { error ->
128+
fail("Failure downloading $from after $attempts attempt(s)", error)
129+
}
130+
131+
retry(attempts, lazyDefault = lazyDefault) {
132+
from.openStream().use { source ->
133+
to.outputStream().use { target ->
134+
val bytesCopied: Long
135+
val timeNanos = measureNanoTime {
136+
bytesCopied = source.copyTo(target)
137+
}
138+
logger.info {
139+
"$bytesCopied byte(s) copied in ${timeNanos / 1000 / 1e3} ms."
140+
}
87141
}
88142
}
89143
}
90144
}
91145

92146
@BeforeAll
93147
@JvmStatic
148+
@Suppress("AVOID_NULL_CHECKS")
94149
internal fun beforeAll() {
150+
val forkedJavaHome = System.getenv("JAVA_HOME")
151+
if (forkedJavaHome != null) {
152+
val javaHome = System.getProperty("java.home")
153+
if (javaHome != null && !Path(javaHome).isSameFileAs(Path(forkedJavaHome))) {
154+
logger.warn {
155+
"Current JDK home is $javaHome. Forked tests may use a different JDK at $forkedJavaHome."
156+
}
157+
}
158+
logger.warn {
159+
"Make sure JAVA_HOME ($forkedJavaHome) points to a Java 8 or Java 11 home. Java 17 is not yet supported."
160+
}
161+
}
162+
163+
logger.info {
164+
"The base directory for the smoke test is $baseDirectory."
165+
}
166+
95167
val diktatDir: String =
96-
Paths.get("../diktat-ruleset/target")
168+
Path("../diktat-ruleset/target")
97169
.takeIf { it.exists() }
98170
?.listDirectoryEntries()
99171
?.single { it.name.contains("diktat") }
100172
?.pathString ?: ""
101173

102-
val diktat = File("src/test/resources/test/smoke/diktat.jar")
103-
val diktatFrom = File(diktatDir)
104-
val save = File("src/test/resources/test/smoke/${getSaveForCurrentOs()}")
105-
val ktlint = File("src/test/resources/test/smoke/ktlint")
106-
107-
ktlint.createNewFile()
108-
save.createNewFile()
109-
diktat.createNewFile()
174+
val diktatFrom = Path(diktatDir)
175+
val diktat = baseDirectory / "diktat.jar"
176+
val save = baseDirectory / getSaveForCurrentOs()
177+
val ktlint = baseDirectory / "ktlint"
110178

111-
downloadFile("https://github.com/saveourtool/save-cli/releases/download/v$SAVE_VERSION/${getSaveForCurrentOs()}", save)
112-
downloadFile("https://github.com/pinterest/ktlint/releases/download/$KTLINT_VERSION/ktlint", ktlint)
179+
downloadFile(URL("https://github.com/saveourtool/save-cli/releases/download/v$SAVE_VERSION/${getSaveForCurrentOs()}"), save)
180+
downloadFile(URL("https://github.com/pinterest/ktlint/releases/download/$KTLINT_VERSION/ktlint"), ktlint)
113181

114-
FileUtils.copyFile(diktatFrom, diktat)
182+
diktatFrom.copyTo(diktat)
115183
}
116184

117185
@AfterAll
118186
@JvmStatic
119187
internal fun afterAll() {
120-
val diktat = File("src/test/resources/test/smoke/diktat.jar")
121-
val configFile = File("src/test/resources/test/smoke/diktat-analysis.yml")
122-
val save = File("src/test/resources/test/smoke/${getSaveForCurrentOs()}")
123-
val ktlint = File("src/test/resources/test/smoke/ktlint")
124-
125-
diktat.delete()
126-
configFile.delete()
127-
ktlint.delete()
128-
save.delete()
188+
val diktat = baseDirectory / "diktat.jar"
189+
val save = baseDirectory / getSaveForCurrentOs()
190+
val ktlint = baseDirectory / "ktlint"
191+
192+
diktat.deleteIfExistsSilently()
193+
ktlint.deleteIfExistsSilently()
194+
save.deleteIfExistsSilently()
129195
}
130196
}
131197
}

0 commit comments

Comments
 (0)