-
-
Notifications
You must be signed in to change notification settings - Fork 2.9k
Expand file tree
/
Copy pathPrefsRobolectricTest.kt
More file actions
165 lines (151 loc) · 7 KB
/
Copy pathPrefsRobolectricTest.kt
File metadata and controls
165 lines (151 loc) · 7 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright (c) 2025 Brayan Oliveira <brayandso.dev@gmail.com>
package com.ichi2.anki.settings
import android.content.res.Resources
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.github.ivanshafran.sharedpreferencesmock.SPMockBuilder
import com.ichi2.anki.EmptyApplicationCategory
import com.ichi2.anki.R
import com.ichi2.anki.RobolectricTest
import com.ichi2.anki.libanki.utils.append
import com.ichi2.anki.preferences.HeaderFragment
import com.ichi2.anki.preferences.PreferenceTestUtils
import com.ichi2.anki.preferences.PreferenceTestUtils.getAttrsFromXml
import com.ichi2.anki.preferences.PreferenceTestUtils.resValue
import com.ichi2.anki.preferences.SettingsFragment
import com.ichi2.anki.settings.enums.PrefEnum
import com.ichi2.testutils.EmptyApplication
import io.mockk.every
import io.mockk.mockk
import org.hamcrest.MatcherAssert.assertThat
import org.hamcrest.Matchers.equalTo
import org.junit.Test
import org.junit.experimental.categories.Category
import org.junit.runner.RunWith
import org.mockito.kotlin.any
import org.mockito.kotlin.anyOrNull
import org.mockito.kotlin.doAnswer
import org.mockito.kotlin.spy
import org.mockito.kotlin.whenever
import org.robolectric.annotation.Config
import kotlin.reflect.KClass
import kotlin.reflect.KVisibility
import kotlin.reflect.full.createType
import kotlin.reflect.full.isSubtypeOf
import kotlin.reflect.full.memberProperties
import kotlin.test.assertContains
import kotlin.test.assertEquals
@RunWith(AndroidJUnit4::class)
@Config(application = EmptyApplication::class)
@Category(EmptyApplicationCategory::class)
class PrefsRobolectricTest : RobolectricTest() {
private fun getKeysAndDefaultValues(): MutableMap<String, Any?> {
val sharedPrefsSpy = spy(SPMockBuilder().createSharedPreferences())
val prefs = PrefsRepository(sharedPrefsSpy, targetContext.resources)
val keysAndDefaultValues: MutableMap<String, Any?> = mutableMapOf()
doAnswer { invocation ->
val key = invocation.arguments[0] as String
val value = invocation.arguments[1]
keysAndDefaultValues[key] =
if (value is String) {
value.resValue()
} else {
value
}
invocation.callRealMethod()
}.run {
whenever(sharedPrefsSpy).getBoolean(any(), any())
whenever(sharedPrefsSpy).getString(any(), anyOrNull())
whenever(sharedPrefsSpy).getInt(any(), any())
}
for (property in PrefsRepository::class.memberProperties) {
if (property.visibility != KVisibility.PUBLIC) continue
property.getter.call(prefs)
}
return keysAndDefaultValues
}
@Test
fun `all default values match the preference XMLs`() {
val keysAndDefaultValues = getKeysAndDefaultValues()
val developerOptionsKeys = PreferenceTestUtils.getDeveloperOptionsKeys(targetContext)
val prefs =
PreferenceTestUtils
.getAllPreferencesFragments(targetContext)
.asSequence()
.filter { it !is HeaderFragment }
.filterIsInstance<SettingsFragment>()
.map { it.preferenceResource }
.flatMap { getAttrsFromXml(targetContext, it, listOf("defaultValue", "key")) }
.filter { it["key"] != null }
.associate { it["key"]!!.resValue() to it["defaultValue"]?.resValue().toString() }
for ((key, defaultValue) in keysAndDefaultValues.entries) {
if (key !in prefs || key in developerOptionsKeys) continue
val prefsDefaultValue = prefs.getValue(key)
assertThat("The default value of '$key' matches the preference XML", defaultValue.toString(), equalTo(prefsDefaultValue))
}
}
private fun getPropertyNamesAndKeys(): MutableMap<String, String> {
val sharedPrefsSpy = spy(SPMockBuilder().createSharedPreferences())
val mockResources = mockk<Resources>()
every { mockResources.getString(any()) } answers { invocation.args[0].toString() }
val prefs = PrefsRepository(sharedPrefsSpy, mockResources)
val keys = mutableListOf<String>()
doAnswer { invocation ->
val key = "@${invocation.arguments[0]}".resValue()
keys.append(key)
invocation.callRealMethod()
}.run {
whenever(sharedPrefsSpy).getBoolean(any(), any())
whenever(sharedPrefsSpy).getString(any(), anyOrNull())
whenever(sharedPrefsSpy).getInt(any(), any())
}
val propertyNamesAndKeys = mutableMapOf<String, String>()
for (property in PrefsRepository::class.memberProperties) {
if (property.visibility != KVisibility.PUBLIC) continue
property.getter.call(prefs)
propertyNamesAndKeys[property.name] = keys.last()
}
return propertyNamesAndKeys
}
@Suppress("UNCHECKED_CAST")
@Test
fun `PrefEnum values match their preference entries`() {
// Prefs property name (String) -> Key (String)
val allPropertiesAndKeys = getPropertyNamesAndKeys()
val enumProperties =
Prefs::class.memberProperties.filter {
it.returnType.isSubtypeOf(PrefEnum::class.createType())
}
// Key (String) -> Prefs property
val enumPropertiesMap = enumProperties.associateBy { allPropertiesAndKeys.getValue(it.name) }
// Key (String) -> PrefEnum entryValues (List<String>)
val prefsEnumKeysAndValues = mutableMapOf<String, List<String>>()
for ((key, property) in enumPropertiesMap.entries) {
val enumConstants = ((property.returnType.classifier as KClass<*>).java.enumConstants) as Array<PrefEnum>
prefsEnumKeysAndValues[key] = enumConstants.map { targetContext.resources.getString(it.entryResId) }
}
val listPreferences =
PreferenceTestUtils
.getAllPreferencesFragments(targetContext)
.filterIsInstance<SettingsFragment>()
.map { it.preferenceResource }
.flatMap { getAttrsFromXml(targetContext, it, listOf("key", "entryValues")) }
.filter { it["entryValues"] != null }
.associate {
it["key"]!!.resValue() to
PreferenceTestUtils.attrToStringArray(it["entryValues"]!!, targetContext).toList()
}
// Theme preferences are the source of truth, and use `TR` so no longer fit the pattern.
val enumDerivedEntryValueKeys =
setOf(
R.string.app_theme_key,
R.string.day_theme_key,
R.string.night_theme_key,
).map { targetContext.getString(it) }
for ((key, enumValues) in prefsEnumKeysAndValues) {
if (key in enumDerivedEntryValueKeys) continue
assertContains(listPreferences, key)
assertEquals(enumValues, listPreferences[key])
}
}
}