diff --git a/.gitignore b/.gitignore index 531f05c8a20..224c3b3ef25 100644 --- a/.gitignore +++ b/.gitignore @@ -25,3 +25,4 @@ bazel-* .bazelproject .aswb *.pb +coverage_reports diff --git a/scripts/src/java/org/oppia/android/scripts/coverage/BUILD.bazel b/scripts/src/java/org/oppia/android/scripts/coverage/BUILD.bazel index 24cc091c3e7..246ff4403e4 100644 --- a/scripts/src/java/org/oppia/android/scripts/coverage/BUILD.bazel +++ b/scripts/src/java/org/oppia/android/scripts/coverage/BUILD.bazel @@ -12,6 +12,7 @@ kt_jvm_library( ], visibility = ["//scripts:oppia_script_binary_visibility"], deps = [ + ":coverage_reporter", ":coverage_runner", "//scripts/src/java/org/oppia/android/scripts/common:bazel_client", "//scripts/src/java/org/oppia/android/scripts/proto:script_exemptions_java_proto", @@ -30,3 +31,16 @@ kt_jvm_library( "//scripts/src/java/org/oppia/android/scripts/proto:coverage_java_proto", ], ) + +kt_jvm_library( + name = "coverage_reporter", + testonly = True, + srcs = [ + "CoverageReporter.kt", + ], + visibility = ["//scripts:oppia_script_binary_visibility"], + deps = [ + "//scripts/src/java/org/oppia/android/scripts/common:bazel_client", + "//scripts/src/java/org/oppia/android/scripts/proto:coverage_java_proto", + ], +) diff --git a/scripts/src/java/org/oppia/android/scripts/coverage/CoverageReporter.kt b/scripts/src/java/org/oppia/android/scripts/coverage/CoverageReporter.kt new file mode 100644 index 00000000000..70c0cf9baab --- /dev/null +++ b/scripts/src/java/org/oppia/android/scripts/coverage/CoverageReporter.kt @@ -0,0 +1,236 @@ +package org.oppia.android.scripts.coverage + +import org.oppia.android.scripts.proto.Coverage +import org.oppia.android.scripts.proto.CoverageReport +import java.io.File + +/** + * Class responsible for generating rich text coverage report. + * + * @param repoRoot the root directory of the repository + * @param coverageReportList the list of coverage data proto + * @param reportFormat the format in which the report will be generated + */ +class CoverageReporter( + private val repoRoot: String, + private val coverageReportList: List, + private val reportFormat: ReportFormat, +) { + private val computedCoverageRatio = computeCoverageRatio() + private val formattedCoveragePercentage = "%.2f".format(computedCoverageRatio * 100) + + private val filePath = coverageReportList.firstOrNull()?.filePath ?: "Unknown" + + private val totalLinesFound = coverageReportList.getOrNull(0)?.linesFound ?: 0 + private val totalLinesHit = coverageReportList.getOrNull(0)?.linesHit ?: 0 + + /** + * Generates a rich text report for the analysed coverage data based on the specified format. + * It supports Markdown and HTML formats. + * + * @return a pair where the first value is the computed coverage ratio represented in [0, 1] + * and the second value is the generated report text + */ + fun generateRichTextReport(): Pair { + println("report format: $reportFormat") + return when (reportFormat) { + ReportFormat.MARKDOWN -> generateMarkdownReport() + ReportFormat.HTML -> generateHtmlReport() + } + } + + private fun generateMarkdownReport(): Pair { + val markdownContent = + """ + ## Coverage Report + + - **Covered File:** $filePath + - **Coverage percentage:** $formattedCoveragePercentage% covered + - **Line coverage:** $totalLinesHit / $totalLinesFound lines covered + """.trimIndent() + + println("\n$markdownContent") + + return Pair(computedCoverageRatio, markdownContent) + } + + private fun generateHtmlReport(): Pair { + var htmlContent = + """ + + + + + + Coverage Report + + + +

Coverage Report

+
+
+ Covered File: $filePath
+
+
+ Covered +
+ Uncovered +
+
+
+
Coverage percentage: $formattedCoveragePercentage%
+
Line coverage: $totalLinesHit / $totalLinesFound covered
+
+
+ + + + + + + + + """.trimIndent() + + val fileContent = File(repoRoot, filePath).readLines() + val coverageMap = coverageReportList + .firstOrNull()?.coveredLineList?.associateBy { it.lineNumber } + + fileContent.forEachIndexed { index, line -> + val lineNumber = index + 1 + val lineClass = when (coverageMap?.get(lineNumber)?.coverage) { + Coverage.FULL -> "covered-line" + Coverage.NONE -> "not-covered-line" + else -> "uncovered-line" + } + htmlContent += """ + + + + + """.trimIndent() + } + + htmlContent += """ + +
Line NoSource Code
${lineNumber.toString().padStart(4, ' ')}$line
+ + + """.trimIndent() + + return Pair(computedCoverageRatio, htmlContent) + } + + private fun computeCoverageRatio(): Float { + val report = coverageReportList.getOrNull(0) + return if (report != null && report.linesFound != 0) { + report.linesHit.toFloat() / report.linesFound.toFloat() + } else { + 0f + } + } +} + +/** Represents the different types of formats available to generate code coverage reports. */ +enum class ReportFormat { + /** Indicates that the report should be formatted in .md format. */ + MARKDOWN, + /** Indicates that the report should be formatted in .html format. */ + HTML +} diff --git a/scripts/src/java/org/oppia/android/scripts/coverage/CoverageRunner.kt b/scripts/src/java/org/oppia/android/scripts/coverage/CoverageRunner.kt index 189f72a4463..e1ff9cdb221 100644 --- a/scripts/src/java/org/oppia/android/scripts/coverage/CoverageRunner.kt +++ b/scripts/src/java/org/oppia/android/scripts/coverage/CoverageRunner.kt @@ -104,7 +104,10 @@ class CoverageRunner( } private fun extractTargetName(bazelTestTarget: String): String { - val targetName = bazelTestTarget.substringAfterLast(":").trim() + val targetName = bazelTestTarget + .substringAfterLast("/") + .substringAfterLast(":") + .trim() return targetName.removeSuffix("LocalTest").removeSuffix("Test") } diff --git a/scripts/src/java/org/oppia/android/scripts/coverage/RunCoverage.kt b/scripts/src/java/org/oppia/android/scripts/coverage/RunCoverage.kt index 448519f947d..bf65548aecc 100644 --- a/scripts/src/java/org/oppia/android/scripts/coverage/RunCoverage.kt +++ b/scripts/src/java/org/oppia/android/scripts/coverage/RunCoverage.kt @@ -19,10 +19,12 @@ import java.util.concurrent.TimeUnit * Arguments: * - path_to_root: directory path to the root of the Oppia Android repository. * - relative_path_to_file: the relative path to the file to analyse coverage + * - reportFormat: the format of the coverage report. Defaults to MARKDOWN if not specified. + * Available options: MARKDOWN, HTML. * * Example: * bazel run //scripts:run_coverage -- $(pwd) - * utility/src/main/java/org/oppia/android/util/parser/math/MathModel.kt + * utility/src/main/java/org/oppia/android/util/parser/math/MathModel.kt HTML * Example with custom process timeout: * bazel run //scripts:run_coverage -- $(pwd) * utility/src/main/java/org/oppia/android/util/parser/math/MathModel.kt processTimeout=10 @@ -31,6 +33,14 @@ import java.util.concurrent.TimeUnit fun main(vararg args: String) { val repoRoot = args[0] val filePath = args[1] + val format = args.getOrNull(2) + val reportFormat = when { + format.equals("HTML", ignoreCase = true) -> ReportFormat.HTML + format.equals("MARKDOWN", ignoreCase = true) || format == null -> ReportFormat.MARKDOWN + else -> throw IllegalArgumentException("Unsupported report format: $format") + } + + val reportOutputPath = getReportOutputPath(repoRoot, filePath, reportFormat) if (!File(repoRoot, filePath).exists()) { error("File doesn't exist: $filePath.") @@ -45,7 +55,14 @@ fun main(vararg args: String) { scriptBgDispatcher, processTimeout = processTimeout, processTimeoutUnit = TimeUnit.MINUTES ) - RunCoverage(repoRoot, filePath, commandExecutor, scriptBgDispatcher).execute() + RunCoverage( + repoRoot, + filePath, + reportFormat, + reportOutputPath, + commandExecutor, + scriptBgDispatcher + ).execute() } } @@ -60,6 +77,8 @@ fun main(vararg args: String) { class RunCoverage( private val repoRoot: String, private val filePath: String, + private val reportFormat: ReportFormat, + private val reportOutputPath: String, private val commandExecutor: CommandExecutor, private val scriptBgDispatcher: ScriptBackgroundCoroutineDispatcher ) { @@ -79,15 +98,14 @@ class RunCoverage( * @return a list of lists containing coverage data for each requested test target, if * the file is exempted from having a test file, an empty list is returned */ - fun execute(): List { + fun execute(): String { val testFileExemptionList = loadTestFileExemptionsProto(testFileExemptionTextProto) .testFileExemptionList .filter { it.testFileNotRequired } .map { it.exemptedFilePath } if (filePath in testFileExemptionList) { - println("This file is exempted from having a test file; skipping coverage check.") - return emptyList() + return "This file is exempted from having a test file; skipping coverage check." } val testFilePaths = findTestFile(repoRoot, filePath) @@ -97,9 +115,26 @@ class RunCoverage( val testTargets = bazelClient.retrieveBazelTargets(testFilePaths) - return testTargets.mapNotNull { testTarget -> + val coverageReports = testTargets.mapNotNull { testTarget -> runCoverageForTarget(testTarget) } + + coverageReports.takeIf { it.isNotEmpty() }?.run { + val reporter = CoverageReporter(repoRoot, this, reportFormat) + val (computedCoverageRatio, reportText) = reporter.generateRichTextReport() + + File(reportOutputPath).apply { + parentFile?.mkdirs() + writeText(reportText) + } + + if (File(reportOutputPath).exists()) { + println("\nComputed Coverage Ratio is: $computedCoverageRatio") + println("\nGenerated report at: $reportOutputPath\n") + } + } ?: println("No coverage reports generated.") + + return reportOutputPath } private fun runCoverageForTarget(testTarget: String): CoverageReport { @@ -136,6 +171,19 @@ private fun findTestFile(repoRoot: String, filePath: String): List { .map { it.relativeTo(repoRootFile).path } } +private fun getReportOutputPath( + repoRoot: String, + filePath: String, + reportFormat: ReportFormat +): String { + val fileWithoutExtension = filePath.substringBeforeLast(".") + val defaultFilename = when (reportFormat) { + ReportFormat.HTML -> "coverage.html" + ReportFormat.MARKDOWN -> "coverage.md" + } + return "$repoRoot/coverage_reports/$fileWithoutExtension/$defaultFilename" +} + private fun loadTestFileExemptionsProto(testFileExemptiontextProto: String): TestFileExemptions { return File("$testFileExemptiontextProto.pb").inputStream().use { stream -> TestFileExemptions.newBuilder().also { builder -> diff --git a/scripts/src/javatests/org/oppia/android/scripts/coverage/BUILD.bazel b/scripts/src/javatests/org/oppia/android/scripts/coverage/BUILD.bazel index 6c200e83d8a..d67bf159ebe 100644 --- a/scripts/src/javatests/org/oppia/android/scripts/coverage/BUILD.bazel +++ b/scripts/src/javatests/org/oppia/android/scripts/coverage/BUILD.bazel @@ -4,6 +4,19 @@ Tests corresponding to developer scripts that help with obtaining coverage data load("@io_bazel_rules_kotlin//kotlin:jvm.bzl", "kt_jvm_test") +kt_jvm_test( + name = "RunCoverageTest", + srcs = ["RunCoverageTest.kt"], + deps = [ + "//scripts:test_file_check_assets", + "//scripts/src/java/org/oppia/android/scripts/coverage:run_coverage_lib", + "//scripts/src/java/org/oppia/android/scripts/testing:test_bazel_workspace", + "//testing:assertion_helpers", + "//third_party:com_google_truth_truth", + "//third_party:org_jetbrains_kotlin_kotlin-test-junit", + ], +) + kt_jvm_test( name = "CoverageRunnerTest", srcs = ["CoverageRunnerTest.kt"], @@ -17,12 +30,10 @@ kt_jvm_test( ) kt_jvm_test( - name = "RunCoverageTest", - srcs = ["RunCoverageTest.kt"], + name = "CoverageReporterTest", + srcs = ["CoverageReporterTest.kt"], deps = [ - "//scripts:test_file_check_assets", - "//scripts/src/java/org/oppia/android/scripts/coverage:run_coverage_lib", - "//scripts/src/java/org/oppia/android/scripts/testing:test_bazel_workspace", + "//scripts/src/java/org/oppia/android/scripts/coverage:coverage_reporter", "//testing:assertion_helpers", "//third_party:com_google_truth_truth", "//third_party:org_jetbrains_kotlin_kotlin-test-junit", diff --git a/scripts/src/javatests/org/oppia/android/scripts/coverage/CoverageReporterTest.kt b/scripts/src/javatests/org/oppia/android/scripts/coverage/CoverageReporterTest.kt new file mode 100644 index 00000000000..67db3ebb1a4 --- /dev/null +++ b/scripts/src/javatests/org/oppia/android/scripts/coverage/CoverageReporterTest.kt @@ -0,0 +1,266 @@ +package org.oppia.android.scripts.coverage + +import com.google.common.truth.Truth.assertThat +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.junit.rules.TemporaryFolder +import org.oppia.android.scripts.proto.CoverageReport + +class CoverageReporterTest { + @field:[Rule JvmField] val tempFolder = TemporaryFolder() + + private lateinit var reporter: CoverageReporter + private lateinit var validCoverageReport: CoverageReport + private val emptyCoverageReportList = listOf() + + @Before + fun setUp() { + validCoverageReport = CoverageReport.newBuilder() + .setFilePath("SampleFile.kt") + .setLinesFound(10) + .setLinesHit(8) + .build() + } + + @Test + fun testCoverageReporter_validData_generatesCorrectCoverageRatio() { + reporter = CoverageReporter( + tempFolder.root.absolutePath, + listOf(validCoverageReport), + ReportFormat.MARKDOWN + ) + val expectedCoverageRatio = 0.8F + val (coverageRatio, _) = reporter.generateRichTextReport() + assertThat(expectedCoverageRatio).isEqualTo(coverageRatio) + } + + @Test + fun testCoverageReporter_noLinesFound_generatesZeroCoverageRatio() { + val expectedZeroCoverageRatio = 0F + // to check divided by zero error doesn't occur + val report = validCoverageReport.toBuilder().setLinesFound(0).build() + reporter = CoverageReporter( + tempFolder.root.absolutePath, + listOf(report), + ReportFormat.MARKDOWN + ) + val (coverageRatio, _) = reporter.generateRichTextReport() + assertThat(expectedZeroCoverageRatio).isEqualTo(coverageRatio) + } + + @Test + fun testCoverageReporter_generateMarkdownReport_hasCorrectContentAndFormatting() { + reporter = CoverageReporter( + tempFolder.root.absolutePath, + listOf(validCoverageReport), + ReportFormat.MARKDOWN + ) + val (_, reportText) = reporter.generateRichTextReport() + + val expectedMarkdown = + """ + ## Coverage Report + + - **Covered File:** SampleFile.kt + - **Coverage percentage:** 80.00% covered + - **Line coverage:** 8 / 10 lines covered + """.trimIndent() + + assertThat(reportText).isEqualTo(expectedMarkdown) + } + + @Test + fun testCoverageReporter_generateHTMLReport_hasCorrectContentAndFormatting() { + val sourceFile = tempFolder.newFile("SampleFile.kt") + sourceFile.writeText( + """ + fun main() { + println("Hello, World!") + val x = 10 + val y = 20 + val sum = x + y + println("Sum: 30") + for (i in 1..10) { + println(i) + } + } + """.trimIndent() + ) + + reporter = CoverageReporter( + tempFolder.root.absolutePath, + listOf(validCoverageReport), + ReportFormat.HTML + ) + val (_, reportText) = reporter.generateRichTextReport() + + val expectedHTML = + """ + + + + + + Coverage Report + + + +

Coverage Report

+
+
+ Covered File: SampleFile.kt
+
+
+ Covered +
+ Uncovered +
+
+
+
Coverage percentage: 80.00%
+
Line coverage: 8 / 10 covered
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Line NoSource Code
1 fun main() {
2 println("Hello, World!")
3 val x = 10
4 val y = 20
5 val sum = x + y
6 println("Sum: 30")
7 for (i in 1..10) {
8 println(i)
9 }
10}
+ + + """.trimIndent() + + assertThat(reportText).isEqualTo(expectedHTML) + } +} diff --git a/scripts/src/javatests/org/oppia/android/scripts/coverage/CoverageRunnerTest.kt b/scripts/src/javatests/org/oppia/android/scripts/coverage/CoverageRunnerTest.kt index 7d795703912..68fd8e7e3e6 100644 --- a/scripts/src/javatests/org/oppia/android/scripts/coverage/CoverageRunnerTest.kt +++ b/scripts/src/javatests/org/oppia/android/scripts/coverage/CoverageRunnerTest.kt @@ -76,7 +76,7 @@ class CoverageRunnerTest { companion object { fun sumNumbers(a: Int, b: Int): Any { - return if (a ==0 && b == 0) { + return if (a == 0 && b == 0) { "Both numbers are zero" } else { a + b @@ -122,7 +122,7 @@ class CoverageRunnerTest { val expectedResult = CoverageReport.newBuilder() .setBazelTestTarget("//coverage/test/java/com/example:TwoSumTest") .setFilePath("coverage/main/java/com/example/TwoSum.kt") - .setFileSha1Hash("f6fb075e115775f6729615a79f0e7e34fe9735b5") + .setFileSha1Hash("1020b8f405555b3f4537fd07b912d3fb9ffa3354") .addCoveredLine( CoveredLine.newBuilder() .setLineNumber(3) diff --git a/scripts/src/javatests/org/oppia/android/scripts/coverage/RunCoverageTest.kt b/scripts/src/javatests/org/oppia/android/scripts/coverage/RunCoverageTest.kt index 8c03e97a5c4..b00415fcc31 100644 --- a/scripts/src/javatests/org/oppia/android/scripts/coverage/RunCoverageTest.kt +++ b/scripts/src/javatests/org/oppia/android/scripts/coverage/RunCoverageTest.kt @@ -8,9 +8,6 @@ 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.proto.Coverage -import org.oppia.android.scripts.proto.CoverageReport -import org.oppia.android.scripts.proto.CoveredLine import org.oppia.android.scripts.testing.TestBazelWorkspace import org.oppia.android.testing.assertThrows import java.io.ByteArrayOutputStream @@ -31,10 +28,14 @@ class RunCoverageTest { private lateinit var testBazelWorkspace: TestBazelWorkspace private lateinit var sampleFilePath: String + private lateinit var sampleMDOutputPath: String + private lateinit var sampleHTMLOutputPath: String @Before fun setUp() { sampleFilePath = "/path/to/Sample.kt" + sampleMDOutputPath = "${tempFolder.root}/coverage_reports/report.md" + sampleHTMLOutputPath = "${tempFolder.root}/coverage_reports/report.html" testBazelWorkspace = TestBazelWorkspace(tempFolder) System.setOut(PrintStream(outContent)) } @@ -71,19 +72,22 @@ class RunCoverageTest { fun testRunCoverage_testFileExempted_noCoverage() { val exemptedFilePath = "app/src/main/java/org/oppia/android/app/activity/ActivityComponent.kt" - RunCoverage( + val result = RunCoverage( "${tempFolder.root}", exemptedFilePath, + ReportFormat.MARKDOWN, + sampleMDOutputPath, commandExecutor, scriptBgDispatcher ).execute() - assertThat(outContent.toString()) - .isEqualTo("This file is exempted from having a test file; skipping coverage check.\n") + assertThat(result).isEqualTo( + "This file is exempted from having a test file; skipping coverage check." + ) } @Test - fun testRunCoverage_sampleTests_returnsCoverageData() { + fun testRunCoverage_sampleTestsDefaultFormat_returnsCoverageData() { testBazelWorkspace.initEmptyWorkspace() val sourceContent = @@ -94,7 +98,7 @@ class RunCoverageTest { companion object { fun sumNumbers(a: Int, b: Int): Any { - return if (a ==0 && b == 0) { + return if (a == 0 && b == 0) { "Both numbers are zero" } else { a + b @@ -131,52 +135,101 @@ class RunCoverageTest { testSubpackage = "coverage/test/java/com/example" ) - val result = RunCoverage( + main( "${tempFolder.root}", "coverage/main/java/com/example/TwoSum.kt", + ) + + val outputReportText = File( + "${tempFolder.root}/coverage_reports/coverage/main/java/com/example/TwoSum/coverage.md" + ).readText() + + val expectedResult = + """ + ## Coverage Report + + - **Covered File:** coverage/main/java/com/example/TwoSum.kt + - **Coverage percentage:** 75.00% covered + - **Line coverage:** 3 / 4 lines covered + """.trimIndent() + + assertThat(outputReportText).isEqualTo(expectedResult) + } + + @Test + fun testRunCoverage_sampleTestsMarkdownFormat_returnsCoverageData() { + testBazelWorkspace.initEmptyWorkspace() + + val sourceContent = + """ + package com.example + + class TwoSum { + + companion object { + fun sumNumbers(a: Int, b: Int): Any { + return if (a == 0 && b == 0) { + "Both numbers are zero" + } else { + a + b + } + } + } + } + """.trimIndent() + + val testContent = + """ + package com.example + + import org.junit.Assert.assertEquals + import org.junit.Test + + class TwoSumTest { + + @Test + fun testSumNumbers() { + assertEquals(TwoSum.sumNumbers(0, 1), 1) + assertEquals(TwoSum.sumNumbers(3, 4), 7) + assertEquals(TwoSum.sumNumbers(0, 0), "Both numbers are zero") + } + } + """.trimIndent() + + testBazelWorkspace.addSourceAndTestFileWithContent( + filename = "TwoSum", + testFilename = "TwoSumTest", + sourceContent = sourceContent, + testContent = testContent, + sourceSubpackage = "coverage/main/java/com/example", + testSubpackage = "coverage/test/java/com/example" + ) + + RunCoverage( + "${tempFolder.root}", + "coverage/main/java/com/example/TwoSum.kt", + ReportFormat.MARKDOWN, + sampleMDOutputPath, longCommandExecutor, scriptBgDispatcher ).execute() - val expectedResult = listOf( - CoverageReport.newBuilder() - .setBazelTestTarget("//coverage/test/java/com/example:TwoSumTest") - .setFilePath("coverage/main/java/com/example/TwoSum.kt") - .setFileSha1Hash("f6fb075e115775f6729615a79f0e7e34fe9735b5") - .addCoveredLine( - CoveredLine.newBuilder() - .setLineNumber(3) - .setCoverage(Coverage.NONE) - .build() - ) - .addCoveredLine( - CoveredLine.newBuilder() - .setLineNumber(7) - .setCoverage(Coverage.FULL) - .build() - ) - .addCoveredLine( - CoveredLine.newBuilder() - .setLineNumber(8) - .setCoverage(Coverage.FULL) - .build() - ) - .addCoveredLine( - CoveredLine.newBuilder() - .setLineNumber(10) - .setCoverage(Coverage.FULL) - .build() - ) - .setLinesFound(4) - .setLinesHit(3) - .build() - ) + val outputReportText = File(sampleMDOutputPath).readText() - assertThat(result).isEqualTo(expectedResult) + val expectedResult = + """ + ## Coverage Report + + - **Covered File:** coverage/main/java/com/example/TwoSum.kt + - **Coverage percentage:** 75.00% covered + - **Line coverage:** 3 / 4 lines covered + """.trimIndent() + + assertThat(outputReportText).isEqualTo(expectedResult) } @Test - fun testRunCoverage_scriptTests_returnsCoverageData() { + fun testRunCoverage_scriptTestsMarkdownFormat_returnsCoverageData() { testBazelWorkspace.initEmptyWorkspace() val sourceContent = @@ -224,52 +277,31 @@ class RunCoverageTest { testSubpackage = "scripts/javatests/com/example" ) - val result = RunCoverage( + RunCoverage( "${tempFolder.root}", "scripts/java/com/example/TwoSum.kt", + ReportFormat.MARKDOWN, + sampleMDOutputPath, longCommandExecutor, scriptBgDispatcher ).execute() - val expectedResult = listOf( - CoverageReport.newBuilder() - .setBazelTestTarget("//scripts/javatests/com/example:TwoSumTest") - .setFilePath("scripts/java/com/example/TwoSum.kt") - .setFileSha1Hash("1020b8f405555b3f4537fd07b912d3fb9ffa3354") - .addCoveredLine( - CoveredLine.newBuilder() - .setLineNumber(3) - .setCoverage(Coverage.NONE) - .build() - ) - .addCoveredLine( - CoveredLine.newBuilder() - .setLineNumber(7) - .setCoverage(Coverage.FULL) - .build() - ) - .addCoveredLine( - CoveredLine.newBuilder() - .setLineNumber(8) - .setCoverage(Coverage.FULL) - .build() - ) - .addCoveredLine( - CoveredLine.newBuilder() - .setLineNumber(10) - .setCoverage(Coverage.FULL) - .build() - ) - .setLinesFound(4) - .setLinesHit(3) - .build() - ) + val outputReportText = File(sampleMDOutputPath).readText() - assertThat(result).isEqualTo(expectedResult) + val expectedResult = + """ + ## Coverage Report + + - **Covered File:** scripts/java/com/example/TwoSum.kt + - **Coverage percentage:** 75.00% covered + - **Line coverage:** 3 / 4 lines covered + """.trimIndent() + + assertThat(outputReportText).isEqualTo(expectedResult) } @Test - fun testRunCoverage_appTests_returnsCoverageData() { + fun testRunCoverage_appTestsMarkdownFormat_returnsCoverageData() { testBazelWorkspace.initEmptyWorkspace() val sourceContent = @@ -280,7 +312,7 @@ class RunCoverageTest { companion object { fun sumNumbers(a: Int, b: Int): Any { - return if (a ==0 && b == 0) { + return if (a == 0 && b == 0) { "Both numbers are zero" } else { a + b @@ -317,52 +349,31 @@ class RunCoverageTest { testSubpackage = "app/test/java/com/example" ) - val result = RunCoverage( + RunCoverage( "${tempFolder.root}", "app/main/java/com/example/TwoSum.kt", + ReportFormat.MARKDOWN, + sampleMDOutputPath, longCommandExecutor, scriptBgDispatcher ).execute() - val expectedResult = listOf( - CoverageReport.newBuilder() - .setBazelTestTarget("//app/test/java/com/example:TwoSumTest") - .setFilePath("app/main/java/com/example/TwoSum.kt") - .setFileSha1Hash("f6fb075e115775f6729615a79f0e7e34fe9735b5") - .addCoveredLine( - CoveredLine.newBuilder() - .setLineNumber(3) - .setCoverage(Coverage.NONE) - .build() - ) - .addCoveredLine( - CoveredLine.newBuilder() - .setLineNumber(7) - .setCoverage(Coverage.FULL) - .build() - ) - .addCoveredLine( - CoveredLine.newBuilder() - .setLineNumber(8) - .setCoverage(Coverage.FULL) - .build() - ) - .addCoveredLine( - CoveredLine.newBuilder() - .setLineNumber(10) - .setCoverage(Coverage.FULL) - .build() - ) - .setLinesFound(4) - .setLinesHit(3) - .build() - ) + val outputReportText = File(sampleMDOutputPath).readText() + + val expectedResult = + """ + ## Coverage Report + + - **Covered File:** app/main/java/com/example/TwoSum.kt + - **Coverage percentage:** 75.00% covered + - **Line coverage:** 3 / 4 lines covered + """.trimIndent() - assertThat(result).isEqualTo(expectedResult) + assertThat(outputReportText).isEqualTo(expectedResult) } @Test - fun testRunCoverage_localTests_returnsCoverageData() { + fun testRunCoverage_localTestsMarkdownFormat_returnsCoverageData() { testBazelWorkspace.initEmptyWorkspace() val sourceContent = @@ -373,7 +384,7 @@ class RunCoverageTest { companion object { fun sumNumbers(a: Int, b: Int): Any { - return if (a ==0 && b == 0) { + return if (a == 0 && b == 0) { "Both numbers are zero" } else { a + b @@ -410,52 +421,31 @@ class RunCoverageTest { testSubpackage = "app/test/java/com/example" ) - val result = RunCoverage( + RunCoverage( "${tempFolder.root}", "app/main/java/com/example/TwoSum.kt", + ReportFormat.MARKDOWN, + sampleMDOutputPath, longCommandExecutor, scriptBgDispatcher ).execute() - val expectedResult = listOf( - CoverageReport.newBuilder() - .setBazelTestTarget("//app/test/java/com/example:TwoSumLocalTest") - .setFilePath("app/main/java/com/example/TwoSum.kt") - .setFileSha1Hash("f6fb075e115775f6729615a79f0e7e34fe9735b5") - .addCoveredLine( - CoveredLine.newBuilder() - .setLineNumber(3) - .setCoverage(Coverage.NONE) - .build() - ) - .addCoveredLine( - CoveredLine.newBuilder() - .setLineNumber(7) - .setCoverage(Coverage.FULL) - .build() - ) - .addCoveredLine( - CoveredLine.newBuilder() - .setLineNumber(8) - .setCoverage(Coverage.FULL) - .build() - ) - .addCoveredLine( - CoveredLine.newBuilder() - .setLineNumber(10) - .setCoverage(Coverage.FULL) - .build() - ) - .setLinesFound(4) - .setLinesHit(3) - .build() - ) + val outputReportText = File(sampleMDOutputPath).readText() + + val expectedResult = + """ + ## Coverage Report + + - **Covered File:** app/main/java/com/example/TwoSum.kt + - **Coverage percentage:** 75.00% covered + - **Line coverage:** 3 / 4 lines covered + """.trimIndent() - assertThat(result).isEqualTo(expectedResult) + assertThat(outputReportText).isEqualTo(expectedResult) } @Test - fun testRunCoverage_sharedTests_returnsCoverageData() { + fun testRunCoverage_sharedTestsMarkdownFormat_returnsCoverageData() { testBazelWorkspace.initEmptyWorkspace() val sourceContent = @@ -466,7 +456,7 @@ class RunCoverageTest { companion object { fun sumNumbers(a: Int, b: Int): Any { - return if (a ==0 && b == 0) { + return if (a == 0 && b == 0) { "Both numbers are zero" } else { a + b @@ -503,52 +493,31 @@ class RunCoverageTest { testSubpackage = "app/sharedTest/java/com/example" ) - val result = RunCoverage( + RunCoverage( "${tempFolder.root}", "app/main/java/com/example/TwoSum.kt", + ReportFormat.MARKDOWN, + sampleMDOutputPath, longCommandExecutor, scriptBgDispatcher ).execute() - val expectedResult = listOf( - CoverageReport.newBuilder() - .setBazelTestTarget("//app/sharedTest/java/com/example:TwoSumTest") - .setFilePath("app/main/java/com/example/TwoSum.kt") - .setFileSha1Hash("f6fb075e115775f6729615a79f0e7e34fe9735b5") - .addCoveredLine( - CoveredLine.newBuilder() - .setLineNumber(3) - .setCoverage(Coverage.NONE) - .build() - ) - .addCoveredLine( - CoveredLine.newBuilder() - .setLineNumber(7) - .setCoverage(Coverage.FULL) - .build() - ) - .addCoveredLine( - CoveredLine.newBuilder() - .setLineNumber(8) - .setCoverage(Coverage.FULL) - .build() - ) - .addCoveredLine( - CoveredLine.newBuilder() - .setLineNumber(10) - .setCoverage(Coverage.FULL) - .build() - ) - .setLinesFound(4) - .setLinesHit(3) - .build() - ) + val outputReportText = File(sampleMDOutputPath).readText() + + val expectedResult = + """ + ## Coverage Report + + - **Covered File:** app/main/java/com/example/TwoSum.kt + - **Coverage percentage:** 75.00% covered + - **Line coverage:** 3 / 4 lines covered + """.trimIndent() - assertThat(result).isEqualTo(expectedResult) + assertThat(outputReportText).isEqualTo(expectedResult) } @Test - fun testRunCoverage_sharedAndLocalTests_returnsCoverageData() { + fun testRunCoverage_sharedAndLocalTestsMarkdownFormat_returnsCoverageData() { testBazelWorkspace.initEmptyWorkspace() val sourceContent = @@ -559,7 +528,7 @@ class RunCoverageTest { companion object { fun sumNumbers(a: Int, b: Int): Any { - return if (a ==0 && b == 0) { + return if (a == 0 && b == 0) { "Both numbers are zero" } else { a + b @@ -613,79 +582,1490 @@ class RunCoverageTest { subpackage = "app" ) - val result = RunCoverage( + RunCoverage( "${tempFolder.root}", "app/main/java/com/example/TwoSum.kt", + ReportFormat.MARKDOWN, + sampleMDOutputPath, + longCommandExecutor, + scriptBgDispatcher + ).execute() + + val outputReportText = File(sampleMDOutputPath).readText() + + val expectedResult = + """ + ## Coverage Report + + - **Covered File:** app/main/java/com/example/TwoSum.kt + - **Coverage percentage:** 75.00% covered + - **Line coverage:** 3 / 4 lines covered + """.trimIndent() + + assertThat(outputReportText).isEqualTo(expectedResult) + } + + @Test + fun testRunCoverage_sampleTestsHTMLFormat_returnsCoverageData() { + testBazelWorkspace.initEmptyWorkspace() + + val sourceContent = + """ + package com.example + + class TwoSum { + + companion object { + fun sumNumbers(a: Int, b: Int): Any { + return if (a == 0 && b == 0) { + "Both numbers are zero" + } else { + a + b + } + } + } + } + """.trimIndent() + + val testContent = + """ + package com.example + + import org.junit.Assert.assertEquals + import org.junit.Test + + class TwoSumTest { + + @Test + fun testSumNumbers() { + assertEquals(TwoSum.sumNumbers(0, 1), 1) + assertEquals(TwoSum.sumNumbers(3, 4), 7) + assertEquals(TwoSum.sumNumbers(0, 0), "Both numbers are zero") + } + } + """.trimIndent() + + testBazelWorkspace.addSourceAndTestFileWithContent( + filename = "TwoSum", + testFilename = "TwoSumTest", + sourceContent = sourceContent, + testContent = testContent, + sourceSubpackage = "coverage/main/java/com/example", + testSubpackage = "coverage/test/java/com/example" + ) + + RunCoverage( + "${tempFolder.root}", + "coverage/main/java/com/example/TwoSum.kt", + ReportFormat.HTML, + sampleHTMLOutputPath, longCommandExecutor, scriptBgDispatcher ).execute() - val expectedResult = listOf( - CoverageReport.newBuilder() - .setBazelTestTarget("//app/sharedTest/java/com/example:TwoSumTest") - .setFilePath("app/main/java/com/example/TwoSum.kt") - .setFileSha1Hash("f6fb075e115775f6729615a79f0e7e34fe9735b5") - .addCoveredLine( - CoveredLine.newBuilder() - .setLineNumber(3) - .setCoverage(Coverage.NONE) - .build() - ) - .addCoveredLine( - CoveredLine.newBuilder() - .setLineNumber(7) - .setCoverage(Coverage.FULL) - .build() - ) - .addCoveredLine( - CoveredLine.newBuilder() - .setLineNumber(8) - .setCoverage(Coverage.FULL) - .build() - ) - .addCoveredLine( - CoveredLine.newBuilder() - .setLineNumber(10) - .setCoverage(Coverage.FULL) - .build() - ) - .setLinesFound(4) - .setLinesHit(3) - .build(), - CoverageReport.newBuilder() - .setBazelTestTarget("//app/test/java/com/example:TwoSumLocalTest") - .setFilePath("app/main/java/com/example/TwoSum.kt") - .setFileSha1Hash("f6fb075e115775f6729615a79f0e7e34fe9735b5") - .addCoveredLine( - CoveredLine.newBuilder() - .setLineNumber(3) - .setCoverage(Coverage.NONE) - .build() - ) - .addCoveredLine( - CoveredLine.newBuilder() - .setLineNumber(7) - .setCoverage(Coverage.FULL) - .build() - ) - .addCoveredLine( - CoveredLine.newBuilder() - .setLineNumber(8) - .setCoverage(Coverage.FULL) - .build() - ) - .addCoveredLine( - CoveredLine.newBuilder() - .setLineNumber(10) - .setCoverage(Coverage.FULL) - .build() - ) - .setLinesFound(4) - .setLinesHit(3) - .build() + val outputReportText = File(sampleHTMLOutputPath).readText() + + val expectedResult = + """ + + + + + + Coverage Report + + + +

Coverage Report

+
+
+ Covered File: coverage/main/java/com/example/TwoSum.kt
+
+
+ Covered +
+ Uncovered +
+
+
+
Coverage percentage: 75.00%
+
Line coverage: 3 / 4 covered
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Line NoSource Code
1package com.example
2
3class TwoSum {
4
5 companion object {
6 fun sumNumbers(a: Int, b: Int): Any {
7 return if (a == 0 && b == 0) {
8 "Both numbers are zero"
9 } else {
10 a + b
11 }
12 }
13 }
14}
+ + + """.trimIndent() + + assertThat(outputReportText).isEqualTo(expectedResult) + } + + @Test + fun testRunCoverage_scriptTestsHTMLFormat_returnsCoverageData() { + testBazelWorkspace.initEmptyWorkspace() + + val sourceContent = + """ + package com.example + + class TwoSum { + + companion object { + fun sumNumbers(a: Int, b: Int): Any { + return if (a == 0 && b == 0) { + "Both numbers are zero" + } else { + a + b + } + } + } + } + """.trimIndent() + + val testContent = + """ + package com.example + + import org.junit.Assert.assertEquals + import org.junit.Test + + class TwoSumTest { + + @Test + fun testSumNumbers() { + assertEquals(TwoSum.sumNumbers(0, 1), 1) + assertEquals(TwoSum.sumNumbers(3, 4), 7) + assertEquals(TwoSum.sumNumbers(0, 0), "Both numbers are zero") + } + } + """.trimIndent() + + testBazelWorkspace.addSourceAndTestFileWithContent( + filename = "TwoSum", + testFilename = "TwoSumTest", + sourceContent = sourceContent, + testContent = testContent, + sourceSubpackage = "scripts/java/com/example", + testSubpackage = "scripts/javatests/com/example" ) - assertThat(result).isEqualTo(expectedResult) + RunCoverage( + "${tempFolder.root}", + "scripts/java/com/example/TwoSum.kt", + ReportFormat.HTML, + sampleHTMLOutputPath, + longCommandExecutor, + scriptBgDispatcher + ).execute() + + val outputReportText = File(sampleHTMLOutputPath).readText() + + val expectedResult = + """ + + + + + + Coverage Report + + + +

Coverage Report

+
+
+ Covered File: scripts/java/com/example/TwoSum.kt
+
+
+ Covered +
+ Uncovered +
+
+
+
Coverage percentage: 75.00%
+
Line coverage: 3 / 4 covered
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Line NoSource Code
1package com.example
2
3class TwoSum {
4
5 companion object {
6 fun sumNumbers(a: Int, b: Int): Any {
7 return if (a == 0 && b == 0) {
8 "Both numbers are zero"
9 } else {
10 a + b
11 }
12 }
13 }
14}
+ + + """.trimIndent() + + assertThat(outputReportText).isEqualTo(expectedResult) + } + + @Test + fun testRunCoverage_appTestsHTMLFormat_returnsCoverageData() { + testBazelWorkspace.initEmptyWorkspace() + + val sourceContent = + """ + package com.example + + class TwoSum { + + companion object { + fun sumNumbers(a: Int, b: Int): Any { + return if (a == 0 && b == 0) { + "Both numbers are zero" + } else { + a + b + } + } + } + } + """.trimIndent() + + val testContent = + """ + package com.example + + import org.junit.Assert.assertEquals + import org.junit.Test + + class TwoSumTest { + + @Test + fun testSumNumbers() { + assertEquals(TwoSum.sumNumbers(0, 1), 1) + assertEquals(TwoSum.sumNumbers(3, 4), 7) + assertEquals(TwoSum.sumNumbers(0, 0), "Both numbers are zero") + } + } + """.trimIndent() + + testBazelWorkspace.addSourceAndTestFileWithContent( + filename = "TwoSum", + testFilename = "TwoSumTest", + sourceContent = sourceContent, + testContent = testContent, + sourceSubpackage = "app/main/java/com/example", + testSubpackage = "app/test/java/com/example" + ) + + RunCoverage( + "${tempFolder.root}", + "app/main/java/com/example/TwoSum.kt", + ReportFormat.HTML, + sampleHTMLOutputPath, + longCommandExecutor, + scriptBgDispatcher + ).execute() + + val outputReportText = File(sampleHTMLOutputPath).readText() + + val expectedResult = + """ + + + + + + Coverage Report + + + +

Coverage Report

+
+
+ Covered File: app/main/java/com/example/TwoSum.kt
+
+
+ Covered +
+ Uncovered +
+
+
+
Coverage percentage: 75.00%
+
Line coverage: 3 / 4 covered
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Line NoSource Code
1package com.example
2
3class TwoSum {
4
5 companion object {
6 fun sumNumbers(a: Int, b: Int): Any {
7 return if (a == 0 && b == 0) {
8 "Both numbers are zero"
9 } else {
10 a + b
11 }
12 }
13 }
14}
+ + + """.trimIndent() + + assertThat(outputReportText).isEqualTo(expectedResult) + } + + @Test + fun testRunCoverage_localTestsHTMLFormat_returnsCoverageData() { + testBazelWorkspace.initEmptyWorkspace() + + val sourceContent = + """ + package com.example + + class TwoSum { + + companion object { + fun sumNumbers(a: Int, b: Int): Any { + return if (a == 0 && b == 0) { + "Both numbers are zero" + } else { + a + b + } + } + } + } + """.trimIndent() + + val testContent = + """ + package com.example + + import org.junit.Assert.assertEquals + import org.junit.Test + + class TwoSumLocalTest { + + @Test + fun testSumNumbers() { + assertEquals(TwoSum.sumNumbers(0, 1), 1) + assertEquals(TwoSum.sumNumbers(3, 4), 7) + assertEquals(TwoSum.sumNumbers(0, 0), "Both numbers are zero") + } + } + """.trimIndent() + + testBazelWorkspace.addSourceAndTestFileWithContent( + filename = "TwoSum", + testFilename = "TwoSumLocalTest", + sourceContent = sourceContent, + testContent = testContent, + sourceSubpackage = "app/main/java/com/example", + testSubpackage = "app/test/java/com/example" + ) + + RunCoverage( + "${tempFolder.root}", + "app/main/java/com/example/TwoSum.kt", + ReportFormat.HTML, + sampleHTMLOutputPath, + longCommandExecutor, + scriptBgDispatcher + ).execute() + + val outputReportText = File(sampleHTMLOutputPath).readText() + + val expectedResult = + """ + + + + + + Coverage Report + + + +

Coverage Report

+
+
+ Covered File: app/main/java/com/example/TwoSum.kt
+
+
+ Covered +
+ Uncovered +
+
+
+
Coverage percentage: 75.00%
+
Line coverage: 3 / 4 covered
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Line NoSource Code
1package com.example
2
3class TwoSum {
4
5 companion object {
6 fun sumNumbers(a: Int, b: Int): Any {
7 return if (a == 0 && b == 0) {
8 "Both numbers are zero"
9 } else {
10 a + b
11 }
12 }
13 }
14}
+ + + """.trimIndent() + + assertThat(outputReportText).isEqualTo(expectedResult) + } + + @Test + fun testRunCoverage_sharedTestsHTMLFormat_returnsCoverageData() { + testBazelWorkspace.initEmptyWorkspace() + + val sourceContent = + """ + package com.example + + class TwoSum { + + companion object { + fun sumNumbers(a: Int, b: Int): Any { + return if (a == 0 && b == 0) { + "Both numbers are zero" + } else { + a + b + } + } + } + } + """.trimIndent() + + val testContent = + """ + package com.example + + import org.junit.Assert.assertEquals + import org.junit.Test + + class TwoSumTest { + + @Test + fun testSumNumbers() { + assertEquals(TwoSum.sumNumbers(0, 1), 1) + assertEquals(TwoSum.sumNumbers(3, 4), 7) + assertEquals(TwoSum.sumNumbers(0, 0), "Both numbers are zero") + } + } + """.trimIndent() + + testBazelWorkspace.addSourceAndTestFileWithContent( + filename = "TwoSum", + testFilename = "TwoSumTest", + sourceContent = sourceContent, + testContent = testContent, + sourceSubpackage = "app/main/java/com/example", + testSubpackage = "app/sharedTest/java/com/example" + ) + + RunCoverage( + "${tempFolder.root}", + "app/main/java/com/example/TwoSum.kt", + ReportFormat.HTML, + sampleHTMLOutputPath, + longCommandExecutor, + scriptBgDispatcher + ).execute() + + val outputReportText = File(sampleHTMLOutputPath).readText() + + val expectedResult = + """ + + + + + + Coverage Report + + + +

Coverage Report

+
+
+ Covered File: app/main/java/com/example/TwoSum.kt
+
+
+ Covered +
+ Uncovered +
+
+
+
Coverage percentage: 75.00%
+
Line coverage: 3 / 4 covered
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Line NoSource Code
1package com.example
2
3class TwoSum {
4
5 companion object {
6 fun sumNumbers(a: Int, b: Int): Any {
7 return if (a == 0 && b == 0) {
8 "Both numbers are zero"
9 } else {
10 a + b
11 }
12 }
13 }
14}
+ + + """.trimIndent() + + assertThat(outputReportText).isEqualTo(expectedResult) + } + + @Test + fun testRunCoverage_sharedAndLocalTestsHTMLFormat_returnsCoverageData() { + testBazelWorkspace.initEmptyWorkspace() + + val sourceContent = + """ + package com.example + + class TwoSum { + + companion object { + fun sumNumbers(a: Int, b: Int): Any { + return if (a == 0 && b == 0) { + "Both numbers are zero" + } else { + a + b + } + } + } + } + """.trimIndent() + + val testContentShared = + """ + package com.example + + import org.junit.Assert.assertEquals + import org.junit.Test + + class TwoSumTest { + + @Test + fun testSumNumbers() { + assertEquals(TwoSum.sumNumbers(0, 1), 1) + assertEquals(TwoSum.sumNumbers(3, 4), 7) + assertEquals(TwoSum.sumNumbers(0, 0), "Both numbers are zero") + } + } + """.trimIndent() + + val testContentLocal = + """ + package com.example + + import org.junit.Assert.assertEquals + import org.junit.Test + + class TwoSumLocalTest { + + @Test + fun testSumNumbers() { + assertEquals(TwoSum.sumNumbers(0, 1), 1) + assertEquals(TwoSum.sumNumbers(3, 4), 7) + assertEquals(TwoSum.sumNumbers(0, 0), "Both numbers are zero") + } + } + """.trimIndent() + + testBazelWorkspace.addMultiLevelSourceAndTestFileWithContent( + filename = "TwoSum", + sourceContent = sourceContent, + testContentShared = testContentShared, + testContentLocal = testContentLocal, + subpackage = "app" + ) + + RunCoverage( + "${tempFolder.root}", + "app/main/java/com/example/TwoSum.kt", + ReportFormat.HTML, + sampleHTMLOutputPath, + longCommandExecutor, + scriptBgDispatcher + ).execute() + + val outputReportText = File(sampleHTMLOutputPath).readText() + + val expectedResult = + """ + + + + + + Coverage Report + + + +

Coverage Report

+
+
+ Covered File: app/main/java/com/example/TwoSum.kt
+
+
+ Covered +
+ Uncovered +
+
+
+
Coverage percentage: 75.00%
+
Line coverage: 3 / 4 covered
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Line NoSource Code
1package com.example
2
3class TwoSum {
4
5 companion object {
6 fun sumNumbers(a: Int, b: Int): Any {
7 return if (a == 0 && b == 0) {
8 "Both numbers are zero"
9 } else {
10 a + b
11 }
12 }
13 }
14}
+ + + """.trimIndent() + + assertThat(outputReportText).isEqualTo(expectedResult) } private fun initializeCommandExecutorWithLongProcessWaitTime(): CommandExecutorImpl {