Skip to content
Open
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 @@ -7,4 +7,6 @@ import kotlinx.serialization.Serializable
data class OONINetTest(
@SerialName("test_name") val name: String,
@SerialName("inputs") val inputs: List<String>? = null,
@SerialName("is_background_run_enabled_default") val isBackgroundRunEnabled: Boolean = false,
@SerialName("is_manual_run_enabled_default") val isManualRunEnabled: Boolean = false,
)

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -22,32 +22,25 @@ data class Descriptor(
val expirationDate: LocalDateTime?,
val netTests: List<NetTest>,
val longRunningTests: List<NetTest> = emptyList(),
val source: Source,
val source: InstalledTestDescriptorModel,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had mentioned this before, but keeping both the Descriptor and the InstalledTestDescriptorModel is just a big duplication of fields now. If we are to keep both because it helps with UpdateStatus, then we should remove the duplication:

  • The Descriptor class should just have the source and UpdateStatus
  • Optionally, InstalledTestDescriptorModel could be renamed to Descriptor and Descriptor could become a DescriptorItem or DescriptorWithUpdateStatus

val updateStatus: UpdateStatus,
val enabled: Boolean = true,
val summaryType: SummaryType,
) {
sealed interface Source {
data class Default(
val value: DefaultTestDescriptor,
) : Source

data class Installed(
val value: InstalledTestDescriptorModel,
) : Source
}

val isExpired get() = expirationDate != null && expirationDate < LocalDateTime.now()

val updatedDescriptor
get() = (updateStatus as? UpdateStatus.Updatable)?.updatedDescriptor

val key: String
get() = when (source) {
is Source.Default -> name
is Source.Installed -> source.value.id.value
get() {
val descriptorId = source.id.value
return if (isDefault()) {
OoniTest.fromId(descriptorId.toLong())?.key ?: descriptorId
} else {
descriptorId
}
}

val allTests get() = netTests + longRunningTests

val estimatedDuration
Expand All @@ -57,6 +50,11 @@ data class Descriptor(

val isWebConnectivityOnly get() =
allTests.size == 1 && allTests.first().test == TestType.WebConnectivity

val settingsPrefix: String?
get() = if (isDefault()) null else source.id.value

fun isDefault(): Boolean = source.isDefaultTestDescriptor
}

fun List<Descriptor>.notExpired() = filter { !it.isExpired }
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,20 @@ import kotlinx.datetime.format
import kotlinx.datetime.format.MonthNames
import kotlinx.datetime.format.char
import kotlinx.serialization.Serializable
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
import ooniprobe.composeapp.generated.resources.Dashboard_Runv2_Overview_Description
import ooniprobe.composeapp.generated.resources.Dashboard_Runv2_Overview_LastUpdated
import ooniprobe.composeapp.generated.resources.Res
import ooniprobe.composeapp.generated.resources.TestResults_NotAvailable
import ooniprobe.composeapp.generated.resources.performance_datausage
import ooniprobe.composeapp.generated.resources.small_datausage
import ooniprobe.composeapp.generated.resources.test_circumvention
import ooniprobe.composeapp.generated.resources.test_experimental
import ooniprobe.composeapp.generated.resources.test_instant_messaging
import ooniprobe.composeapp.generated.resources.test_performance
import ooniprobe.composeapp.generated.resources.test_websites
import ooniprobe.composeapp.generated.resources.websites_datausage
import org.jetbrains.compose.resources.StringResource
import org.jetbrains.compose.resources.stringResource
import org.ooni.engine.models.SummaryType
import org.ooni.probe.data.TestDescriptor
Expand All @@ -24,6 +28,26 @@ import org.ooni.probe.shared.hexToColor
import org.ooni.probe.shared.stringMonthArrayResource
import org.ooni.probe.shared.toEpoch

enum class OoniTest(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be in a separate file, there's no need to be here.

val id: Long,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This id should be a String and not Long.

val key: String,
) {
WEBSITES(10470L, "websites"),
INSTANT_MESSAGING(10471L, "instant_messaging"),
CIRCUMVENTION(10472L, "circumvention"),
PERFORMANCE(10473L, "performance"),
EXPERIMENTAL(10474L, "experimental"),
Comment on lines +35 to +39
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We're using regular capitalization for enum and sealed classes in this project: WEBSITES -> Websites.

;

companion object {
private val map = entries.associateBy(OoniTest::id)

fun fromId(id: Long) = map[id]

fun isValidId(id: Long): Boolean = entries.any { it.id == id }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since we're already caching the entries as a map, this could be fromId(id) != null.

}
}

@Serializable
data class InstalledTestDescriptorModel(
val id: Id,
Expand Down Expand Up @@ -56,6 +80,8 @@ data class InstalledTestDescriptorModel(
val revision: Long,
)

val isDefaultTestDescriptor get() = OoniTest.isValidId(id.value.toLongOrNull() ?: -1L)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure if we should keep with the default concept or not. It's confusing with the NewsMediaScan descriptors, which are default, but not OONI tests. Maybe split this between isOoniDescriptor and isUninstallable.


val key get() = Key(id, revision)

val previousRevisions
Expand Down Expand Up @@ -85,15 +111,31 @@ fun InstalledTestDescriptorModel.toDescriptor(updateStatus: UpdateStatus = Updat
icon = icon?.let(InstalledDescriptorIcons::getIconFromValue),
color = color?.hexToColor(),
animation = icon?.let { determineAnimation(it) } ?: animation?.let(Animation::fromFileName),
dataUsage = { null },
dataUsage = { if (isDefaultTestDescriptor) stringResource(getDataUsage()) else null },
expirationDate = expirationDate,
netTests = netTests.orEmpty(),
source = Descriptor.Source.Installed(this),
source = this,
updateStatus = updateStatus,
// In the future, this will become a DB field with a value provided by the back-end
summaryType = SummaryType.Anomaly,
)

fun InstalledTestDescriptorModel.getDataUsage(): StringResource =
when (
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's an enum class, so you can just do:

when (OoniTest.fromId(key.id.value)) {
    OoniTest.WEBSITES -> Res.string.websites_datausage
    ...
}

OoniTest
.fromId(
this.key.id.value
.toLong(),
)?.key
) {
OoniTest.WEBSITES.key -> Res.string.websites_datausage
OoniTest.INSTANT_MESSAGING.key -> Res.string.small_datausage
OoniTest.CIRCUMVENTION.key -> Res.string.small_datausage
OoniTest.PERFORMANCE.key -> Res.string.performance_datausage
OoniTest.EXPERIMENTAL.key -> Res.string.TestResults_NotAvailable
else -> Res.string.TestResults_NotAvailable
}

private val iconAnimationMap = mapOf(
Res.drawable.test_websites to Animation.Websites,
Res.drawable.test_instant_messaging to Animation.InstantMessaging,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,30 +18,9 @@ sealed interface RunSpecification {

@Serializable
data class Test(
val source: Source,
val source: InstalledTestDescriptorModel.Id,
val netTests: List<NetTest>,
) {
@Serializable
sealed interface Source {
@Serializable
data class Default(
val name: String,
) : Source

@Serializable
data class Installed(
val id: InstalledTestDescriptorModel.Id,
) : Source

companion object {
fun fromDescriptor(descriptor: Descriptor) =
when (descriptor.source) {
is Descriptor.Source.Default -> Default(descriptor.name)
is Descriptor.Source.Installed -> Installed(descriptor.source.value.id)
}
}
}
}
)

/*
* Remove the URL inputs from the spec if we already have them in our database,
Expand Down Expand Up @@ -70,5 +49,5 @@ sealed interface RunSpecification {
}

private val Test.isWebsites
get() = (source as? Test.Source.Default)?.name == "websites"
get() = source.value == OoniTest.WEBSITES.id.toString()
}
Original file line number Diff line number Diff line change
Expand Up @@ -205,11 +205,7 @@ class PreferenceRepository(
isAutoRun: Boolean,
) = getPreferenceKey(
name = netTest.test.preferenceKey,
prefix = (descriptor.source as? Descriptor.Source.Installed)
?.value
?.id
?.value
?.toString(),
prefix = descriptor.settingsPrefix,
autoRun = isAutoRun,
)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,6 @@ import org.ooni.probe.domain.FinishInProgressData
import org.ooni.probe.domain.GetAutoRunSettings
import org.ooni.probe.domain.GetAutoRunSpecification
import org.ooni.probe.domain.GetBootstrapTestDescriptors
import org.ooni.probe.domain.GetDefaultTestDescriptors
import org.ooni.probe.domain.GetEnginePreferences
import org.ooni.probe.domain.GetFirstRun
import org.ooni.probe.domain.GetLastResultOfDescriptor
Expand Down Expand Up @@ -303,7 +302,6 @@ class Dependencies(
private val getBootstrapTestDescriptors by lazy {
GetBootstrapTestDescriptors(readAssetFile, json, backgroundContext)
}
private val getDefaultTestDescriptors by lazy { GetDefaultTestDescriptors() }
private val getProxySettings by lazy { GetProxySettings(preferenceRepository) }
private val getEnginePreferences by lazy {
GetEnginePreferences(
Expand Down Expand Up @@ -366,7 +364,6 @@ class Dependencies(
@VisibleForTesting
val getTestDescriptors by lazy {
GetTestDescriptors(
getDefaultTestDescriptors = getDefaultTestDescriptors::invoke,
listAllInstalledTestDescriptors = testDescriptorRepository::listAll,
listLatestInstalledTestDescriptors = testDescriptorRepository::listLatest,
observeDescriptorsUpdateState = descriptorUpdateStateManager::observe,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ class GetAutoRunSpecification(
return RunSpecification.Full(
tests = descriptors.map { descriptor ->
RunSpecification.Test(
source = RunSpecification.Test.Source.fromDescriptor(descriptor),
source = descriptor.source.id,
netTests = descriptor.netTests,
)
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ class GetBootstrapTestDescriptors(
) {
suspend operator fun invoke(): List<InstalledTestDescriptorModel> =
withContext(backgroundContext) {

val descriptorsJson = Res.readBytes("files/assets/descriptors.json").decodeToString()
val descriptors =
try {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ fun List<Descriptor>.forResult(result: ResultModel): Descriptor? =
result.descriptorKey
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can now stop using the concept of descriptorKey and start using InstalledTestDescriptorModel.Id everywhere.

?.let { key ->
firstOrNull {
it.source is Descriptor.Source.Installed && it.source.value.key == key
it.source?.key?.toString() == key.toString()
}
}
?: firstOrNull { it.name == result.descriptorName }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ class RunDescriptors(
) {
val result = ResultModel(
descriptorName = descriptor.name,
descriptorKey = (descriptor.source as? Descriptor.Source.Installed)?.value?.key,
descriptorKey = descriptor.source?.key,
taskOrigin = taskOrigin,
)
val resultId = storeResult(result)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,8 +67,8 @@ class RunNetTest(
testTotal = spec.testTotal,
)
}
val installedDescriptorId =
(spec.descriptor.source as? Descriptor.Source.Installed)?.value?.id
// Ensure descriptor id is not available for ooni descriptors
val installedDescriptorId = if (spec.descriptor.isDefault()) null else spec.descriptor.source.id

startTest(
spec.netTest,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,18 @@ package org.ooni.probe.domain.descriptors
import androidx.compose.runtime.Composable
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.map
import ooniprobe.composeapp.generated.resources.Res
import ooniprobe.composeapp.generated.resources.Settings_TestOptions_LongRunningTest
import org.jetbrains.compose.resources.stringResource
import org.ooni.engine.models.WebConnectivityCategory
import org.ooni.probe.data.models.DefaultTestDescriptor
import org.ooni.probe.data.models.Descriptor
import org.ooni.probe.data.models.DescriptorsUpdateState
import org.ooni.probe.data.models.InstalledTestDescriptorModel
import org.ooni.probe.data.models.SettingsKey
import org.ooni.probe.data.models.UpdateStatus
import org.ooni.probe.data.models.toDescriptor

class GetTestDescriptors(
private val getDefaultTestDescriptors: () -> List<DefaultTestDescriptor>,
private val listAllInstalledTestDescriptors: () -> Flow<List<InstalledTestDescriptorModel>>,
private val listLatestInstalledTestDescriptors: () -> Flow<List<InstalledTestDescriptorModel>>,
private val observeDescriptorsUpdateState: () -> Flow<DescriptorsUpdateState>,
Expand All @@ -38,14 +34,12 @@ class GetTestDescriptors(
return combine(
installedDescriptorFlow(),
observeDescriptorsUpdateState(),
flowOf(getDefaultTestDescriptors()),
isWebsitesDescriptorEnabled(),
) { installedDescriptors, descriptorUpdates, defaultDescriptors, isWebsitesEnabled ->
) { installedDescriptors, descriptorUpdates, isWebsitesEnabled ->
val updatedDescriptors = installedDescriptors.map { item ->
item.toDescriptor(updateStatus = descriptorUpdates.getStatusOf(item.id))
}
val allDescriptors = defaultDescriptors.map { it.toDescriptor() } + updatedDescriptors
return@combine allDescriptors.map {
return@combine updatedDescriptors.map {
it.copy(enabled = it.name != "websites" || isWebsitesEnabled)
}
}
Expand All @@ -55,30 +49,6 @@ class GetTestDescriptors(
getPreferenceValues(WebConnectivityCategory.entries.mapNotNull { it.settingsKey })
.map { preferences -> preferences.any { it.value == true } }

private fun DefaultTestDescriptor.toDescriptor() =
Descriptor(
name = label,
title = { stringResource(title) },
shortDescription = { stringResource(shortDescription) },
description = {
if (label == "experimental") {
stringResource(description, experimentalLinks())
} else {
stringResource(description)
}
},
icon = icon,
color = color,
animation = animation,
dataUsage = { stringResource(dataUsage) },
expirationDate = null,
netTests = netTests,
longRunningTests = longRunningTests,
source = Descriptor.Source.Default(this),
updateStatus = UpdateStatus.NotApplicable,
summaryType = summaryType,
)

@Composable
private fun experimentalLinks() =
"""
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,17 +34,7 @@ class GetTestDescriptorsBySpec(
// Is this descriptor contained in the RunSpecification's list of tests
private fun RunSpecification.Full.forDescriptor(descriptor: Descriptor) =
tests.firstOrNull { specTest ->
when (descriptor.source) {
is Descriptor.Source.Default -> {
specTest.source is RunSpecification.Test.Source.Default &&
specTest.source.name == descriptor.name
}

is Descriptor.Source.Installed -> {
specTest.source is RunSpecification.Test.Source.Installed &&
specTest.source.id == descriptor.source.value.id
}
}
specTest.source == descriptor.source.id
}

/*
Expand Down
Loading