Skip to content
This repository has been archived by the owner on Aug 5, 2024. It is now read-only.

Commit

Permalink
[feature] Fix task workflow
Browse files Browse the repository at this point in the history
  • Loading branch information
agluszak authored and qodana-bot committed Mar 28, 2024
1 parent e62f321 commit 73008c5
Show file tree
Hide file tree
Showing 11 changed files with 125 additions and 520 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,12 @@ import org.jetbrains.bsp.bazel.logger.BspClientLogger
class BazelProcess internal constructor(
private val process: Process,
private val logger: BspClientLogger? = null,
private val originId: String? = null,
) {

fun waitAndGetResult(cancelChecker: CancelChecker, ensureAllOutputRead: Boolean = false): BazelProcessResult {
val stopwatch = Stopwatch.start()
val outputProcessor: OutputProcessor =
if (logger!= null) {
if (logger != null) {
if (ensureAllOutputRead) SyncOutputProcessor(process, logger::message, LOGGER::info)
else AsyncOutputProcessor(process, logger::message, LOGGER::info)
} else {
Expand All @@ -34,8 +33,7 @@ class BazelProcess internal constructor(
}

private fun logCompletion(exitCode: Int, duration: Duration) {
logger?.copy(originId = originId)
?.message("Command completed in %s (exit code %d)", Format.duration(duration), exitCode)
logger?.message("Command completed in %s (exit code %d)", Format.duration(duration), exitCode)
}

companion object {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,13 +68,12 @@ class BazelRunner private constructor(
logInvocation(processArgs, environment, originId)
val processBuilder = ProcessBuilder(processArgs)
processBuilder.environment() += environment
val outputLogger = bspClientLogger.takeIf { parseProcessOutput }
val outputLogger = bspClientLogger.takeIf { parseProcessOutput }?.copy(originId = originId)
workspaceRoot?.let { processBuilder.directory(it.toFile()) }
val process = processBuilder.start()
return BazelProcess(
process,
if (originId == null) outputLogger else outputLogger?.copy(originId = originId),
originId
outputLogger
)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,35 +14,23 @@ import ch.epfl.scala.bsp4j.TestStart
import ch.epfl.scala.bsp4j.TestStatus
import ch.epfl.scala.bsp4j.TestTask

class BspClientTestNotifier(private val bspClient: BuildClient) {
class BspClientTestNotifier(private val bspClient: BuildClient, private val originId: String) {
/**
* Notifies the client about starting a single test or a test suite
*
* @param isSuite `true` if a test suite has been started, `false` if it was a single test instead
* @param displayName display name of the started test / test suite
* @param taskId TaskId of the started test / test suite
*/
fun startTest(isSuite: Boolean, displayName: String?, taskId: TaskId?) {
fun startTest(displayName: String?, taskId: TaskId) {
val testStart = TestStart(displayName)
val taskStartParams = TaskStartParams(taskId)
taskStartParams.originId = originId
taskStartParams.dataKind = TaskStartDataKind.TEST_START
taskStartParams.data = testStart
taskStartParams.message = if (isSuite) SUITE_TAG else TEST_TAG
bspClient.onBuildTaskStart(taskStartParams)
}

/**
* Notifies the client about finishing a test suite. Synonymous to:
* ```
* finishTest(true, displayName, taskId, TestStatus.PASSED, "")
* ```
*
* @param displayName display name of the finished test suite
* @param taskId TaskId if the finished test suite
*/
fun finishTestSuite(displayName: String?, taskId: TaskId?) {
finishTest(true, displayName, taskId, TestStatus.PASSED, "")
}

/**
* Notifies the client about finishing a single test or a test suite
Expand All @@ -54,13 +42,13 @@ class BspClientTestNotifier(private val bspClient: BuildClient) {
* @param status status of the performed test (does not matter for test suites)
* @param message additional message concerning the test execution
*/
fun finishTest(isSuite: Boolean, displayName: String?, taskId: TaskId?, status: TestStatus?, message: String?) {
fun finishTest(displayName: String?, taskId: TaskId, status: TestStatus?, message: String?) {
val testFinish = TestFinish(displayName, status)
testFinish.message = message
val taskFinishParams = TaskFinishParams(taskId, StatusCode.OK)
taskFinishParams.originId = originId
taskFinishParams.dataKind = TaskFinishDataKind.TEST_FINISH
taskFinishParams.data = testFinish
taskFinishParams.message = if (isSuite) SUITE_TAG else TEST_TAG
bspClient.onBuildTaskFinish(taskFinishParams)
}

Expand All @@ -70,7 +58,7 @@ class BspClientTestNotifier(private val bspClient: BuildClient) {
* @param targetIdentifier identifier of the testing target being executed
* @param taskId TaskId of the testing target execution
*/
fun beginTestTarget(targetIdentifier: BuildTargetIdentifier?, taskId: TaskId?) {
fun beginTestTarget(targetIdentifier: BuildTargetIdentifier?, taskId: TaskId) {
val testingBegin = TestTask(targetIdentifier)
val taskStartParams = TaskStartParams(taskId)
taskStartParams.dataKind = TaskStartDataKind.TEST_TASK
Expand All @@ -84,15 +72,10 @@ class BspClientTestNotifier(private val bspClient: BuildClient) {
* @param testReport report concerning conducted tests
* @param taskId TaskId of the testing target execution
*/
fun endTestTarget(testReport: TestReport?, taskId: TaskId?) {
fun endTestTarget(testReport: TestReport, taskId: TaskId) {
val taskFinishParams = TaskFinishParams(taskId, StatusCode.OK)
taskFinishParams.dataKind = TaskFinishDataKind.TEST_REPORT
taskFinishParams.data = testReport
bspClient.onBuildTaskFinish(taskFinishParams)
}

companion object {
private const val SUITE_TAG = "<S>"
private const val TEST_TAG = "<T>"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import org.jetbrains.bsp.bazel.bazelrunner.BazelInfoResolver
import org.jetbrains.bsp.bazel.bazelrunner.BazelInfoStorage
import org.jetbrains.bsp.bazel.bazelrunner.BazelRunner
import org.jetbrains.bsp.bazel.logger.BspClientLogger
import org.jetbrains.bsp.bazel.logger.BspClientTestNotifier
import org.jetbrains.bsp.bazel.server.bsp.BazelBspServerLifetime
import org.jetbrains.bsp.bazel.server.bsp.BazelServices
import org.jetbrains.bsp.bazel.server.bsp.BspIntegrationData
Expand Down Expand Up @@ -59,7 +58,6 @@ class BazelBspServer(

private fun bspServerData(
bspClientLogger: BspClientLogger,
bspClientTestNotifier: BspClientTestNotifier,
bazelRunner: BazelRunner,
compilationManager: BazelBspCompilationManager,
bazelInfo: BazelInfo,
Expand Down Expand Up @@ -95,7 +93,6 @@ class BazelBspServer(
bazelRunner = bazelRunner,
workspaceContextProvider = workspaceContextProvider,
bspClientLogger = bspClientLogger,
bspClientTestNotifier = bspClientTestNotifier,
bazelPathsResolver = bazelPathsResolver,
additionalBuildTargetsProvider = additionalBuildTargetsProvider,
hasAnyProblems = bspState,
Expand Down Expand Up @@ -193,10 +190,8 @@ class BazelBspServer(
val bazelPathsResolver = BazelPathsResolver(bazelInfo)
val compilationManager =
BazelBspCompilationManager(bazelRunner, bazelPathsResolver, bspState, client, workspaceRoot)
val bspClientTestNotifier = BspClientTestNotifier(client)
bspServerData(
bspClientLogger,
bspClientTestNotifier,
bazelRunner,
compilationManager,
bazelInfo,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,14 @@ import ch.epfl.scala.bsp4j.BuildTargetIdentifier
import ch.epfl.scala.bsp4j.CompileReport
import ch.epfl.scala.bsp4j.CompileTask
import ch.epfl.scala.bsp4j.PublishDiagnosticsParams
import ch.epfl.scala.bsp4j.StatusCode
import ch.epfl.scala.bsp4j.TaskFinishDataKind
import ch.epfl.scala.bsp4j.TaskFinishParams
import ch.epfl.scala.bsp4j.TaskId
import ch.epfl.scala.bsp4j.TaskStartDataKind
import ch.epfl.scala.bsp4j.TaskStartParams
import ch.epfl.scala.bsp4j.TestReport
import ch.epfl.scala.bsp4j.TestStatus
import com.google.devtools.build.lib.buildeventstream.BuildEventStreamProtos
import com.google.devtools.build.v1.BuildEvent
import com.google.devtools.build.v1.PublishBuildEventGrpc
Expand All @@ -23,6 +26,7 @@ import org.apache.logging.log4j.Logger
import org.jetbrains.bsp.bazel.commons.Constants
import org.jetbrains.bsp.bazel.commons.ExitCodeMapper
import org.jetbrains.bsp.bazel.logger.BspClientLogger
import org.jetbrains.bsp.bazel.logger.BspClientTestNotifier
import org.jetbrains.bsp.bazel.server.diagnostics.DiagnosticsService
import org.jetbrains.bsp.bazel.server.paths.BazelPathsResolver
import java.io.IOException
Expand Down Expand Up @@ -82,6 +86,67 @@ class BepServer(
fetchNamedSet(event)
processCompletedEvent(event)
processAbortedEvent(event)
processTestResult(event)
processTestSummary(event)
}

private fun processTestResult(event: BuildEventStreamProtos.BuildEvent) {
if (event.hasTestResult()) {
if (originId == null) {
return
}

val bspClientTestNotifier = BspClientTestNotifier(bspClient, originId)

val testResult = event.testResult

val parentId = TaskId(UUID.randomUUID().toString())
val childId = TaskId(UUID.randomUUID().toString())
childId.parents = listOf(parentId.id)

bspClientTestNotifier.beginTestTarget(target, parentId)

// TODO: this is the place where we could parse the test result and produce individual test events
// TODO: there's some other interesting data
// If testing is requested, a TestResult event is sent for each test attempt,
// shard, and run per test. This allows BEP consumers to identify precisely
// which test actions failed their tests and identify the test outputs
// (such as logs, test.xml files) for each test action.

val testStatus = when (testResult.status) {
BuildEventStreamProtos.TestStatus.NO_STATUS -> TestStatus.SKIPPED
BuildEventStreamProtos.TestStatus.PASSED -> TestStatus.PASSED
BuildEventStreamProtos.TestStatus.FLAKY -> TestStatus.FAILED
BuildEventStreamProtos.TestStatus.TIMEOUT -> TestStatus.FAILED
BuildEventStreamProtos.TestStatus.FAILED -> TestStatus.FAILED
BuildEventStreamProtos.TestStatus.INCOMPLETE -> TestStatus.SKIPPED
BuildEventStreamProtos.TestStatus.REMOTE_FAILURE -> TestStatus.IGNORED
BuildEventStreamProtos.TestStatus.FAILED_TO_BUILD -> TestStatus.CANCELLED
BuildEventStreamProtos.TestStatus.TOOL_HALTED_BEFORE_TESTING -> TestStatus.SKIPPED
else -> TestStatus.FAILED
}

bspClientTestNotifier.startTest("Test", childId)
bspClientTestNotifier.finishTest("Test", childId, testStatus, "Test finished")

val passed = if (testStatus == TestStatus.PASSED) 1 else 0
val failed = if (testStatus == TestStatus.FAILED) 1 else 0
val ignored = if (testStatus == TestStatus.IGNORED) 1 else 0
val cancelled = if (testStatus == TestStatus.CANCELLED) 1 else 0
val skipped = if (testStatus == TestStatus.SKIPPED) 1 else 0

val testReport = TestReport(
target, passed, failed, ignored, cancelled, skipped
)

bspClientTestNotifier.endTestTarget(testReport, parentId)
}
}

private fun processTestSummary(event: BuildEventStreamProtos.BuildEvent) {
if (event.hasTestSummary()) {
// TODO: this is probably only relevant in remote scenarios
}
}

private fun fetchNamedSet(event: BuildEventStreamProtos.BuildEvent) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ import org.jetbrains.bsp.bazel.server.diagnostics.DiagnosticsService
import org.jetbrains.bsp.bazel.server.paths.BazelPathsResolver
import org.jetbrains.bsp.bazel.workspacecontext.TargetsSpec
import java.nio.file.Path
import java.util.Optional

// TODO: remove this file once we untangle the spaghetti and use the method from ExecuteService

class BazelBspCompilationManager(
private val bazelRunner: BazelRunner,
Expand All @@ -20,29 +21,12 @@ class BazelBspCompilationManager(
val client: BuildClient,
val workspaceRoot: Path,
) {
fun buildTargetsWithBep(
cancelChecker: CancelChecker, targetSpecs: TargetsSpec, originId: String, isAndroidEnabled: Boolean,
): BepBuildResult {
val androidFlags = listOf(
experimentalGoogleLegacyApi(),
experimentalEnableAndroidMigrationApis()
)
val flagsToUse = if (isAndroidEnabled) androidFlags else emptyList()
return buildTargetsWithBep(
cancelChecker = cancelChecker,
targetSpecs = targetSpecs,
extraFlags = flagsToUse,
originId = originId,
environment = emptyList(),
)
}

fun buildTargetsWithBep(
cancelChecker: CancelChecker,
targetSpecs: TargetsSpec,
extraFlags: List<String>,
originId: String?,
environment: List<Pair<String, String>>
extraFlags: List<String> = emptyList(),
originId: String? = null,
environment: List<Pair<String, String>> = emptyList()
): BepBuildResult {
val target = targetSpecs.values.firstOrNull()
val diagnosticsService = DiagnosticsService(workspaceRoot, hasAnyProblems)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ public class BepReader {
private final CompletableFuture<Boolean> bepReaderFinished;

private final Logger logger = LogManager.getLogger(BepReader.class);
void start() {
public void start() {
new Thread(() -> {
try {
logger.info("Start listening to BEP events");
Expand All @@ -46,11 +46,11 @@ void start() {
}).start();
}

void finishBuild() {
public void finishBuild() {
bazelBuildFinished.complete(true);
}

void await() throws ExecutionException, InterruptedException {
public void await() throws ExecutionException, InterruptedException {
bepReaderFinished.get();
}

Expand Down
Loading

0 comments on commit 73008c5

Please sign in to comment.