From 9eaea02016c399415c7f033d2e8212f9572c3c48 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9rgio=20Santos?= Date: Wed, 11 Sep 2024 11:50:12 +0100 Subject: [PATCH 1/2] Delete all results --- .../drawable/ic_delete_all.xml | 10 ++ .../values/strings-common.xml | 3 + .../disk/{DeleteFile.kt => DeleteFiles.kt} | 8 +- .../data/repositories/ResultRepository.kt | 10 ++ .../kotlin/org/ooni/probe/di/Dependencies.kt | 17 ++- .../org/ooni/probe/domain/DeleteAllResults.kt | 14 ++ .../org/ooni/probe/domain/RunNetTest.kt | 6 +- .../probe/domain/UploadMissingMeasurements.kt | 6 +- .../ooni/probe/ui/results/ResultsScreen.kt | 128 ++++++++++++++++-- .../ooni/probe/ui/results/ResultsViewModel.kt | 32 +++-- .../org/ooni/probe/data/Measurement.sq | 3 + .../sqldelight/org/ooni/probe/data/Network.sq | 3 + .../sqldelight/org/ooni/probe/data/Result.sq | 3 + .../domain/UploadMissingMeasurementsTest.kt | 6 +- .../probe/ui/results/ResultsScreenTest.kt | 75 ++++++++-- 15 files changed, 268 insertions(+), 56 deletions(-) create mode 100644 composeApp/src/commonMain/composeResources/drawable/ic_delete_all.xml rename composeApp/src/commonMain/kotlin/org/ooni/probe/data/disk/{DeleteFile.kt => DeleteFiles.kt} (67%) create mode 100644 composeApp/src/commonMain/kotlin/org/ooni/probe/domain/DeleteAllResults.kt diff --git a/composeApp/src/commonMain/composeResources/drawable/ic_delete_all.xml b/composeApp/src/commonMain/composeResources/drawable/ic_delete_all.xml new file mode 100644 index 000000000..35540eb4a --- /dev/null +++ b/composeApp/src/commonMain/composeResources/drawable/ic_delete_all.xml @@ -0,0 +1,10 @@ + + + diff --git a/composeApp/src/commonMain/composeResources/values/strings-common.xml b/composeApp/src/commonMain/composeResources/values/strings-common.xml index 7248b6ca0..aee5558a8 100644 --- a/composeApp/src/commonMain/composeResources/values/strings-common.xml +++ b/composeApp/src/commonMain/composeResources/values/strings-common.xml @@ -40,6 +40,7 @@ Test Results Test Results + No tests have been run yet. Try running one! Unknown N/A @@ -162,6 +163,8 @@ Retry Cancel OK + Do you want to delete all test results? + Delete Unable to download URL list. Please try again. diff --git a/composeApp/src/commonMain/kotlin/org/ooni/probe/data/disk/DeleteFile.kt b/composeApp/src/commonMain/kotlin/org/ooni/probe/data/disk/DeleteFiles.kt similarity index 67% rename from composeApp/src/commonMain/kotlin/org/ooni/probe/data/disk/DeleteFile.kt rename to composeApp/src/commonMain/kotlin/org/ooni/probe/data/disk/DeleteFiles.kt index e200d25ba..a65423320 100644 --- a/composeApp/src/commonMain/kotlin/org/ooni/probe/data/disk/DeleteFile.kt +++ b/composeApp/src/commonMain/kotlin/org/ooni/probe/data/disk/DeleteFiles.kt @@ -4,15 +4,15 @@ import okio.FileSystem import okio.Path import okio.Path.Companion.toPath -interface DeleteFile { +interface DeleteFiles { suspend operator fun invoke(path: Path) } -class DeleteFileOkio( +class DeleteFilesOkio( private val fileSystem: FileSystem, private val baseFilesDir: String, -) : DeleteFile { +) : DeleteFiles { override suspend fun invoke(path: Path) { - fileSystem.delete(baseFilesDir.toPath().resolve(path)) + fileSystem.deleteRecursively(baseFilesDir.toPath().resolve(path)) } } diff --git a/composeApp/src/commonMain/kotlin/org/ooni/probe/data/repositories/ResultRepository.kt b/composeApp/src/commonMain/kotlin/org/ooni/probe/data/repositories/ResultRepository.kt index 9e8c70a54..7d19d9511 100644 --- a/composeApp/src/commonMain/kotlin/org/ooni/probe/data/repositories/ResultRepository.kt +++ b/composeApp/src/commonMain/kotlin/org/ooni/probe/data/repositories/ResultRepository.kt @@ -92,6 +92,16 @@ class ResultRepository( database.resultQueries.deleteByRunId(resultId.value) } + suspend fun deleteAll() { + withContext(backgroundDispatcher) { + database.transaction { + database.measurementQueries.deleteAll() + database.resultQueries.deleteAll() + database.networkQueries.deleteAll() + } + } + } + private fun Result.toModel(): ResultModel? { return ResultModel( id = ResultModel.Id(id), 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 a53657e59..eb7c53864 100644 --- a/composeApp/src/commonMain/kotlin/org/ooni/probe/di/Dependencies.kt +++ b/composeApp/src/commonMain/kotlin/org/ooni/probe/di/Dependencies.kt @@ -21,8 +21,8 @@ import org.ooni.engine.NetworkTypeFinder import org.ooni.engine.OonimkallBridge import org.ooni.engine.TaskEventMapper import org.ooni.probe.Database -import org.ooni.probe.data.disk.DeleteFile -import org.ooni.probe.data.disk.DeleteFileOkio +import org.ooni.probe.data.disk.DeleteFiles +import org.ooni.probe.data.disk.DeleteFilesOkio import org.ooni.probe.data.disk.ReadFile import org.ooni.probe.data.disk.ReadFileOkio import org.ooni.probe.data.disk.WriteFile @@ -39,6 +39,7 @@ import org.ooni.probe.data.repositories.ResultRepository import org.ooni.probe.data.repositories.TestDescriptorRepository import org.ooni.probe.data.repositories.UrlRepository import org.ooni.probe.domain.BootstrapTestDescriptors +import org.ooni.probe.domain.DeleteAllResults import org.ooni.probe.domain.DeleteTestDescriptor import org.ooni.probe.domain.DownloadUrls import org.ooni.probe.domain.FetchDescriptor @@ -109,7 +110,7 @@ class Dependencies( private val readFile: ReadFile by lazy { ReadFileOkio(FileSystem.SYSTEM, baseFileDir) } private val writeFile: WriteFile by lazy { WriteFileOkio(FileSystem.SYSTEM, baseFileDir) } - private val deleteFile: DeleteFile by lazy { DeleteFileOkio(FileSystem.SYSTEM, baseFileDir) } + private val deleteFiles: DeleteFiles by lazy { DeleteFilesOkio(FileSystem.SYSTEM, baseFileDir) } // Engine @@ -147,6 +148,9 @@ class Dependencies( urlRepository::createOrUpdateByUrl, ) } + private val deleteAllResults by lazy { + DeleteAllResults(resultRepository::deleteAll, deleteFiles::invoke) + } private val fetchDescriptor by lazy { FetchDescriptor( engineHttpDo = engine::httpDo, @@ -202,7 +206,7 @@ class Dependencies( storeMeasurement = measurementRepository::createOrUpdate, storeNetwork = networkRepository::createIfNew, writeFile = writeFile, - deleteFile = deleteFile, + deleteFiles = deleteFiles, json = json, spec = spec, ) @@ -235,7 +239,7 @@ class Dependencies( deleteMeasurementByResultRunId = measurementRepository::deleteByResultRunId, selectMeasurementsByResultRunId = measurementRepository::selectByResultRunId, deleteResultByRunId = resultRepository::deleteByRunId, - deleteFile = deleteFile::invoke, + deleteFile = deleteFiles::invoke, ) } @@ -253,7 +257,7 @@ class Dependencies( getMeasurementsNotUploaded = measurementRepository.listNotUploaded(), submitMeasurement = engine::submitMeasurements, readFile = readFile, - deleteFile = deleteFile, + deleteFiles = deleteFiles, updateMeasurement = measurementRepository::createOrUpdate, ) } @@ -299,6 +303,7 @@ class Dependencies( goToResult = goToResult, goToUpload = goToUpload, getResults = getResults::invoke, + deleteAllResults = deleteAllResults::invoke, ) fun runningViewModel( diff --git a/composeApp/src/commonMain/kotlin/org/ooni/probe/domain/DeleteAllResults.kt b/composeApp/src/commonMain/kotlin/org/ooni/probe/domain/DeleteAllResults.kt new file mode 100644 index 000000000..b963b5047 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/org/ooni/probe/domain/DeleteAllResults.kt @@ -0,0 +1,14 @@ +package org.ooni.probe.domain + +import okio.Path +import okio.Path.Companion.toPath + +class DeleteAllResults( + private val deleteAllResultsFromDatabase: suspend () -> Unit, + private val deleteFiles: suspend (Path) -> Unit, +) { + suspend operator fun invoke() { + deleteAllResultsFromDatabase() + deleteFiles("Measurement".toPath()) + } +} diff --git a/composeApp/src/commonMain/kotlin/org/ooni/probe/domain/RunNetTest.kt b/composeApp/src/commonMain/kotlin/org/ooni/probe/domain/RunNetTest.kt index a9801c35a..e5ecdb6c3 100644 --- a/composeApp/src/commonMain/kotlin/org/ooni/probe/domain/RunNetTest.kt +++ b/composeApp/src/commonMain/kotlin/org/ooni/probe/domain/RunNetTest.kt @@ -9,7 +9,7 @@ import kotlinx.serialization.json.Json import org.ooni.engine.models.TaskEvent import org.ooni.engine.models.TaskEventResult import org.ooni.engine.models.TaskOrigin -import org.ooni.probe.data.disk.DeleteFile +import org.ooni.probe.data.disk.DeleteFiles import org.ooni.probe.data.disk.WriteFile import org.ooni.probe.data.models.Descriptor import org.ooni.probe.data.models.InstalledTestDescriptorModel @@ -29,7 +29,7 @@ class RunNetTest( private val storeResult: suspend (ResultModel) -> ResultModel.Id, private val setCurrentTestState: ((TestRunState) -> TestRunState) -> Unit, private val writeFile: WriteFile, - private val deleteFile: DeleteFile, + private val deleteFiles: DeleteFiles, private val json: Json, private val spec: Specification, ) { @@ -252,7 +252,7 @@ class RunNetTest( is TaskEvent.TaskTerminated -> { val measurement = measurements[event.index] ?: return if (measurement.isUploaded) { - measurement.reportFilePath?.let { deleteFile(it) } + measurement.reportFilePath?.let { deleteFiles(it) } } } } diff --git a/composeApp/src/commonMain/kotlin/org/ooni/probe/domain/UploadMissingMeasurements.kt b/composeApp/src/commonMain/kotlin/org/ooni/probe/domain/UploadMissingMeasurements.kt index 134b652aa..d40f021f4 100644 --- a/composeApp/src/commonMain/kotlin/org/ooni/probe/domain/UploadMissingMeasurements.kt +++ b/composeApp/src/commonMain/kotlin/org/ooni/probe/domain/UploadMissingMeasurements.kt @@ -6,7 +6,7 @@ import kotlinx.coroutines.flow.first import org.ooni.engine.Engine.MkException import org.ooni.engine.OonimkallBridge.SubmitMeasurementResults import org.ooni.engine.models.Result -import org.ooni.probe.data.disk.DeleteFile +import org.ooni.probe.data.disk.DeleteFiles import org.ooni.probe.data.disk.ReadFile import org.ooni.probe.data.models.MeasurementModel @@ -14,7 +14,7 @@ class UploadMissingMeasurements( private val getMeasurementsNotUploaded: Flow>, private val submitMeasurement: suspend (String) -> Result, private val readFile: ReadFile, - private val deleteFile: DeleteFile, + private val deleteFiles: DeleteFiles, private val updateMeasurement: suspend (MeasurementModel) -> Unit, ) { operator fun invoke(): Flow = @@ -49,7 +49,7 @@ class UploadMissingMeasurements( reportId = MeasurementModel.ReportId(submitResult.updatedReportId), ), ) - deleteFile(reportFilePath) + deleteFiles(reportFilePath) } .onFailure { exception -> failedToUpload++ diff --git a/composeApp/src/commonMain/kotlin/org/ooni/probe/ui/results/ResultsScreen.kt b/composeApp/src/commonMain/kotlin/org/ooni/probe/ui/results/ResultsScreen.kt index 38c6cd959..e23b8f1fa 100644 --- a/composeApp/src/commonMain/kotlin/org/ooni/probe/ui/results/ResultsScreen.kt +++ b/composeApp/src/commonMain/kotlin/org/ooni/probe/ui/results/ResultsScreen.kt @@ -3,37 +3,54 @@ package org.ooni.probe.ui.results import androidx.compose.foundation.background import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items +import androidx.compose.material3.AlertDialog +import androidx.compose.material3.CircularProgressIndicator import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Surface import androidx.compose.material3.Text import androidx.compose.material3.TextButton import androidx.compose.material3.TopAppBar import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.alpha +import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp import kotlinx.datetime.LocalDate import kotlinx.datetime.LocalDate.Companion.Format import kotlinx.datetime.format import kotlinx.datetime.format.MonthNames import kotlinx.datetime.format.char +import ooniprobe.composeapp.generated.resources.Modal_Cancel +import ooniprobe.composeapp.generated.resources.Modal_Delete +import ooniprobe.composeapp.generated.resources.Modal_DoYouWantToDeleteAllTests import ooniprobe.composeapp.generated.resources.Res import ooniprobe.composeapp.generated.resources.Snackbar_ResultsNotUploaded_Text import ooniprobe.composeapp.generated.resources.Snackbar_ResultsSomeNotUploaded_Text import ooniprobe.composeapp.generated.resources.Snackbar_ResultsSomeNotUploaded_UploadAll +import ooniprobe.composeapp.generated.resources.TestResults_Overview_NoTestsHaveBeenRun import ooniprobe.composeapp.generated.resources.TestResults_Overview_Title import ooniprobe.composeapp.generated.resources.TestResults_UnknownASN import ooniprobe.composeapp.generated.resources.ic_cloud_off +import ooniprobe.composeapp.generated.resources.ic_delete_all import ooniprobe.composeapp.generated.resources.measurements_count import ooniprobe.composeapp.generated.resources.months +import ooniprobe.composeapp.generated.resources.ooni_empty_state import ooniprobe.composeapp.generated.resources.task_origin_auto_run import ooniprobe.composeapp.generated.resources.task_origin_manual import org.jetbrains.compose.resources.painterResource @@ -50,31 +67,61 @@ fun ResultsScreen( state: ResultsViewModel.State, onEvent: (ResultsViewModel.Event) -> Unit, ) { + var showDeleteConfirm by remember { mutableStateOf(false) } + Column { TopAppBar( title = { Text(stringResource(Res.string.TestResults_Overview_Title)) }, + actions = { + if (!state.isLoading && state.results.any()) { + IconButton(onClick = { showDeleteConfirm = true }) { + Icon( + painterResource(Res.drawable.ic_delete_all), + contentDescription = stringResource(Res.string.Modal_Delete), + ) + } + } + }, ) - if (state.results.any { it.value.any { item -> !item.allMeasurementsUploaded } }) { + if (state.anyMissingUpload) { UploadResults(onUploadClick = { onEvent(ResultsViewModel.Event.UploadClick) }) } - LazyColumn { - state.results.forEach { (date, results) -> - stickyHeader(key = date.toString()) { - ResultDateHeader(date) - } - items(items = results) { result -> - ResultItem( - item = result, - onResultClick = { onEvent(ResultsViewModel.Event.ResultClick(result)) }, - ) + if (state.isLoading) { + LoadingResults() + } else if (state.results.isEmpty()) { + EmptyResults() + } else { + LazyColumn { + state.results.forEach { (date, results) -> + stickyHeader(key = date.toString()) { + ResultDateHeader(date) + } + items(items = results) { result -> + ResultItem( + item = result, + onResultClick = { onEvent(ResultsViewModel.Event.ResultClick(result)) }, + ) + } } } } } + + if (showDeleteConfirm) { + DeleteConfirmDialog( + onConfirm = { + onEvent(ResultsViewModel.Event.DeleteAllClick) + showDeleteConfirm = false + }, + onDismiss = { + showDeleteConfirm = false + }, + ) + } } @Composable @@ -95,6 +142,39 @@ private fun UploadResults(onUploadClick: () -> Unit) { } } +@Composable +private fun LoadingResults() { + Box( + contentAlignment = Alignment.Center, + modifier = Modifier.fillMaxSize(), + ) { + CircularProgressIndicator() + } +} + +@Composable +private fun EmptyResults() { + Column( + verticalArrangement = Arrangement.Center, + horizontalAlignment = Alignment.CenterHorizontally, + modifier = Modifier + .fillMaxSize() + .padding(horizontal = 36.dp) + .padding(bottom = 120.dp) // Optical alignment + .alpha(0.5f), + ) { + Icon( + painterResource(Res.drawable.ooni_empty_state), + contentDescription = null, + ) + Text( + stringResource(Res.string.TestResults_Overview_NoTestsHaveBeenRun), + textAlign = TextAlign.Center, + modifier = Modifier.padding(top = 16.dp), + ) + } +} + @Composable private fun ResultDateHeader(date: LocalDate) { val monthNames = stringArrayResource(Res.array.months) @@ -192,3 +272,29 @@ private fun ResultItem( } } } + +@Composable +private fun DeleteConfirmDialog( + onConfirm: () -> Unit, + onDismiss: () -> Unit, +) { + AlertDialog( + onDismissRequest = { onDismiss() }, + text = { + Text(stringResource(Res.string.Modal_DoYouWantToDeleteAllTests)) + }, + confirmButton = { + TextButton(onClick = { onConfirm() }) { + Text( + stringResource(Res.string.Modal_Delete), + color = MaterialTheme.colorScheme.error, + ) + } + }, + dismissButton = { + TextButton(onClick = { onDismiss() }) { + Text(stringResource(Res.string.Modal_Cancel)) + } + }, + ) +} diff --git a/composeApp/src/commonMain/kotlin/org/ooni/probe/ui/results/ResultsViewModel.kt b/composeApp/src/commonMain/kotlin/org/ooni/probe/ui/results/ResultsViewModel.kt index a9d6f6e4c..2d1184fa2 100644 --- a/composeApp/src/commonMain/kotlin/org/ooni/probe/ui/results/ResultsViewModel.kt +++ b/composeApp/src/commonMain/kotlin/org/ooni/probe/ui/results/ResultsViewModel.kt @@ -18,23 +18,23 @@ class ResultsViewModel( goToResult: (ResultModel.Id) -> Unit, goToUpload: () -> Unit, getResults: () -> Flow>, + deleteAllResults: suspend () -> Unit, ) : ViewModel() { private val events = MutableSharedFlow(extraBufferCapacity = 1) - private val _state = - MutableStateFlow( - State( - results = emptyMap(), - isLoading = true, - ), - ) + private val _state = MutableStateFlow(State()) val state = _state.asStateFlow() init { getResults() .onEach { results -> val groupedResults = results.groupBy { it.monthAndYear } - _state.update { it.copy(results = groupedResults) } + _state.update { + it.copy( + results = groupedResults, + isLoading = false, + ) + } } .launchIn(viewModelScope) @@ -47,6 +47,11 @@ class ResultsViewModel( .filterIsInstance() .onEach { goToUpload() } .launchIn(viewModelScope) + + events + .filterIsInstance() + .onEach { deleteAllResults() } + .launchIn(viewModelScope) } fun onEvent(event: Event) { @@ -59,13 +64,18 @@ class ResultsViewModel( } data class State( - val results: Map>, - val isLoading: Boolean, - ) + val results: Map> = emptyMap(), + val isLoading: Boolean = true, + ) { + val anyMissingUpload + get() = results.any { it.value.any { item -> !item.allMeasurementsUploaded } } + } sealed interface Event { data class ResultClick(val result: ResultListItem) : Event data object UploadClick : Event + + data object DeleteAllClick : Event } } diff --git a/composeApp/src/commonMain/sqldelight/org/ooni/probe/data/Measurement.sq b/composeApp/src/commonMain/sqldelight/org/ooni/probe/data/Measurement.sq index fbb88b0c8..fd9ee95e0 100644 --- a/composeApp/src/commonMain/sqldelight/org/ooni/probe/data/Measurement.sq +++ b/composeApp/src/commonMain/sqldelight/org/ooni/probe/data/Measurement.sq @@ -41,6 +41,9 @@ INSERT OR REPLACE INTO Measurement ( rerun_network ) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?); +deleteAll: +DELETE FROM Measurement; + selectLastInsertedRowId: SELECT last_insert_rowid(); diff --git a/composeApp/src/commonMain/sqldelight/org/ooni/probe/data/Network.sq b/composeApp/src/commonMain/sqldelight/org/ooni/probe/data/Network.sq index 18da29502..ffd94866d 100644 --- a/composeApp/src/commonMain/sqldelight/org/ooni/probe/data/Network.sq +++ b/composeApp/src/commonMain/sqldelight/org/ooni/probe/data/Network.sq @@ -17,6 +17,9 @@ INSERT OR REPLACE INTO Network ( network_type ) VALUES (?,?,?,?,?,?); +deleteAll: +DELETE FROM Network; + selectLastInsertedRowId: SELECT last_insert_rowid(); diff --git a/composeApp/src/commonMain/sqldelight/org/ooni/probe/data/Result.sq b/composeApp/src/commonMain/sqldelight/org/ooni/probe/data/Result.sq index 148b2038e..15ba5fe0b 100644 --- a/composeApp/src/commonMain/sqldelight/org/ooni/probe/data/Result.sq +++ b/composeApp/src/commonMain/sqldelight/org/ooni/probe/data/Result.sq @@ -31,6 +31,9 @@ INSERT OR REPLACE INTO Result ( markAsViewed: UPDATE Result SET is_viewed = 1 WHERE id = ?; +deleteAll: +DELETE FROM Result; + selectLastInsertedRowId: SELECT last_insert_rowid(); diff --git a/composeApp/src/commonTest/kotlin/org/ooni/probe/domain/UploadMissingMeasurementsTest.kt b/composeApp/src/commonTest/kotlin/org/ooni/probe/domain/UploadMissingMeasurementsTest.kt index e1e92d9f6..535f84d06 100644 --- a/composeApp/src/commonTest/kotlin/org/ooni/probe/domain/UploadMissingMeasurementsTest.kt +++ b/composeApp/src/commonTest/kotlin/org/ooni/probe/domain/UploadMissingMeasurementsTest.kt @@ -7,7 +7,7 @@ import org.ooni.engine.Engine import org.ooni.engine.OonimkallBridge import org.ooni.engine.models.Failure import org.ooni.engine.models.Success -import org.ooni.probe.data.disk.DeleteFile +import org.ooni.probe.data.disk.DeleteFiles import org.ooni.probe.data.disk.ReadFile import org.ooni.probe.data.models.MeasurementModel import org.ooni.testing.factories.MeasurementModelFactory @@ -39,7 +39,7 @@ class UploadMissingMeasurementsTest { readFile = object : ReadFile { override suspend fun invoke(path: Path): String = "" }, - deleteFile = object : DeleteFile { + deleteFiles = object : DeleteFiles { override suspend fun invoke(path: Path) {} }, updateMeasurement = { newModel = it }, @@ -69,7 +69,7 @@ class UploadMissingMeasurementsTest { readFile = object : ReadFile { override suspend fun invoke(path: Path): String = "" }, - deleteFile = object : DeleteFile { + deleteFiles = object : DeleteFiles { override suspend fun invoke(path: Path) {} }, updateMeasurement = { newModel = it }, diff --git a/composeApp/src/commonTest/kotlin/org/ooni/probe/ui/results/ResultsScreenTest.kt b/composeApp/src/commonTest/kotlin/org/ooni/probe/ui/results/ResultsScreenTest.kt index 10e906f57..329ee865c 100644 --- a/composeApp/src/commonTest/kotlin/org/ooni/probe/ui/results/ResultsScreenTest.kt +++ b/composeApp/src/commonTest/kotlin/org/ooni/probe/ui/results/ResultsScreenTest.kt @@ -1,9 +1,15 @@ package org.ooni.probe.ui.results +import androidx.compose.ui.test.onNodeWithContentDescription import androidx.compose.ui.test.onNodeWithText import androidx.compose.ui.test.performClick import androidx.compose.ui.test.runComposeUiTest +import kotlinx.coroutines.test.runTest import kotlinx.datetime.LocalDate +import ooniprobe.composeapp.generated.resources.Modal_Delete +import ooniprobe.composeapp.generated.resources.Res +import ooniprobe.composeapp.generated.resources.TestResults_Overview_NoTestsHaveBeenRun +import org.jetbrains.compose.resources.getString import org.ooni.probe.data.models.ResultListItem import org.ooni.testing.factories.DescriptorFactory import org.ooni.testing.factories.NetworkModelFactory @@ -15,13 +21,7 @@ class ResultsScreenTest { @Test fun showResults() = runComposeUiTest { - val item = ResultListItem( - result = ResultModelFactory.build(), - descriptor = DescriptorFactory.buildDescriptorWithInstalled(), - network = NetworkModelFactory.build(), - measurementsCount = 4, - allMeasurementsUploaded = false, - ) + val item = buildItem() var title: String? = null setContent { @@ -47,13 +47,7 @@ class ResultsScreenTest { fun recordClick() = runComposeUiTest { val events = mutableListOf() - val item = ResultListItem( - result = ResultModelFactory.build(), - descriptor = DescriptorFactory.buildDescriptorWithInstalled(), - network = NetworkModelFactory.build(), - measurementsCount = 4, - allMeasurementsUploaded = false, - ) + val item = buildItem() var title: String? = null setContent { @@ -71,7 +65,58 @@ class ResultsScreenTest { } onNodeWithText(title!!).performClick() - assertEquals(1, events.size) assertEquals(ResultsViewModel.Event.ResultClick(item), events.first()) } + + @Test + fun deleteAllClick() = + runComposeUiTest { + val events = mutableListOf() + val item = buildItem() + setContent { + ResultsScreen( + state = ResultsViewModel.State( + results = mapOf( + LocalDate(2024, 1, 1) to listOf(item), + ), + isLoading = false, + ), + onEvent = events::add, + ) + } + + runTest { + onNodeWithContentDescription(getString(Res.string.Modal_Delete)).performClick() + onNodeWithText(getString(Res.string.Modal_Delete)).performClick() + assertEquals(ResultsViewModel.Event.DeleteAllClick, events.first()) + } + } + + @Test + fun emptyResults() = + runComposeUiTest { + setContent { + ResultsScreen( + state = ResultsViewModel.State( + results = emptyMap(), + isLoading = false, + ), + onEvent = {}, + ) + } + + runTest { + onNodeWithText(getString(Res.string.TestResults_Overview_NoTestsHaveBeenRun)) + .assertExists() + } + } + + private fun buildItem() = + ResultListItem( + result = ResultModelFactory.build(), + descriptor = DescriptorFactory.buildDescriptorWithInstalled(), + network = NetworkModelFactory.build(), + measurementsCount = 4, + allMeasurementsUploaded = false, + ) } From 97670871d71ef4d3adebc7a832f94f8a35089b7e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9rgio=20Santos?= Date: Mon, 16 Sep 2024 14:34:14 +0100 Subject: [PATCH 2/2] Increase CI memory allocation --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 52e71f42e..f1cb0cd02 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,7 +1,7 @@ kotlin.code.style=official #Gradle -org.gradle.jvmargs=-Xmx2048M -Dfile.encoding=UTF-8 -Dkotlin.daemon.jvm.options\="-Xmx2048M" +org.gradle.jvmargs=-Xmx4g -Dfile.encoding=UTF-8 -Dkotlin.daemon.jvm.options\="-Xmx4g" #Android android.nonTransitiveRClass=true