remove androidx.datastore dependency

commit_hash:e54c4e6529a08d4ee2f7a2e5afa225c939424563
This commit is contained in:
gulevsky
2026-02-13 23:59:39 +03:00
parent 6cf50b603e
commit 08edbc8ce5
12 changed files with 454 additions and 47 deletions
+5
View File
@@ -1480,6 +1480,9 @@
"client/android/div/src/main/java/com/yandex/div/internal/spannable/PositionAwareReplacementSpan.kt":"divkit/public/client/android/div/src/main/java/com/yandex/div/internal/spannable/PositionAwareReplacementSpan.kt",
"client/android/div/src/main/java/com/yandex/div/internal/spannable/TextColorSpan.kt":"divkit/public/client/android/div/src/main/java/com/yandex/div/internal/spannable/TextColorSpan.kt",
"client/android/div/src/main/java/com/yandex/div/internal/spannable/TypefaceSpan.kt":"divkit/public/client/android/div/src/main/java/com/yandex/div/internal/spannable/TypefaceSpan.kt",
"client/android/div/src/main/java/com/yandex/div/internal/storage/DataEditor.kt":"divkit/public/client/android/div/src/main/java/com/yandex/div/internal/storage/DataEditor.kt",
"client/android/div/src/main/java/com/yandex/div/internal/storage/DataState.kt":"divkit/public/client/android/div/src/main/java/com/yandex/div/internal/storage/DataState.kt",
"client/android/div/src/main/java/com/yandex/div/internal/storage/DataStorage.kt":"divkit/public/client/android/div/src/main/java/com/yandex/div/internal/storage/DataStorage.kt",
"client/android/div/src/main/java/com/yandex/div/internal/util/Position.java":"divkit/public/client/android/div/src/main/java/com/yandex/div/internal/util/Position.java",
"client/android/div/src/main/java/com/yandex/div/internal/util/Utils.kt":"divkit/public/client/android/div/src/main/java/com/yandex/div/internal/util/Utils.kt",
"client/android/div/src/main/java/com/yandex/div/internal/util/ViewGroups.kt":"divkit/public/client/android/div/src/main/java/com/yandex/div/internal/util/ViewGroups.kt",
@@ -1493,6 +1496,7 @@
"client/android/div/src/main/java/com/yandex/div/internal/viewpool/ViewPool.kt":"divkit/public/client/android/div/src/main/java/com/yandex/div/internal/viewpool/ViewPool.kt",
"client/android/div/src/main/java/com/yandex/div/internal/viewpool/ViewPoolProfiler.kt":"divkit/public/client/android/div/src/main/java/com/yandex/div/internal/viewpool/ViewPoolProfiler.kt",
"client/android/div/src/main/java/com/yandex/div/internal/viewpool/ViewPreCreationProfile.kt":"divkit/public/client/android/div/src/main/java/com/yandex/div/internal/viewpool/ViewPreCreationProfile.kt",
"client/android/div/src/main/java/com/yandex/div/internal/viewpool/ViewPreCreationProfileParser.kt":"divkit/public/client/android/div/src/main/java/com/yandex/div/internal/viewpool/ViewPreCreationProfileParser.kt",
"client/android/div/src/main/java/com/yandex/div/internal/viewpool/optimization/PerformanceDependentSession.kt":"divkit/public/client/android/div/src/main/java/com/yandex/div/internal/viewpool/optimization/PerformanceDependentSession.kt",
"client/android/div/src/main/java/com/yandex/div/internal/viewpool/optimization/PerformanceDependentSessionProfiler.kt":"divkit/public/client/android/div/src/main/java/com/yandex/div/internal/viewpool/optimization/PerformanceDependentSessionProfiler.kt",
"client/android/div/src/main/java/com/yandex/div/internal/viewpool/optimization/ViewPreCreationProfileOptimizer.kt":"divkit/public/client/android/div/src/main/java/com/yandex/div/internal/viewpool/optimization/ViewPreCreationProfileOptimizer.kt",
@@ -1672,6 +1676,7 @@
"client/android/div/src/test/java/com/yandex/div/interactive/IntegrationMultiplatformTest.kt":"divkit/public/client/android/div/src/test/java/com/yandex/div/interactive/IntegrationMultiplatformTest.kt",
"client/android/div/src/test/java/com/yandex/div/interactive/IntegrationTestCase.kt":"divkit/public/client/android/div/src/test/java/com/yandex/div/interactive/IntegrationTestCase.kt",
"client/android/div/src/test/java/com/yandex/div/interactive/IntegrationTestLogger.kt":"divkit/public/client/android/div/src/test/java/com/yandex/div/interactive/IntegrationTestLogger.kt",
"client/android/div/src/test/java/com/yandex/div/internal/storage/DataStorageTest.kt":"divkit/public/client/android/div/src/test/java/com/yandex/div/internal/storage/DataStorageTest.kt",
"client/android/div/src/test/java/com/yandex/div/internal/viewpool/ProfilingSessionExtensionTest.kt":"divkit/public/client/android/div/src/test/java/com/yandex/div/internal/viewpool/ProfilingSessionExtensionTest.kt",
"client/android/div/src/test/java/com/yandex/div/internal/widget/AutoEllipsizeHelperTest.kt":"divkit/public/client/android/div/src/test/java/com/yandex/div/internal/widget/AutoEllipsizeHelperTest.kt",
"client/android/div/src/test/java/com/yandex/div/internal/widget/TransientViewTest.kt":"divkit/public/client/android/div/src/test/java/com/yandex/div/internal/widget/TransientViewTest.kt",
-1
View File
@@ -32,7 +32,6 @@ buildscript {
classpath libs.dokka
classpath libs.kotlin.allopen
classpath libs.kotlin.gradle.plugin
classpath libs.kotlin.serialization.plugin
classpath libs.kotlin.binaryCompatibilityValidator
classpath libs.unmock.plugin
}
-4
View File
@@ -10,7 +10,6 @@ apply from: "${project.projectDir}/../div-tests.gradle"
apply from: "${project.projectDir}/../publish-android.gradle"
apply plugin: 'de.mobilej.unmock'
apply plugin: 'kotlinx-serialization'
def expressionApiDir = "${project.projectDir}/../../../expression-api/"
def crossplatformProjectDir = "${project.projectDir}/../../../test_data/"
@@ -67,9 +66,6 @@ dependencies {
exclude group: "androidx.fragment", module: "fragment"
}
implementation libs.androidx.datastore
implementation libs.kotlin.serialization.json
testImplementation project(path: ':expression-test-common')
testImplementation libs.androidx.test.coreKtx
@@ -0,0 +1,34 @@
package com.yandex.div.internal.storage
import java.io.InputStream
import java.io.OutputStream
import java.nio.charset.Charset
internal interface DataReader<T> {
fun read(input: InputStream): T
}
internal interface DataWriter<T> {
fun write(value: T, output: OutputStream)
}
internal interface DataEditor<T> : DataReader<T>, DataWriter<T>
internal abstract class TextDataEditor<T>(
private val charset: Charset = Charsets.UTF_8
) : DataEditor<T> {
override fun read(input: InputStream): T {
val fileData = input.reader(charset).use { it.readText() }
return read(fileData)
}
abstract fun read(input: String): T
override fun write(value: T, output: OutputStream) {
val fileData = write(value)
output.writer(charset).use { it.write(fileData) }
}
abstract fun write(value: T): String
}
@@ -0,0 +1,12 @@
package com.yandex.div.internal.storage
internal sealed interface DataState<T> {
object Initial : DataState<Any>
class WithData<T>(val value: T) : DataState<T>
class WithException<T>(val exception: Throwable) : DataState<T>
object Finalized : DataState<Any>
}
@@ -0,0 +1,143 @@
package com.yandex.div.internal.storage
import android.content.Context
import androidx.annotation.VisibleForTesting
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onStart
import kotlinx.coroutines.flow.onSubscription
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import kotlinx.coroutines.withContext
import java.io.File
internal class DataStorage<T> private constructor(
context: Context,
fileName: String,
private val coroutineScope: CoroutineScope,
private val editor: DataEditor<T>,
) {
private val coroutineContext = coroutineScope.coroutineContext
private val dataFile = File(context.applicationContext.filesDir, fileName)
private val mutex = Mutex()
@Suppress("UNCHECKED_CAST")
private val dataStateFlow = MutableStateFlow<DataState<T>>(
value = DataState.Initial as DataState<T>
)
val data: StateFlow<T?> = dataStateFlow.onStart { restoreState() }
.map { state -> evaluateState(state) }
.stateIn(coroutineScope, SharingStarted.WhileSubscribed(), null)
init {
dataStateFlow.onSubscription { }
coroutineContext[Job]?.invokeOnCompletion {
@Suppress("UNCHECKED_CAST")
dataStateFlow.value = DataState.Finalized as DataState<T>
}
}
private fun evaluateState(state: DataState<T>): T? {
return when (state) {
is DataState.Initial -> null
is DataState.WithData<T> -> state.value
is DataState.WithException<T> -> throw state.exception
is DataState.Finalized -> error("Cannot read from closed storage")
}
}
private suspend fun restoreState() {
return withContext(coroutineContext) {
mutex.withLock {
if (dataStateFlow.value is DataState.Initial) {
try {
val value = editor.read(dataFile.inputStream())
dataStateFlow.value = DataState.WithData(value)
} catch (e: Exception) {
dataStateFlow.value = DataState.WithException(e)
}
}
}
}
}
suspend fun update(value: T) {
withContext(coroutineContext) {
mutex.withLock {
try {
if (!dataFile.exists()) {
dataFile.parentFile?.mkdirs()
dataFile.createNewFile()
}
val outputStream = dataFile.outputStream()
outputStream.use { output ->
editor.write(value, output)
}
dataStateFlow.value = DataState.WithData(value)
} catch (e: Exception) {
dataStateFlow.value = DataState.WithException(e)
}
}
}
}
suspend fun clear(): Boolean {
return withContext(coroutineContext) {
mutex.withLock {
try {
val result = dataFile.delete()
@Suppress("UNCHECKED_CAST")
dataStateFlow.value = DataState.Initial as DataState<T>
result
} catch (e: Exception) {
dataStateFlow.value = DataState.WithException(e)
false
}
}
}
}
companion object {
@JvmStatic
fun <T> create(
context: Context,
fileName: String,
editor: DataEditor<T>
): DataStorage<T> {
return DataStorage(
context = context,
fileName = fileName,
coroutineScope = CoroutineScope(Dispatchers.IO + SupervisorJob()),
editor = editor
)
}
@JvmStatic
@VisibleForTesting
internal fun <T> create(
context: Context,
fileName: String,
editor: DataEditor<T>,
coroutineScope: CoroutineScope
): DataStorage<T> {
return DataStorage(
context = context,
fileName = fileName,
coroutineScope = coroutineScope,
editor = editor
)
}
}
}
@@ -1,15 +1,11 @@
package com.yandex.div.internal.viewpool
import kotlinx.serialization.Serializable
@Serializable
data class PreCreationModel(
val capacity: Int,
val min: Int = 0,
val max: Int = Int.MAX_VALUE
)
@Serializable
data class ViewPreCreationProfile(
val id: String? = null,
val text: PreCreationModel = PreCreationModel(20),
@@ -0,0 +1,97 @@
package com.yandex.div.internal.viewpool
import org.json.JSONObject
internal object PreCreationModelParser {
private const val KEY_CAPACITY = "capacity"
private const val KEY_MIN = "min"
private const val KEY_MAX = "max"
fun serialize(model: PreCreationModel): JSONObject {
return JSONObject().apply {
put(KEY_CAPACITY, model.capacity)
put(KEY_MIN, model.min)
put(KEY_MAX, model.max)
}
}
fun deserialize(json: JSONObject): PreCreationModel {
return PreCreationModel(
capacity = json.getInt(KEY_CAPACITY),
min = json.getInt(KEY_MIN),
max = json.getInt(KEY_MAX),
)
}
}
internal object ViewPreCreationProfileParser {
private const val KEY_ID = "id"
private const val KEY_TEXT = "text"
private const val KEY_IMAGE = "image"
private const val KEY_GIF_IMAGE = "gifImage"
private const val KEY_OVERLAP_CONTAINER = "overlapContainer"
private const val KEY_LINEAR_CONTAINER = "linearContainer"
private const val KEY_WRAP_CONTAINER = "wrapContainer"
private const val KEY_GRID = "grid"
private const val KEY_GALLERY = "gallery"
private const val KEY_PAGER = "pager"
private const val KEY_TAB = "tab"
private const val KEY_STATE = "state"
private const val KEY_CUSTOM = "custom"
private const val KEY_INDICATOR = "indicator"
private const val KEY_SLIDER = "slider"
private const val KEY_INPUT = "input"
private const val KEY_SELECT = "select"
private const val KEY_VIDEO = "video"
private const val KEY_SWITCH = "switch"
fun serialize(profile: ViewPreCreationProfile): JSONObject {
return JSONObject().apply {
putOpt(KEY_ID, profile.id)
put(KEY_TEXT, PreCreationModelParser.serialize(profile.text))
put(KEY_IMAGE, PreCreationModelParser.serialize(profile.image))
put(KEY_GIF_IMAGE, PreCreationModelParser.serialize(profile.gifImage))
put(KEY_OVERLAP_CONTAINER, PreCreationModelParser.serialize(profile.overlapContainer))
put(KEY_LINEAR_CONTAINER, PreCreationModelParser.serialize(profile.linearContainer))
put(KEY_WRAP_CONTAINER, PreCreationModelParser.serialize(profile.wrapContainer))
put(KEY_GRID, PreCreationModelParser.serialize(profile.grid))
put(KEY_GALLERY, PreCreationModelParser.serialize(profile.gallery))
put(KEY_PAGER, PreCreationModelParser.serialize(profile.pager))
put(KEY_TAB, PreCreationModelParser.serialize(profile.tab))
put(KEY_STATE, PreCreationModelParser.serialize(profile.state))
put(KEY_CUSTOM, PreCreationModelParser.serialize(profile.custom))
put(KEY_INDICATOR, PreCreationModelParser.serialize(profile.indicator))
put(KEY_SLIDER, PreCreationModelParser.serialize(profile.slider))
put(KEY_INPUT, PreCreationModelParser.serialize(profile.input))
put(KEY_SELECT, PreCreationModelParser.serialize(profile.select))
put(KEY_VIDEO, PreCreationModelParser.serialize(profile.video))
put(KEY_SWITCH, PreCreationModelParser.serialize(profile.switch))
}
}
fun deserialize(json: JSONObject): ViewPreCreationProfile {
return ViewPreCreationProfile(
id = json.optString(KEY_ID),
text = PreCreationModelParser.deserialize(json.getJSONObject(KEY_TEXT)),
image = PreCreationModelParser.deserialize(json.getJSONObject(KEY_IMAGE)),
gifImage = PreCreationModelParser.deserialize(json.getJSONObject(KEY_GIF_IMAGE)),
overlapContainer = PreCreationModelParser.deserialize(json.getJSONObject(KEY_OVERLAP_CONTAINER)),
linearContainer = PreCreationModelParser.deserialize(json.getJSONObject(KEY_LINEAR_CONTAINER)),
wrapContainer = PreCreationModelParser.deserialize(json.getJSONObject(KEY_WRAP_CONTAINER)),
grid = PreCreationModelParser.deserialize(json.getJSONObject(KEY_GRID)),
gallery = PreCreationModelParser.deserialize(json.getJSONObject(KEY_GALLERY)),
pager = PreCreationModelParser.deserialize(json.getJSONObject(KEY_PAGER)),
tab = PreCreationModelParser.deserialize(json.getJSONObject(KEY_TAB)),
state = PreCreationModelParser.deserialize(json.getJSONObject(KEY_STATE)),
custom = PreCreationModelParser.deserialize(json.getJSONObject(KEY_CUSTOM)),
indicator = PreCreationModelParser.deserialize(json.getJSONObject(KEY_INDICATOR)),
slider = PreCreationModelParser.deserialize(json.getJSONObject(KEY_SLIDER)),
input = PreCreationModelParser.deserialize(json.getJSONObject(KEY_INPUT)),
select = PreCreationModelParser.deserialize(json.getJSONObject(KEY_SELECT)),
video = PreCreationModelParser.deserialize(json.getJSONObject(KEY_VIDEO)),
switch = PreCreationModelParser.deserialize(json.getJSONObject(KEY_SWITCH)),
)
}
}
@@ -3,7 +3,6 @@ package com.yandex.div.internal.viewpool.optimization
import androidx.annotation.AnyThread
import androidx.collection.ArrayMap
import com.yandex.div.core.view2.DivViewCreator
import kotlinx.serialization.Serializable
sealed class PerformanceDependentSession {
sealed class ViewObtainmentStatistics {
@@ -47,7 +46,6 @@ sealed class PerformanceDependentSession {
}
class Detailed : PerformanceDependentSession() {
@Serializable
data class ViewObtainment(
val obtainmentTime: Long,
val obtainmentDuration: Long,
@@ -1,24 +1,18 @@
package com.yandex.div.internal.viewpool.optimization
import android.content.Context
import androidx.datastore.core.DataStore
import androidx.datastore.core.DataStoreFactory
import androidx.datastore.core.Serializer
import com.yandex.div.core.annotations.Mockable
import com.yandex.div.core.dagger.DivScope
import com.yandex.div.core.dagger.Names.APP_CONTEXT
import com.yandex.div.internal.KLog
import com.yandex.div.internal.storage.DataStorage
import com.yandex.div.internal.storage.TextDataEditor
import com.yandex.div.internal.viewpool.ViewPreCreationProfile
import com.yandex.div.internal.viewpool.ViewPreCreationProfileParser
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.withContext
import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.decodeFromStream
import kotlinx.serialization.json.encodeToStream
import java.io.File
import java.io.InputStream
import java.io.OutputStream
import org.json.JSONObject
import java.util.WeakHashMap
import javax.inject.Inject
import javax.inject.Named
@@ -26,35 +20,30 @@ import javax.inject.Named
@DivScope
@Mockable
class ViewPreCreationProfileRepository @Inject constructor(
@Named(APP_CONTEXT) private val context: Context,
@param:Named(APP_CONTEXT) private val context: Context,
private val defaultProfile: ViewPreCreationProfile
) {
suspend fun get(id: String): ViewPreCreationProfile = withContext(Dispatchers.IO) {
runCatching { context.getStoreForId(id).data.first() }
runCatching { context.getStorageForId(context, id).data.first() }
.onFailure { KLog.e(TAG, it) }
.getOrNull() ?: defaultProfile.copy(id = id)
}
suspend fun save(profile: ViewPreCreationProfile): Boolean = withContext(Dispatchers.IO) {
runCatching { context.getStoreForId(profile.id!!).updateData { profile } }
runCatching { context.getStorageForId(context, profile.id!!).update(profile) }
.onFailure { KLog.e(TAG, it) }
.isSuccess
}
@OptIn(ExperimentalSerializationApi::class)
private object ViewPreCreationProfileSerializer : Serializer<ViewPreCreationProfile?> {
private val json = Json { encodeDefaults = false }
private object ViewPreCreationProfileEditor : TextDataEditor<ViewPreCreationProfile>() {
override val defaultValue: ViewPreCreationProfile? = null
override fun read(input: String): ViewPreCreationProfile {
return ViewPreCreationProfileParser.deserialize(JSONObject(input))
}
override suspend fun readFrom(input: InputStream) = runCatching {
json.decodeFromStream<ViewPreCreationProfile?>(input)
}.onFailure { KLog.e(TAG, it) }.getOrNull()
override suspend fun writeTo(t: ViewPreCreationProfile?, output: OutputStream) {
runCatching {
json.encodeToStream(t, output)
}.onFailure { KLog.e(TAG, it) }
override fun write(value: ViewPreCreationProfile): String {
return ViewPreCreationProfileParser.serialize(value).toString()
}
}
@@ -62,13 +51,14 @@ class ViewPreCreationProfileRepository @Inject constructor(
const val TAG = "OptimizedViewPreCreationProfileRepository"
const val STORE_PATH = "divkit_optimized_viewpool_profile_%s.json"
val stores = WeakHashMap<String, DataStore<ViewPreCreationProfile?>>()
val stores = WeakHashMap<String, DataStorage<ViewPreCreationProfile>>()
fun Context.getStoreForId(id: String): DataStore<ViewPreCreationProfile?> =
fun Context.getStorageForId(context: Context, id: String): DataStorage<ViewPreCreationProfile> =
stores.getOrPut(id) {
DataStoreFactory.create(
serializer = ViewPreCreationProfileSerializer,
produceFile = { File(filesDir, STORE_PATH.format(id)) }
DataStorage.create(
context = context,
fileName = STORE_PATH,
editor = ViewPreCreationProfileEditor
)
}
}
@@ -0,0 +1,143 @@
package com.yandex.div.internal.storage
import android.app.Application
import androidx.test.core.app.ApplicationProvider
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.launch
import kotlinx.coroutines.test.StandardTestDispatcher
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.UnconfinedTestDispatcher
import kotlinx.coroutines.test.advanceUntilIdle
import kotlinx.coroutines.test.runTest
import org.junit.Assert.assertEquals
import org.junit.Assert.assertFalse
import org.junit.Assert.assertNull
import org.junit.Assert.assertTrue
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.kotlin.any
import org.mockito.kotlin.doAnswer
import org.mockito.kotlin.eq
import org.mockito.kotlin.mock
import org.mockito.kotlin.verify
import org.mockito.kotlin.whenever
import org.robolectric.RobolectricTestRunner
import java.io.File
@ExperimentalCoroutinesApi
@RunWith(RobolectricTestRunner::class)
class DataStorageTest {
private val context = ApplicationProvider.getApplicationContext<Application>()
private val editor = mock<DataEditor<String>> {
on { write(any(), any()) } doAnswer { Unit }
}
private val testFile = File(context.filesDir, "test_file")
@Test
fun `data should be null initially`() = runTest {
val storage = createDataStorage(editor)
collectFlow(storage.data)
assertNull(storage.data.value)
}
@Test
fun `update should write data to file and update state`() = runTest {
val storage = createDataStorage(editor)
val testData = "test data"
storage.update(testData)
collectFlow(storage.data)
advanceUntilIdle()
verify(editor).write(eq(testData), any())
assertEquals(testData, storage.data.value)
}
@Test
fun `clear should delete file and reset state`() = runTest {
val storage = createDataStorage(editor)
val testData = "test data"
storage.update(testData)
collectFlow(storage.data)
advanceUntilIdle()
assertEquals(testData, storage.data.value)
val result = storage.clear()
advanceUntilIdle()
assertTrue(result)
assertNull(storage.data.value)
}
@Test
fun `data should be read from file on initialization`() = runTest {
testFile.parentFile?.mkdirs()
testFile.createNewFile()
val testData = "test data"
whenever(editor.read(any())).thenReturn(testData)
val storage = createDataStorage(editor)
collectFlow(storage.data)
advanceUntilIdle()
assertEquals(testData, storage.data.value)
}
@Test
fun `data should be null if file reading fails`() = runTest {
whenever(editor.read(any())).thenThrow(RuntimeException("Read error"))
val storage = createDataStorage(editor)
collectFlow(storage.data)
advanceUntilIdle()
assertNull(storage.data.value)
}
@Test
fun `update should handle exceptions`() = runTest {
val testData = "test data"
whenever(editor.write(any(), any())).thenThrow(RuntimeException("Write error"))
val storage = createDataStorage(editor)
storage.update(testData)
collectFlow(storage.data)
advanceUntilIdle()
// Data should remain null due to exception
assertNull(storage.data.value)
}
@Test
fun `clear should handle exceptions`() = runTest {
// Make file undeletable by making it a non-empty directory
testFile.mkdirs()
File(testFile, "sub_file").createNewFile()
val storage = createDataStorage(editor)
val result = storage.clear()
advanceUntilIdle()
assertFalse(result)
}
private fun <T> TestScope.createDataStorage(editor: DataEditor<T>): DataStorage<T> {
val coroutineDispatcher = StandardTestDispatcher(testScheduler)
val coroutineScope = TestScope(coroutineDispatcher)
return DataStorage.create(context, testFile.name, editor, coroutineScope)
}
private fun <T> TestScope.collectFlow(flow: Flow<T>): Job {
return backgroundScope.launch(UnconfinedTestDispatcher(testScheduler)) {
flow.collect()
}
}
}
-6
View File
@@ -19,7 +19,6 @@ kotlin = "2.2.10"
kotlin-binaryCompatibilityValidator = "0.18.1"
kotlin-coroutines = "1.10.2"
kotlin-ksp = "2.3.4"
kotlin-serialization = "1.9.0"
kotlinpoet = "2.2.0"
lottie = "6.1.0"
markwon = "4.6.2"
@@ -46,7 +45,6 @@ androidx-collection = "1.5.0"
androidx-compose-bom = "2024.04.00"
androidx-constraint = "2.2.1"
androidx-core = "1.16.0"
androidx-datastore = "1.1.7"
androidx-espresso = "3.7.0"
androidx-fragment = "1.8.9"
androidx-lifecycle = "2.9.4"
@@ -80,7 +78,6 @@ androidx-collection = { module = "androidx.collection:collection", version.ref =
androidx-constraintlayout = { module = "androidx.constraintlayout:constraintlayout", version.ref = "androidx-constraint" }
androidx-core = { module = "androidx.core:core", version.ref = "androidx-core" }
androidx-coreKtx = { module = "androidx.core:core-ktx", version.ref = "androidx-core" }
androidx-datastore = { module = "androidx.datastore:datastore", version.ref = "androidx-datastore" }
androidx-fragment = { module = "androidx.fragment:fragment-ktx", version.ref = "androidx-fragment" }
androidx-material = { module = "com.google.android.material:material", version.ref = "androidx-material" }
androidx-preference = { module = "androidx.preference:preference-ktx", version.ref = "androidx-preference" }
@@ -159,9 +156,6 @@ kotlin-corountines-android = { module = "org.jetbrains.kotlinx:kotlinx-coroutine
kotlin-corountines-core = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "kotlin-coroutines" }
kotlin-corountines-test = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-test", version.ref = "kotlin-coroutines" }
kotlin-serialization-json = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "kotlin-serialization" }
kotlin-serialization-plugin = { module = "org.jetbrains.kotlin:kotlin-serialization", version.ref = "kotlin" }
kotlinpoet = { module = "com.squareup:kotlinpoet", version.ref = "kotlinpoet" }
lottie = { module = "com.airbnb.android:lottie", version.ref = "lottie" }