Commit 96f55389 authored by Michael Droettboom's avatar Michael Droettboom
Browse files

1566488: Persist start_times to avoid 0 between start and end

parent 8935953c
......@@ -22,14 +22,19 @@ internal class PingMaker(
private val applicationContext: Context
) {
private val logger = Logger("glean/PingMaker")
private val pingStartTimes: MutableMap<String, String> = mutableMapOf()
private val objectStartTime = getISOTimeString()
internal val sharedPreferences: SharedPreferences? by lazy {
internal val sharedPreferencesSeq: SharedPreferences? by lazy {
applicationContext.getSharedPreferences(
this.javaClass.canonicalName,
Context.MODE_PRIVATE
)
}
val sharedPreferencesStartTimes: SharedPreferences? by lazy {
applicationContext.getSharedPreferences(
"{this.javaClass.canonicalName}StartTimes",
Context.MODE_PRIVATE
)
}
/**
* Get the ping sequence number for a given ping. This is a
......@@ -40,7 +45,7 @@ internal class PingMaker(
* @return sequence number
*/
internal fun getPingSeq(pingName: String): Int {
sharedPreferences?.let {
sharedPreferencesSeq?.let {
val key = "${pingName}_seq"
val currentValue = it.getInt(key, 0)
val editor = it.edit()
......@@ -59,11 +64,50 @@ internal class PingMaker(
* Reset all ping sequence numbers.
*/
internal fun resetPingSequenceNumbers() {
sharedPreferences?.let {
sharedPreferencesSeq?.let {
it.edit().clear().apply()
}
}
/**
* Get the start time for a given ping.
* This is always equal to the end time of the last time the ping was sent.
*
* @param pingName The name of the ping
* @return start time
*/
internal fun getPingStartTime(pingName: String): String {
sharedPreferencesStartTimes?.let {
val key = "${pingName}_start_time"
val currentValue = it.getString(key, objectStartTime)!!
return currentValue
}
// This clause should happen in testing only, where a sharedPreferences object
// isn't guaranteed to exist if using a mocked ApplicationContext
logger.error("Couldn't get SharedPreferences object for ping start times")
return objectStartTime
}
/**
* Set the start time for a given ping.
*
* @param pingName The name of the ping
* @param startTime The start time to set for the ping
*/
internal fun setPingStartTime(pingName: String, startTime: String) {
sharedPreferencesStartTimes?.let {
val key = "${pingName}_start_time"
val editor = it.edit()
editor.putString(key, startTime)
editor.apply()
}
// This clause should happen in testing only, where a sharedPreferences object
// isn't guaranteed to exist if using a mocked ApplicationContext
logger.error("Couldn't get SharedPreferences object for ping start times")
}
/**
* Return the object containing the "ping_info" section of a ping.
*
......@@ -83,12 +127,12 @@ internal class PingMaker(
// This needs to be a bit more involved for start-end times. "start_time" is
// the time the ping was generated the last time. If not available, we use the
// date the object was initialized.
val startTime = if (pingName in pingStartTimes) pingStartTimes[pingName] else objectStartTime
val startTime = getPingStartTime(pingName)
pingInfo.put("start_time", startTime)
val endTime = getISOTimeString()
pingInfo.put("end_time", endTime)
// Update the start time with the current time.
pingStartTimes[pingName] = endTime
setPingStartTime(pingName, endTime)
return pingInfo
}
......
......@@ -72,6 +72,48 @@ class PingMakerTest {
<= OffsetDateTime.parse(pingInfo.getString("end_time")))
}
@Test
fun `ping_info must persist start_time`() {
val applicationContext = ApplicationProvider.getApplicationContext<Context>()
val maker = PingMaker(
StorageEngineManager(
storageEngines = mapOf(
"engine2" to MockStorageEngine(JSONObject(mapOf("a.b" to "foo")))
),
applicationContext = applicationContext
),
applicationContext
)
// Insert a dummy value into the first maker to make sure it's picked up by
// the second maker
maker.setPingStartTime(customPing.name, "2100-01-01")
val maker2 = PingMaker(
StorageEngineManager(
storageEngines = mapOf(
"engine2" to MockStorageEngine(JSONObject(mapOf("a.b" to "foo")))
),
applicationContext = applicationContext
),
applicationContext
)
// Gather the data. We expect an empty ping with the "ping_info" information
val data = maker2.collect(customPing).orEmpty()
assertTrue("We expect a non-empty JSON blob", "{}" != data)
// Parse the data so that we can easily check the other fields
val jsonData = JSONObject(data)
val pingInfo = jsonData["ping_info"] as JSONObject
assertNotNull(pingInfo)
// "start_time" and "end_time" must be valid ISO8601 dates. DateTimeFormatter would
// throw otherwise.
assertEquals("2100-01-01", pingInfo.getString("start_time"))
DateTimeFormatter.ISO_OFFSET_DATE_TIME.parse(pingInfo.getString("end_time"))
}
@Test
fun `getPingInfo() must report all the required fields`() {
val maker = PingMaker(
......@@ -193,7 +235,7 @@ class PingMakerTest {
// Clear the sharedPreferences on the PingMaker so we can test that the
// numbers start at zero.
maker.sharedPreferences?.let {
maker.sharedPreferencesSeq?.let {
val editor = it.edit()
editor.clear()
editor.apply()
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment