refactor(preferences): DataStore 설정 저장 안정성을 높인다
This commit is contained in:
@@ -0,0 +1,88 @@
|
||||
package kr.co.vividnext.sodalive.runtime
|
||||
|
||||
import android.content.Context
|
||||
import androidx.datastore.core.DataStore
|
||||
import androidx.datastore.preferences.core.Preferences
|
||||
import androidx.datastore.preferences.core.booleanPreferencesKey
|
||||
import androidx.datastore.preferences.core.edit
|
||||
import androidx.datastore.preferences.core.longPreferencesKey
|
||||
import androidx.datastore.preferences.core.stringPreferencesKey
|
||||
import androidx.test.core.app.ApplicationProvider
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import kr.co.vividnext.sodalive.chat.talk.room.ChatRoomPreferenceManager
|
||||
import kr.co.vividnext.sodalive.common.AppPreferencesDataStoreProvider
|
||||
import kr.co.vividnext.sodalive.common.Constants
|
||||
import kr.co.vividnext.sodalive.common.SharedPreferenceManager
|
||||
import org.junit.After
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Assert.assertTrue
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import kotlinx.coroutines.runBlocking
|
||||
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class DataStoreRuntimeRegressionTest {
|
||||
|
||||
private val context: Context
|
||||
get() = ApplicationProvider.getApplicationContext()
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
SharedPreferenceManager.init(context)
|
||||
ChatRoomPreferenceManager.init(context)
|
||||
}
|
||||
|
||||
@After
|
||||
fun tearDown() {
|
||||
SharedPreferenceManager.resetForTest()
|
||||
ChatRoomPreferenceManager.resetForTest()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun sharedPreferenceManager_readsPersistedSnapshotImmediatelyOnInit() {
|
||||
runBlocking {
|
||||
AppPreferencesDataStoreProvider.get(context).edit { preferences ->
|
||||
preferences[stringPreferencesKey(PREF_APP_LANGUAGE_CODE)] = "en"
|
||||
preferences[booleanPreferencesKey(Constants.PREF_IS_PLAYER_SERVICE_RUNNING)] = true
|
||||
}
|
||||
}
|
||||
|
||||
SharedPreferenceManager.resetForTest()
|
||||
SharedPreferenceManager.init(context)
|
||||
|
||||
assertEquals("en", SharedPreferenceManager.appLanguageCode)
|
||||
assertTrue(SharedPreferenceManager.isPlayerServiceRunning)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun chatRoomPreferenceManager_readsPersistedSnapshotImmediatelyOnInit() {
|
||||
val roomId = 13579L
|
||||
val visibleKey = "chat_bg_visible_room_$roomId"
|
||||
val imageIdKey = "chat_bg_image_id_room_$roomId"
|
||||
|
||||
runBlocking {
|
||||
chatRoomDataStore().edit { preferences ->
|
||||
preferences[booleanPreferencesKey(visibleKey)] = false
|
||||
preferences[longPreferencesKey(imageIdKey)] = 777L
|
||||
}
|
||||
}
|
||||
|
||||
ChatRoomPreferenceManager.resetForTest()
|
||||
ChatRoomPreferenceManager.init(context)
|
||||
|
||||
assertEquals(false, ChatRoomPreferenceManager.getBoolean(visibleKey, true))
|
||||
assertEquals(777L, ChatRoomPreferenceManager.getLong(imageIdKey, -1L))
|
||||
}
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
private fun chatRoomDataStore(): DataStore<Preferences> {
|
||||
val field = ChatRoomPreferenceManager::class.java.getDeclaredField("dataStore")
|
||||
field.isAccessible = true
|
||||
return field.get(ChatRoomPreferenceManager) as DataStore<Preferences>
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val PREF_APP_LANGUAGE_CODE = "pref_app_language_code"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
package kr.co.vividnext.sodalive.runtime
|
||||
|
||||
import androidx.media3.session.MediaController
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import androidx.test.platform.app.InstrumentationRegistry
|
||||
import com.google.common.util.concurrent.ListenableFuture
|
||||
import com.google.common.util.concurrent.SettableFuture
|
||||
import kr.co.vividnext.sodalive.audio_content.playlist.detail.AudioContentPlaylistDetailActivity
|
||||
import kr.co.vividnext.sodalive.main.MainActivity
|
||||
import org.junit.Assert.assertSame
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class MiniPlayerConnectionGuardTest {
|
||||
|
||||
@Test
|
||||
fun mainActivity_skipsConnectingWhenFutureAlreadyExists() {
|
||||
val activity = createOnMainThread { MainActivity() }
|
||||
val sentinel = SettableFuture.create<MediaController>()
|
||||
setPrivateField(activity, "mediaControllerFuture", sentinel)
|
||||
|
||||
invokePrivateNoArg(activity, "connectPlayerService")
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
val after = getPrivateField(activity, "mediaControllerFuture") as? ListenableFuture<MediaController>
|
||||
assertSame(sentinel, after)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun playlistDetailActivity_skipsConnectingWhenFutureAlreadyExists() {
|
||||
val activity = createOnMainThread { AudioContentPlaylistDetailActivity() }
|
||||
val sentinel = SettableFuture.create<MediaController>()
|
||||
setPrivateField(activity, "mediaControllerFuture", sentinel)
|
||||
|
||||
invokePrivateNoArg(activity, "connectPlayerService")
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
val after = getPrivateField(activity, "mediaControllerFuture") as? ListenableFuture<MediaController>
|
||||
assertSame(sentinel, after)
|
||||
}
|
||||
|
||||
private fun setPrivateField(target: Any, fieldName: String, value: Any?) {
|
||||
val field = target.javaClass.getDeclaredField(fieldName)
|
||||
field.isAccessible = true
|
||||
field.set(target, value)
|
||||
}
|
||||
|
||||
private fun getPrivateField(target: Any, fieldName: String): Any? {
|
||||
val field = target.javaClass.getDeclaredField(fieldName)
|
||||
field.isAccessible = true
|
||||
return field.get(target)
|
||||
}
|
||||
|
||||
private fun invokePrivateNoArg(target: Any, methodName: String) {
|
||||
val method = target.javaClass.getDeclaredMethod(methodName)
|
||||
method.isAccessible = true
|
||||
method.invoke(target)
|
||||
}
|
||||
|
||||
private fun <T> createOnMainThread(factory: () -> T): T {
|
||||
var instance: Any? = null
|
||||
InstrumentationRegistry.getInstrumentation().runOnMainSync {
|
||||
instance = factory()
|
||||
}
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
return instance as T
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user