Skip to content

Commit

Permalink
For mozilla-mobile#12207 - Support optionally persisting the default …
Browse files Browse the repository at this point in the history
…value of a string preference

Moved the tests for string preferences to a new file where we can use
Robolectric for just this preferences type and test the actual behavior without
relying on mocking which would prevent full testing the change because of the
two readings using a `null` default.
  • Loading branch information
Mugurell committed May 20, 2022
1 parent 13deac0 commit 7df51a4
Show file tree
Hide file tree
Showing 4 changed files with 143 additions and 39 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -67,11 +67,21 @@ private class LongPreference(

private class StringPreference(
private val key: String,
private val default: String
private val default: String,
private val persistDefaultIfNotExists: Boolean = false,
) : ReadWriteProperty<PreferencesHolder, String> {

override fun getValue(thisRef: PreferencesHolder, property: KProperty<*>): String =
thisRef.preferences.getString(key, default) ?: default
override fun getValue(thisRef: PreferencesHolder, property: KProperty<*>): String {
return thisRef.preferences.getString(key, null) ?: run {
when (persistDefaultIfNotExists) {
true -> {
thisRef.preferences.edit().putString(key, default).apply()
thisRef.preferences.getString(key, null)!!
}
false -> default
}
}
}

override fun setValue(thisRef: PreferencesHolder, property: KProperty<*>, value: String) =
thisRef.preferences.edit().putString(key, value).apply()
Expand Down Expand Up @@ -147,17 +157,26 @@ fun longPreference(key: String, default: Long): ReadWriteProperty<PreferencesHol

/**
* Property delegate for getting and setting a string shared preference.
* Optionally this will persist the default value if one is not already persisted.
*
* Example usage:
* ```
* class Settings : PreferenceHolder {
* ...
* var permissionsEnabledEnum by stringPreference("permissions_enabled", default = "blocked")
* var permissionsEnabledEnum by stringPreference(
* "permissions_enabled",
* default = "blocked",
* persistDefaultIfNotExists = true,
* )
* }
* ```
*/
fun stringPreference(key: String, default: String): ReadWriteProperty<PreferencesHolder, String> =
StringPreference(key, default)
fun stringPreference(
key: String,
default: String,
persistDefaultIfNotExists: Boolean = false,
): ReadWriteProperty<PreferencesHolder, String> =
StringPreference(key, default, persistDefaultIfNotExists)

/**
* Property delegate for getting and setting a string set shared preference.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */

package mozilla.components.support.ktx.android.content

import android.content.Context
import android.content.SharedPreferences
import androidx.test.ext.junit.runners.AndroidJUnit4
import mozilla.components.support.test.robolectric.testContext
import org.junit.After
import org.junit.Assert.assertEquals
import org.junit.Assert.assertNull
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith

@RunWith(AndroidJUnit4::class)
class SharedPreferencesStringTest {
private val key = "key"
private val defaultValue = "defaultString"
private lateinit var preferencesHolder: StringTestPreferenceHolder
private lateinit var testPreferences: SharedPreferences

@Before
fun setup() {
testPreferences = testContext.getSharedPreferences("test", Context.MODE_PRIVATE)
}

@After
fun tearDown() {
testPreferences.edit().clear().apply()
}

@Test
fun `GIVEN string does not exist and asked to persist the default WHEN asked for it THEN persist the default and return it`() {
preferencesHolder = StringTestPreferenceHolder(
persistDefaultIfNotExists = true
)

val result = preferencesHolder.string

assertEquals(defaultValue, result)
assertEquals(defaultValue, testPreferences.getString(key, null))
}

@Test
fun `GIVEN string does not exist and not asked to persist the default WHEN asked for it THEN return the default but not persist it`() {
preferencesHolder = StringTestPreferenceHolder(
persistDefaultIfNotExists = false
)

val result = preferencesHolder.string

assertEquals(defaultValue, result)
assertNull(testPreferences.getString(key, null))
}

@Test
fun `GIVEN string exists and asked to persist the default WHEN asked for it THEN return the existing string and don't persist the default`() {
testPreferences.edit().putString(key, "test").apply()
preferencesHolder = StringTestPreferenceHolder(
persistDefaultIfNotExists = true
)

val result = preferencesHolder.string

assertEquals("test", result)
}

@Test
fun `GIVEN string exists and not asked to persist the default WHEN asked for it THEN return the existing string and don't persist the default`() {
testPreferences.edit().putString(key, "test").apply()
preferencesHolder = StringTestPreferenceHolder(
persistDefaultIfNotExists = true
)

val result = preferencesHolder.string

assertEquals("test", result)
}

@Test
fun `GIVEN a value exists WHEN asked to persist a new value THEN update the persisted value`() {
testPreferences.edit().putString(key, "test").apply()
preferencesHolder = StringTestPreferenceHolder()

preferencesHolder.string = "update"

assertEquals(
"update",
testPreferences.getString(key, null)
)
}

@Test
fun `GIVEN a value does not exist WHEN asked to persist a new value THEN persist the requested value`() {
preferencesHolder = StringTestPreferenceHolder()

preferencesHolder.string = "test"

assertEquals(
"test",
testPreferences.getString(key, null)
)
}

private inner class StringTestPreferenceHolder(
persistDefaultIfNotExists: Boolean = false,
) : PreferencesHolder {
override val preferences = testPreferences

var string by stringPreference(key, defaultValue, persistDefaultIfNotExists)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -170,39 +170,6 @@ class SharedPreferencesTest {
verify(sharedPrefs).getLong("long", 23)
}

@Test
fun `getter returns string from shared preferences`() {
val holder = MockPreferencesHolder()
`when`(sharedPrefs.getString(eq("string"), anyString())).thenReturn("foo")

assertEquals("foo", holder.string)
verify(sharedPrefs).getString("string", "")
}

@Test
fun `setter applies string to shared preferences`() {
val holder = MockPreferencesHolder(defaultString = "foo")
holder.string = "bar"

verify(editor).putString("string", "bar")
verify(editor).apply()
}

@Test
fun `getter uses default string value`() {
val holderDefault = MockPreferencesHolder()
// Call the getter for the test
holderDefault.string

verify(sharedPrefs).getString("string", "")

val holderOther = MockPreferencesHolder(defaultString = "hello")
// Call the getter for the test
holderOther.string

verify(sharedPrefs).getString("string", "hello")
}

@Test
fun `getter returns string set from shared preferences`() {
val holder = MockPreferencesHolder()
Expand Down
3 changes: 3 additions & 0 deletions docs/changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ permalink: /changelog/
* [Gecko](https://github.com/mozilla-mobile/android-components/blob/main/buildSrc/src/main/java/Gecko.kt)
* [Configuration](https://github.com/mozilla-mobile/android-components/blob/main/.config.yml)

* **support-ktx**
* 🌟️ Add support for optionally persisting the default value when `stringPreference` is used to read a string from SharedPreferences. [issue #12207](https://github.com/mozilla-mobile/android-components/issues/12207).

* **service-pocket**
* 🌟️ Add support for Pocket sponsored stories.
* See component's [README](https://github.com/mozilla-mobile/android-components/blob/main/components/service/pocket/README.md) to get more info.
Expand Down

0 comments on commit 7df51a4

Please sign in to comment.