1 /* 2 * Copyright (C) 2023 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 package com.android.dream.lowlight 17 18 import android.animation.Animator 19 import android.app.DreamManager 20 import android.content.ComponentName 21 import android.testing.AndroidTestingRunner 22 import androidx.test.filters.SmallTest 23 import kotlinx.coroutines.ExperimentalCoroutinesApi 24 import kotlinx.coroutines.test.StandardTestDispatcher 25 import kotlinx.coroutines.test.TestScope 26 import kotlinx.coroutines.test.advanceTimeBy 27 import kotlinx.coroutines.test.runCurrent 28 import kotlinx.coroutines.test.runTest 29 import org.junit.Before 30 import org.junit.Test 31 import org.junit.runner.RunWith 32 import org.mockito.Mock 33 import org.mockito.Mockito.clearInvocations 34 import org.mockito.Mockito.never 35 import org.mockito.Mockito.times 36 import org.mockito.Mockito.verify 37 import org.mockito.MockitoAnnotations 38 import src.com.android.dream.lowlight.utils.any 39 import src.com.android.dream.lowlight.utils.withArgCaptor 40 41 @OptIn(ExperimentalCoroutinesApi::class) 42 @SmallTest 43 @RunWith(AndroidTestingRunner::class) 44 class LowLightDreamManagerTest { 45 @Mock 46 private lateinit var mDreamManager: DreamManager 47 @Mock 48 private lateinit var mEnterAnimator: Animator 49 @Mock 50 private lateinit var mExitAnimator: Animator 51 52 private lateinit var mTransitionCoordinator: LowLightTransitionCoordinator 53 private lateinit var mLowLightDreamManager: LowLightDreamManager 54 private lateinit var testScope: TestScope 55 56 @Before setUpnull57 fun setUp() { 58 MockitoAnnotations.initMocks(this) 59 testScope = TestScope(StandardTestDispatcher()) 60 61 mTransitionCoordinator = LowLightTransitionCoordinator() 62 mTransitionCoordinator.setLowLightEnterListener( 63 object : LowLightTransitionCoordinator.LowLightEnterListener { 64 override fun onBeforeEnterLowLight() = mEnterAnimator 65 }) 66 mTransitionCoordinator.setLowLightExitListener( 67 object : LowLightTransitionCoordinator.LowLightExitListener { 68 override fun onBeforeExitLowLight() = mExitAnimator 69 }) 70 71 mLowLightDreamManager = LowLightDreamManager( 72 coroutineScope = testScope, 73 dreamManager = mDreamManager, 74 lowLightTransitionCoordinator = mTransitionCoordinator, 75 lowLightDreamComponent = DREAM_COMPONENT, 76 lowLightTransitionTimeoutMs = LOW_LIGHT_TIMEOUT_MS 77 ) 78 } 79 80 @Test setAmbientLightMode_lowLight_setSystemDreamnull81 fun setAmbientLightMode_lowLight_setSystemDream() = testScope.runTest { 82 mLowLightDreamManager.setAmbientLightMode(LowLightDreamManager.AMBIENT_LIGHT_MODE_LOW_LIGHT) 83 runCurrent() 84 verify(mDreamManager, never()).setSystemDreamComponent(DREAM_COMPONENT) 85 completeEnterAnimations() 86 runCurrent() 87 verify(mDreamManager).setSystemDreamComponent(DREAM_COMPONENT) 88 } 89 90 @Test <lambda>null91 fun setAmbientLightMode_regularLight_clearSystemDream() = testScope.runTest { 92 mLowLightDreamManager.setAmbientLightMode(LowLightDreamManager.AMBIENT_LIGHT_MODE_REGULAR) 93 runCurrent() 94 verify(mDreamManager, never()).setSystemDreamComponent(null) 95 completeExitAnimations() 96 runCurrent() 97 verify(mDreamManager).setSystemDreamComponent(null) 98 } 99 100 @Test <lambda>null101 fun setAmbientLightMode_defaultUnknownMode_clearSystemDream() = testScope.runTest { 102 // Set to low light first. 103 mLowLightDreamManager.setAmbientLightMode(LowLightDreamManager.AMBIENT_LIGHT_MODE_LOW_LIGHT) 104 runCurrent() 105 completeEnterAnimations() 106 runCurrent() 107 verify(mDreamManager).setSystemDreamComponent(DREAM_COMPONENT) 108 clearInvocations(mDreamManager) 109 110 // Return to default unknown mode. 111 mLowLightDreamManager.setAmbientLightMode(LowLightDreamManager.AMBIENT_LIGHT_MODE_UNKNOWN) 112 runCurrent() 113 completeExitAnimations() 114 runCurrent() 115 verify(mDreamManager).setSystemDreamComponent(null) 116 } 117 118 @Test <lambda>null119 fun setAmbientLightMode_dreamComponentNotSet_doNothing() = testScope.runTest { 120 val lowLightDreamManager = LowLightDreamManager( 121 coroutineScope = testScope, 122 dreamManager = mDreamManager, 123 lowLightTransitionCoordinator = mTransitionCoordinator, 124 lowLightDreamComponent = null, 125 lowLightTransitionTimeoutMs = LOW_LIGHT_TIMEOUT_MS 126 ) 127 lowLightDreamManager.setAmbientLightMode(LowLightDreamManager.AMBIENT_LIGHT_MODE_LOW_LIGHT) 128 runCurrent() 129 verify(mEnterAnimator, never()).addListener(any()) 130 verify(mDreamManager, never()).setSystemDreamComponent(any()) 131 } 132 133 @Test <lambda>null134 fun setAmbientLightMode_multipleTimesBeforeAnimationEnds_cancelsPrevious() = testScope.runTest { 135 mLowLightDreamManager.setAmbientLightMode(LowLightDreamManager.AMBIENT_LIGHT_MODE_LOW_LIGHT) 136 runCurrent() 137 // If we reset the light mode back to regular before the previous animation finishes, it 138 // should be ignored. 139 mLowLightDreamManager.setAmbientLightMode(LowLightDreamManager.AMBIENT_LIGHT_MODE_REGULAR) 140 runCurrent() 141 completeEnterAnimations() 142 completeExitAnimations() 143 runCurrent() 144 verify(mDreamManager, times(1)).setSystemDreamComponent(null) 145 } 146 147 @Test <lambda>null148 fun setAmbientLightMode_animatorNeverFinishes_timesOut() = testScope.runTest { 149 mLowLightDreamManager.setAmbientLightMode(LowLightDreamManager.AMBIENT_LIGHT_MODE_LOW_LIGHT) 150 advanceTimeBy(delayTimeMillis = LOW_LIGHT_TIMEOUT_MS + 1) 151 // Animation never finishes, but we should still set the system dream 152 verify(mDreamManager).setSystemDreamComponent(DREAM_COMPONENT) 153 } 154 155 @Test <lambda>null156 fun setAmbientLightMode_animationCancelled_SetsSystemDream() = testScope.runTest { 157 mLowLightDreamManager.setAmbientLightMode(LowLightDreamManager.AMBIENT_LIGHT_MODE_LOW_LIGHT) 158 runCurrent() 159 cancelEnterAnimations() 160 runCurrent() 161 // Animation never finishes, but we should still set the system dream 162 verify(mDreamManager).setSystemDreamComponent(DREAM_COMPONENT) 163 } 164 cancelEnterAnimationsnull165 private fun cancelEnterAnimations() { 166 val listener = withArgCaptor { verify(mEnterAnimator).addListener(capture()) } 167 listener.onAnimationCancel(mEnterAnimator) 168 } 169 completeEnterAnimationsnull170 private fun completeEnterAnimations() { 171 val listener = withArgCaptor { verify(mEnterAnimator).addListener(capture()) } 172 listener.onAnimationEnd(mEnterAnimator) 173 } 174 completeExitAnimationsnull175 private fun completeExitAnimations() { 176 val listener = withArgCaptor { verify(mExitAnimator).addListener(capture()) } 177 listener.onAnimationEnd(mExitAnimator) 178 } 179 180 companion object { 181 private val DREAM_COMPONENT = ComponentName("test_package", "test_dream") 182 private const val LOW_LIGHT_TIMEOUT_MS: Long = 1000 183 } 184 }