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

Commit 0881a761 authored by Juan Sebastian Martinez's avatar Juan Sebastian Martinez
Browse files

Optimizing MagneticRowManagerImplTest by more than 90%

Two optimizations can be done to improve the running time of the Unit
tests. First, it is not necessary to fully inflate a real
ExpandableNotificationRow backed by a Notification and NotificationEntry
to test the logic of its magnetic behavior. Significant improvements are
achieved by replacing the rows in the tests with mocks, each with a
reference to a testable MagneticRowListener.

Second, we can replace the NotificationTargetsHelper dependency with a
testable object that allows to set the magnetic targets during test
setup. This avoids having to run the target finding algorithm for each
test and makes NotificationTargetsHelper more testable. This CL makes
NotificationTargetsHelper an interface to achieve this result.

Test: NotificationTargetsHelperTest
Test: MagneticRowManagerImplTest
Test: Verified test run about 90% faster when using atest
Bug: 427255241
Flag: NONE improving unit tests
Change-Id: Iaa72066e626d571f69af755695bfe4f5d68183a1
parent 182e1409
Loading
Loading
Loading
Loading
+69 −55
Original line number Diff line number Diff line
@@ -27,11 +27,12 @@ import com.android.systemui.Flags
import com.android.systemui.SysuiTestCase
import com.android.systemui.haptics.fakeVibratorHelper
import com.android.systemui.haptics.msdl.fakeMSDLPlayer
import com.android.systemui.haptics.vibratorHelper
import com.android.systemui.kosmos.testScope
import com.android.systemui.statusbar.notification.Roundable
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
import com.android.systemui.statusbar.notification.row.createRowGroup
import com.android.systemui.statusbar.notification.stack.MagneticNotificationRowManagerImpl.State
import com.android.systemui.statusbar.phone.fake
import com.android.systemui.statusbar.phone.notificationTargetsHelper
import com.android.systemui.testKosmos
import com.google.android.msdl.data.model.MSDLToken
import com.google.common.truth.Truth.assertThat
@@ -41,6 +42,7 @@ import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.kotlin.mock
import org.mockito.kotlin.whenever

@SmallTest
@RunWith(AndroidJUnit4::class)
@@ -48,28 +50,37 @@ import org.mockito.kotlin.mock
class MagneticNotificationRowManagerImplTest : SysuiTestCase() {

    private val kosmos = testKosmos()
    private val childrenNumber = 5
    private val childrenCount = 5
    private val stackScrollLayout = mock<NotificationStackScrollLayout>()
    private val sectionsManager = mock<NotificationSectionsManager>()
    private val msdlPlayer = kosmos.fakeMSDLPlayer
    private val vibratorHelper = kosmos.fakeVibratorHelper
    private var canRowBeDismissed = true
    private var magneticAnimationsCancelled = MutableList(childrenNumber) { false }

    private val underTest = kosmos.magneticNotificationRowManagerImpl

    private lateinit var children: NotificationChildrenContainer
    private lateinit var magneticRowListeners: MutableList<TestableMagneticRowListener>
    private lateinit var children: MutableList<ExpandableNotificationRow>
    private lateinit var swipedRow: ExpandableNotificationRow

    @Before
    fun setUp() {
        allowTestableLooperAsMainThread()
        children = kosmos.createRowGroup(childrenNumber).childrenContainer
        swipedRow = children.attachedChildren[childrenNumber / 2]
        children.attachedChildren.forEachIndexed { index, row ->
            row.magneticRowListener = row.magneticRowListener.asTestableListener(index)
        magneticRowListeners = mutableListOf()
        children = mutableListOf()
        val magneticRoundableTargets = mutableListOf<MagneticRoundableTarget>()

        repeat(childrenCount) {
            val row = mock<ExpandableNotificationRow>()
            val listener = TestableMagneticRowListener()
            whenever(row.magneticRowListener).thenReturn(listener)
            val magneticRoundableTarget = MagneticRoundableTarget(mock<Roundable>(), listener)

            magneticRowListeners.add(listener)
            children.add(row)
            magneticRoundableTargets.add(magneticRoundableTarget)
        }
        magneticAnimationsCancelled.replaceAll { false }

        swipedRow = children[childrenCount / 2]
        kosmos.notificationTargetsHelper.fake.magneticRoundableTargets = magneticRoundableTargets
    }

    @Test
@@ -102,7 +113,7 @@ class MagneticNotificationRowManagerImplTest : SysuiTestCase() {
        kosmos.testScope.runTest {
            // GIVEN an IDLE state
            // WHEN setting a translation for the swiped row
            val row = children.attachedChildren[childrenNumber / 2]
            val row = children[childrenCount / 2]
            underTest.setMagneticRowTranslation(row, translation = 100f)

            // THEN no magnetic translations are set
@@ -118,7 +129,7 @@ class MagneticNotificationRowManagerImplTest : SysuiTestCase() {
            setTargets()

            // WHEN setting a translation for a row that is not being swiped
            val differentRow = children.attachedChildren[childrenNumber / 2 - 1]
            val differentRow = children[childrenCount / 2 - 1]
            val canSetMagneticTranslation =
                underTest.setMagneticRowTranslation(differentRow, translation = 100f)

@@ -145,7 +156,8 @@ class MagneticNotificationRowManagerImplTest : SysuiTestCase() {

            // THEN the targets continue to be pulled and translations are set
            assertThat(underTest.currentState).isEqualTo(State.PULLING)
            assertThat(swipedRow.translation).isEqualTo(underTest.swipedRowMultiplier * translation)
            assertThat(swipedRow.testableTranslation())
                .isEqualTo(underTest.swipedRowMultiplier * translation)
        }

    @Test
@@ -158,7 +170,7 @@ class MagneticNotificationRowManagerImplTest : SysuiTestCase() {
            )

            // GIVEN that targets are set and the rows are being pulled
            canRowBeDismissed = false
            magneticRowListeners[childrenCount / 2].canBeDismissed = false
            setTargets()
            underTest.setMagneticRowTranslation(swipedRow, translation = 100f)

@@ -169,7 +181,7 @@ class MagneticNotificationRowManagerImplTest : SysuiTestCase() {
            // THEN the targets continue to be pulled and reduced translations are set
            val expectedTranslation = getReducedTranslation(translation)
            assertThat(underTest.currentState).isEqualTo(State.PULLING)
            assertThat(swipedRow.translation).isEqualTo(expectedTranslation)
            assertThat(swipedRow.testableTranslation()).isEqualTo(expectedTranslation)
        }

    @Test
@@ -204,7 +216,7 @@ class MagneticNotificationRowManagerImplTest : SysuiTestCase() {
            )

            // GIVEN that targets are set and the rows are being pulled
            canRowBeDismissed = false
            magneticRowListeners[childrenCount / 2].canBeDismissed = false
            setTargets()
            underTest.setMagneticRowTranslation(swipedRow, translation = 100f)

@@ -215,7 +227,7 @@ class MagneticNotificationRowManagerImplTest : SysuiTestCase() {
            // THEN the swiped view does not detach and the reduced translation is set
            val expectedTranslation = getReducedTranslation(translation)
            assertThat(underTest.currentState).isEqualTo(State.PULLING)
            assertThat(swipedRow.translation).isEqualTo(expectedTranslation)
            assertThat(swipedRow.testableTranslation()).isEqualTo(expectedTranslation)
        }

    @Test
@@ -284,14 +296,14 @@ class MagneticNotificationRowManagerImplTest : SysuiTestCase() {
            underTest.onMagneticInteractionEnd(swipedRow, dismissing = false, velocity = null)

            // THEN magnetic animations are cancelled
            assertThat(magneticAnimationsCancelled[childrenNumber / 2]).isTrue()
            assertThat(magneticRowListeners[childrenCount / 2].magneticAnimationCancelled).isTrue()
        }

    @Test
    fun onMagneticInteractionEnd_forMagneticNeighbor_cancelsMagneticAnimations() =
        kosmos.testScope.runTest {
            val neighborIndex = childrenNumber / 2 - 1
            val neighborRow = children.attachedChildren[neighborIndex]
            val neighborIndex = childrenCount / 2 - 1
            val neighborRow = children[neighborIndex]

            // GIVEN that targets are set
            setTargets()
@@ -300,7 +312,7 @@ class MagneticNotificationRowManagerImplTest : SysuiTestCase() {
            underTest.onMagneticInteractionEnd(neighborRow, dismissing = false, velocity = null)

            // THEN magnetic animations are cancelled
            assertThat(magneticAnimationsCancelled[neighborIndex]).isTrue()
            assertThat(magneticRowListeners[neighborIndex].magneticAnimationCancelled).isTrue()
        }

    @Test
@@ -460,8 +472,8 @@ class MagneticNotificationRowManagerImplTest : SysuiTestCase() {
            underTest.setMagneticRowTranslation(swipedRow, translation = 100f)

            // THEN the detach direction for a non-swiped row is zero
            val neighborIndex = childrenNumber / 2 - 1
            val neighborRow = children.attachedChildren[neighborIndex]
            val neighborIndex = childrenCount / 2 - 1
            val neighborRow = children[neighborIndex]
            val detachDirection = underTest.getDetachDirection(neighborRow)
            assertThat(detachDirection).isEqualTo(0)
        }
@@ -499,7 +511,7 @@ class MagneticNotificationRowManagerImplTest : SysuiTestCase() {
            underTest.onMagneticInteractionEnd(swipedRow, dismissing = true, velocity = 5000f)

            // WHEN we begin interacting with another row
            swipedRow = children.attachedChildren.first()
            swipedRow = children.first()
            setTargets()
            underTest.setMagneticRowTranslation(swipedRow, translation = 100f)

@@ -544,35 +556,37 @@ class MagneticNotificationRowManagerImplTest : SysuiTestCase() {
            originalTranslation *
            MagneticNotificationRowManagerImpl.MAGNETIC_REDUCTION

    private fun MagneticRowListener.asTestableListener(rowIndex: Int): MagneticRowListener {
        val delegate = this
        return object : MagneticRowListener {
    private fun ExpandableNotificationRow.testableTranslation(): Float =
        (magneticRowListener as TestableMagneticRowListener).currentTranslation

    private inner class TestableMagneticRowListener : MagneticRowListener {

        var currentTranslation = 0f
            private set

        var magneticAnimationCancelled = false
            private set

        var canBeDismissed = true

        override fun setMagneticTranslation(translation: Float) {
                delegate.setMagneticTranslation(translation)
            currentTranslation = translation
        }

        override fun triggerMagneticForce(
            endTranslation: Float,
            springForce: SpringForce,
            startVelocity: Float,
            ) {
                delegate.triggerMagneticForce(endTranslation, springForce, startVelocity)
            }
        ) {}

        override fun cancelMagneticAnimations() {
                magneticAnimationsCancelled[rowIndex] = true
                delegate.cancelMagneticAnimations()
            magneticAnimationCancelled = true
        }

            override fun cancelTranslationAnimations() {
                delegate.cancelTranslationAnimations()
            }
        override fun cancelTranslationAnimations() {}

            override fun canRowBeDismissed(): Boolean {
                return canRowBeDismissed
            }
        override fun canRowBeDismissed(): Boolean = canBeDismissed

        override fun getRowLoggingKey(): String = "testable listener"
    }
}
}
+1 −1
Original line number Diff line number Diff line
@@ -29,7 +29,7 @@ class NotificationTargetsHelperTest : SysuiTestCase() {
        allowTestableLooperAsMainThread()
    }

    private fun notificationTargetsHelper() = NotificationTargetsHelper()
    private fun notificationTargetsHelper() = NotificationTargetsHelperImpl()

    @Test
    fun targetsForFirstNotificationInGroup() {
+7 −0
Original line number Diff line number Diff line
@@ -95,6 +95,8 @@ import com.android.systemui.statusbar.notification.stack.MagneticNotificationRow
import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
import com.android.systemui.statusbar.notification.stack.NotificationSectionsManager;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
import com.android.systemui.statusbar.notification.stack.NotificationTargetsHelper;
import com.android.systemui.statusbar.notification.stack.NotificationTargetsHelperImpl;
import com.android.systemui.statusbar.notification.stack.OnboardingAffordanceCommands;
import com.android.systemui.statusbar.notification.stack.StackScrollAlgorithm;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
@@ -359,4 +361,9 @@ public interface NotificationsModule {
    @Binds
    EntryAdapterFactory provideEntryAdapterFactory(EntryAdapterFactoryImpl impl);

    /** Provides the instance of {@link NotificationTargetsHelper} */
    @Binds
    @SysUISingleton
    NotificationTargetsHelper provideNotificationTargetsHelper(NotificationTargetsHelperImpl impl);

}
+47 −31
Original line number Diff line number Diff line
@@ -9,12 +9,7 @@ import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
import com.android.systemui.statusbar.notification.row.ExpandableView
import javax.inject.Inject

/**
 * Utility class that helps us find the targets of an animation, often used to find the notification
 * ([Roundable]) above and below the current one (see [findRoundableTargets]).
 */
@SysUISingleton
class NotificationTargetsHelper @Inject constructor() {
interface NotificationTargetsHelper {

    /**
     * This method looks for views that can be rounded (and implement [Roundable]) during a
@@ -28,6 +23,51 @@ class NotificationTargetsHelper @Inject constructor() {
        viewSwiped: ExpandableNotificationRow,
        stackScrollLayout: NotificationStackScrollLayout,
        sectionsManager: NotificationSectionsManager,
    ): RoundableTargets

    /**
     * This method looks for [MagneticRoundableTarget]s that can magnetically attach to a swiped
     * [ExpandableNotificationRow].
     *
     * The [MagneticRoundableTarget] for the swiped row is at the center of the list. From the
     * center towards the left, the list contains the closest notification targets above the swiped
     * row. From the center towards the right, the list contains the closest targets below the row.
     *
     * The list is filled from the center outwards, stopping at the first target that is not a valid
     * [MagneticRoundableTarget] (see [ExpandableView.isValidMagneticTargetForSwiped]). Positions
     * where the list halted could also be added if the target is a valid boundary (see
     * [ExpandableView.isValidMagneticBoundary]). If neither condition is met, the position is
     * filled with an empty [MagneticRoundableTarget]. In addition to the [numTargets] required, the
     * list contains two additional targets used as [Roundable] boundaries that are not affected by
     * a magnetic swipe. These will be returned at the beginning and end of the list.
     *
     * @param[viewSwiped] The [ExpandableNotificationRow] that is swiped.
     * @param[stackScrollLayout] [NotificationStackScrollLayout] container.
     * @param[sectionsManager] The [NotificationSectionsManager].
     * @param[numTargets] The number of targets in the resulting list, including the swiped view.
     * @return The list of [MagneticRoundableTarget]s above and below the swiped
     *   [ExpandableNotificationRow]. The list includes two [Roundable] targets as boundaries of the
     *   list. They are position at the beginning and end of the list.
     */
    fun findMagneticRoundableTargets(
        viewSwiped: ExpandableNotificationRow,
        stackScrollLayout: NotificationStackScrollLayout,
        sectionsManager: NotificationSectionsManager,
        numTargets: Int,
    ): List<MagneticRoundableTarget>
}

/**
 * Utility class that helps us find the targets of an animation, often used to find the notification
 * ([Roundable]) above and below the current one (see [findRoundableTargets]).
 */
@SysUISingleton
class NotificationTargetsHelperImpl @Inject constructor() : NotificationTargetsHelper {

    override fun findRoundableTargets(
        viewSwiped: ExpandableNotificationRow,
        stackScrollLayout: NotificationStackScrollLayout,
        sectionsManager: NotificationSectionsManager,
    ): RoundableTargets {
        val viewBefore: Roundable?
        val viewAfter: Roundable?
@@ -73,31 +113,7 @@ class NotificationTargetsHelper @Inject constructor() {
        return RoundableTargets(before = viewBefore, swiped = viewSwiped, after = viewAfter)
    }

    /**
     * This method looks for [MagneticRoundableTarget]s that can magnetically attach to a swiped
     * [ExpandableNotificationRow].
     *
     * The [MagneticRoundableTarget] for the swiped row is at the center of the list. From the
     * center towards the left, the list contains the closest notification targets above the swiped
     * row. From the center towards the right, the list contains the closest targets below the row.
     *
     * The list is filled from the center outwards, stopping at the first target that is not a valid
     * [MagneticRoundableTarget] (see [ExpandableView.isValidMagneticTargetForSwiped]). Positions
     * where the list halted could also be added if the target is a valid boundary (see
     * [ExpandableView.isValidMagneticBoundary]). If neither condition is met, the position is
     * filled with an empty [MagneticRoundableTarget]. In addition to the [numTargets] required, the
     * list contains two additional targets used as [Roundable] boundaries that are not affected by
     * a magnetic swipe. These will be returned at the beginning and end of the list.
     *
     * @param[viewSwiped] The [ExpandableNotificationRow] that is swiped.
     * @param[stackScrollLayout] [NotificationStackScrollLayout] container.
     * @param[sectionsManager] The [NotificationSectionsManager].
     * @param[numTargets] The number of targets in the resulting list, including the swiped view.
     * @return The list of [MagneticRoundableTarget]s above and below the swiped
     *   [ExpandableNotificationRow]. The list includes two [Roundable] targets as boundaries of the
     *   list. They are position at the beginning and end of the list.
     */
    fun findMagneticRoundableTargets(
    override fun findMagneticRoundableTargets(
        viewSwiped: ExpandableNotificationRow,
        stackScrollLayout: NotificationStackScrollLayout,
        sectionsManager: NotificationSectionsManager,
+2 −1
Original line number Diff line number Diff line
@@ -21,6 +21,7 @@ import com.android.systemui.haptics.msdl.msdlPlayer
import com.android.systemui.haptics.vibratorHelper
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.statusbar.notification.row.NotificationRowLogger
import com.android.systemui.statusbar.phone.notificationTargetsHelper
import com.android.systemui.util.time.systemClock
import org.mockito.kotlin.mock

@@ -32,7 +33,7 @@ val Kosmos.magneticNotificationRowManagerImpl by
        MagneticNotificationRowManagerImpl(
            msdlPlayer,
            vibratorHelper,
            NotificationTargetsHelper(),
            notificationTargetsHelper,
            NotificationRoundnessManager(dumpManager),
            mock<NotificationRowLogger>(),
            systemClock,
Loading