diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp index dca9e21482378..01b47ead659dc 100644 --- a/packages/SystemUI/Android.bp +++ b/packages/SystemUI/Android.bp @@ -407,6 +407,7 @@ android_library { static_libs: [ "SystemUI-tests-base", "androidx.test.uiautomator_uiautomator", + "androidx.core_core-animation-testing", "mockito-target-extended-minus-junit4", "androidx.test.ext.junit", "androidx.test.ext.truth", @@ -476,6 +477,7 @@ android_robolectric_test { ], static_libs: [ "androidx.test.uiautomator_uiautomator", + "androidx.core_core-animation-testing", "androidx.test.ext.junit", "inline-mockito-robolectric-prebuilt", ], diff --git a/packages/SystemUI/tests/src/android/animation/AnimatorTestRuleIsolationTest.kt b/packages/SystemUI/tests/src/android/animation/AnimatorTestRuleIsolationTest.kt index 7e105cf19e2bc..0fe2283c85431 100644 --- a/packages/SystemUI/tests/src/android/animation/AnimatorTestRuleIsolationTest.kt +++ b/packages/SystemUI/tests/src/android/animation/AnimatorTestRuleIsolationTest.kt @@ -31,7 +31,7 @@ import org.junit.runner.RunWith */ @RunWith(AndroidTestingRunner::class) @SmallTest -@RunWithLooper(setAsMainLooper = true) +@RunWithLooper class AnimatorTestRuleIsolationTest : SysuiTestCase() { @get:Rule val animatorTestRule = AnimatorTestRule() diff --git a/packages/SystemUI/tests/src/android/animation/AnimatorTestRulePrecisionTest.kt b/packages/SystemUI/tests/src/android/animation/AnimatorTestRulePrecisionTest.kt index 6c40368528026..cc7f7e4067d93 100644 --- a/packages/SystemUI/tests/src/android/animation/AnimatorTestRulePrecisionTest.kt +++ b/packages/SystemUI/tests/src/android/animation/AnimatorTestRulePrecisionTest.kt @@ -28,7 +28,7 @@ import org.junit.runner.RunWith @RunWith(AndroidTestingRunner::class) @SmallTest -@RunWithLooper(setAsMainLooper = true) +@RunWithLooper class AnimatorTestRulePrecisionTest : SysuiTestCase() { @get:Rule val animatorTestRule = AnimatorTestRule() diff --git a/packages/SystemUI/tests/src/androidx/core/animation/AnimatorTestRuleIsolationTest.kt b/packages/SystemUI/tests/src/androidx/core/animation/AnimatorTestRuleIsolationTest.kt index d034093a71b3d..2d84fbafcf6c9 100644 --- a/packages/SystemUI/tests/src/androidx/core/animation/AnimatorTestRuleIsolationTest.kt +++ b/packages/SystemUI/tests/src/androidx/core/animation/AnimatorTestRuleIsolationTest.kt @@ -31,7 +31,7 @@ import org.junit.runner.RunWith */ @RunWith(AndroidTestingRunner::class) @SmallTest -@RunWithLooper(setAsMainLooper = true) +@RunWithLooper class AnimatorTestRuleIsolationTest : SysuiTestCase() { @get:Rule val animatorTestRule = AnimatorTestRule() diff --git a/packages/SystemUI/tests/src/com/android/systemui/ExpandHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/ExpandHelperTest.java index 44a2b682bf37a..ba27fcd49fac4 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/ExpandHelperTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/ExpandHelperTest.java @@ -25,12 +25,12 @@ import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import android.testing.TestableLooper.RunWithLooper; -import androidx.core.animation.AnimatorTestRule; import androidx.core.animation.ObjectAnimator; import androidx.test.annotation.UiThreadTest; import androidx.test.filters.SmallTest; import com.android.keyguard.KeyguardUpdateMonitor; +import com.android.systemui.animation.AnimatorTestRule; import com.android.systemui.flags.FakeFeatureFlags; import com.android.systemui.flags.Flags; import com.android.systemui.statusbar.NotificationMediaManager; diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/AnimatorTestRuleOrderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/animation/AnimatorTestRuleOrderTest.kt new file mode 100644 index 0000000000000..a7e7dd074a334 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/animation/AnimatorTestRuleOrderTest.kt @@ -0,0 +1,183 @@ +/* + * Copyright (C) 2023 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.animation + +import android.testing.AndroidTestingRunner +import android.testing.TestableLooper.RunWithLooper +import androidx.core.animation.doOnEnd +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.util.doOnEnd +import com.google.common.truth.Truth.assertThat +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith + +@RunWith(AndroidTestingRunner::class) +@SmallTest +@RunWithLooper +class AnimatorTestRuleOrderTest : SysuiTestCase() { + + @get:Rule val animatorTestRule = AnimatorTestRule() + + var value1: Float = -1f + var value2: Float = -1f + + private inline fun animateThisX( + propertyName: String, + duration: Long, + startDelay: Long = 0, + crossinline onEndAction: () -> Unit, + ) { + androidx.core.animation.ObjectAnimator.ofFloat(this, propertyName, 0f, 1f).also { + it.interpolator = null + it.duration = duration + it.startDelay = startDelay + it.doOnEnd { onEndAction() } + it.start() + } + } + + private inline fun animateThisP( + propertyName: String, + duration: Long, + startDelay: Long = 0, + crossinline onEndAction: () -> Unit, + ) { + android.animation.ObjectAnimator.ofFloat(this, propertyName, 0f, 1f).also { + it.interpolator = null + it.duration = duration + it.startDelay = startDelay + it.doOnEnd { onEndAction() } + it.start() + } + } + + @Test + fun testTwoAnimators() { + var ended1 = false + var ended2 = false + animateThisP("value1", duration = 100) { ended1 = true } + animateThisX("value2", duration = 200) { ended2 = true } + assertThat(value1).isEqualTo(0f) + assertThat(value2).isEqualTo(0f) + assertThat(ended1).isFalse() + assertThat(ended2).isFalse() + + animatorTestRule.advanceTimeBy(99) + assertThat(value1).isEqualTo(0.99f) + assertThat(value2).isEqualTo(0.495f) + assertThat(ended1).isFalse() + assertThat(ended2).isFalse() + + animatorTestRule.advanceTimeBy(1) + assertThat(value1).isEqualTo(1f) + assertThat(value2).isEqualTo(0.5f) + assertThat(ended1).isTrue() + assertThat(ended2).isFalse() + + animatorTestRule.advanceTimeBy(99) + assertThat(value1).isEqualTo(1f) + assertThat(value2).isEqualTo(0.995f) + assertThat(ended1).isTrue() + assertThat(ended2).isFalse() + + animatorTestRule.advanceTimeBy(1) + assertThat(value1).isEqualTo(1f) + assertThat(value2).isEqualTo(1f) + assertThat(ended1).isTrue() + assertThat(ended2).isTrue() + } + + @Test + fun testChainedAnimatorsPlatformThenX() { + var ended1 = false + var ended2 = false + animateThisP("value1", duration = 100) { + ended1 = true + animateThisX("value2", duration = 100) { ended2 = true } + } + + assertThat(value1).isEqualTo(0f) + assertThat(value2).isEqualTo(-1f) + assertThat(ended1).isFalse() + assertThat(ended2).isFalse() + + animatorTestRule.advanceTimeBy(50) + assertThat(value1).isEqualTo(0.5f) + assertThat(value2).isEqualTo(-1f) + assertThat(ended1).isFalse() + assertThat(ended2).isFalse() + + animatorTestRule.advanceTimeBy(50) + assertThat(value1).isEqualTo(1f) + assertThat(value2).isEqualTo(0f) + assertThat(ended1).isTrue() + assertThat(ended2).isFalse() + + animatorTestRule.advanceTimeBy(50) + assertThat(value1).isEqualTo(1f) + assertThat(value2).isEqualTo(0.5f) + assertThat(ended1).isTrue() + assertThat(ended2).isFalse() + + animatorTestRule.advanceTimeBy(50) + assertThat(value1).isEqualTo(1f) + assertThat(value2).isEqualTo(1f) + assertThat(ended1).isTrue() + assertThat(ended2).isTrue() + } + + @Test + fun testChainedAnimatorsXThenPlatform() { + var ended1 = false + var ended2 = false + animateThisX("value1", duration = 100) { + ended1 = true + animateThisP("value2", duration = 100) { ended2 = true } + } + + assertThat(value1).isEqualTo(0f) + assertThat(value2).isEqualTo(-1f) + assertThat(ended1).isFalse() + assertThat(ended2).isFalse() + + animatorTestRule.advanceTimeBy(50) + assertThat(value1).isEqualTo(0.5f) + assertThat(value2).isEqualTo(-1f) + assertThat(ended1).isFalse() + assertThat(ended2).isFalse() + + animatorTestRule.advanceTimeBy(50) + assertThat(value1).isEqualTo(1f) + assertThat(value2).isEqualTo(0f) + assertThat(ended1).isTrue() + assertThat(ended2).isFalse() + + animatorTestRule.advanceTimeBy(50) + assertThat(value1).isEqualTo(1f) + assertThat(value2).isEqualTo(0.5f) + assertThat(ended1).isTrue() + assertThat(ended2).isFalse() + + animatorTestRule.advanceTimeBy(50) + assertThat(value1).isEqualTo(1f) + assertThat(value2).isEqualTo(1f) + assertThat(ended1).isTrue() + assertThat(ended2).isTrue() + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/LightRevealScrimRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/LightRevealScrimRepositoryTest.kt index ec30732dda232..dcaafe8dd0529 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/LightRevealScrimRepositoryTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/LightRevealScrimRepositoryTest.kt @@ -19,10 +19,10 @@ package com.android.systemui.keyguard.data.repository import android.graphics.Point import android.testing.AndroidTestingRunner import android.testing.TestableLooper -import androidx.core.animation.AnimatorTestRule import androidx.test.filters.SmallTest import com.android.systemui.RoboPilotTest import com.android.systemui.SysuiTestCase +import com.android.systemui.animation.AnimatorTestRule import com.android.systemui.coroutines.collectLastValue import com.android.systemui.keyguard.shared.model.BiometricUnlockModel import com.android.systemui.keyguard.shared.model.BiometricUnlockSource diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/events/SystemEventChipAnimationControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/events/SystemEventChipAnimationControllerTest.kt index 0cfca614a256a..2e223f6d8c1fe 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/events/SystemEventChipAnimationControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/events/SystemEventChipAnimationControllerTest.kt @@ -24,9 +24,9 @@ import android.util.Pair import android.view.Gravity import android.view.View import android.widget.FrameLayout -import androidx.core.animation.AnimatorTestRule import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase +import com.android.systemui.animation.AnimatorTestRule import com.android.systemui.flags.FakeFeatureFlags import com.android.systemui.statusbar.phone.StatusBarContentInsetsChangedListener import com.android.systemui.statusbar.phone.StatusBarContentInsetsProvider diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/events/SystemStatusAnimationSchedulerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/events/SystemStatusAnimationSchedulerImplTest.kt index 2af0cebf35194..414256fb1d5b9 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/events/SystemStatusAnimationSchedulerImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/events/SystemStatusAnimationSchedulerImplTest.kt @@ -22,9 +22,9 @@ import android.testing.AndroidTestingRunner import android.testing.TestableLooper.RunWithLooper import android.view.View import android.widget.FrameLayout -import androidx.core.animation.AnimatorTestRule import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase +import com.android.systemui.animation.AnimatorTestRule import com.android.systemui.dump.DumpManager import com.android.systemui.flags.FakeFeatureFlags import com.android.systemui.flags.Flags diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinatorTest.kt index 78c0982df4143..40edea2149ce9 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinatorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinatorTest.kt @@ -18,9 +18,9 @@ package com.android.systemui.statusbar.notification import android.testing.AndroidTestingRunner import android.testing.TestableLooper -import androidx.core.animation.AnimatorTestRule import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase +import com.android.systemui.animation.AnimatorTestRule import com.android.systemui.dump.DumpManager import com.android.systemui.dump.logcatLogBuffer import com.android.systemui.plugins.statusbar.StatusBarStateController diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java index 5dcb90144b707..823155b0d7e62 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java @@ -45,12 +45,12 @@ import android.view.View; import android.view.ViewPropertyAnimator; import android.widget.FrameLayout; -import androidx.core.animation.AnimatorTestRule; import androidx.test.filters.SmallTest; import com.android.keyguard.KeyguardUpdateMonitor; import com.android.systemui.R; import com.android.systemui.SysuiBaseFragmentTest; +import com.android.systemui.animation.AnimatorTestRule; import com.android.systemui.dump.DumpManager; import com.android.systemui.flags.FeatureFlags; import com.android.systemui.log.LogBuffer; diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/MultiSourceMinAlphaControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/MultiSourceMinAlphaControllerTest.kt index 2617613d1fc5a..2ce060c5d097e 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/MultiSourceMinAlphaControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/MultiSourceMinAlphaControllerTest.kt @@ -19,9 +19,9 @@ package com.android.systemui.statusbar.phone.fragment import android.testing.AndroidTestingRunner import android.testing.TestableLooper import android.view.View -import androidx.core.animation.AnimatorTestRule import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase +import com.android.systemui.animation.AnimatorTestRule import junit.framework.Assert.assertEquals import org.junit.Before import org.junit.Rule diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputViewTest.java index 7c285b8aa1a9a..ef39ff8ed521a 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputViewTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputViewTest.java @@ -62,7 +62,6 @@ import android.window.OnBackInvokedDispatcher; import android.window.WindowOnBackInvokedDispatcher; import androidx.annotation.NonNull; -import androidx.core.animation.AnimatorTestRule; import androidx.test.filters.SmallTest; import com.android.internal.logging.UiEventLogger; @@ -70,6 +69,7 @@ import com.android.internal.logging.testing.UiEventLoggerFake; import com.android.systemui.Dependency; import com.android.systemui.R; import com.android.systemui.SysuiTestCase; +import com.android.systemui.animation.AnimatorTestRule; import com.android.systemui.flags.FakeFeatureFlags; import com.android.systemui.flags.Flags; import com.android.systemui.statusbar.NotificationRemoteInputManager; diff --git a/packages/SystemUI/tests/utils/src/android/animation/AnimatorTestRule.java b/packages/SystemUI/tests/utils/src/android/animation/AnimatorTestRule.java index 6535f333f428e..19c68e86e5dc0 100644 --- a/packages/SystemUI/tests/utils/src/android/animation/AnimatorTestRule.java +++ b/packages/SystemUI/tests/utils/src/android/animation/AnimatorTestRule.java @@ -18,6 +18,7 @@ package android.animation; import android.animation.AnimationHandler.AnimationFrameCallback; import android.annotation.NonNull; +import android.annotation.Nullable; import android.os.Looper; import android.os.SystemClock; import android.util.AndroidRuntimeException; @@ -31,6 +32,7 @@ import org.junit.runners.model.Statement; import java.util.ArrayList; import java.util.List; +import java.util.function.Consumer; /** * JUnit {@link TestRule} that can be used to run {@link Animator}s without actually waiting for the @@ -125,14 +127,40 @@ public final class AnimatorTestRule implements TestRule { * @param timeDelta the amount of milliseconds to advance */ public void advanceTimeBy(long timeDelta) { + advanceTimeBy(timeDelta, null); + } + + /** + * Advances the animation clock by the given amount of delta in milliseconds. This call will + * produce an animation frame to all the ongoing animations. This method needs to be + * called on the same thread as {@link Animator#start()}. + *
+ * This method is not for test authors, but for rule authors to ensure that multiple animators
+ * can be advanced in sync.
+ *
+ * @param timeDelta the amount of milliseconds to advance
+ * @param preFrameAction a consumer to be passed the timeDelta following the time advancement
+ * but prior to the frame production.
+ */
+ public void advanceTimeBy(long timeDelta, @Nullable Consumer