diff --git a/composeApp/src/commonMain/kotlin/org/ooni/engine/Engine.kt b/composeApp/src/commonMain/kotlin/org/ooni/engine/Engine.kt index 9f33c6f0..4e6dba0a 100644 --- a/composeApp/src/commonMain/kotlin/org/ooni/engine/Engine.kt +++ b/composeApp/src/commonMain/kotlin/org/ooni/engine/Engine.kt @@ -2,10 +2,11 @@ package org.ooni.engine import androidx.annotation.VisibleForTesting import co.touchlab.kermit.Logger -import kotlinx.coroutines.CancellationException +import kotlinx.coroutines.async import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.channelFlow import kotlinx.coroutines.flow.flowOn +import kotlinx.coroutines.flow.take import kotlinx.coroutines.isActive import kotlinx.serialization.encodeToString import kotlinx.serialization.json.Json @@ -35,6 +36,7 @@ class Engine( private val isBatteryCharging: suspend () -> Boolean, private val platformInfo: PlatformInfo, private val getEnginePreferences: suspend () -> EnginePreferences, + private val observeCancelTestRun: () -> Flow, private val backgroundContext: CoroutineContext, ) { fun startTask( @@ -52,19 +54,30 @@ class Engine( try { task = bridge.startTask(settingsSerialized) + val cancelJob = async { + observeCancelTestRun() + .take(1) + .collect { + task.interrupt() + } + } + while (!task.isDone() && isActive) { val eventJson = task.waitForNextEvent() val taskEventResult = json.decodeFromString(eventJson) taskEventMapper(taskEventResult)?.let { send(it) } } - } catch (e: CancellationException) { - Logger.d("Test cancelled") - throw e + + if (cancelJob.isActive) { + cancelJob.cancel() + } } catch (e: Exception) { Logger.d("Error while running task", e) throw MkException(e) } finally { - task?.interrupt() + if (task?.isDone() == false) { + task.interrupt() + } } }.flowOn(backgroundContext) diff --git a/composeApp/src/commonMain/kotlin/org/ooni/probe/di/Dependencies.kt b/composeApp/src/commonMain/kotlin/org/ooni/probe/di/Dependencies.kt index 6d99e0f8..80d1e277 100644 --- a/composeApp/src/commonMain/kotlin/org/ooni/probe/di/Dependencies.kt +++ b/composeApp/src/commonMain/kotlin/org/ooni/probe/di/Dependencies.kt @@ -183,6 +183,7 @@ class Dependencies( isBatteryCharging = isBatteryCharging, platformInfo = platformInfo, getEnginePreferences = getEnginePreferences::invoke, + observeCancelTestRun = testStateManager::observeCancels, backgroundContext = backgroundContext, ) } diff --git a/composeApp/src/commonMain/kotlin/org/ooni/probe/domain/RunDescriptors.kt b/composeApp/src/commonMain/kotlin/org/ooni/probe/domain/RunDescriptors.kt index 4c92207b..5b4ae4b3 100644 --- a/composeApp/src/commonMain/kotlin/org/ooni/probe/domain/RunDescriptors.kt +++ b/composeApp/src/commonMain/kotlin/org/ooni/probe/domain/RunDescriptors.kt @@ -65,23 +65,20 @@ class RunDescriptors( spec: RunSpecification, ) { coroutineScope { - val runJob = async { - // Actually running the descriptors - descriptors.forEachIndexed { index, descriptor -> - runDescriptor(descriptor, index, spec.taskOrigin, spec.isRerun) - } - } // Observe if a cancel request has been made val cancelJob = async { observeCancelTestRun .take(1) .collect { setCurrentTestState { TestRunState.Stopping } - runJob.cancel() } } - runJob.await() + // Actually running the descriptors + descriptors.forEachIndexed { index, descriptor -> + if (isRunStopped()) return@forEachIndexed + runDescriptor(descriptor, index, spec.taskOrigin, spec.isRerun) + } if (cancelJob.isActive) { cancelJob.cancel() @@ -139,6 +136,7 @@ class RunDescriptors( val resultId = storeResult(result) descriptor.allTests.forEachIndexed { testIndex, netTest -> + if (isRunStopped()) return@forEachIndexed runNetTest( RunNetTest.Specification( descriptor = descriptor, @@ -155,4 +153,6 @@ class RunDescriptors( markResultAsDone(resultId) } + + private suspend fun isRunStopped() = getCurrentTestRunState.first() is TestRunState.Stopping } diff --git a/composeApp/src/commonTest/kotlin/org/ooni/engine/EngineTest.kt b/composeApp/src/commonTest/kotlin/org/ooni/engine/EngineTest.kt index 22c07a29..30dd91fa 100644 --- a/composeApp/src/commonTest/kotlin/org/ooni/engine/EngineTest.kt +++ b/composeApp/src/commonTest/kotlin/org/ooni/engine/EngineTest.kt @@ -1,6 +1,7 @@ package org.ooni.engine import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.flow.emptyFlow import kotlinx.coroutines.flow.toList import kotlinx.coroutines.test.runTest import org.ooni.engine.models.EnginePreferences @@ -85,6 +86,7 @@ class EngineTest { maxRuntime = null, ) }, + observeCancelTestRun = { emptyFlow() }, backgroundContext = Dispatchers.Unconfined, ) }