Improve AmbientState test coverage

AmbientState is a critical state holder for system holder but does not contains unit tests. The tests here are extracted from the attempt to convert AmbientState to Kotlin, and introduce a basic unit test coverage that we can build upon to increase changes confidence. Because AmbientState is a simple state holder, the tests are mostly checking property modifications.

We did not include test for any property that does not contain a custom getter or setter.

Test: atest ~/tm-qpr-dev/frameworks/base/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/AmbientStateTest.kt

Fixes: 242754590

Change-Id: Ieb95b9845ee044ae293a3934b9a688c879c64249
This commit is contained in:
Marcello Galhardo
2022-09-05 16:55:18 +00:00
parent 7b46b29bce
commit faeaa31049
5 changed files with 424 additions and 10 deletions

View File

@@ -23,6 +23,8 @@ import android.annotation.Nullable;
import android.content.Context;
import android.util.MathUtils;
import androidx.annotation.VisibleForTesting;
import com.android.systemui.Dumpable;
import com.android.systemui.R;
import com.android.systemui.dagger.SysUISingleton;
@@ -137,7 +139,17 @@ public class AmbientState implements Dumpable {
* True right after we swipe up on lockscreen and have not finished the fling down that follows.
* False when we stop flinging or leave lockscreen.
*/
private boolean mNeedFlingAfterLockscreenSwipeUp = false;
private boolean mIsFlingRequiredAfterLockScreenSwipeUp = false;
@VisibleForTesting
public boolean isFlingRequiredAfterLockScreenSwipeUp() {
return mIsFlingRequiredAfterLockScreenSwipeUp;
}
@VisibleForTesting
public void setFlingRequiredAfterLockScreenSwipeUp(boolean value) {
mIsFlingRequiredAfterLockScreenSwipeUp = value;
}
/**
* @return Height of the notifications panel without top padding when expansion completes.
@@ -181,7 +193,7 @@ public class AmbientState implements Dumpable {
public void setSwipingUp(boolean isSwipingUp) {
if (!isSwipingUp && mIsSwipingUp) {
// Just stopped swiping up.
mNeedFlingAfterLockscreenSwipeUp = true;
mIsFlingRequiredAfterLockScreenSwipeUp = true;
}
mIsSwipingUp = isSwipingUp;
}
@@ -196,10 +208,10 @@ public class AmbientState implements Dumpable {
/**
* @param isFlinging Whether we are flinging the shade open or closed.
*/
public void setIsFlinging(boolean isFlinging) {
public void setFlinging(boolean isFlinging) {
if (isOnKeyguard() && !isFlinging && mIsFlinging) {
// Just stopped flinging.
mNeedFlingAfterLockscreenSwipeUp = false;
mIsFlingRequiredAfterLockScreenSwipeUp = false;
}
mIsFlinging = isFlinging;
}
@@ -508,7 +520,7 @@ public class AmbientState implements Dumpable {
public void setStatusBarState(int statusBarState) {
if (mStatusBarState != StatusBarState.KEYGUARD) {
mNeedFlingAfterLockscreenSwipeUp = false;
mIsFlingRequiredAfterLockScreenSwipeUp = false;
}
mStatusBarState = statusBarState;
}
@@ -576,7 +588,7 @@ public class AmbientState implements Dumpable {
* @return Whether we need to do a fling down after swiping up on lockscreen.
*/
public boolean isFlingingAfterSwipeUpOnLockscreen() {
return mIsFlinging && mNeedFlingAfterLockscreenSwipeUp;
return mIsFlinging && mIsFlingRequiredAfterLockScreenSwipeUp;
}
/**
@@ -744,7 +756,8 @@ public class AmbientState implements Dumpable {
pw.println("mIsSwipingUp=" + mIsSwipingUp);
pw.println("mPanelTracking=" + mPanelTracking);
pw.println("mIsFlinging=" + mIsFlinging);
pw.println("mNeedFlingAfterLockscreenSwipeUp=" + mNeedFlingAfterLockscreenSwipeUp);
pw.println("mIsFlingRequiredAfterLockScreenSwipeUp="
+ mIsFlingRequiredAfterLockScreenSwipeUp);
pw.println("mZDistanceBetweenElements=" + mZDistanceBetweenElements);
pw.println("mBaseZHeight=" + mBaseZHeight);
}

View File

@@ -5042,7 +5042,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
@ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
public void setPanelFlinging(boolean flinging) {
mAmbientState.setIsFlinging(flinging);
mAmbientState.setFlinging(flinging);
if (!flinging) {
// re-calculate the stack height which was frozen while flinging
updateStackPosition();

View File

@@ -0,0 +1,390 @@
/*
* Copyright (C) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License
*/
package com.android.systemui.statusbar.notification.stack
import android.testing.AndroidTestingRunner
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.dump.DumpManager
import com.android.systemui.statusbar.StatusBarState
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager
import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
private const val MAX_PULSE_HEIGHT = 100000f
@RunWith(AndroidTestingRunner::class)
@SmallTest
class AmbientStateTest : SysuiTestCase() {
private val dumpManager = mock<DumpManager>()
private val sectionProvider = StackScrollAlgorithm.SectionProvider { _, _ -> false }
private val bypassController = StackScrollAlgorithm.BypassController { false }
private val statusBarKeyguardViewManager = mock<StatusBarKeyguardViewManager>()
private lateinit var sut: AmbientState
@Before
fun setUp() {
sut =
AmbientState(
context,
dumpManager,
sectionProvider,
bypassController,
statusBarKeyguardViewManager,
)
}
// region isDimmed
@Test
fun isDimmed_whenTrue_shouldReturnTrue() {
sut.arrangeDimmed(true)
assertThat(sut.isDimmed).isTrue()
}
@Test
fun isDimmed_whenFalse_shouldReturnFalse() {
sut.arrangeDimmed(false)
assertThat(sut.isDimmed).isFalse()
}
@Test
fun isDimmed_whenDozeAmountIsEmpty_shouldReturnTrue() {
sut.arrangeDimmed(true)
sut.dozeAmount = 0f
assertThat(sut.isDimmed).isTrue()
}
@Test
fun isDimmed_whenPulseExpandingIsFalse_shouldReturnTrue() {
sut.arrangeDimmed(true)
sut.arrangePulseExpanding(false)
sut.dozeAmount = 1f // arrangePulseExpanding changes dozeAmount
assertThat(sut.isDimmed).isTrue()
}
// endregion
// region pulseHeight
@Test
fun pulseHeight_whenValueChanged_shouldCallListener() {
var listenerCalledCount = 0
sut.pulseHeight = MAX_PULSE_HEIGHT
sut.setOnPulseHeightChangedListener { listenerCalledCount++ }
sut.pulseHeight = 0f
assertThat(listenerCalledCount).isEqualTo(1)
}
@Test
fun pulseHeight_whenSetSameValue_shouldDoNothing() {
var listenerCalledCount = 0
sut.pulseHeight = MAX_PULSE_HEIGHT
sut.setOnPulseHeightChangedListener { listenerCalledCount++ }
sut.pulseHeight = MAX_PULSE_HEIGHT
assertThat(listenerCalledCount).isEqualTo(0)
}
@Test
fun pulseHeight_whenValueIsFull_shouldReturn0() {
sut.pulseHeight = MAX_PULSE_HEIGHT
assertThat(sut.pulseHeight).isEqualTo(0f)
}
@Test
fun pulseHeight_whenValueIsNotFull_shouldReturnValue() {
val expected = MAX_PULSE_HEIGHT - 0.1f
sut.pulseHeight = expected
assertThat(sut.pulseHeight).isEqualTo(expected)
}
// endregion
// region statusBarState
@Test
fun statusBarState_whenPreviousStateIsNotKeyguardAndChange_shouldSetIsFlingRequiredToFalse() {
sut.setStatusBarState(StatusBarState.SHADE)
sut.isFlingRequiredAfterLockScreenSwipeUp = true
sut.setStatusBarState(StatusBarState.KEYGUARD)
assertThat(sut.isFlingRequiredAfterLockScreenSwipeUp).isFalse()
}
@Test
fun statusBarState_whenPreviousStateIsKeyguardAndChange_shouldDoNothing() {
sut.setStatusBarState(StatusBarState.KEYGUARD)
sut.isFlingRequiredAfterLockScreenSwipeUp = true
sut.setStatusBarState(StatusBarState.SHADE)
assertThat(sut.isFlingRequiredAfterLockScreenSwipeUp).isTrue()
}
// endregion
// region hideAmount
@Test
fun hideAmount_whenSetToFullValue_shouldReturnZeroFromPulseHeight() {
sut.hideAmount = 0f
sut.pulseHeight = 1f
sut.hideAmount = 1f
assertThat(sut.pulseHeight).isEqualTo(0f)
}
@Test
fun hideAmount_whenSetToAnyNotFullValue_shouldDoNothing() {
sut.hideAmount = 1f
sut.pulseHeight = 1f
sut.hideAmount = 0f
assertThat(sut.pulseHeight).isEqualTo(1f)
}
// endregion
// region dozeAmount
@Test
fun dozeAmount_whenDozeAmountIsSetToFullDozing_shouldReturnZeroFromPulseHeight() {
sut.dozeAmount = 0f
sut.pulseHeight = 1f
sut.dozeAmount = 1f
assertThat(sut.pulseHeight).isEqualTo(0f)
}
@Test
fun dozeAmount_whenDozeAmountIsSetToFullAwake_shouldReturnZeroFromPulseHeight() {
sut.dozeAmount = 1f
sut.pulseHeight = 1f
sut.dozeAmount = 0f
assertThat(sut.pulseHeight).isEqualTo(0f)
}
@Test
fun dozeAmount_whenDozeAmountIsSetAnyValueNotFullAwakeOrDozing_shouldDoNothing() {
sut.dozeAmount = 1f
sut.pulseHeight = 1f
sut.dozeAmount = 0.5f
assertThat(sut.pulseHeight).isEqualTo(1f)
}
// endregion
// region trackedHeadsUpRow
@Test
fun trackedHeadsUpRow_whenIsAboveTheShelf_shouldReturnInstance() {
sut.trackedHeadsUpRow = mock { whenever(isAboveShelf).thenReturn(true) }
assertThat(sut.trackedHeadsUpRow).isNotNull()
}
@Test
fun trackedHeadsUpRow_whenIsNotAboveTheShelf_shouldReturnNull() {
sut.trackedHeadsUpRow = mock { whenever(isAboveShelf).thenReturn(false) }
assertThat(sut.trackedHeadsUpRow).isNull()
}
// endregion
// region isSwipingUp
@Test
fun isSwipingUp_whenValueChangedToTrue_shouldRequireFling() {
sut.isSwipingUp = false
sut.isFlingRequiredAfterLockScreenSwipeUp = false
sut.isSwipingUp = true
assertThat(sut.isFlingRequiredAfterLockScreenSwipeUp).isFalse()
}
@Test
fun isSwipingUp_whenValueChangedToFalse_shouldRequireFling() {
sut.isSwipingUp = true
sut.isFlingRequiredAfterLockScreenSwipeUp = false
sut.isSwipingUp = false
assertThat(sut.isFlingRequiredAfterLockScreenSwipeUp).isTrue()
}
// endregion
// region isFlinging
@Test
fun isFlinging_shouldNotNeedFling() {
sut.arrangeFlinging(true)
sut.setFlinging(false)
assertThat(sut.isFlingRequiredAfterLockScreenSwipeUp).isFalse()
}
@Test
fun isFlinging_whenNotOnLockScreen_shouldDoNothing() {
sut.arrangeFlinging(true)
sut.setStatusBarState(StatusBarState.SHADE)
sut.isFlingRequiredAfterLockScreenSwipeUp = true
sut.setFlinging(false)
assertThat(sut.isFlingRequiredAfterLockScreenSwipeUp).isTrue()
}
@Test
fun isFlinging_whenValueChangedToTrue_shouldDoNothing() {
sut.arrangeFlinging(false)
sut.setFlinging(true)
assertThat(sut.isFlingRequiredAfterLockScreenSwipeUp).isTrue()
}
// endregion
// region scrollY
@Test
fun scrollY_shouldSetValueGreaterThanZero() {
sut.scrollY = 0
sut.scrollY = 1
assertThat(sut.scrollY).isEqualTo(1)
}
@Test
fun scrollY_shouldNotSetValueLessThanZero() {
sut.scrollY = 0
sut.scrollY = -1
assertThat(sut.scrollY).isEqualTo(0)
}
// endregion
// region setOverScrollAmount
fun setOverScrollAmount_shouldSetValueOnTop() {
sut.setOverScrollAmount(/* amount = */ 10f, /* onTop = */ true)
val resultOnTop = sut.getOverScrollAmount(/* top = */ true)
val resultOnBottom = sut.getOverScrollAmount(/* top = */ false)
assertThat(resultOnTop).isEqualTo(10f)
assertThat(resultOnBottom).isEqualTo(0f)
}
fun setOverScrollAmount_shouldSetValueOnBottom() {
sut.setOverScrollAmount(/* amount = */ 10f, /* onTop = */ false)
val resultOnTop = sut.getOverScrollAmount(/* top */ true)
val resultOnBottom = sut.getOverScrollAmount(/* top */ false)
assertThat(resultOnTop).isEqualTo(0f)
assertThat(resultOnBottom).isEqualTo(10f)
}
// endregion
// region IsPulseExpanding
@Test
fun isPulseExpanding_shouldReturnTrue() {
sut.arrangePulseExpanding(true)
assertThat(sut.isPulseExpanding).isTrue()
}
@Test
fun isPulseExpanding_whenPulseHeightIsMax_shouldReturnFalse() {
sut.arrangePulseExpanding(true)
sut.pulseHeight = MAX_PULSE_HEIGHT
assertThat(sut.isPulseExpanding).isFalse()
}
@Test
fun isPulseExpanding_whenDozeAmountIsZero_shouldReturnFalse() {
sut.arrangePulseExpanding(true)
sut.dozeAmount = 0f
assertThat(sut.isPulseExpanding).isFalse()
}
@Test
fun isPulseExpanding_whenHideAmountIsFull_shouldReturnFalse() {
sut.arrangePulseExpanding(true)
sut.hideAmount = 1f
assertThat(sut.isPulseExpanding).isFalse()
}
// endregion
// region isOnKeyguard
@Test
fun isOnKeyguard_whenStatusBarStateIsKeyguard_shouldReturnTrue() {
sut.setStatusBarState(StatusBarState.KEYGUARD)
assertThat(sut.isOnKeyguard).isTrue()
}
@Test
fun isOnKeyguard_whenStatusBarStateIsNotKeyguard_shouldReturnFalse() {
sut.setStatusBarState(StatusBarState.SHADE)
assertThat(sut.isOnKeyguard).isFalse()
}
// endregion
}
// region Arrange helper methods.
private fun AmbientState.arrangeDimmed(value: Boolean) {
isDimmed = value
dozeAmount = if (value) 0f else 1f
arrangePulseExpanding(!value)
}
private fun AmbientState.arrangePulseExpanding(value: Boolean) {
if (value) {
dozeAmount = 1f
hideAmount = 0f
pulseHeight = 0f
} else {
dozeAmount = 0f
hideAmount = 1f
pulseHeight = MAX_PULSE_HEIGHT
}
}
private fun AmbientState.arrangeFlinging(value: Boolean) {
setStatusBarState(StatusBarState.KEYGUARD)
setFlinging(value)
isFlingRequiredAfterLockScreenSwipeUp = true
}
// endregion

View File

@@ -250,7 +250,7 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase {
// Validate that when the animation ends the stackEndHeight is recalculated immediately
clearInvocations(mAmbientState);
mStackScroller.setPanelFlinging(false);
verify(mAmbientState).setIsFlinging(eq(false));
verify(mAmbientState).setFlinging(eq(false));
verify(mAmbientState).setStackEndHeight(anyFloat());
verify(mAmbientState).setStackHeight(anyFloat());
}

View File

@@ -26,6 +26,7 @@ package com.android.systemui.util.mockito
import org.mockito.ArgumentCaptor
import org.mockito.ArgumentMatcher
import org.mockito.Mockito
import org.mockito.stubbing.OngoingStubbing
/**
* Returns Mockito.eq() as nullable type to avoid java.lang.IllegalStateException when
@@ -77,8 +78,18 @@ inline fun <reified T : Any> argumentCaptor(): ArgumentCaptor<T> =
* Helper function for creating new mocks, without the need to pass in a [Class] instance.
*
* Generic T is nullable because implicitly bounded by Any?.
*
* @param apply builder function to simplify stub configuration by improving type inference.
*/
inline fun <reified T : Any> mock(): T = Mockito.mock(T::class.java)
inline fun <reified T : Any> mock(apply: T.() -> Unit = {}): T = Mockito.mock(T::class.java)
.apply(apply)
/**
* Helper function for stubbing methods without the need to use backticks.
*
* @see Mockito.when
*/
fun <T> whenever(methodCall: T): OngoingStubbing<T> = Mockito.`when`(methodCall)
/**
* A kotlin implemented wrapper of [ArgumentCaptor] which prevents the following exception when