From ddb6d46b6f9e8ff9743e43a9ec306dcdc577dba4 Mon Sep 17 00:00:00 2001 From: Torsten Grote Date: Mon, 19 Sep 2022 14:59:48 -0300 Subject: [PATCH] Change repo attribute icons to be localized This affects anti-features and categories. Reflection diffing has been made more robust in the process with the earlier FileV2 hack removed and better error messages. --- .../org/fdroid/database/RepositoryDiffTest.kt | 22 ++- .../java/org/fdroid/database/TestUtils.kt | 4 +- .../java/org/fdroid/database/Repository.kt | 8 +- .../java/org/fdroid/database/RepositoryDao.kt | 6 +- libs/index/build.gradle | 2 + .../org/fdroid/index/v2/ReflectionDiffer.kt | 128 +++++++++++++----- .../fdroid/index/v2/ReflectionDifferTest.kt | 50 +++++++ .../kotlin/org/fdroid/index/v2/IndexV2.kt | 9 +- .../src/main/assets/diff-empty-max/1337.json | 28 ++-- .../src/main/assets/diff-empty-max/23.json | 32 +++-- .../src/main/assets/diff-empty-max/42.json | 32 +++-- .../src/main/assets/diff-empty-mid/23.json | 8 +- .../src/main/assets/diff-empty-mid/42.json | 8 +- .../src/main/assets/index-max-v2.json | 32 +++-- .../src/main/assets/index-mid-v2.json | 8 +- .../main/kotlin/org/fdroid/test/TestDataV2.kt | 20 +-- .../kotlin/org/fdroid/test/TestRepoUtils.kt | 4 +- 17 files changed, 278 insertions(+), 123 deletions(-) diff --git a/libs/database/src/dbTest/java/org/fdroid/database/RepositoryDiffTest.kt b/libs/database/src/dbTest/java/org/fdroid/database/RepositoryDiffTest.kt index 0aa71f618..0c733255d 100644 --- a/libs/database/src/dbTest/java/org/fdroid/database/RepositoryDiffTest.kt +++ b/libs/database/src/dbTest/java/org/fdroid/database/RepositoryDiffTest.kt @@ -11,7 +11,7 @@ import org.fdroid.index.v2.ReleaseChannelV2 import org.fdroid.index.v2.RepoV2 import org.fdroid.test.DiffUtils.applyDiff import org.fdroid.test.DiffUtils.randomDiff -import org.fdroid.test.TestRepoUtils.getRandomFileV2 +import org.fdroid.test.TestRepoUtils.getRandomLocalizedFileV2 import org.fdroid.test.TestRepoUtils.getRandomLocalizedTextV2 import org.fdroid.test.TestRepoUtils.getRandomMirror import org.fdroid.test.TestRepoUtils.getRandomRepo @@ -116,13 +116,17 @@ internal class RepositoryDiffTest : DbTest() { fun antiFeaturesDiff() { val repo = getRandomRepo().copy(antiFeatures = getRandomMap { getRandomString() to AntiFeatureV2( - icon = getRandomFileV2(), + icon = getRandomLocalizedFileV2(), name = getRandomLocalizedTextV2(), description = getRandomLocalizedTextV2(), ) }) val antiFeatures = repo.antiFeatures.randomDiff { - AntiFeatureV2(getRandomFileV2(), getRandomLocalizedTextV2(), getRandomLocalizedTextV2()) + AntiFeatureV2( + icon = getRandomLocalizedFileV2(), + name = getRandomLocalizedTextV2(), + description = getRandomLocalizedTextV2(), + ) } val json = """ { @@ -141,7 +145,7 @@ internal class RepositoryDiffTest : DbTest() { fun antiFeatureKeyChangeDiff() { val antiFeatureKey = getRandomString() val antiFeature = AntiFeatureV2( - icon = getRandomFileV2(), + icon = getRandomLocalizedFileV2(), name = getRandomLocalizedTextV2(), description = getRandomLocalizedTextV2(), ) @@ -150,7 +154,7 @@ internal class RepositoryDiffTest : DbTest() { @Suppress("UNCHECKED_CAST") val newAntiFeatures = mapOf(antiFeatureKey to antiFeature.copy( - icon = null, + icon = emptyMap(), name = getRandomLocalizedTextV2(), description = getRandomLocalizedTextV2(), )) @@ -173,13 +177,17 @@ internal class RepositoryDiffTest : DbTest() { fun categoriesDiff() { val repo = getRandomRepo().copy(categories = getRandomMap { getRandomString() to CategoryV2( - icon = getRandomFileV2(), + icon = getRandomLocalizedFileV2(), name = getRandomLocalizedTextV2(), description = getRandomLocalizedTextV2(), ) }) val categories = repo.categories.randomDiff { - CategoryV2(getRandomFileV2(), getRandomLocalizedTextV2(), getRandomLocalizedTextV2()) + CategoryV2( + icon = getRandomLocalizedFileV2(), + name = getRandomLocalizedTextV2(), + description = getRandomLocalizedTextV2(), + ) } val json = """ { diff --git a/libs/database/src/dbTest/java/org/fdroid/database/TestUtils.kt b/libs/database/src/dbTest/java/org/fdroid/database/TestUtils.kt index b4ea0c63f..483785154 100644 --- a/libs/database/src/dbTest/java/org/fdroid/database/TestUtils.kt +++ b/libs/database/src/dbTest/java/org/fdroid/database/TestUtils.kt @@ -31,8 +31,8 @@ internal object TestUtils { val expectedAntiFeatures = repoV2.antiFeatures.toRepoAntiFeatures(repoId).toSet() assertEquals(expectedAntiFeatures, repo.antiFeatures.toSet()) // categories - val expectedCategories = repoV2.categories.toRepoCategories(repoId).toSet() - assertEquals(expectedCategories, repo.categories.toSet()) + val expectedCategories = repoV2.categories.toRepoCategories(repoId).sortedBy { it.id } + assertEquals(expectedCategories, repo.categories.sortedBy { it.id }) // release channels val expectedReleaseChannels = repoV2.releaseChannels.toRepoReleaseChannel(repoId).toSet() assertEquals(expectedReleaseChannels, repo.releaseChannels.toSet()) diff --git a/libs/database/src/main/java/org/fdroid/database/Repository.kt b/libs/database/src/main/java/org/fdroid/database/Repository.kt index c585d7bf0..ce7eb3aee 100644 --- a/libs/database/src/main/java/org/fdroid/database/Repository.kt +++ b/libs/database/src/main/java/org/fdroid/database/Repository.kt @@ -237,7 +237,7 @@ internal fun MirrorV2.toMirror(repoId: Long) = Mirror( * An attribute belonging to a [Repository]. */ public abstract class RepoAttribute { - public abstract val icon: FileV2? + public abstract val icon: LocalizedFileV2 internal abstract val name: LocalizedTextV2 internal abstract val description: LocalizedTextV2 @@ -264,7 +264,7 @@ public abstract class RepoAttribute { public data class AntiFeature internal constructor( internal val repoId: Long, internal val id: String, - @Embedded(prefix = "icon_") public override val icon: FileV2? = null, + override val icon: LocalizedFileV2, override val name: LocalizedTextV2, override val description: LocalizedTextV2, ) : RepoAttribute() { @@ -299,7 +299,7 @@ internal fun Map.toRepoAntiFeatures(repoId: Long) = map { public data class Category internal constructor( internal val repoId: Long, public val id: String, - @Embedded(prefix = "icon_") public override val icon: FileV2? = null, + override val icon: LocalizedFileV2, override val name: LocalizedTextV2, override val description: LocalizedTextV2, ) : RepoAttribute() { @@ -334,7 +334,7 @@ internal fun Map.toRepoCategories(repoId: Long) = map { public data class ReleaseChannel( internal val repoId: Long, internal val id: String, - @Embedded(prefix = "icon_") public override val icon: FileV2? = null, + override val icon: LocalizedFileV2 = emptyMap(), override val name: LocalizedTextV2, override val description: LocalizedTextV2, ) : RepoAttribute() { diff --git a/libs/database/src/main/java/org/fdroid/database/RepositoryDao.kt b/libs/database/src/main/java/org/fdroid/database/RepositoryDao.kt index e096256cf..1dc36c887 100644 --- a/libs/database/src/main/java/org/fdroid/database/RepositoryDao.kt +++ b/libs/database/src/main/java/org/fdroid/database/RepositoryDao.kt @@ -272,7 +272,7 @@ internal interface RepositoryDaoInt : RepositoryDao { jsonObjectKey = "antiFeatures", itemList = repo.antiFeatures, itemFinder = { key, item -> item.id == key }, - newItem = { key -> AntiFeature(repoId, key, null, emptyMap(), emptyMap()) }, + newItem = { key -> AntiFeature(repoId, key, emptyMap(), emptyMap(), emptyMap()) }, deleteAll = { deleteAntiFeatures(repoId) }, deleteOne = { key -> deleteAntiFeature(repoId, key) }, insertReplace = { list -> insertAntiFeatures(list) }, @@ -283,7 +283,7 @@ internal interface RepositoryDaoInt : RepositoryDao { jsonObjectKey = "categories", itemList = repo.categories, itemFinder = { key, item -> item.id == key }, - newItem = { key -> Category(repoId, key, null, emptyMap(), emptyMap()) }, + newItem = { key -> Category(repoId, key, emptyMap(), emptyMap(), emptyMap()) }, deleteAll = { deleteCategories(repoId) }, deleteOne = { key -> deleteCategory(repoId, key) }, insertReplace = { list -> insertCategories(list) }, @@ -294,7 +294,7 @@ internal interface RepositoryDaoInt : RepositoryDao { jsonObjectKey = "releaseChannels", itemList = repo.releaseChannels, itemFinder = { key, item -> item.id == key }, - newItem = { key -> ReleaseChannel(repoId, key, null, emptyMap(), emptyMap()) }, + newItem = { key -> ReleaseChannel(repoId, key, emptyMap(), emptyMap(), emptyMap()) }, deleteAll = { deleteReleaseChannels(repoId) }, deleteOne = { key -> deleteReleaseChannel(repoId, key) }, insertReplace = { list -> insertReleaseChannels(list) }, diff --git a/libs/index/build.gradle b/libs/index/build.gradle index 5f73988b7..69a0ec101 100644 --- a/libs/index/build.gradle +++ b/libs/index/build.gradle @@ -70,6 +70,8 @@ kotlin { } } androidTest { + // needed because of https://issuetracker.google.com/issues/231701341 + kotlin.srcDir("src/commonTest/kotlin") dependencies { implementation 'junit:junit:4.13.2' implementation 'io.mockk:mockk:1.12.4' diff --git a/libs/index/src/androidMain/kotlin/org/fdroid/index/v2/ReflectionDiffer.kt b/libs/index/src/androidMain/kotlin/org/fdroid/index/v2/ReflectionDiffer.kt index 589dbad34..3c709e970 100644 --- a/libs/index/src/androidMain/kotlin/org/fdroid/index/v2/ReflectionDiffer.kt +++ b/libs/index/src/androidMain/kotlin/org/fdroid/index/v2/ReflectionDiffer.kt @@ -17,8 +17,10 @@ import kotlinx.serialization.serializer import kotlin.reflect.KClass import kotlin.reflect.KFunction import kotlin.reflect.KParameter +import kotlin.reflect.KType import kotlin.reflect.full.memberProperties import kotlin.reflect.full.primaryConstructor +import kotlin.reflect.typeOf /** * A class using Kotlin reflection to implement JSON Merge Patch (RFC 7386) against data classes. @@ -28,32 +30,9 @@ import kotlin.reflect.full.primaryConstructor */ public object ReflectionDiffer { - @Throws(SerializationException::class) - public fun applyDiff( - obj: Map, - diff: JsonObject, - isFileV2: Boolean = false, - ): Map = obj.toMutableMap().apply { - diff.entries.forEach { (key, value) -> - when (value) { - is JsonNull -> remove(key) - is JsonPrimitive -> set(key, value.jsonPrimitive.content) - is JsonObject -> { - val newValue: Any = if (isFileV2) { - constructFromJson(FileV2::class.primaryConstructor!!, value.jsonObject) - } else { - applyDiff(HashMap(), value.jsonObject) - } - set(key, newValue) - } - else -> e("unsupported map value: $value") - } - } - } - @Throws(SerializationException::class) public fun applyDiff(obj: T, diff: JsonObject): T { - val constructor = obj::class.primaryConstructor ?: e("no primary constructor") + val constructor = obj::class.primaryConstructor ?: e("no primary constructor ${obj::class}") val params = HashMap() constructor.parameters.forEach { parameter -> val prop = obj::class.memberProperties.find { memberProperty -> @@ -79,14 +58,7 @@ public object ReflectionDiffer { List::class -> diff[prop.name]?.jsonArrayOrNull()?.map { it.primitiveOrNull()?.contentOrNull ?: e("${prop.name} non-primitive array") } ?: e("${prop.name} no array") - Map::class -> if (prop.name == "icon") applyDiff( - obj = prop.getter.call(obj) as? Map ?: emptyMap(), - diff = diff[prop.name]?.jsonObjectOrNull() ?: e("${prop.name} no map"), - isFileV2 = true, // yes this is super hacky - ) else applyDiff( - obj = prop.getter.call(obj) as? Map ?: emptyMap(), - diff = diff[prop.name]?.jsonObjectOrNull() ?: e("${prop.name} no map"), - ) + Map::class -> diffMap(prop.returnType, prop.getter.call(obj), prop.name, diff) else -> { val newObj = prop.getter.call(obj) val jsonObject = diff[prop.name] as? JsonObject ?: e("${prop.name} no dict") @@ -134,10 +106,7 @@ public object ReflectionDiffer { List::class -> diff[prop.name]?.jsonArrayOrNull()?.map { it.primitiveOrNull()?.contentOrNull ?: e("${prop.name} non-primitive array") } ?: e("${prop.name} no array") - Map::class -> applyDiff( - obj = HashMap(), - diff = diff[prop.name]?.jsonObjectOrNull() ?: e("${prop.name} no dict"), - ) + Map::class -> diffMap(prop.type, null, prop.name, diff) else -> constructFromJson( factory = (prop.type.classifier as KClass<*>).primaryConstructor!!, diff = diff[prop.name]?.jsonObjectOrNull() ?: e("${prop.name} no dict"), @@ -147,6 +116,93 @@ public object ReflectionDiffer { return factory.callBy(params) } + @Suppress("UNCHECKED_CAST") + private fun diffMap(type: KType, obj: T?, key: String?, diff: JsonObject) = when (type) { + typeOf() -> applyTextDiff( + obj = obj as? LocalizedTextV2 ?: HashMap(), + diff = diff[key]?.jsonObjectOrNull() ?: e("$key no map"), + ) + typeOf() -> applyTextDiff( + obj = obj as? LocalizedTextV2? ?: HashMap(), + diff = diff[key]?.jsonObjectOrNull() ?: e("$key no map"), + ) + typeOf() -> applyFileDiff( + obj = obj as? LocalizedFileV2 ?: HashMap(), + diff = diff[key]?.jsonObjectOrNull() ?: e("$key no map"), + ) + typeOf() -> applyFileDiff( + obj = obj as? LocalizedFileV2? ?: HashMap(), + diff = diff[key]?.jsonObjectOrNull() ?: e("$key no map"), + ) + typeOf>() -> applyMapTextDiff( + obj = obj as? Map ?: HashMap(), + diff = diff[key]?.jsonObjectOrNull() ?: e("$key no map"), + ) + typeOf?>() -> applyMapTextDiff( + obj = obj as? Map? ?: HashMap(), + diff = diff[key]?.jsonObjectOrNull() ?: e("$key no map"), + ) + else -> e("Unknown map: $key: $type = ${diff[key]}") + } + + @Throws(SerializationException::class) + private fun applyTextDiff( + obj: LocalizedTextV2, + diff: JsonObject, + ): LocalizedTextV2 = obj.toMutableMap().apply { + diff.entries.forEach { (locale, textElement) -> + if (textElement is JsonNull) { + remove(locale) + return@forEach + } + val text = textElement.primitiveOrNull()?.contentOrNull + ?: throw SerializationException("no string: $textElement") + set(locale, text) + } + } + + @Throws(SerializationException::class) + private fun applyFileDiff( + obj: LocalizedFileV2, + diff: JsonObject, + ): LocalizedFileV2 = obj.toMutableMap().apply { + diff.entries.forEach { (locale, fileV2Element) -> + if (fileV2Element is JsonNull) { + remove(locale) + return@forEach + } + val fileV2Object = fileV2Element.jsonObjectOrNull() + ?: throw SerializationException("no FileV2: $fileV2Element") + val fileV2 = if (locale in obj) { + applyDiff(obj[locale] as FileV2, fileV2Object) + } else { + constructFromJson(FileV2::class.primaryConstructor!!, fileV2Object) + } + set(locale, fileV2) + } + } + + @Throws(SerializationException::class) + private fun applyMapTextDiff( + obj: Map, + diff: JsonObject, + ): Map = obj.toMutableMap().apply { + diff.entries.forEach { (key, localizedTextElement) -> + if (localizedTextElement is JsonNull) { + remove(key) + return@forEach + } + val localizedTextObject = localizedTextElement.jsonObjectOrNull() + ?: throw SerializationException("no FileV2: $localizedTextElement") + val localizedText = if (key in obj) { + applyTextDiff(obj[key] as LocalizedTextV2, localizedTextObject) + } else { + applyTextDiff(HashMap(), localizedTextObject) + } + set(key, localizedText) + } + } + private fun JsonElement.primitiveOrNull(): JsonPrimitive? = try { jsonPrimitive } catch (e: IllegalArgumentException) { diff --git a/libs/index/src/androidTest/kotlin/org/fdroid/index/v2/ReflectionDifferTest.kt b/libs/index/src/androidTest/kotlin/org/fdroid/index/v2/ReflectionDifferTest.kt index 735302ebb..2e9b7460c 100644 --- a/libs/index/src/androidTest/kotlin/org/fdroid/index/v2/ReflectionDifferTest.kt +++ b/libs/index/src/androidTest/kotlin/org/fdroid/index/v2/ReflectionDifferTest.kt @@ -14,6 +14,7 @@ import org.fdroid.test.DiffUtils.clean import org.fdroid.test.DiffUtils.cleanMetadata import org.fdroid.test.DiffUtils.cleanRepo import org.fdroid.test.DiffUtils.cleanVersion +import org.fdroid.test.LOCALE import java.io.File import java.io.FileInputStream import kotlin.reflect.full.primaryConstructor @@ -66,6 +67,55 @@ internal class ReflectionDifferTest { endPath = "$assetPath/index-max-v2.json", ) + @Test + fun testLocalizedFileV2() { + val category1 = CategoryV2( + name = mapOf(LOCALE to "Cat1"), + icon = mapOf(LOCALE to FileV2( + name = "file1", + sha256 = "hash", + size = 1, + )), + ) + val category2 = CategoryV2( + name = mapOf(LOCALE to "Cat2"), + ) + val diff1 = JsonObject( + mapOf("icon" to JsonObject( + mapOf(LOCALE to JsonObject( + mapOf("name" to JsonPrimitive("file1b")) + )) + )) + ) + val diff2 = JsonObject( + mapOf("icon" to JsonObject( + mapOf(LOCALE to JsonObject( + mapOf( + "name" to JsonPrimitive("file2"), + "sha256" to JsonPrimitive("hash2"), + "size" to JsonPrimitive(2L), + ) + )) + )) + ) + val diffedCat1 = ReflectionDiffer.applyDiff(category1, diff1) + val diffedCat2 = ReflectionDiffer.applyDiff(category2, diff2) + val diffedIcon1 = diffedCat1.icon[LOCALE] + val diffedIcon2 = diffedCat2.icon[LOCALE] + val expectedIcon1 = FileV2( + name = "file1b", + sha256 = "hash", + size = 1, + ) + val expectedIcon2 = FileV2( + name = "file2", + sha256 = "hash2", + size = 2, + ) + assertEquals(expectedIcon1, diffedIcon1) + assertEquals(expectedIcon2, diffedIcon2) + } + @Test fun testClassWithoutPrimaryConstructor() { class NoConstructor { diff --git a/libs/index/src/commonMain/kotlin/org/fdroid/index/v2/IndexV2.kt b/libs/index/src/commonMain/kotlin/org/fdroid/index/v2/IndexV2.kt index a51bd3488..0b6faa1b1 100644 --- a/libs/index/src/commonMain/kotlin/org/fdroid/index/v2/IndexV2.kt +++ b/libs/index/src/commonMain/kotlin/org/fdroid/index/v2/IndexV2.kt @@ -58,6 +58,7 @@ public data class FileV2( if (string == null) return null return json.decodeFromString(string) } + @JvmStatic public fun fromPath(path: String): FileV2 = FileV2(path) } @@ -93,8 +94,8 @@ public data class RepoV2( ) { public fun walkFiles(fileConsumer: (FileV2?) -> Unit) { icon.values.forEach { fileConsumer(it) } - antiFeatures.values.forEach { fileConsumer(it.icon) } - categories.values.forEach { fileConsumer(it.icon) } + antiFeatures.values.forEach { it.icon.values.forEach { icon -> fileConsumer(icon) } } + categories.values.forEach { it.icon.values.forEach { icon -> fileConsumer(icon) } } } } @@ -110,14 +111,14 @@ public data class MirrorV2( @Serializable public data class AntiFeatureV2( - val icon: FileV2? = null, + val icon: LocalizedFileV2 = emptyMap(), val name: LocalizedTextV2, val description: LocalizedTextV2 = emptyMap(), ) @Serializable public data class CategoryV2( - val icon: FileV2? = null, + val icon: LocalizedFileV2 = emptyMap(), val name: LocalizedTextV2, val description: LocalizedTextV2 = emptyMap(), ) diff --git a/libs/sharedTest/src/main/assets/diff-empty-max/1337.json b/libs/sharedTest/src/main/assets/diff-empty-max/1337.json index 5bef7a8ba..cba38a830 100644 --- a/libs/sharedTest/src/main/assets/diff-empty-max/1337.json +++ b/libs/sharedTest/src/main/assets/diff-empty-max/1337.json @@ -47,9 +47,11 @@ }, "AntiFeature": { "icon": { - "name": "/icons/antifeature.png", - "sha256": "24758e480ae66297c7947f107db9ea03d2933c9d5c110d02046977cf78d43def", - "size": 254916 + "en-US": { + "name": "/icons/antifeature.png", + "sha256": "24758e480ae66297c7947f107db9ea03d2933c9d5c110d02046977cf78d43def", + "size": 254916 + } } }, "NonFreeNet": { @@ -70,9 +72,11 @@ "System": null, "Cat3": { "icon": { - "name": "/icons/cat3.png", - "sha256": "54758e480ae76297c7947f107db9ea03d2933c9d5c110d02046977cf78d43def", - "size": 9223372036854775807 + "en-US": { + "name": "/icons/cat3.png", + "sha256": "54758e480ae76297c7947f107db9ea03d2933c9d5c110d02046977cf78d43def", + "size": 9223372036854775807 + } }, "name": { "en-US": "Cat3" @@ -83,9 +87,11 @@ }, "Cat2": { "icon": { - "name": "/icons/cat2.png", - "sha256": "54758e480ae76297c7947f107db9ea03d2933c9d5c110d02046977cf78d43def", - "size": 9223372036854775807 + "en-US": { + "name": "/icons/cat2.png", + "sha256": "54758e480ae76297c7947f107db9ea03d2933c9d5c110d02046977cf78d43def", + "size": 9223372036854775807 + } }, "name": { "en-US": "Cat3" @@ -96,7 +102,9 @@ }, "Cat1": { "icon": { - "name": "/icons/cat1.png" + "en-US": { + "name": "/icons/cat1.png" + } }, "description": { "en-US": "Cat1" diff --git a/libs/sharedTest/src/main/assets/diff-empty-max/23.json b/libs/sharedTest/src/main/assets/diff-empty-max/23.json index a5acc84d1..e81e0d3da 100644 --- a/libs/sharedTest/src/main/assets/diff-empty-max/23.json +++ b/libs/sharedTest/src/main/assets/diff-empty-max/23.json @@ -45,9 +45,11 @@ }, "AntiFeature": { "icon": { - "name": "/icons/antifeature.png", - "sha256": "24758e480ae66297c7947f107db9ea03d2933c9d5c110d02046977cf78d43def", - "size": 254916 + "en-US": { + "name": "/icons/antifeature.png", + "sha256": "24758e480ae66297c7947f107db9ea03d2933c9d5c110d02046977cf78d43def", + "size": 254916 + } }, "name": { "en-US": "AntiFeature" @@ -73,9 +75,11 @@ "categories": { "Cat3": { "icon": { - "name": "/icons/cat3.png", - "sha256": "54758e480ae76297c7947f107db9ea03d2933c9d5c110d02046977cf78d43def", - "size": 9223372036854775807 + "en-US": { + "name": "/icons/cat3.png", + "sha256": "54758e480ae76297c7947f107db9ea03d2933c9d5c110d02046977cf78d43def", + "size": 9223372036854775807 + } }, "name": { "en-US": "Cat3" @@ -86,9 +90,11 @@ }, "Cat2": { "icon": { - "name": "/icons/cat2.png", - "sha256": "54758e480ae76297c7947f107db9ea03d2933c9d5c110d02046977cf78d43def", - "size": 9223372036854775807 + "en-US": { + "name": "/icons/cat2.png", + "sha256": "54758e480ae76297c7947f107db9ea03d2933c9d5c110d02046977cf78d43def", + "size": 9223372036854775807 + } }, "name": { "en-US": "Cat3" @@ -99,9 +105,11 @@ }, "Cat1": { "icon": { - "name": "/icons/cat1.png", - "sha256": "54758e480ae76297c7947f107db9ea03d2933c9d5c110d02046977cf78d43def", - "size": 9223372036854775807 + "en-US": { + "name": "/icons/cat1.png", + "sha256": "54758e480ae76297c7947f107db9ea03d2933c9d5c110d02046977cf78d43def", + "size": 9223372036854775807 + } }, "name": { "en-US": "Cat1" diff --git a/libs/sharedTest/src/main/assets/diff-empty-max/42.json b/libs/sharedTest/src/main/assets/diff-empty-max/42.json index 88cf0b1b7..5ea9dfa97 100644 --- a/libs/sharedTest/src/main/assets/diff-empty-max/42.json +++ b/libs/sharedTest/src/main/assets/diff-empty-max/42.json @@ -45,9 +45,11 @@ }, "AntiFeature": { "icon": { - "name": "/icons/antifeature.png", - "sha256": "24758e480ae66297c7947f107db9ea03d2933c9d5c110d02046977cf78d43def", - "size": 254916 + "en-US": { + "name": "/icons/antifeature.png", + "sha256": "24758e480ae66297c7947f107db9ea03d2933c9d5c110d02046977cf78d43def", + "size": 254916 + } }, "name": { "en-US": "AntiFeature" @@ -73,9 +75,11 @@ "categories": { "Cat3": { "icon": { - "name": "/icons/cat3.png", - "sha256": "54758e480ae76297c7947f107db9ea03d2933c9d5c110d02046977cf78d43def", - "size": 9223372036854775807 + "en-US": { + "name": "/icons/cat3.png", + "sha256": "54758e480ae76297c7947f107db9ea03d2933c9d5c110d02046977cf78d43def", + "size": 9223372036854775807 + } }, "name": { "en-US": "Cat3" @@ -86,9 +90,11 @@ }, "Cat2": { "icon": { - "name": "/icons/cat2.png", - "sha256": "54758e480ae76297c7947f107db9ea03d2933c9d5c110d02046977cf78d43def", - "size": 9223372036854775807 + "en-US": { + "name": "/icons/cat2.png", + "sha256": "54758e480ae76297c7947f107db9ea03d2933c9d5c110d02046977cf78d43def", + "size": 9223372036854775807 + } }, "name": { "en-US": "Cat3" @@ -99,9 +105,11 @@ }, "Cat1": { "icon": { - "name": "/icons/cat1.png", - "sha256": "54758e480ae76297c7947f107db9ea03d2933c9d5c110d02046977cf78d43def", - "size": 9223372036854775807 + "en-US": { + "name": "/icons/cat1.png", + "sha256": "54758e480ae76297c7947f107db9ea03d2933c9d5c110d02046977cf78d43def", + "size": 9223372036854775807 + } }, "name": { "en-US": "Cat1" diff --git a/libs/sharedTest/src/main/assets/diff-empty-mid/23.json b/libs/sharedTest/src/main/assets/diff-empty-mid/23.json index 104cb4a33..2c6ec47fc 100644 --- a/libs/sharedTest/src/main/assets/diff-empty-mid/23.json +++ b/libs/sharedTest/src/main/assets/diff-empty-mid/23.json @@ -40,9 +40,11 @@ "categories": { "Cat1": { "icon": { - "name": "/icons/cat2.png", - "sha256": "54758e480ae76297c7947f107db9ea03d2933c9d5c110d02046977cf78d43def", - "size": 9223372036854775807 + "en-US": { + "name": "/icons/cat2.png", + "sha256": "54758e480ae76297c7947f107db9ea03d2933c9d5c110d02046977cf78d43def", + "size": 9223372036854775807 + } }, "name": { "en-US": "Cat1" diff --git a/libs/sharedTest/src/main/assets/diff-empty-mid/42.json b/libs/sharedTest/src/main/assets/diff-empty-mid/42.json index aaf931490..bed2ee9df 100644 --- a/libs/sharedTest/src/main/assets/diff-empty-mid/42.json +++ b/libs/sharedTest/src/main/assets/diff-empty-mid/42.json @@ -40,9 +40,11 @@ "categories": { "Cat1": { "icon": { - "name": "/icons/cat2.png", - "sha256": "54758e480ae76297c7947f107db9ea03d2933c9d5c110d02046977cf78d43def", - "size": 9223372036854775807 + "en-US": { + "name": "/icons/cat2.png", + "sha256": "54758e480ae76297c7947f107db9ea03d2933c9d5c110d02046977cf78d43def", + "size": 9223372036854775807 + } }, "name": { "en-US": "Cat1" diff --git a/libs/sharedTest/src/main/assets/index-max-v2.json b/libs/sharedTest/src/main/assets/index-max-v2.json index 23f93134a..b9d0113b2 100644 --- a/libs/sharedTest/src/main/assets/index-max-v2.json +++ b/libs/sharedTest/src/main/assets/index-max-v2.json @@ -49,9 +49,11 @@ }, "AntiFeature": { "icon": { - "name": "/icons/antifeature.png", - "sha256": "24758e480ae66297c7947f107db9ea03d2933c9d5c110d02046977cf78d43def", - "size": 254916 + "en-US": { + "name": "/icons/antifeature.png", + "sha256": "24758e480ae66297c7947f107db9ea03d2933c9d5c110d02046977cf78d43def", + "size": 254916 + } }, "name": { "en-US": "AntiFeature" @@ -77,9 +79,11 @@ "categories": { "Cat3": { "icon": { - "name": "/icons/cat3.png", - "sha256": "54758e480ae76297c7947f107db9ea03d2933c9d5c110d02046977cf78d43def", - "size": 9223372036854775807 + "en-US": { + "name": "/icons/cat3.png", + "sha256": "54758e480ae76297c7947f107db9ea03d2933c9d5c110d02046977cf78d43def", + "size": 9223372036854775807 + } }, "name": { "en-US": "Cat3" @@ -90,9 +94,11 @@ }, "Cat2": { "icon": { - "name": "/icons/cat2.png", - "sha256": "54758e480ae76297c7947f107db9ea03d2933c9d5c110d02046977cf78d43def", - "size": 9223372036854775807 + "en-US": { + "name": "/icons/cat2.png", + "sha256": "54758e480ae76297c7947f107db9ea03d2933c9d5c110d02046977cf78d43def", + "size": 9223372036854775807 + } }, "name": { "en-US": "Cat3" @@ -103,9 +109,11 @@ }, "Cat1": { "icon": { - "name": "/icons/cat1.png", - "sha256": "54758e480ae76297c7947f107db9ea03d2933c9d5c110d02046977cf78d43def", - "size": 9223372036854775807 + "en-US": { + "name": "/icons/cat1.png", + "sha256": "54758e480ae76297c7947f107db9ea03d2933c9d5c110d02046977cf78d43def", + "size": 9223372036854775807 + } }, "name": { "en-US": "Cat1" diff --git a/libs/sharedTest/src/main/assets/index-mid-v2.json b/libs/sharedTest/src/main/assets/index-mid-v2.json index e63381508..8fdc20f83 100644 --- a/libs/sharedTest/src/main/assets/index-mid-v2.json +++ b/libs/sharedTest/src/main/assets/index-mid-v2.json @@ -40,9 +40,11 @@ "categories": { "Cat1": { "icon": { - "name": "/icons/cat2.png", - "sha256": "54758e480ae76297c7947f107db9ea03d2933c9d5c110d02046977cf78d43def", - "size": 9223372036854775807 + "en-US": { + "name": "/icons/cat2.png", + "sha256": "54758e480ae76297c7947f107db9ea03d2933c9d5c110d02046977cf78d43def", + "size": 9223372036854775807 + } }, "name": { "en-US": "Cat1" diff --git a/libs/sharedTest/src/main/kotlin/org/fdroid/test/TestDataV2.kt b/libs/sharedTest/src/main/kotlin/org/fdroid/test/TestDataV2.kt index aeba7cc88..cafd07929 100644 --- a/libs/sharedTest/src/main/kotlin/org/fdroid/test/TestDataV2.kt +++ b/libs/sharedTest/src/main/kotlin/org/fdroid/test/TestDataV2.kt @@ -187,11 +187,11 @@ object TestDataMidV2 { categories = mapOf( "Cat1" to CategoryV2( name = mapOf(LOCALE to "Cat1"), - icon = FileV2( + icon = mapOf(LOCALE to FileV2( name = "/icons/cat2.png", sha256 = "54758e480ae76297c7947f107db9ea03d2933c9d5c110d02046977cf78d43def", size = Long.MAX_VALUE, - ), + )), ), "System" to CategoryV2( name = emptyMap(), @@ -737,11 +737,11 @@ object TestDataMaxV2 { name = emptyMap(), ), "AntiFeature" to AntiFeatureV2( - icon = FileV2( + icon = mapOf(LOCALE to FileV2( name = "/icons/antifeature.png", sha256 = "24758e480ae66297c7947f107db9ea03d2933c9d5c110d02046977cf78d43def", size = 254916, - ), + )), name = mapOf(LOCALE to "AntiFeature"), description = mapOf(LOCALE to "A bad anti-feature, we can't show to users."), ), @@ -756,29 +756,29 @@ object TestDataMaxV2 { categories = mapOf( "Cat3" to CategoryV2( name = mapOf(LOCALE to "Cat3"), - icon = FileV2( + icon = mapOf(LOCALE to FileV2( name = "/icons/cat3.png", sha256 = "54758e480ae76297c7947f107db9ea03d2933c9d5c110d02046977cf78d43def", size = Long.MAX_VALUE, - ), + )), description = mapOf(LOCALE to "Cat3"), ), "Cat2" to CategoryV2( name = mapOf(LOCALE to "Cat3"), - icon = FileV2( + icon = mapOf(LOCALE to FileV2( name = "/icons/cat2.png", sha256 = "54758e480ae76297c7947f107db9ea03d2933c9d5c110d02046977cf78d43def", size = Long.MAX_VALUE, - ), + )), description = mapOf(LOCALE to "Cat3"), ), "Cat1" to CategoryV2( name = mapOf(LOCALE to "Cat1"), - icon = FileV2( + icon = mapOf(LOCALE to FileV2( name = "/icons/cat1.png", sha256 = "54758e480ae76297c7947f107db9ea03d2933c9d5c110d02046977cf78d43def", size = Long.MAX_VALUE, - ), + )), description = mapOf(LOCALE to "Cat1"), ), "NoMoreSystem" to CategoryV2( diff --git a/libs/sharedTest/src/main/kotlin/org/fdroid/test/TestRepoUtils.kt b/libs/sharedTest/src/main/kotlin/org/fdroid/test/TestRepoUtils.kt index e4726d8ea..e3e4b6bd3 100644 --- a/libs/sharedTest/src/main/kotlin/org/fdroid/test/TestRepoUtils.kt +++ b/libs/sharedTest/src/main/kotlin/org/fdroid/test/TestRepoUtils.kt @@ -46,14 +46,14 @@ object TestRepoUtils { timestamp = System.currentTimeMillis(), antiFeatures = TestUtils.getRandomMap { getRandomString() to AntiFeatureV2( - icon = getRandomFileV2(), + icon = getRandomLocalizedFileV2(), name = getRandomLocalizedTextV2(), description = getRandomLocalizedTextV2(), ) }, categories = TestUtils.getRandomMap { getRandomString() to CategoryV2( - icon = getRandomFileV2(), + icon = getRandomLocalizedFileV2(), name = getRandomLocalizedTextV2(), description = getRandomLocalizedTextV2(), )