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 
17 package com.android.intentresolver
18 
19 import android.content.res.Resources
20 import android.view.View
21 import android.view.Window
22 import androidx.activity.ComponentActivity
23 import androidx.lifecycle.Lifecycle
24 import androidx.lifecycle.testing.TestLifecycleOwner
25 import kotlinx.coroutines.Dispatchers
26 import kotlinx.coroutines.ExperimentalCoroutinesApi
27 import kotlinx.coroutines.test.StandardTestDispatcher
28 import kotlinx.coroutines.test.TestCoroutineScheduler
29 import kotlinx.coroutines.test.resetMain
30 import kotlinx.coroutines.test.setMain
31 import org.junit.After
32 import org.junit.Before
33 import org.junit.Test
34 import org.mockito.kotlin.any
35 import org.mockito.kotlin.doReturn
36 import org.mockito.kotlin.mock
37 import org.mockito.kotlin.times
38 import org.mockito.kotlin.verify
39 
40 private const val TIMEOUT_MS = 200
41 
42 @OptIn(ExperimentalCoroutinesApi::class)
43 class EnterTransitionAnimationDelegateTest {
44     private val elementName = "shared-element"
45     private val scheduler = TestCoroutineScheduler()
46     private val dispatcher = StandardTestDispatcher(scheduler)
47     private val lifecycleOwner = TestLifecycleOwner()
48 
49     private val transitionTargetView =
<lambda>null50         mock<View> {
51             // avoid the request-layout path in the delegate
52             on { isInLayout } doReturn true
53         }
54 
55     private val windowMock = mock<Window>()
56     private val resourcesMock =
<lambda>null57         mock<Resources> { on { getInteger(any<Int>()) } doReturn TIMEOUT_MS }
58     private val activity =
<lambda>null59         mock<ComponentActivity> {
60             on { lifecycle } doReturn lifecycleOwner.lifecycle
61             on { resources } doReturn resourcesMock
62             on { isActivityTransitionRunning } doReturn true
63             on { window } doReturn windowMock
64         }
65 
<lambda>null66     private val testSubject = EnterTransitionAnimationDelegate(activity) { transitionTargetView }
67 
68     @Before
setupnull69     fun setup() {
70         Dispatchers.setMain(dispatcher)
71         lifecycleOwner.handleLifecycleEvent(Lifecycle.Event.ON_CREATE)
72     }
73 
74     @After
cleanupnull75     fun cleanup() {
76         lifecycleOwner.handleLifecycleEvent(Lifecycle.Event.ON_DESTROY)
77         Dispatchers.resetMain()
78     }
79 
80     @Test
test_postponeTransition_timeoutnull81     fun test_postponeTransition_timeout() {
82         testSubject.postponeTransition()
83         testSubject.markOffsetCalculated()
84 
85         scheduler.advanceTimeBy(TIMEOUT_MS + 1L)
86         verify(activity) { 1 * { mock.startPostponedEnterTransition() } }
87         verify(windowMock) { 0 * { setWindowAnimations(any<Int>()) } }
88     }
89 
90     @Test
test_postponeTransition_animation_resumes_only_oncenull91     fun test_postponeTransition_animation_resumes_only_once() {
92         testSubject.postponeTransition()
93         testSubject.markOffsetCalculated()
94         testSubject.onTransitionElementReady(elementName)
95         testSubject.markOffsetCalculated()
96         testSubject.onTransitionElementReady(elementName)
97 
98         scheduler.advanceTimeBy(TIMEOUT_MS + 1L)
99         verify(activity, times(1)).startPostponedEnterTransition()
100     }
101 
102     @Test
test_postponeTransition_resume_animation_conditionsnull103     fun test_postponeTransition_resume_animation_conditions() {
104         testSubject.postponeTransition()
105         verify(activity) { 0 * { startPostponedEnterTransition() } }
106 
107         testSubject.markOffsetCalculated()
108         verify(activity) { 0 * { startPostponedEnterTransition() } }
109 
110         testSubject.onAllTransitionElementsReady()
111         verify(activity) { 1 * { startPostponedEnterTransition() } }
112     }
113 }
114