blob: 4831fcd67314bcce33888fa8b28480d0af037329 [file] [log] [blame]
Jorim Jaggi21c39a72017-10-20 15:47:51 +02001/*
2 * Copyright (C) 2017 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
17package com.android.server.wm;
18
19import static java.util.concurrent.TimeUnit.SECONDS;
20import static org.junit.Assert.assertEquals;
21import static org.junit.Assert.assertFalse;
22import static org.junit.Assert.assertTrue;
23import static org.mockito.Mockito.any;
24import static org.mockito.Mockito.atLeast;
25import static org.mockito.Mockito.atLeastOnce;
26import static org.mockito.Mockito.eq;
Jorim Jaggi6c7e5612017-12-12 02:17:10 +010027import static org.mockito.Mockito.times;
Jorim Jaggi21c39a72017-10-20 15:47:51 +020028import static org.mockito.Mockito.verify;
Jorim Jaggi6c7e5612017-12-12 02:17:10 +010029import static org.mockito.Mockito.when;
Jorim Jaggi21c39a72017-10-20 15:47:51 +020030
Jorim Jaggi6c7e5612017-12-12 02:17:10 +010031import android.animation.AnimationHandler;
Jorim Jaggi21c39a72017-10-20 15:47:51 +020032import android.animation.AnimationHandler.AnimationFrameCallbackProvider;
Jorim Jaggi6c7e5612017-12-12 02:17:10 +010033import android.animation.ValueAnimator;
Jorim Jaggi21c39a72017-10-20 15:47:51 +020034import android.graphics.Matrix;
35import android.graphics.Point;
36import android.platform.test.annotations.Presubmit;
37import android.support.test.filters.SmallTest;
38import android.support.test.runner.AndroidJUnit4;
39import android.view.Choreographer;
40import android.view.Choreographer.FrameCallback;
41import android.view.SurfaceControl;
42import android.view.SurfaceControl.Transaction;
43import android.view.animation.Animation;
44import android.view.animation.TranslateAnimation;
45
46import com.android.server.wm.LocalAnimationAdapter.AnimationSpec;
Jorim Jaggi6c7e5612017-12-12 02:17:10 +010047import com.android.server.wm.SurfaceAnimationRunner.AnimatorFactory;
Jorim Jaggi21c39a72017-10-20 15:47:51 +020048
49import org.junit.Before;
50import org.junit.Rule;
51import org.junit.Test;
52import org.junit.runner.RunWith;
53import org.mockito.Mock;
54import org.mockito.junit.MockitoJUnit;
55import org.mockito.junit.MockitoRule;
56
57import java.util.concurrent.CountDownLatch;
58
59/**
60 * Test class for {@link SurfaceAnimationRunner}.
61 *
Jorim Jaggia5e10572017-11-15 14:36:26 +010062 * atest FrameworksServicesTests:com.android.server.wm.SurfaceAnimationRunnerTest
Jorim Jaggi21c39a72017-10-20 15:47:51 +020063 */
64@SmallTest
65@Presubmit
66@RunWith(AndroidJUnit4.class)
67public class SurfaceAnimationRunnerTest extends WindowTestsBase {
68
69 @Mock SurfaceControl mMockSurface;
70 @Mock Transaction mMockTransaction;
Jorim Jaggi6c7e5612017-12-12 02:17:10 +010071 @Mock AnimationSpec mMockAnimationSpec;
Jorim Jaggi21c39a72017-10-20 15:47:51 +020072 @Rule public MockitoRule mMockitoRule = MockitoJUnit.rule();
73
74 private SurfaceAnimationRunner mSurfaceAnimationRunner;
75 private CountDownLatch mFinishCallbackLatch;
76
77 @Before
78 public void setUp() throws Exception {
79 super.setUp();
80 mFinishCallbackLatch = new CountDownLatch(1);
Jorim Jaggi6c7e5612017-12-12 02:17:10 +010081 mSurfaceAnimationRunner = new SurfaceAnimationRunner(null /* callbackProvider */, null,
Jorim Jaggi21c39a72017-10-20 15:47:51 +020082 mMockTransaction);
83 }
84
85 private void finishedCallback() {
86 mFinishCallbackLatch.countDown();
87 }
88
89 @Test
90 public void testAnimation() throws Exception {
91 mSurfaceAnimationRunner
92 .startAnimation(createTranslateAnimation(), mMockSurface, mMockTransaction,
93 this::finishedCallback);
94
95 // Ensure that the initial transformation has been applied.
96 final Matrix m = new Matrix();
97 m.setTranslate(-10, 0);
98 verify(mMockTransaction, atLeastOnce()).setMatrix(eq(mMockSurface), eq(m), any());
99 verify(mMockTransaction, atLeastOnce()).setAlpha(eq(mMockSurface), eq(1.0f));
100
101 mFinishCallbackLatch.await(1, SECONDS);
102 assertFinishCallbackCalled();
103
104 m.setTranslate(10, 0);
105 verify(mMockTransaction, atLeastOnce()).setMatrix(eq(mMockSurface), eq(m), any());
106
107 // At least 3 times: After initialization, first frame, last frame.
108 verify(mMockTransaction, atLeast(3)).setAlpha(eq(mMockSurface), eq(1.0f));
109 }
110
111 @Test
112 public void testCancel_notStarted() throws Exception {
Jorim Jaggi6c7e5612017-12-12 02:17:10 +0100113 mSurfaceAnimationRunner = new SurfaceAnimationRunner(new NoOpFrameCallbackProvider(), null,
Jorim Jaggia5e10572017-11-15 14:36:26 +0100114 mMockTransaction);
Jorim Jaggi21c39a72017-10-20 15:47:51 +0200115 mSurfaceAnimationRunner
116 .startAnimation(createTranslateAnimation(), mMockSurface, mMockTransaction,
117 this::finishedCallback);
118 mSurfaceAnimationRunner.onAnimationCancelled(mMockSurface);
119 waitUntilHandlersIdle();
120 assertTrue(mSurfaceAnimationRunner.mPendingAnimations.isEmpty());
121 assertFinishCallbackNotCalled();
Jorim Jaggi21c39a72017-10-20 15:47:51 +0200122 }
123
124 @Test
125 public void testCancel_running() throws Exception {
Jorim Jaggi6c7e5612017-12-12 02:17:10 +0100126 mSurfaceAnimationRunner = new SurfaceAnimationRunner(new NoOpFrameCallbackProvider(), null,
Jorim Jaggia5e10572017-11-15 14:36:26 +0100127 mMockTransaction);
Jorim Jaggi6c7e5612017-12-12 02:17:10 +0100128 mSurfaceAnimationRunner.startAnimation(createTranslateAnimation(), mMockSurface,
129 mMockTransaction, this::finishedCallback);
Jorim Jaggi21c39a72017-10-20 15:47:51 +0200130 waitUntilNextFrame();
131 assertFalse(mSurfaceAnimationRunner.mRunningAnimations.isEmpty());
132 mSurfaceAnimationRunner.onAnimationCancelled(mMockSurface);
133 assertTrue(mSurfaceAnimationRunner.mRunningAnimations.isEmpty());
134 waitUntilHandlersIdle();
135 assertFinishCallbackNotCalled();
Jorim Jaggi21c39a72017-10-20 15:47:51 +0200136 }
137
Jorim Jaggi6c7e5612017-12-12 02:17:10 +0100138 @Test
139 public void testCancel_sneakyCancelBeforeUpdate() throws Exception {
140 mSurfaceAnimationRunner = new SurfaceAnimationRunner(null, () -> new ValueAnimator() {
141 {
142 setFloatValues(0f, 1f);
143 }
144
145 @Override
146 public void addUpdateListener(AnimatorUpdateListener listener) {
147 super.addUpdateListener(animation -> {
148 // Sneaky test cancels animation just before applying frame to simulate
149 // interleaving of multiple threads. Muahahaha
150 if (animation.getCurrentPlayTime() > 0) {
151 mSurfaceAnimationRunner.onAnimationCancelled(mMockSurface);
152 }
153 listener.onAnimationUpdate(animation);
154 });
155 }
156 }, mMockTransaction);
157 when(mMockAnimationSpec.getDuration()).thenReturn(200L);
158 mSurfaceAnimationRunner.startAnimation(mMockAnimationSpec, mMockSurface, mMockTransaction,
159 this::finishedCallback);
160 waitUntilNextFrame();
161 assertFalse(mSurfaceAnimationRunner.mRunningAnimations.isEmpty());
162 verify(mMockAnimationSpec, atLeastOnce()).apply(any(), any(), eq(0L));
163 }
164
Jorim Jaggid6d97162018-01-05 18:28:36 +0100165 @Test
166 public void testDeferStartingAnimations() throws Exception {
167 mSurfaceAnimationRunner.deferStartingAnimations();
168 mSurfaceAnimationRunner.startAnimation(createTranslateAnimation(), mMockSurface,
169 mMockTransaction, this::finishedCallback);
170 waitUntilNextFrame();
171 assertTrue(mSurfaceAnimationRunner.mRunningAnimations.isEmpty());
172 mSurfaceAnimationRunner.continueStartingAnimations();
173 waitUntilNextFrame();
174 assertFalse(mSurfaceAnimationRunner.mRunningAnimations.isEmpty());
175 mFinishCallbackLatch.await(1, SECONDS);
176 assertFinishCallbackCalled();
177 }
178
Jorim Jaggi21c39a72017-10-20 15:47:51 +0200179 private void waitUntilNextFrame() throws Exception {
180 final CountDownLatch latch = new CountDownLatch(1);
181 mSurfaceAnimationRunner.mChoreographer.postCallback(Choreographer.CALLBACK_COMMIT,
182 latch::countDown, null /* token */);
183 latch.await();
184 }
185
186 private void assertFinishCallbackCalled() {
187 assertEquals(0, mFinishCallbackLatch.getCount());
188 }
189
190 private void assertFinishCallbackNotCalled() {
191 assertEquals(1, mFinishCallbackLatch.getCount());
192 }
193
194 private AnimationSpec createTranslateAnimation() {
195 final Animation a = new TranslateAnimation(-10, 10, 0, 0);
196 a.initialize(0, 0, 0, 0);
197 a.setDuration(50);
Jorim Jaggi2e3c31d2017-11-20 19:49:00 +0100198 return new WindowAnimationSpec(a, new Point(0, 0), false /* canSkipFirstFrame */);
Jorim Jaggi21c39a72017-10-20 15:47:51 +0200199 }
200
201 /**
202 * Callback provider that doesn't animate at all.
203 */
204 private static final class NoOpFrameCallbackProvider implements AnimationFrameCallbackProvider {
205
206 @Override
207 public void postFrameCallback(FrameCallback callback) {
208 }
209
210 @Override
211 public void postCommitCallback(Runnable runnable) {
212 }
213
214 @Override
215 public long getFrameTime() {
216 return 0;
217 }
218
219 @Override
220 public long getFrameDelay() {
221 return 0;
222 }
223
224 @Override
225 public void setFrameDelay(long delay) {
226 }
227 }
228}