Fix various issues regarding Notification Profile scheduling.

- Timezone conversion when detecting scheduled profile
- Not automatically enabling a scheduled profile on creation regardless
  of when other profiles were enabled/disabled
This commit is contained in:
Cody Henthorne
2021-12-08 17:28:36 -05:00
parent 372b0d9f2b
commit a8a104242a
11 changed files with 126 additions and 104 deletions

View File

@@ -7,6 +7,7 @@ import org.junit.Test
import org.thoughtcrime.securesms.util.toMillis
import java.time.DayOfWeek
import java.time.LocalDateTime
import java.time.ZoneOffset
import java.util.TimeZone
class NotificationProfileScheduleTest {
@@ -37,105 +38,105 @@ class NotificationProfileScheduleTest {
fun `when time is within enabled schedule 9am to 5pm then return true`() {
val schedule = NotificationProfileSchedule(id = 1L, enabled = true, start = 900, end = 1700, daysEnabled = setOf(DayOfWeek.SUNDAY))
assertTrue(schedule.isCurrentlyActive(sunday9am.toMillis()))
assertTrue(schedule.isCurrentlyActive(sunday9am.plusHours(1).toMillis()))
assertTrue(schedule.isCurrentlyActive(sunday9am.toMillis(ZoneOffset.UTC)))
assertTrue(schedule.isCurrentlyActive(sunday9am.plusHours(1).toMillis(ZoneOffset.UTC)))
}
@Test
fun `when time is outside enabled schedule 9am to 5pm then return false`() {
val schedule = NotificationProfileSchedule(id = 1L, enabled = true, start = 900, end = 1700, daysEnabled = setOf(DayOfWeek.SUNDAY))
assertFalse(schedule.isCurrentlyActive(sunday1am.toMillis()))
assertFalse(schedule.isCurrentlyActive(sunday10pm.toMillis()))
assertFalse(schedule.isCurrentlyActive(monday1am.toMillis()))
assertFalse(schedule.isCurrentlyActive(monday9am.toMillis()))
assertFalse(schedule.isCurrentlyActive(monday10pm.toMillis()))
assertFalse(schedule.isCurrentlyActive(tuesday9am.toMillis()))
assertFalse(schedule.isCurrentlyActive(sunday1am.toMillis(ZoneOffset.UTC)))
assertFalse(schedule.isCurrentlyActive(sunday10pm.toMillis(ZoneOffset.UTC)))
assertFalse(schedule.isCurrentlyActive(monday1am.toMillis(ZoneOffset.UTC)))
assertFalse(schedule.isCurrentlyActive(monday9am.toMillis(ZoneOffset.UTC)))
assertFalse(schedule.isCurrentlyActive(monday10pm.toMillis(ZoneOffset.UTC)))
assertFalse(schedule.isCurrentlyActive(tuesday9am.toMillis(ZoneOffset.UTC)))
}
@Test
fun `when time is inside enabled with day wrapping schedule 10pm to 2am then return true`() {
val schedule = NotificationProfileSchedule(id = 1L, enabled = true, start = 2100, end = 200, daysEnabled = setOf(DayOfWeek.MONDAY))
assertTrue(schedule.isCurrentlyActive(monday10pm.toMillis()))
assertTrue(schedule.isCurrentlyActive(tuesday1am.toMillis()))
assertTrue(schedule.isCurrentlyActive(monday10pm.toMillis(ZoneOffset.UTC)))
assertTrue(schedule.isCurrentlyActive(tuesday1am.toMillis(ZoneOffset.UTC)))
}
@Test
fun `when time is outside enabled with day wrapping schedule 10pm to 2am then return false`() {
val schedule = NotificationProfileSchedule(id = 1L, enabled = true, start = 2100, end = 200, daysEnabled = setOf(DayOfWeek.MONDAY))
assertFalse(schedule.isCurrentlyActive(sunday1am.toMillis()))
assertFalse(schedule.isCurrentlyActive(sunday10pm.toMillis()))
assertFalse(schedule.isCurrentlyActive(monday1am.toMillis()))
assertFalse(schedule.isCurrentlyActive(monday9am.toMillis()))
assertFalse(schedule.isCurrentlyActive(tuesday9am.toMillis()))
assertFalse(schedule.isCurrentlyActive(tuesday10pm.toMillis()))
assertFalse(schedule.isCurrentlyActive(sunday1am.toMillis(ZoneOffset.UTC)))
assertFalse(schedule.isCurrentlyActive(sunday10pm.toMillis(ZoneOffset.UTC)))
assertFalse(schedule.isCurrentlyActive(monday1am.toMillis(ZoneOffset.UTC)))
assertFalse(schedule.isCurrentlyActive(monday9am.toMillis(ZoneOffset.UTC)))
assertFalse(schedule.isCurrentlyActive(tuesday9am.toMillis(ZoneOffset.UTC)))
assertFalse(schedule.isCurrentlyActive(tuesday10pm.toMillis(ZoneOffset.UTC)))
}
@Test
fun `when time is inside enabled schedule 12am to 10am then return false`() {
val schedule = NotificationProfileSchedule(id = 1L, enabled = true, start = 0, end = 1000, daysEnabled = setOf(DayOfWeek.SUNDAY))
assertTrue(schedule.isCurrentlyActive(sunday0am.toMillis()))
assertTrue(schedule.isCurrentlyActive(sunday1am.toMillis()))
assertTrue(schedule.isCurrentlyActive(sunday9am.toMillis()))
assertTrue(schedule.isCurrentlyActive(sunday0am.toMillis(ZoneOffset.UTC)))
assertTrue(schedule.isCurrentlyActive(sunday1am.toMillis(ZoneOffset.UTC)))
assertTrue(schedule.isCurrentlyActive(sunday9am.toMillis(ZoneOffset.UTC)))
}
@Test
fun `when time is inside enabled schedule 12am to 12am then return false`() {
val schedule = NotificationProfileSchedule(id = 1L, enabled = true, start = 0, end = 2400, daysEnabled = setOf(DayOfWeek.SUNDAY))
assertTrue(schedule.isCurrentlyActive(sunday0am.toMillis()))
assertTrue(schedule.isCurrentlyActive(sunday1am.toMillis()))
assertTrue(schedule.isCurrentlyActive(sunday9am.toMillis()))
assertTrue(schedule.isCurrentlyActive(sunday10pm.toMillis()))
assertTrue(schedule.isCurrentlyActive(sunday0am.toMillis(ZoneOffset.UTC)))
assertTrue(schedule.isCurrentlyActive(sunday1am.toMillis(ZoneOffset.UTC)))
assertTrue(schedule.isCurrentlyActive(sunday9am.toMillis(ZoneOffset.UTC)))
assertTrue(schedule.isCurrentlyActive(sunday10pm.toMillis(ZoneOffset.UTC)))
}
@Test
fun `when time is outside enabled schedule 12am to 12am then return false`() {
val schedule = NotificationProfileSchedule(id = 1L, enabled = true, start = 0, end = 2400, daysEnabled = setOf(DayOfWeek.SUNDAY))
assertFalse(schedule.isCurrentlyActive(monday0am.toMillis()))
assertFalse(schedule.isCurrentlyActive(monday1am.toMillis()))
assertFalse(schedule.isCurrentlyActive(monday9am.toMillis()))
assertFalse(schedule.isCurrentlyActive(monday10pm.toMillis()))
assertFalse(schedule.isCurrentlyActive(tuesday1am.toMillis()))
assertFalse(schedule.isCurrentlyActive(tuesday9am.toMillis()))
assertFalse(schedule.isCurrentlyActive(tuesday10pm.toMillis()))
assertFalse(schedule.isCurrentlyActive(monday0am.toMillis(ZoneOffset.UTC)))
assertFalse(schedule.isCurrentlyActive(monday1am.toMillis(ZoneOffset.UTC)))
assertFalse(schedule.isCurrentlyActive(monday9am.toMillis(ZoneOffset.UTC)))
assertFalse(schedule.isCurrentlyActive(monday10pm.toMillis(ZoneOffset.UTC)))
assertFalse(schedule.isCurrentlyActive(tuesday1am.toMillis(ZoneOffset.UTC)))
assertFalse(schedule.isCurrentlyActive(tuesday9am.toMillis(ZoneOffset.UTC)))
assertFalse(schedule.isCurrentlyActive(tuesday10pm.toMillis(ZoneOffset.UTC)))
}
@Test
fun `when enabled schedule 12am to 12am for all days then return true`() {
val schedule = NotificationProfileSchedule(id = 1L, enabled = true, start = 0, end = 2400, daysEnabled = DayOfWeek.values().toSet())
assertTrue(schedule.isCurrentlyActive(sunday0am.toMillis()))
assertTrue(schedule.isCurrentlyActive(sunday1am.toMillis()))
assertTrue(schedule.isCurrentlyActive(sunday9am.toMillis()))
assertTrue(schedule.isCurrentlyActive(sunday10pm.toMillis()))
assertTrue(schedule.isCurrentlyActive(monday0am.toMillis()))
assertTrue(schedule.isCurrentlyActive(monday1am.toMillis()))
assertTrue(schedule.isCurrentlyActive(monday9am.toMillis()))
assertTrue(schedule.isCurrentlyActive(monday10pm.toMillis()))
assertTrue(schedule.isCurrentlyActive(tuesday1am.toMillis()))
assertTrue(schedule.isCurrentlyActive(tuesday9am.toMillis()))
assertTrue(schedule.isCurrentlyActive(tuesday10pm.toMillis()))
assertTrue(schedule.isCurrentlyActive(sunday0am.toMillis(ZoneOffset.UTC)))
assertTrue(schedule.isCurrentlyActive(sunday1am.toMillis(ZoneOffset.UTC)))
assertTrue(schedule.isCurrentlyActive(sunday9am.toMillis(ZoneOffset.UTC)))
assertTrue(schedule.isCurrentlyActive(sunday10pm.toMillis(ZoneOffset.UTC)))
assertTrue(schedule.isCurrentlyActive(monday0am.toMillis(ZoneOffset.UTC)))
assertTrue(schedule.isCurrentlyActive(monday1am.toMillis(ZoneOffset.UTC)))
assertTrue(schedule.isCurrentlyActive(monday9am.toMillis(ZoneOffset.UTC)))
assertTrue(schedule.isCurrentlyActive(monday10pm.toMillis(ZoneOffset.UTC)))
assertTrue(schedule.isCurrentlyActive(tuesday1am.toMillis(ZoneOffset.UTC)))
assertTrue(schedule.isCurrentlyActive(tuesday9am.toMillis(ZoneOffset.UTC)))
assertTrue(schedule.isCurrentlyActive(tuesday10pm.toMillis(ZoneOffset.UTC)))
}
@Test
fun `when disabled schedule 12am to 12am for all days then return false`() {
val schedule = NotificationProfileSchedule(id = 1L, enabled = false, start = 0, end = 2400, daysEnabled = DayOfWeek.values().toSet())
assertFalse(schedule.isCurrentlyActive(sunday0am.toMillis()))
assertFalse(schedule.isCurrentlyActive(sunday1am.toMillis()))
assertFalse(schedule.isCurrentlyActive(sunday9am.toMillis()))
assertFalse(schedule.isCurrentlyActive(sunday10pm.toMillis()))
assertFalse(schedule.isCurrentlyActive(monday0am.toMillis()))
assertFalse(schedule.isCurrentlyActive(monday1am.toMillis()))
assertFalse(schedule.isCurrentlyActive(monday9am.toMillis()))
assertFalse(schedule.isCurrentlyActive(monday10pm.toMillis()))
assertFalse(schedule.isCurrentlyActive(tuesday1am.toMillis()))
assertFalse(schedule.isCurrentlyActive(tuesday9am.toMillis()))
assertFalse(schedule.isCurrentlyActive(tuesday10pm.toMillis()))
assertFalse(schedule.isCurrentlyActive(sunday0am.toMillis(ZoneOffset.UTC)))
assertFalse(schedule.isCurrentlyActive(sunday1am.toMillis(ZoneOffset.UTC)))
assertFalse(schedule.isCurrentlyActive(sunday9am.toMillis(ZoneOffset.UTC)))
assertFalse(schedule.isCurrentlyActive(sunday10pm.toMillis(ZoneOffset.UTC)))
assertFalse(schedule.isCurrentlyActive(monday0am.toMillis(ZoneOffset.UTC)))
assertFalse(schedule.isCurrentlyActive(monday1am.toMillis(ZoneOffset.UTC)))
assertFalse(schedule.isCurrentlyActive(monday9am.toMillis(ZoneOffset.UTC)))
assertFalse(schedule.isCurrentlyActive(monday10pm.toMillis(ZoneOffset.UTC)))
assertFalse(schedule.isCurrentlyActive(tuesday1am.toMillis(ZoneOffset.UTC)))
assertFalse(schedule.isCurrentlyActive(tuesday9am.toMillis(ZoneOffset.UTC)))
assertFalse(schedule.isCurrentlyActive(tuesday10pm.toMillis(ZoneOffset.UTC)))
}
}

View File

@@ -15,6 +15,7 @@ import org.thoughtcrime.securesms.util.toMillis
import java.time.DayOfWeek
import java.time.LocalDateTime
import java.time.ZoneId
import java.time.ZoneOffset
@RunWith(RobolectricTestRunner::class)
@Config(manifest = Config.NONE, application = Application::class)
@@ -69,30 +70,30 @@ class NotificationProfilesTest {
fun `when first is scheduled and second is not manually enabled and now is within schedule return first`() {
val schedule = NotificationProfileSchedule(id = 3L, true, start = 700, daysEnabled = setOf(DayOfWeek.SUNDAY))
val profiles = listOf(first.copy(schedule = schedule), second)
assertThat("active profile is first", NotificationProfiles.getActiveProfile(profiles, sunday9am.toMillis(), utc), `is`(profiles[0]))
assertThat("active profile is first", NotificationProfiles.getActiveProfile(profiles, sunday9am.toMillis(ZoneOffset.UTC), utc), `is`(profiles[0]))
}
@Test
fun `when first is scheduled and second is manually enabled forever within first's schedule then return second`() {
signalStore.dataSet.putLong(NotificationProfileValues.KEY_MANUALLY_ENABLED_PROFILE, second.id)
signalStore.dataSet.putLong(NotificationProfileValues.KEY_MANUALLY_ENABLED_UNTIL, Long.MAX_VALUE)
signalStore.dataSet.putLong(NotificationProfileValues.KEY_MANUALLY_DISABLED_AT, sunday830am.toMillis())
signalStore.dataSet.putLong(NotificationProfileValues.KEY_MANUALLY_DISABLED_AT, sunday830am.toMillis(ZoneOffset.UTC))
val schedule = NotificationProfileSchedule(id = 3L, true, start = 700, daysEnabled = setOf(DayOfWeek.SUNDAY))
val profiles = listOf(first.copy(schedule = schedule), second)
assertThat("active profile is first", NotificationProfiles.getActiveProfile(profiles, sunday9am.toMillis(), utc), `is`(profiles[1]))
assertThat("active profile is first", NotificationProfiles.getActiveProfile(profiles, sunday9am.toMillis(ZoneOffset.UTC), utc), `is`(profiles[1]))
}
@Test
fun `when first is scheduled and second is manually enabled forever before first's schedule start then return first`() {
signalStore.dataSet.putLong(NotificationProfileValues.KEY_MANUALLY_ENABLED_PROFILE, second.id)
signalStore.dataSet.putLong(NotificationProfileValues.KEY_MANUALLY_ENABLED_UNTIL, Long.MAX_VALUE)
signalStore.dataSet.putLong(NotificationProfileValues.KEY_MANUALLY_DISABLED_AT, sunday830am.toMillis())
signalStore.dataSet.putLong(NotificationProfileValues.KEY_MANUALLY_DISABLED_AT, sunday830am.toMillis(ZoneOffset.UTC))
val schedule = NotificationProfileSchedule(id = 3L, true, start = 900, daysEnabled = setOf(DayOfWeek.SUNDAY))
val profiles = listOf(first.copy(schedule = schedule), second)
assertThat("active profile is first", NotificationProfiles.getActiveProfile(profiles, sunday930am.toMillis(), utc), `is`(profiles[0]))
assertThat("active profile is first", NotificationProfiles.getActiveProfile(profiles, sunday930am.toMillis(ZoneOffset.UTC), utc), `is`(profiles[0]))
}
@Test
@@ -101,41 +102,41 @@ class NotificationProfilesTest {
val secondSchedule = NotificationProfileSchedule(id = 4L, true, start = 800, daysEnabled = setOf(DayOfWeek.SUNDAY))
val profiles = listOf(first.copy(schedule = firstSchedule), second.copy(schedule = secondSchedule))
assertThat("active profile is second", NotificationProfiles.getActiveProfile(profiles, sunday9am.toMillis(), utc), `is`(profiles[1]))
assertThat("active profile is second", NotificationProfiles.getActiveProfile(profiles, sunday9am.toMillis(ZoneOffset.UTC), utc), `is`(profiles[1]))
}
@Test
fun `when first and second have overlapping schedules and first is created before second and first is manually enabled within overlapping schedule then return first`() {
signalStore.dataSet.putLong(NotificationProfileValues.KEY_MANUALLY_ENABLED_PROFILE, first.id)
signalStore.dataSet.putLong(NotificationProfileValues.KEY_MANUALLY_ENABLED_UNTIL, Long.MAX_VALUE)
signalStore.dataSet.putLong(NotificationProfileValues.KEY_MANUALLY_DISABLED_AT, sunday830am.toMillis())
signalStore.dataSet.putLong(NotificationProfileValues.KEY_MANUALLY_DISABLED_AT, sunday830am.toMillis(ZoneOffset.UTC))
val firstSchedule = NotificationProfileSchedule(id = 3L, true, start = 700, daysEnabled = setOf(DayOfWeek.SUNDAY))
val secondSchedule = NotificationProfileSchedule(id = 4L, true, start = 700, daysEnabled = setOf(DayOfWeek.SUNDAY))
val profiles = listOf(first.copy(schedule = firstSchedule), second.copy(schedule = secondSchedule))
assertThat("active profile is first", NotificationProfiles.getActiveProfile(profiles, sunday9am.toMillis(), utc), `is`(profiles[0]))
assertThat("active profile is first", NotificationProfiles.getActiveProfile(profiles, sunday9am.toMillis(ZoneOffset.UTC), utc), `is`(profiles[0]))
}
@Test
fun `when profile is manually enabled for set time after schedule end and now is after schedule end but before manual then return profile`() {
signalStore.dataSet.putLong(NotificationProfileValues.KEY_MANUALLY_ENABLED_PROFILE, first.id)
signalStore.dataSet.putLong(NotificationProfileValues.KEY_MANUALLY_ENABLED_UNTIL, sunday930am.toMillis())
signalStore.dataSet.putLong(NotificationProfileValues.KEY_MANUALLY_DISABLED_AT, sunday830am.toMillis())
signalStore.dataSet.putLong(NotificationProfileValues.KEY_MANUALLY_ENABLED_UNTIL, sunday930am.toMillis(ZoneOffset.UTC))
signalStore.dataSet.putLong(NotificationProfileValues.KEY_MANUALLY_DISABLED_AT, sunday830am.toMillis(ZoneOffset.UTC))
val schedule = NotificationProfileSchedule(id = 3L, true, start = 700, end = 845, daysEnabled = setOf(DayOfWeek.SUNDAY))
val profiles = listOf(first.copy(schedule = schedule))
assertThat("active profile is first", NotificationProfiles.getActiveProfile(profiles, sunday9am.toMillis(), utc), `is`(profiles[0]))
assertThat("active profile is first", NotificationProfiles.getActiveProfile(profiles, sunday9am.toMillis(ZoneOffset.UTC), utc), `is`(profiles[0]))
}
@Test
fun `when profile is manually enabled for set time before schedule end and now is after manual but before schedule end then return null`() {
signalStore.dataSet.putLong(NotificationProfileValues.KEY_MANUALLY_ENABLED_PROFILE, first.id)
signalStore.dataSet.putLong(NotificationProfileValues.KEY_MANUALLY_ENABLED_UNTIL, sunday9am.toMillis())
signalStore.dataSet.putLong(NotificationProfileValues.KEY_MANUALLY_DISABLED_AT, sunday830am.toMillis())
signalStore.dataSet.putLong(NotificationProfileValues.KEY_MANUALLY_ENABLED_UNTIL, sunday9am.toMillis(ZoneOffset.UTC))
signalStore.dataSet.putLong(NotificationProfileValues.KEY_MANUALLY_DISABLED_AT, sunday830am.toMillis(ZoneOffset.UTC))
val schedule = NotificationProfileSchedule(id = 3L, true, start = 700, end = 1000, daysEnabled = setOf(DayOfWeek.SUNDAY))
val profiles = listOf(first.copy(schedule = schedule))
assertThat("active profile is null", NotificationProfiles.getActiveProfile(profiles, sunday930am.toMillis(), utc), nullValue())
assertThat("active profile is null", NotificationProfiles.getActiveProfile(profiles, sunday930am.toMillis(ZoneOffset.UTC), utc), nullValue())
}
}