Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
# v148.0 (In progress)
### Nimbus
* Adds `PreviousState` on `ExperimentEnrollment` when it is of type `EnrollmentStatus::Enrolled` and getters and setters. `PreviousState::GeckoPref` is added to support previous states for Gecko pref experiments.

[Full Changelog](In progress)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ import org.mozilla.experiments.nimbus.internal.NimbusClient
import org.mozilla.experiments.nimbus.internal.NimbusClientInterface
import org.mozilla.experiments.nimbus.internal.NimbusException
import org.mozilla.experiments.nimbus.internal.PrefUnenrollReason
import org.mozilla.experiments.nimbus.internal.PreviousState
import org.mozilla.experiments.nimbus.internal.RecordedContext
import java.io.File
import java.io.IOException
Expand Down Expand Up @@ -438,6 +439,18 @@ open class Nimbus(
return nimbusClient.unenrollForGeckoPref(geckoPrefState, prefUnenrollReason)
}

override fun registerPreviousGeckoPrefStates(geckoPrefStates: List<GeckoPrefState>) {
dbScope.launch {
withCatchAll("registerPreviousGeckoPrefStates") {
nimbusClient.registerPreviousGeckoPrefStates(geckoPrefStates)
}
}
}

override fun getPreviousState(experimentSlug: String): PreviousState? {
return nimbusClient.getPreviousState(experimentSlug)
}

@WorkerThread
@VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
internal fun optOutOnThisThread(experimentId: String) = withCatchAll("optOut") {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import org.mozilla.experiments.nimbus.internal.EnrollmentChangeEvent
import org.mozilla.experiments.nimbus.internal.ExperimentBranch
import org.mozilla.experiments.nimbus.internal.GeckoPrefState
import org.mozilla.experiments.nimbus.internal.PrefUnenrollReason
import org.mozilla.experiments.nimbus.internal.PreviousState
import java.time.Duration
import java.util.concurrent.TimeUnit

Expand Down Expand Up @@ -191,6 +192,26 @@ interface NimbusInterface : FeaturesInterface, NimbusMessagingInterface, NimbusE
prefUnenrollReason: PrefUnenrollReason,
): List<EnrollmentChangeEvent> = listOf()

/**
* Add the original Gecko pref values as a previous state on each involved enrollment.
*
* @param geckoPrefStates The list of items that should have their enrollment state updated with
* original Gecko pref previous state information.
*/
fun registerPreviousGeckoPrefStates(
geckoPrefStates: List<GeckoPrefState>,
) = Unit

/**
* Retrieves a previous state, if available on an enrolled experiment, from a given slug.
*
* @param experimentSlug The slug of the experiment.
* @return The previous state of the given slug. Will return null if not available or invalid slug.
*/
fun getPreviousState(
experimentSlug: String,
): PreviousState? = null

/**
* Reset internal state in response to application-level telemetry reset.
* Consumers should call this method when the user resets the telemetry state of the
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
package org.mozilla.experiments.nimbus

import android.content.Context
import android.os.Looper
import android.util.Log
import androidx.test.core.app.ApplicationProvider
import kotlinx.coroutines.CancellationException
Expand Down Expand Up @@ -42,12 +43,17 @@ import org.mozilla.experiments.nimbus.internal.GeckoPrefHandler
import org.mozilla.experiments.nimbus.internal.GeckoPrefState
import org.mozilla.experiments.nimbus.internal.JsonObject
import org.mozilla.experiments.nimbus.internal.NimbusException
import org.mozilla.experiments.nimbus.internal.OriginalGeckoPref
import org.mozilla.experiments.nimbus.internal.PrefBranch
import org.mozilla.experiments.nimbus.internal.PrefEnrollmentData
import org.mozilla.experiments.nimbus.internal.PrefUnenrollReason
import org.mozilla.experiments.nimbus.internal.PreviousGeckoPrefState
import org.mozilla.experiments.nimbus.internal.PreviousState
import org.mozilla.experiments.nimbus.internal.RecordedContext
import org.mozilla.experiments.nimbus.internal.getCalculatedAttributes
import org.mozilla.experiments.nimbus.internal.validateEventQueries
import org.robolectric.RobolectricTestRunner
import org.robolectric.Shadows.shadowOf
import java.io.File
import java.util.Calendar
import java.util.concurrent.Executors
Expand Down Expand Up @@ -849,12 +855,13 @@ class NimbusTests {
"number" to GeckoPrefState(
geckoPref = GeckoPref("pref.number", PrefBranch.DEFAULT),
geckoValue = "1",
enrollmentValue = null,
enrollmentValue = PrefEnrollmentData("test-experiment", "42", "about_welcome", "number"),
isUserSet = false,
),
),
),
var setValues: List<GeckoPrefState>? = null,
var originalGeckoPrefValues: List<OriginalGeckoPref>? = null,
) : GeckoPrefHandler {
override fun getPrefsWithState(): Map<String, Map<String, GeckoPrefState>> {
return internalMap
Expand All @@ -863,6 +870,10 @@ class NimbusTests {
override fun setGeckoPrefsState(newPrefsState: List<GeckoPrefState>) {
setValues = newPrefsState
}

override fun setGeckoPrefsOriginalValues(originalGeckoPrefs: List<OriginalGeckoPref>) {
originalGeckoPrefValues = originalGeckoPrefs
}
}

@Test
Expand All @@ -884,6 +895,21 @@ class NimbusTests {
assertEquals("42", handler.setValues?.get(0)?.enrollmentValue?.prefValue)
}

@Test
fun `GeckoPrefHandler setGeckoPrefsOriginalValues function`() {
val handler = TestGeckoPrefHandler()
val originalValues = listOf(
OriginalGeckoPref(
pref = "pref.number",
branch = PrefBranch.DEFAULT,
value = "1",
),
)
handler.setGeckoPrefsOriginalValues(originalValues)
assertEquals(1, handler.originalGeckoPrefValues?.size)
assertEquals("pref.number", handler.originalGeckoPrefValues?.get(0)?.pref)
}

@Test
fun `unenroll for gecko pref functions`() {
val handler = TestGeckoPrefHandler()
Expand Down Expand Up @@ -911,6 +937,36 @@ class NimbusTests {
assertEquals(EnrollmentChangeEventType.DISQUALIFICATION, events[0].change)
assertEquals(0, handler.setValues?.size)
}

@Test
fun `register previous gecko states and check values`() {
val handler = TestGeckoPrefHandler()

val nimbus = createNimbus(geckoPrefHandler = handler)

suspend fun getString(): String {
return testExperimentsJsonString(appInfo, packageName)
}

val job = nimbus.applyLocalExperiments(::getString)
runBlocking {
job.join()
}

assertEquals(1, handler.setValues?.size)
assertEquals("42", handler.setValues?.get(0)?.enrollmentValue?.prefValue)

nimbus.registerPreviousGeckoPrefStates(handler.setValues!!)
shadowOf(Looper.getMainLooper()).idle()

val previousState = nimbus.getPreviousState("test-experiment")
shadowOf(Looper.getMainLooper()).idle()

assertNotNull(previousState)
val geckoPreviousState = previousState as PreviousState.GeckoPref
assertEquals("1", geckoPreviousState!!.v1.originalValues[0].value)
assertEquals("pref.number", geckoPreviousState!!.v1.originalValues[0].pref)
}
}

// Mocking utilities, from mozilla.components.support.test
Expand Down
Loading