1 /* 2 * Copyright (C) 2022 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 /** 18 * Tests for {@link android.app.wallpapereffectsgeneration.WallpaperEffectsGenerationManager} 19 * 20 * atest CtsWallpaperEffectsGenerationServiceTestCases 21 */ 22 package android.wallpapereffectsgeneration.cts; 23 24 import static androidx.test.InstrumentationRegistry.getContext; 25 import static com.android.compatibility.common.util.ShellUtils.runShellCommand; 26 import static com.google.common.truth.Truth.assertWithMessage; 27 import static org.junit.Assert.assertNotNull; 28 import static org.mockito.ArgumentMatchers.any; 29 import static org.mockito.ArgumentMatchers.eq; 30 import static org.mockito.Mockito.reset; 31 import static org.mockito.Mockito.timeout; 32 import static org.mockito.Mockito.verify; 33 import static org.mockito.internal.verification.VerificationModeFactory.atMost; 34 35 import android.app.wallpapereffectsgeneration.CinematicEffectRequest; 36 import android.app.wallpapereffectsgeneration.CinematicEffectResponse; 37 import android.app.wallpapereffectsgeneration.WallpaperEffectsGenerationManager; 38 import android.app.wallpapereffectsgeneration.WallpaperEffectsGenerationManager.CinematicEffectListener; 39 import android.content.Context; 40 import android.graphics.Bitmap; 41 import android.os.Process; 42 import android.util.Log; 43 44 import androidx.annotation.NonNull; 45 import androidx.test.runner.AndroidJUnit4; 46 47 import com.android.compatibility.common.util.RequiredServiceRule; 48 49 import org.junit.After; 50 import org.junit.Before; 51 import org.junit.Rule; 52 import org.junit.Test; 53 import org.junit.runner.RunWith; 54 import org.mockito.Mockito; 55 56 import java.util.concurrent.CountDownLatch; 57 import java.util.concurrent.Executors; 58 import java.util.concurrent.TimeUnit; 59 60 /** 61 * Tests for {@link WallpaperEffectsGenerationManager} 62 * 63 * atest CtsWallpaperEffectsGenerationServiceTestCases 64 */ 65 66 @RunWith(AndroidJUnit4.class) 67 public class WallpaperEffectsGenerationManagerTest { 68 private static final String TAG = "WallpaperEffectsGenerationTest"; 69 private static final boolean DEBUG = false; 70 private static final long VERIFY_TIMEOUT_MS = 5_000; 71 private static final long SERVICE_LIFECYCLE_TIMEOUT_MS = 20_000; 72 73 @Rule 74 public final RequiredServiceRule mRequiredServiceRule = 75 new RequiredServiceRule(Context.WALLPAPER_EFFECTS_GENERATION_SERVICE); 76 77 private WallpaperEffectsGenerationManager mManager; 78 private CtsWallpaperEffectsGenerationService.Watcher mWatcher; 79 private CinematicEffectRequest mInitialTaskRequest = 80 createCinematicEffectRequest("initial-task"); 81 82 @Before setup()83 public void setup() throws Exception { 84 mWatcher = CtsWallpaperEffectsGenerationService.setWatcher(); 85 mManager = getContext().getSystemService(WallpaperEffectsGenerationManager.class); 86 setService(CtsWallpaperEffectsGenerationService.SERVICE_NAME); 87 // The wallpaper effects generation services are created lazily, 88 // call one method to start the service for these tests. 89 mWatcher.verifier = Mockito.mock(CtsWallpaperEffectsGenerationService.class); 90 reset(mWatcher.verifier); 91 CtsCinematicEffectListener ctsCinematicEffectListener = new CtsCinematicEffectListener(); 92 mManager.generateCinematicEffect(mInitialTaskRequest, 93 Executors.newSingleThreadExecutor(), 94 ctsCinematicEffectListener); 95 await(mWatcher.created, "Waiting for onCreated()"); 96 // Check the request the server received is the request sent. 97 await(ctsCinematicEffectListener.mOkResponse, "wait for initial task returned"); 98 verifyService().onGenerateCinematicEffect(eq(mInitialTaskRequest)); 99 } 100 101 @After tearDown()102 public void tearDown() throws Exception { 103 setService(null); 104 await(mWatcher.destroyed, "Waiting for onDestroyed()"); 105 mWatcher = null; 106 CtsWallpaperEffectsGenerationService.clearWatcher(); 107 } 108 109 @Test testGenerateCinematicEffect_okResponse()110 public void testGenerateCinematicEffect_okResponse() { 111 mWatcher.verifier = Mockito.mock(CtsWallpaperEffectsGenerationService.class); 112 reset(mWatcher.verifier); 113 assertNotNull(mManager); 114 CinematicEffectRequest request = createSimpleCinematicEffectRequest("ok-task"); 115 CtsCinematicEffectListener ctsCinematicEffectListener = new CtsCinematicEffectListener(); 116 mManager.generateCinematicEffect(request, Executors.newSingleThreadExecutor(), 117 ctsCinematicEffectListener); 118 await(ctsCinematicEffectListener.mOkResponse, "Result is okay"); 119 verifyService().onGenerateCinematicEffect(eq(request)); 120 } 121 122 @Test testGenerateCinematicEffect_errorResponse()123 public void testGenerateCinematicEffect_errorResponse() { 124 mWatcher.verifier = Mockito.mock(CtsWallpaperEffectsGenerationService.class); 125 reset(mWatcher.verifier); 126 assertNotNull(mManager); 127 CinematicEffectRequest request = createSimpleCinematicEffectRequest("error-task"); 128 CtsCinematicEffectListener ctsCinematicEffectListener = new CtsCinematicEffectListener(); 129 mManager.generateCinematicEffect(request, Executors.newSingleThreadExecutor(), 130 ctsCinematicEffectListener); 131 await(ctsCinematicEffectListener.mErrorResponse, "Result is error"); 132 verifyService().onGenerateCinematicEffect(eq(request)); 133 } 134 135 @Test testGenerateCinematicEffect_pendingResponse()136 public void testGenerateCinematicEffect_pendingResponse() { 137 mWatcher.verifier = Mockito.mock(CtsWallpaperEffectsGenerationService.class); 138 reset(mWatcher.verifier); 139 assertNotNull(mManager); 140 CinematicEffectRequest request1 = createCinematicEffectRequest("pending-task-id"); 141 CinematicEffectRequest request2 = createCinematicEffectRequest("pending-task-id"); 142 CtsCinematicEffectListener ctsCinematicEffectListener1 = new CtsCinematicEffectListener(); 143 CtsCinematicEffectListener ctsCinematicEffectListener2 = new CtsCinematicEffectListener(); 144 mManager.generateCinematicEffect(request1, Executors.newSingleThreadExecutor(), 145 ctsCinematicEffectListener1); 146 mManager.generateCinematicEffect(request2, Executors.newSingleThreadExecutor(), 147 ctsCinematicEffectListener2); 148 await(ctsCinematicEffectListener2.mPendingResponse, 149 "2nd result returned and listener invoked"); 150 await(ctsCinematicEffectListener1.mOkResponse, 151 "1st request returned after long processing"); 152 verifyService().onGenerateCinematicEffect(eq(request1)); 153 verify(mWatcher.verifier, atMost(1)).onGenerateCinematicEffect(any()); 154 } 155 156 @Test testGenerateCinematicEffect_tooManyRequestsResponse()157 public void testGenerateCinematicEffect_tooManyRequestsResponse() { 158 mWatcher.verifier = Mockito.mock(CtsWallpaperEffectsGenerationService.class); 159 reset(mWatcher.verifier); 160 assertNotNull(mManager); 161 CinematicEffectRequest request1 = createCinematicEffectRequest("pending-task-id"); 162 CinematicEffectRequest request2 = createCinematicEffectRequest("other-task-id"); 163 CtsCinematicEffectListener ctsCinematicEffectListener1 = new CtsCinematicEffectListener(); 164 CtsCinematicEffectListener ctsCinematicEffectListener2 = new CtsCinematicEffectListener(); 165 mManager.generateCinematicEffect(request1, Executors.newSingleThreadExecutor(), 166 ctsCinematicEffectListener1); 167 mManager.generateCinematicEffect(request2, Executors.newSingleThreadExecutor(), 168 ctsCinematicEffectListener2); 169 await(ctsCinematicEffectListener2.mTooManyRequestsResponse, 170 "Second request immediately fail with too many requests response"); 171 await(ctsCinematicEffectListener1.mOkResponse, 172 "1st request returned after long processing"); 173 verifyService().onGenerateCinematicEffect(eq(request1)); 174 verify(mWatcher.verifier, atMost(1)).onGenerateCinematicEffect(any()); 175 } 176 177 private static final class CtsCinematicEffectListener implements CinematicEffectListener { 178 CountDownLatch mOkResponse = new CountDownLatch(1); 179 CountDownLatch mErrorResponse = new CountDownLatch(1); 180 CountDownLatch mPendingResponse = new CountDownLatch(1); 181 CountDownLatch mTooManyRequestsResponse = new CountDownLatch(1); 182 183 @Override onCinematicEffectGenerated(CinematicEffectResponse cinematicEffectResponse)184 public void onCinematicEffectGenerated(CinematicEffectResponse cinematicEffectResponse) { 185 Log.d(TAG, "cinematic effect response taskId = " + cinematicEffectResponse.getTaskId() 186 + ", status code = " + cinematicEffectResponse.getStatusCode()); 187 if (cinematicEffectResponse.getStatusCode() 188 == CinematicEffectResponse.CINEMATIC_EFFECT_STATUS_OK) { 189 mOkResponse.countDown(); 190 } else if (cinematicEffectResponse.getStatusCode() 191 == CinematicEffectResponse.CINEMATIC_EFFECT_STATUS_PENDING) { 192 mPendingResponse.countDown(); 193 } else if (cinematicEffectResponse.getStatusCode() 194 == CinematicEffectResponse.CINEMATIC_EFFECT_STATUS_TOO_MANY_REQUESTS) { 195 mTooManyRequestsResponse.countDown(); 196 } else if (cinematicEffectResponse.getStatusCode() 197 == CinematicEffectResponse.CINEMATIC_EFFECT_STATUS_ERROR) { 198 mErrorResponse.countDown(); 199 } 200 } 201 } 202 createCinematicEffectRequest(String taskId)203 private CinematicEffectRequest createCinematicEffectRequest(String taskId) { 204 Bitmap bmp = Bitmap.createBitmap(32, 48, Bitmap.Config.ARGB_8888); 205 return new CinematicEffectRequest(taskId, bmp); 206 } 207 verifyService()208 private CtsWallpaperEffectsGenerationService verifyService() { 209 return verify(mWatcher.verifier, timeout(VERIFY_TIMEOUT_MS)); 210 } 211 setService(String service)212 private void setService(String service) { 213 if (DEBUG) { 214 Log.d(TAG, "Setting WallpaperEffectsGeneration service to " + service); 215 } 216 int userId = Process.myUserHandle().getIdentifier(); 217 String shellCommand = ""; 218 if (service != null) { 219 shellCommand = "cmd wallpaper_effects_generation set temporary-service " 220 + userId + " " + service + " 60000"; 221 } else { 222 shellCommand = "cmd wallpaper_effects_generation set temporary-service " + userId; 223 } 224 if (DEBUG) { 225 Log.d(TAG, "runShellCommand(): " + shellCommand); 226 } 227 runShellCommand(shellCommand); 228 } 229 await(@onNull CountDownLatch latch, @NonNull String message)230 private void await(@NonNull CountDownLatch latch, @NonNull String message) { 231 try { 232 assertWithMessage(message).that( 233 latch.await(SERVICE_LIFECYCLE_TIMEOUT_MS, TimeUnit.MILLISECONDS)).isTrue(); 234 } catch (InterruptedException e) { 235 Thread.currentThread().interrupt(); 236 throw new IllegalStateException("Interrupted while: " + message); 237 } 238 } 239 createSimpleCinematicEffectRequest(String taskId)240 private CinematicEffectRequest createSimpleCinematicEffectRequest(String taskId) { 241 return new CinematicEffectRequest(taskId, 242 Bitmap.createBitmap(32, 48, Bitmap.Config.ARGB_8888)); 243 } 244 } 245