Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit ced62779 authored by Beth Thibodeau's avatar Beth Thibodeau
Browse files

Fix timing issue for flaky tests

Inject SystemClock so that we can mock it in tests and ensure the clock
will always advance. Also switched to using elapsedRealtime, which is
guaranteed to be monotonic

Fixes: 182813365
Test: atest MediaDataManagerTest
Change-Id: I133fb48e8242dc7f8e75bfa5c585c8b6142963aa
parent 4a225373
Loading
Loading
Loading
Loading
+6 −3
Original line number Original line Diff line number Diff line
@@ -25,6 +25,7 @@ import com.android.systemui.broadcast.BroadcastDispatcher
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.settings.CurrentUserTracker
import com.android.systemui.settings.CurrentUserTracker
import com.android.systemui.statusbar.NotificationLockscreenUserManager
import com.android.systemui.statusbar.NotificationLockscreenUserManager
import com.android.systemui.util.time.SystemClock
import java.util.concurrent.Executor
import java.util.concurrent.Executor
import java.util.concurrent.TimeUnit
import java.util.concurrent.TimeUnit
import javax.inject.Inject
import javax.inject.Inject
@@ -36,7 +37,8 @@ private const val DEBUG = true
 * Maximum age of a media control to re-activate on smartspace signal. If there is no media control
 * Maximum age of a media control to re-activate on smartspace signal. If there is no media control
 * available within this time window, smartspace recommendations will be shown instead.
 * available within this time window, smartspace recommendations will be shown instead.
 */
 */
private val SMARTSPACE_MAX_AGE = SystemProperties
@VisibleForTesting
internal val SMARTSPACE_MAX_AGE = SystemProperties
        .getLong("debug.sysui.smartspace_max_age", TimeUnit.HOURS.toMillis(3))
        .getLong("debug.sysui.smartspace_max_age", TimeUnit.HOURS.toMillis(3))


/**
/**
@@ -51,7 +53,8 @@ class MediaDataFilter @Inject constructor(
    private val broadcastDispatcher: BroadcastDispatcher,
    private val broadcastDispatcher: BroadcastDispatcher,
    private val mediaResumeListener: MediaResumeListener,
    private val mediaResumeListener: MediaResumeListener,
    private val lockscreenUserManager: NotificationLockscreenUserManager,
    private val lockscreenUserManager: NotificationLockscreenUserManager,
    @Main private val executor: Executor
    @Main private val executor: Executor,
    private val systemClock: SystemClock
) : MediaDataManager.Listener {
) : MediaDataManager.Listener {
    private val userTracker: CurrentUserTracker
    private val userTracker: CurrentUserTracker
    private val _listeners: MutableSet<MediaDataManager.Listener> = mutableSetOf()
    private val _listeners: MutableSet<MediaDataManager.Listener> = mutableSetOf()
@@ -100,7 +103,7 @@ class MediaDataFilter @Inject constructor(
        hasSmartspace = true
        hasSmartspace = true


        // Before forwarding the smartspace target, first check if we have recently inactive media
        // Before forwarding the smartspace target, first check if we have recently inactive media
        val now = System.currentTimeMillis()
        val now = systemClock.elapsedRealtime()
        val sorted = userEntries.toSortedMap(compareBy {
        val sorted = userEntries.toSortedMap(compareBy {
            userEntries.get(it)?.lastActive ?: -1
            userEntries.get(it)?.lastActive ?: -1
        })
        })
+8 −5
Original line number Original line Diff line number Diff line
@@ -56,6 +56,7 @@ import com.android.systemui.statusbar.notification.row.HybridGroupManager
import com.android.systemui.util.Assert
import com.android.systemui.util.Assert
import com.android.systemui.util.Utils
import com.android.systemui.util.Utils
import com.android.systemui.util.concurrency.DelayableExecutor
import com.android.systemui.util.concurrency.DelayableExecutor
import com.android.systemui.util.time.SystemClock
import java.io.FileDescriptor
import java.io.FileDescriptor
import java.io.IOException
import java.io.IOException
import java.io.PrintWriter
import java.io.PrintWriter
@@ -108,7 +109,8 @@ class MediaDataManager(
    private val activityStarter: ActivityStarter,
    private val activityStarter: ActivityStarter,
    private val smartspaceMediaDataProvider: SmartspaceMediaDataProvider,
    private val smartspaceMediaDataProvider: SmartspaceMediaDataProvider,
    private var useMediaResumption: Boolean,
    private var useMediaResumption: Boolean,
    private val useQsMediaPlayer: Boolean
    private val useQsMediaPlayer: Boolean,
    private val systemClock: SystemClock
) : Dumpable, BcSmartspaceDataPlugin.SmartspaceTargetListener {
) : Dumpable, BcSmartspaceDataPlugin.SmartspaceTargetListener {


    companion object {
    companion object {
@@ -164,12 +166,13 @@ class MediaDataManager(
        mediaDataCombineLatest: MediaDataCombineLatest,
        mediaDataCombineLatest: MediaDataCombineLatest,
        mediaDataFilter: MediaDataFilter,
        mediaDataFilter: MediaDataFilter,
        activityStarter: ActivityStarter,
        activityStarter: ActivityStarter,
        smartspaceMediaDataProvider: SmartspaceMediaDataProvider
        smartspaceMediaDataProvider: SmartspaceMediaDataProvider,
        clock: SystemClock
    ) : this(context, backgroundExecutor, foregroundExecutor, mediaControllerFactory,
    ) : this(context, backgroundExecutor, foregroundExecutor, mediaControllerFactory,
            broadcastDispatcher, dumpManager, mediaTimeoutListener, mediaResumeListener,
            broadcastDispatcher, dumpManager, mediaTimeoutListener, mediaResumeListener,
            mediaSessionBasedFilter, mediaDeviceManager, mediaDataCombineLatest, mediaDataFilter,
            mediaSessionBasedFilter, mediaDeviceManager, mediaDataCombineLatest, mediaDataFilter,
            activityStarter, smartspaceMediaDataProvider, Utils.useMediaResumption(context),
            activityStarter, smartspaceMediaDataProvider, Utils.useMediaResumption(context),
            Utils.useQsMediaPlayer(context))
            Utils.useQsMediaPlayer(context), clock)


    private val appChangeReceiver = object : BroadcastReceiver() {
    private val appChangeReceiver = object : BroadcastReceiver() {
        override fun onReceive(context: Context, intent: Intent) {
        override fun onReceive(context: Context, intent: Intent) {
@@ -474,7 +477,7 @@ class MediaDataManager(
        }
        }


        val mediaAction = getResumeMediaAction(resumeAction)
        val mediaAction = getResumeMediaAction(resumeAction)
        val lastActive = System.currentTimeMillis()
        val lastActive = systemClock.elapsedRealtime()
        foregroundExecutor.execute {
        foregroundExecutor.execute {
            onMediaDataLoaded(packageName, null, MediaData(userId, true, bgColor, appName,
            onMediaDataLoaded(packageName, null, MediaData(userId, true, bgColor, appName,
                    null, desc.subtitle, desc.title, artworkIcon, listOf(mediaAction), listOf(0),
                    null, desc.subtitle, desc.title, artworkIcon, listOf(mediaAction), listOf(0),
@@ -597,7 +600,7 @@ class MediaDataManager(
        val isLocalSession = mediaController.playbackInfo?.playbackType ==
        val isLocalSession = mediaController.playbackInfo?.playbackType ==
            MediaController.PlaybackInfo.PLAYBACK_TYPE_LOCAL
            MediaController.PlaybackInfo.PLAYBACK_TYPE_LOCAL
        val isPlaying = mediaController.playbackState?.let { isPlayingState(it.state) } ?: null
        val isPlaying = mediaController.playbackState?.let { isPlayingState(it.state) } ?: null
        val lastActive = System.currentTimeMillis()
        val lastActive = systemClock.elapsedRealtime()
        foregroundExecutor.execute {
        foregroundExecutor.execute {
            val resumeAction: Runnable? = mediaEntries[key]?.resumeAction
            val resumeAction: Runnable? = mediaEntries[key]?.resumeAction
            val hasCheckedForResume = mediaEntries[key]?.hasCheckedForResume == true
            val hasCheckedForResume = mediaEntries[key]?.hasCheckedForResume == true
+9 −5
Original line number Original line Diff line number Diff line
@@ -25,6 +25,7 @@ import android.testing.TestableLooper
import com.android.systemui.SysuiTestCase
import com.android.systemui.SysuiTestCase
import com.android.systemui.broadcast.BroadcastDispatcher
import com.android.systemui.broadcast.BroadcastDispatcher
import com.android.systemui.statusbar.NotificationLockscreenUserManager
import com.android.systemui.statusbar.NotificationLockscreenUserManager
import com.android.systemui.util.time.FakeSystemClock
import com.google.common.truth.Truth.assertThat
import com.google.common.truth.Truth.assertThat
import org.junit.Before
import org.junit.Before
import org.junit.Test
import org.junit.Test
@@ -80,12 +81,13 @@ class MediaDataFilterTest : SysuiTestCase() {
    private lateinit var dataMain: MediaData
    private lateinit var dataMain: MediaData
    private lateinit var dataGuest: MediaData
    private lateinit var dataGuest: MediaData
    private val device = MediaDeviceData(true, null, DEVICE_NAME)
    private val device = MediaDeviceData(true, null, DEVICE_NAME)
    private val clock = FakeSystemClock()


    @Before
    @Before
    fun setup() {
    fun setup() {
        MockitoAnnotations.initMocks(this)
        MockitoAnnotations.initMocks(this)
        mediaDataFilter = MediaDataFilter(broadcastDispatcher, mediaResumeListener,
        mediaDataFilter = MediaDataFilter(broadcastDispatcher, mediaResumeListener,
                lockscreenUserManager, executor)
                lockscreenUserManager, executor, clock)
        mediaDataFilter.mediaDataManager = mediaDataManager
        mediaDataFilter.mediaDataManager = mediaDataManager
        mediaDataFilter.addListener(listener)
        mediaDataFilter.addListener(listener)


@@ -246,8 +248,9 @@ class MediaDataFilterTest : SysuiTestCase() {


    @Test
    @Test
    fun testOnSmartspaceMediaDataLoaded_noRecentMedia_nonEmptyRecommendation_usesSmartspace() {
    fun testOnSmartspaceMediaDataLoaded_noRecentMedia_nonEmptyRecommendation_usesSmartspace() {
        val dataOld = dataMain.copy(active = false, lastActive = 0L)
        val dataOld = dataMain.copy(active = false, lastActive = clock.elapsedRealtime())
        mediaDataFilter.onMediaDataLoaded(KEY, null, dataOld)
        mediaDataFilter.onMediaDataLoaded(KEY, null, dataOld)
        clock.advanceTime(SMARTSPACE_MAX_AGE + 100)
        mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData)
        mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData)


        verify(listener).onSmartspaceMediaDataLoaded(eq(SMARTSPACE_KEY), eq(smartspaceData))
        verify(listener).onSmartspaceMediaDataLoaded(eq(SMARTSPACE_KEY), eq(smartspaceData))
@@ -258,8 +261,9 @@ class MediaDataFilterTest : SysuiTestCase() {
    fun testOnSmartspaceMediaDataLoaded_noRecentMedia_emptyRecommendation_showsNothing() {
    fun testOnSmartspaceMediaDataLoaded_noRecentMedia_emptyRecommendation_showsNothing() {
        `when`(smartspaceData.iconGrid).thenReturn(listOf())
        `when`(smartspaceData.iconGrid).thenReturn(listOf())


        val dataOld = dataMain.copy(active = false, lastActive = 0L)
        val dataOld = dataMain.copy(active = false, lastActive = clock.elapsedRealtime())
        mediaDataFilter.onMediaDataLoaded(KEY, null, dataOld)
        mediaDataFilter.onMediaDataLoaded(KEY, null, dataOld)
        clock.advanceTime(SMARTSPACE_MAX_AGE + 100)
        mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData)
        mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData)


        verify(listener, never())
        verify(listener, never())
@@ -270,7 +274,7 @@ class MediaDataFilterTest : SysuiTestCase() {
    @Test
    @Test
    fun testOnSmartspaceMediaDataLoaded_hasRecentMedia_usesMedia() {
    fun testOnSmartspaceMediaDataLoaded_hasRecentMedia_usesMedia() {
        // WHEN we have media that was recently played, but not currently active
        // WHEN we have media that was recently played, but not currently active
        val dataCurrent = dataMain.copy(active = false, lastActive = System.currentTimeMillis())
        val dataCurrent = dataMain.copy(active = false, lastActive = clock.elapsedRealtime())
        mediaDataFilter.onMediaDataLoaded(KEY, null, dataCurrent)
        mediaDataFilter.onMediaDataLoaded(KEY, null, dataCurrent)
        verify(listener).onMediaDataLoaded(eq(KEY), eq(null), eq(dataCurrent))
        verify(listener).onMediaDataLoaded(eq(KEY), eq(null), eq(dataCurrent))


@@ -294,7 +298,7 @@ class MediaDataFilterTest : SysuiTestCase() {


    @Test
    @Test
    fun testOnSmartspaceMediaDataRemoved_usedMedia_clearsMedia() {
    fun testOnSmartspaceMediaDataRemoved_usedMedia_clearsMedia() {
        val dataCurrent = dataMain.copy(active = false, lastActive = System.currentTimeMillis())
        val dataCurrent = dataMain.copy(active = false, lastActive = clock.elapsedRealtime())
        mediaDataFilter.onMediaDataLoaded(KEY, null, dataCurrent)
        mediaDataFilter.onMediaDataLoaded(KEY, null, dataCurrent)
        mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData)
        mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData)


+15 −11
Original line number Original line Diff line number Diff line
@@ -81,11 +81,12 @@ class MediaDataManagerTest : SysuiTestCase() {
    lateinit var mediaDataManager: MediaDataManager
    lateinit var mediaDataManager: MediaDataManager
    lateinit var mediaNotification: StatusBarNotification
    lateinit var mediaNotification: StatusBarNotification
    @Captor lateinit var mediaDataCaptor: ArgumentCaptor<MediaData>
    @Captor lateinit var mediaDataCaptor: ArgumentCaptor<MediaData>
    private val clock = FakeSystemClock()


    @Before
    @Before
    fun setup() {
    fun setup() {
        foregroundExecutor = FakeExecutor(FakeSystemClock())
        foregroundExecutor = FakeExecutor(clock)
        backgroundExecutor = FakeExecutor(FakeSystemClock())
        backgroundExecutor = FakeExecutor(clock)
        smartspaceMediaDataProvider = SmartspaceMediaDataProvider()
        smartspaceMediaDataProvider = SmartspaceMediaDataProvider()
        mediaDataManager = MediaDataManager(
        mediaDataManager = MediaDataManager(
            context = context,
            context = context,
@@ -103,7 +104,8 @@ class MediaDataManagerTest : SysuiTestCase() {
            activityStarter = activityStarter,
            activityStarter = activityStarter,
            smartspaceMediaDataProvider = smartspaceMediaDataProvider,
            smartspaceMediaDataProvider = smartspaceMediaDataProvider,
            useMediaResumption = true,
            useMediaResumption = true,
            useQsMediaPlayer = true
            useQsMediaPlayer = true,
            systemClock = clock
        )
        )
        session = MediaSession(context, "MediaDataManagerTestSession")
        session = MediaSession(context, "MediaDataManagerTestSession")
        mediaNotification = SbnBuilder().run {
        mediaNotification = SbnBuilder().run {
@@ -310,7 +312,7 @@ class MediaDataManagerTest : SysuiTestCase() {
            setTitle(SESSION_TITLE)
            setTitle(SESSION_TITLE)
            build()
            build()
        }
        }
        val currentTimeMillis = System.currentTimeMillis()
        val currentTime = clock.elapsedRealtime()
        mediaDataManager.addResumptionControls(USER_ID, desc, Runnable {}, session.sessionToken,
        mediaDataManager.addResumptionControls(USER_ID, desc, Runnable {}, session.sessionToken,
                APP_NAME, pendingIntent, PACKAGE_NAME)
                APP_NAME, pendingIntent, PACKAGE_NAME)
        assertThat(backgroundExecutor.runAllReady()).isEqualTo(1)
        assertThat(backgroundExecutor.runAllReady()).isEqualTo(1)
@@ -322,7 +324,7 @@ class MediaDataManagerTest : SysuiTestCase() {
        assertThat(data.song).isEqualTo(SESSION_TITLE)
        assertThat(data.song).isEqualTo(SESSION_TITLE)
        assertThat(data.app).isEqualTo(APP_NAME)
        assertThat(data.app).isEqualTo(APP_NAME)
        assertThat(data.actions).hasSize(1)
        assertThat(data.actions).hasSize(1)
        assertThat(data.lastActive).isAtLeast(currentTimeMillis)
        assertThat(data.lastActive).isAtLeast(currentTime)
    }
    }


    @Test
    @Test
@@ -380,12 +382,12 @@ class MediaDataManagerTest : SysuiTestCase() {


    @Test
    @Test
    fun testOnMediaDataChanged_updatesLastActiveTime() {
    fun testOnMediaDataChanged_updatesLastActiveTime() {
        val currentTimeMillis = System.currentTimeMillis()
        val currentTime = clock.elapsedRealtime()
        mediaDataManager.onNotificationAdded(KEY, mediaNotification)
        mediaDataManager.onNotificationAdded(KEY, mediaNotification)
        assertThat(backgroundExecutor.runAllReady()).isEqualTo(1)
        assertThat(backgroundExecutor.runAllReady()).isEqualTo(1)
        assertThat(foregroundExecutor.runAllReady()).isEqualTo(1)
        assertThat(foregroundExecutor.runAllReady()).isEqualTo(1)
        verify(listener).onMediaDataLoaded(eq(KEY), eq(null), capture(mediaDataCaptor))
        verify(listener).onMediaDataLoaded(eq(KEY), eq(null), capture(mediaDataCaptor))
        assertThat(mediaDataCaptor.value!!.lastActive).isAtLeast(currentTimeMillis)
        assertThat(mediaDataCaptor.value!!.lastActive).isAtLeast(currentTime)
    }
    }


    @Test
    @Test
@@ -396,12 +398,13 @@ class MediaDataManagerTest : SysuiTestCase() {
        assertThat(foregroundExecutor.runAllReady()).isEqualTo(1)
        assertThat(foregroundExecutor.runAllReady()).isEqualTo(1)


        // WHEN the notification times out
        // WHEN the notification times out
        val currentTimeMillis = System.currentTimeMillis()
        clock.advanceTime(100)
        val currentTime = clock.elapsedRealtime()
        mediaDataManager.setTimedOut(KEY, true, true)
        mediaDataManager.setTimedOut(KEY, true, true)


        // THEN the last active time is not changed
        // THEN the last active time is not changed
        verify(listener).onMediaDataLoaded(eq(KEY), eq(KEY), capture(mediaDataCaptor))
        verify(listener).onMediaDataLoaded(eq(KEY), eq(KEY), capture(mediaDataCaptor))
        assertThat(mediaDataCaptor.value.lastActive).isLessThan(currentTimeMillis)
        assertThat(mediaDataCaptor.value.lastActive).isLessThan(currentTime)
    }
    }


    @Test
    @Test
@@ -417,13 +420,14 @@ class MediaDataManagerTest : SysuiTestCase() {
        mediaDataManager.onMediaDataLoaded(KEY, null, data.copy(resumeAction = Runnable {}))
        mediaDataManager.onMediaDataLoaded(KEY, null, data.copy(resumeAction = Runnable {}))


        // WHEN the notification is removed
        // WHEN the notification is removed
        val currentTimeMillis = System.currentTimeMillis()
        clock.advanceTime(100)
        val currentTime = clock.elapsedRealtime()
        mediaDataManager.onNotificationRemoved(KEY)
        mediaDataManager.onNotificationRemoved(KEY)


        // THEN the last active time is not changed
        // THEN the last active time is not changed
        verify(listener).onMediaDataLoaded(eq(PACKAGE_NAME), eq(KEY), capture(mediaDataCaptor))
        verify(listener).onMediaDataLoaded(eq(PACKAGE_NAME), eq(KEY), capture(mediaDataCaptor))
        assertThat(mediaDataCaptor.value.resumption).isTrue()
        assertThat(mediaDataCaptor.value.resumption).isTrue()
        assertThat(mediaDataCaptor.value.lastActive).isLessThan(currentTimeMillis)
        assertThat(mediaDataCaptor.value.lastActive).isLessThan(currentTime)
    }
    }


    @Test
    @Test