From 7df51a4df785950945ff70278566efaa360c8afb Mon Sep 17 00:00:00 2001 From: Mugurell Date: Fri, 20 May 2022 10:52:14 +0300 Subject: [PATCH] For #12207 - Support optionally persisting the default 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. --- .../ktx/android/content/SharedPreferences.kt | 31 ++++- .../content/SharedPreferencesStringTest.kt | 115 ++++++++++++++++++ .../android/content/SharedPreferencesTest.kt | 33 ----- docs/changelog.md | 3 + 4 files changed, 143 insertions(+), 39 deletions(-) create mode 100644 components/support/ktx/src/test/java/mozilla/components/support/ktx/android/content/SharedPreferencesStringTest.kt diff --git a/components/support/ktx/src/main/java/mozilla/components/support/ktx/android/content/SharedPreferences.kt b/components/support/ktx/src/main/java/mozilla/components/support/ktx/android/content/SharedPreferences.kt index 662d14a6b01..66a157552b8 100644 --- a/components/support/ktx/src/main/java/mozilla/components/support/ktx/android/content/SharedPreferences.kt +++ b/components/support/ktx/src/main/java/mozilla/components/support/ktx/android/content/SharedPreferences.kt @@ -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 { - 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() @@ -147,17 +157,26 @@ fun longPreference(key: String, default: Long): ReadWriteProperty = - StringPreference(key, default) +fun stringPreference( + key: String, + default: String, + persistDefaultIfNotExists: Boolean = false, +): ReadWriteProperty = + StringPreference(key, default, persistDefaultIfNotExists) /** * Property delegate for getting and setting a string set shared preference. diff --git a/components/support/ktx/src/test/java/mozilla/components/support/ktx/android/content/SharedPreferencesStringTest.kt b/components/support/ktx/src/test/java/mozilla/components/support/ktx/android/content/SharedPreferencesStringTest.kt new file mode 100644 index 00000000000..5f47875d659 --- /dev/null +++ b/components/support/ktx/src/test/java/mozilla/components/support/ktx/android/content/SharedPreferencesStringTest.kt @@ -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) + } +} diff --git a/components/support/ktx/src/test/java/mozilla/components/support/ktx/android/content/SharedPreferencesTest.kt b/components/support/ktx/src/test/java/mozilla/components/support/ktx/android/content/SharedPreferencesTest.kt index e05379a11c7..f6ade9c0205 100644 --- a/components/support/ktx/src/test/java/mozilla/components/support/ktx/android/content/SharedPreferencesTest.kt +++ b/components/support/ktx/src/test/java/mozilla/components/support/ktx/android/content/SharedPreferencesTest.kt @@ -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() diff --git a/docs/changelog.md b/docs/changelog.md index 7979dc9b771..fe0b86fb1e3 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -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.