Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,9 @@ import kotlinx.serialization.encodeToString
import ooniprobe.composeapp.generated.resources.Dashboard_Running_Running
import ooniprobe.composeapp.generated.resources.Dashboard_Running_Stopping_Notice
import ooniprobe.composeapp.generated.resources.Dashboard_Running_Stopping_Title
import ooniprobe.composeapp.generated.resources.Modal_ResultsNotUploaded_Uploading
import ooniprobe.composeapp.generated.resources.Notification_StopTest
import ooniprobe.composeapp.generated.resources.Res
import ooniprobe.composeapp.generated.resources.UploadingMissingResults
import ooniprobe.composeapp.generated.resources.notification_channel_name
import org.jetbrains.compose.resources.getString
import org.ooni.probe.AndroidApplication
Expand Down Expand Up @@ -143,7 +143,7 @@ class RunWorker(
val progress = state.uploaded + state.failedToUpload + 1
setContentText(
getString(
Res.string.Modal_ResultsNotUploaded_Uploading,
Res.string.UploadingMissingResults,
"$progress/${state.total}",
),
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -296,4 +296,5 @@
<string name="Settings_Websites_MaxRuntime_New">Maximum Websites test duration</string>
<string name="Settings_AutomatedTesting_RunAutomatically_Description">Tests will run every hour in the background</string>
<string name="Settings_Websites_MaxRuntimeEnabled_Description">Only for manual runs</string>
<string name="UploadingMissingResults">Uploading missing results %1$s</string>
</resources>
2 changes: 2 additions & 0 deletions composeApp/src/commonMain/kotlin/org/ooni/probe/App.kt
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import ooniprobe.composeapp.generated.resources.Res
import org.jetbrains.compose.resources.getString
import org.jetbrains.compose.ui.tooling.preview.Preview
import org.ooni.probe.data.models.DeepLink
import org.ooni.probe.data.models.RunSpecification
import org.ooni.probe.di.Dependencies
import org.ooni.probe.shared.PlatformInfo
import org.ooni.probe.ui.navigation.BottomNavigationBar
Expand Down Expand Up @@ -99,6 +100,7 @@ fun App(
dependencies.bootstrapPreferences()
dependencies.configureDescriptorAutoUpdate()
dependencies.fetchDescriptorUpdate(null)
dependencies.startSingleRunInner(RunSpecification.OnlyUploadMissingResults)
}
LaunchedEffect(Unit) {
dependencies.finishInProgressData()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@ class RunBackgroundTask(
private val uploadMissingMeasurements: (ResultModel.Id?) -> Flow<UploadMissingMeasurements.State>,
private val checkSkipAutoRunNotUploadedLimit: () -> Flow<Boolean>,
private val getNetworkType: () -> NetworkType,
private val getAutoRunSpecification: suspend () -> RunSpecification,
private val runDescriptors: suspend (RunSpecification) -> Unit,
private val getAutoRunSpecification: suspend () -> RunSpecification.Full,
private val runDescriptors: suspend (RunSpecification.Full) -> Unit,
private val setRunBackgroundState: ((RunBackgroundState) -> RunBackgroundState) -> Unit,
private val getRunBackgroundState: () -> Flow<RunBackgroundState>,
private val addRunCancelListener: (() -> Unit) -> Unit,
Expand All @@ -44,52 +44,57 @@ class RunBackgroundTask(
return@channelFlow
}

val uploadCancelled = uploadMissingResults(isAutoRun = spec == null)
if (uploadCancelled) return@channelFlow
val isAutoRun = spec == null
if (isAutoRun || spec is RunSpecification.OnlyUploadMissingResults) {
val uploadCancelled = uploadMissingResults()
if (uploadCancelled) return@channelFlow
}

if (spec is RunSpecification.OnlyUploadMissingResults) {
setRunBackgroundState { RunBackgroundState.Idle() }
return@channelFlow
}

runTests(spec)
runTests(spec as? RunSpecification.Full)

// When a test is cancelled, sometimes the last measurement isn't uploaded

getLatestResult().first()?.id.let { latestResultId ->
val idleState = getRunBackgroundState().first()
uploadMissingResults(isAutoRun = spec == null, resultId = latestResultId)
uploadMissingResults(resultId = latestResultId)
updateState(idleState)
}
}.onCompletion {
clearRunCancelListeners()
}

private suspend fun ProducerScope<RunBackgroundState>.uploadMissingResults(
isAutoRun: Boolean,
resultId: ResultModel.Id? = null,
): Boolean {
private suspend fun ProducerScope<RunBackgroundState>.uploadMissingResults(resultId: ResultModel.Id? = null): Boolean {
val autoUpload = getPreferenceValueByKey(SettingsKey.UPLOAD_RESULTS).first() == true
var isCancelled = false
if (!autoUpload) return false

if ((isAutoRun || resultId != null) && autoUpload) {
coroutineScope {
val uploadJob = async {
uploadMissingMeasurements(resultId)
.collectLatest { uploadState ->
updateState(RunBackgroundState.UploadingMissingResults(uploadState))
}
}
var isCancelled = false

addRunCancelListener {
isCancelled = true
if (uploadJob.isActive) uploadJob.cancel()
CoroutineScope(Dispatchers.Default).launch {
updateState(RunBackgroundState.Stopping)
coroutineScope {
val uploadJob = async {
uploadMissingMeasurements(resultId)
.collectLatest { uploadState ->
updateState(RunBackgroundState.UploadingMissingResults(uploadState))
}
}
}

try {
uploadJob.await()
} catch (e: CancellationException) {
Logger.i("Upload Missing Results (result=$resultId): cancelled")
addRunCancelListener {
isCancelled = true
if (uploadJob.isActive) uploadJob.cancel()
CoroutineScope(Dispatchers.Default).launch {
updateState(RunBackgroundState.Stopping)
}
}

try {
uploadJob.await()
} catch (e: CancellationException) {
Logger.i("Upload Missing Results (result=$resultId): cancelled")
}
}

if (isCancelled) {
Expand All @@ -100,7 +105,7 @@ class RunBackgroundTask(
return false
}

private suspend fun ProducerScope<RunBackgroundState>.runTests(spec: RunSpecification?) {
private suspend fun ProducerScope<RunBackgroundState>.runTests(spec: RunSpecification.Full?) {
if (checkSkipAutoRunNotUploadedLimit().first()) {
Logger.i("Skipping auto-run tests: too many not-uploaded results")
return
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,17 @@ import kotlinx.serialization.Serializable
import org.ooni.engine.models.TaskOrigin

@Serializable
data class RunSpecification(
val tests: List<Test>,
val taskOrigin: TaskOrigin,
val isRerun: Boolean,
) {
sealed interface RunSpecification {
@Serializable
data object OnlyUploadMissingResults : RunSpecification

@Serializable
data class Full(
val tests: List<Test>,
val taskOrigin: TaskOrigin,
val isRerun: Boolean,
) : RunSpecification

@Serializable
data class Test(
val source: Source,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ class Dependencies(
@VisibleForTesting
val buildDataStore: () -> DataStore<Preferences>,
private val isBatteryCharging: () -> Boolean,
private val startSingleRunInner: (RunSpecification) -> Unit,
val startSingleRunInner: (RunSpecification) -> Unit,
private val configureAutoRun: suspend (AutoRunParameters) -> Unit,
val configureDescriptorAutoUpdate: suspend () -> Boolean,
val fetchDescriptorUpdate: suspend (List<InstalledTestDescriptorModel>?) -> Unit,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,10 @@ class GetAutoRunSpecification(
private val getDescriptors: GetTestDescriptors,
private val preferenceRepository: PreferenceRepository,
) {
suspend operator fun invoke(): RunSpecification {
suspend operator fun invoke(): RunSpecification.Full {
val descriptors = getDescriptors().first().filterForAutoRun()

return RunSpecification(
return RunSpecification.Full(
tests = descriptors.map { descriptor ->
RunSpecification.Test(
source = RunSpecification.Test.Source.fromDescriptor(descriptor),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import org.ooni.probe.data.models.RunSpecification
class GetTestDescriptorsBySpec(
private val getTestDescriptors: () -> Flow<List<Descriptor>>,
) {
suspend operator fun invoke(spec: RunSpecification): List<Descriptor> =
suspend operator fun invoke(spec: RunSpecification.Full): List<Descriptor> =
getTestDescriptors()
.first()
.filterNot { it.isExpired }
Expand All @@ -30,7 +30,7 @@ class GetTestDescriptorsBySpec(
}

// Is this descriptor contained in the RunSpecification's list of tests
private fun RunSpecification.forDescriptor(descriptor: Descriptor) =
private fun RunSpecification.Full.forDescriptor(descriptor: Descriptor) =
tests.firstOrNull { specTest ->
when (descriptor.source) {
is Descriptor.Source.Default -> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import org.ooni.probe.shared.now
import kotlin.time.Duration

class RunDescriptors(
private val getTestDescriptorsBySpec: suspend (RunSpecification) -> List<Descriptor>,
private val getTestDescriptorsBySpec: suspend (RunSpecification.Full) -> List<Descriptor>,
private val downloadUrls: suspend (TaskOrigin) -> Result<List<UrlModel>, MkException>,
private val storeResult: suspend (ResultModel) -> ResultModel.Id,
private val markResultAsDone: suspend (ResultModel.Id) -> Unit,
Expand All @@ -32,7 +32,7 @@ class RunDescriptors(
private val getEnginePreferences: suspend () -> EnginePreferences,
private val finishInProgressData: suspend () -> Unit,
) {
suspend operator fun invoke(spec: RunSpecification) {
suspend operator fun invoke(spec: RunSpecification.Full) {
setRunBackgroundState { RunBackgroundState.RunningTests() }

val descriptors = getTestDescriptorsBySpec(spec)
Expand All @@ -55,7 +55,7 @@ class RunDescriptors(

private suspend fun runDescriptorsCancellable(
descriptors: List<Descriptor>,
spec: RunSpecification,
spec: RunSpecification.Full,
) {
addRunCancelListener {
setRunBackgroundState { RunBackgroundState.Stopping }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ class ChooseWebsitesViewModel(
}

startBackgroundRun(
RunSpecification(
RunSpecification.Full(
tests = listOf(
RunSpecification.Test(
source = RunSpecification.Test.Source.Default("websites"),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,9 @@ import ooniprobe.composeapp.generated.resources.Dashboard_Running_EstimatedTimeL
import ooniprobe.composeapp.generated.resources.Dashboard_Running_Running
import ooniprobe.composeapp.generated.resources.Dashboard_Running_Stopping_Notice
import ooniprobe.composeapp.generated.resources.Dashboard_Running_Stopping_Title
import ooniprobe.composeapp.generated.resources.Modal_ResultsNotUploaded_Uploading
import ooniprobe.composeapp.generated.resources.OONIRun_Run
import ooniprobe.composeapp.generated.resources.Res
import ooniprobe.composeapp.generated.resources.UploadingMissingResults
import ooniprobe.composeapp.generated.resources.ic_timer
import org.jetbrains.compose.resources.painterResource
import org.jetbrains.compose.resources.stringResource
Expand Down Expand Up @@ -104,7 +104,7 @@ fun RunBackgroundStateSection(
val progress = uploadState.uploaded + uploadState.failedToUpload + 1
Text(
text = stringResource(
Res.string.Modal_ResultsNotUploaded_Uploading,
Res.string.UploadingMissingResults,
"$progress/${uploadState.total}",
),
style = MaterialTheme.typography.bodyLarge,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ class ResultViewModel(

private fun getRerunSpecification(): RunSpecification? {
val item = _state.value.result ?: return null
return RunSpecification(
return RunSpecification.Full(
tests = listOf(
RunSpecification.Test(
source = RunSpecification.Test.Source.fromDescriptor(item.descriptor),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -224,7 +224,7 @@ class RunViewModel(
.filter { it.isSelected }
.map { it.item }
}
return RunSpecification(
return RunSpecification.Full(
tests =
selectedTests.map { (descriptor, tests) ->
RunSpecification.Test(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ class ChooseWebsitesTest {
}
onNodeWithText("Test ${websites.size} URLs").performClick()

val spec = runSpec
val spec = runSpec as? RunSpecification.Full
assertNotNull(spec)
assertEquals(false, spec.isRerun)
assertEquals(TaskOrigin.OoniRun, spec.taskOrigin)
Expand Down