1 /* 2 * Copyright (C) 2021 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.managedprovisioning.common; 18 19 import static com.android.managedprovisioning.common.RetryLaunchViewModel.LaunchActivityFailureEvent.REASON_EXCEEDED_MAXIMUM_NUMBER_ACTIVITY_LAUNCH_RETRIES; 20 21 import static com.google.common.truth.Truth.assertThat; 22 23 import android.app.Application; 24 import android.app.admin.DevicePolicyManager; 25 import android.content.Context; 26 import android.content.Intent; 27 import android.os.Handler; 28 import android.os.Looper; 29 30 import androidx.test.core.app.ApplicationProvider; 31 import androidx.test.filters.SmallTest; 32 import androidx.test.platform.app.InstrumentationRegistry; 33 34 import com.android.bedstead.nene.utils.Poll; 35 import com.android.managedprovisioning.common.RetryLaunchViewModel.LaunchActivityEvent; 36 import com.android.managedprovisioning.common.RetryLaunchViewModel.LaunchActivityFailureEvent; 37 import com.android.managedprovisioning.common.RetryLaunchViewModel.LaunchActivityWaitingForRetryEvent; 38 39 import org.junit.Before; 40 import org.junit.Test; 41 import org.junit.runner.RunWith; 42 import org.junit.runners.JUnit4; 43 44 import java.time.Duration; 45 import java.util.Arrays; 46 import java.util.Queue; 47 import java.util.concurrent.ConcurrentLinkedQueue; 48 import java.util.stream.Collectors; 49 50 51 @SmallTest 52 @RunWith(JUnit4.class) 53 public class RetryLaunchViewModelTest { 54 private static final int LAUNCH_ROLE_HOLDER_UPDATER_PERIOD_MILLIS = 100; 55 private static final int NO_EVENT_TIMEOUT_MILLIS = 200; 56 private static final int LAUNCH_ROLE_HOLDER_MAX_RETRIES = 1; 57 private static final int ROLE_HOLDER_UPDATE_MAX_RETRIES = 1; 58 private static final LaunchActivityEvent 59 LAUNCH_ACTIVITY_EVENT = createLaunchRoleHolderUpdaterEvent(); 60 private static final LaunchActivityFailureEvent 61 EXCEED_MAX_NUMBER_LAUNCH_RETRIES_EVENT = createExceedMaxNumberLaunchRetriesEvent(); 62 private static final LaunchActivityWaitingForRetryEvent WAITING_FOR_RETRY_EVENT = 63 createWaitingForRetryEvent(); 64 private static final String TEST_DEVICE_MANAGEMENT_ROLE_HOLDER_UPDATER_PACKAGE_NAME = 65 "com.devicemanagementroleholderupdater.test.package"; 66 67 private final Context mApplicationContext = ApplicationProvider.getApplicationContext(); 68 private Handler mHandler; 69 private TestConfig mTestConfig; 70 private boolean mCanLaunchRoleHolderUpdater = true; 71 private RetryLaunchViewModel mViewModel; 72 private Queue<ViewModelEvent> mEvents; 73 private Utils mUtils = new Utils(); 74 75 @Before setUp()76 public void setUp() { 77 mTestConfig = new TestConfig( 78 LAUNCH_ROLE_HOLDER_UPDATER_PERIOD_MILLIS, 79 LAUNCH_ROLE_HOLDER_MAX_RETRIES, 80 ROLE_HOLDER_UPDATE_MAX_RETRIES); 81 mCanLaunchRoleHolderUpdater = true; 82 InstrumentationRegistry.getInstrumentation().runOnMainSync( 83 () -> mHandler = new Handler(Looper.myLooper())); 84 mViewModel = createViewModel(); 85 mEvents = subscribeToViewModelEvents(); 86 } 87 88 @Test tryStartRoleHolderUpdater_launchUpdater_works()89 public void tryStartRoleHolderUpdater_launchUpdater_works() { 90 mViewModel.tryStartActivity(); 91 blockUntilNextUiThreadCycle(); 92 93 assertThat(mEvents).containsExactly(LAUNCH_ACTIVITY_EVENT); 94 } 95 96 @Test tryStartRoleHolderUpdater_rescheduleLaunchUpdater_works()97 public void tryStartRoleHolderUpdater_rescheduleLaunchUpdater_works() { 98 mTestConfig.launchRoleHolderMaxRetries = 2; 99 mCanLaunchRoleHolderUpdater = false; 100 101 mViewModel.tryStartActivity(); 102 mCanLaunchRoleHolderUpdater = true; 103 104 pollForEvents( 105 mEvents, 106 WAITING_FOR_RETRY_EVENT, 107 LAUNCH_ACTIVITY_EVENT); 108 } 109 110 @Test tryStartRoleHolderUpdater_rescheduleLaunchUpdater_exceedsMaxRetryLimit_fails()111 public void tryStartRoleHolderUpdater_rescheduleLaunchUpdater_exceedsMaxRetryLimit_fails() { 112 mTestConfig.roleHolderUpdateMaxRetries = 1; 113 mCanLaunchRoleHolderUpdater = false; 114 115 mViewModel.tryStartActivity(); 116 117 pollForEvents( 118 mEvents, 119 WAITING_FOR_RETRY_EVENT, 120 EXCEED_MAX_NUMBER_LAUNCH_RETRIES_EVENT); 121 } 122 123 @Test stopLaunchRetries_works()124 public void stopLaunchRetries_works() { 125 mTestConfig.roleHolderUpdateMaxRetries = 1; 126 mCanLaunchRoleHolderUpdater = false; 127 128 mViewModel.tryStartActivity(); 129 mViewModel.stopLaunchRetries(); 130 131 pollForEvents(mEvents, WAITING_FOR_RETRY_EVENT); 132 } 133 134 @Test markWaitingForActivityResult_works()135 public void markWaitingForActivityResult_works() { 136 mViewModel.markWaitingForActivityResult(); 137 138 assertThat(mViewModel.isWaitingForActivityResult()).isTrue(); 139 } 140 141 @Test isWaitingForActivityResult_defaultsToFalse()142 public void isWaitingForActivityResult_defaultsToFalse() { 143 assertThat(mViewModel.isWaitingForActivityResult()).isFalse(); 144 } 145 pollForEvents( Queue<ViewModelEvent> actualEvents, ViewModelEvent... expectedEvents)146 private void pollForEvents( 147 Queue<ViewModelEvent> actualEvents, 148 ViewModelEvent... expectedEvents) { 149 for (ViewModelEvent nextExpectedEvent : expectedEvents) { 150 Poll.forValue("CapturedViewModelEvents", () -> actualEvents) 151 .toMeet(actualEventsQueue -> !actualEventsQueue.isEmpty()) 152 .errorOnFail("Expected CapturedViewModelEvents to contain exactly " 153 + Arrays.stream(expectedEvents) 154 .map(Object::toString).collect(Collectors.joining())) 155 .await(); 156 assertThat(actualEvents.remove()).isEqualTo(nextExpectedEvent); 157 } 158 pollForNoEvent(actualEvents); 159 } 160 pollForNoEvent(Queue<ViewModelEvent> capturedViewModelEvents)161 private void pollForNoEvent(Queue<ViewModelEvent> capturedViewModelEvents) { 162 // TODO(b/208237942): A pattern for testing that something does not happen 163 assertThat(Poll.forValue("CapturedViewModelEvents", () -> capturedViewModelEvents) 164 .toMeet(viewModelEvents -> !viewModelEvents.isEmpty()) 165 .timeout(Duration.ofMillis(NO_EVENT_TIMEOUT_MILLIS)) 166 .await()) 167 .isEmpty(); 168 } 169 createExceedMaxNumberLaunchRetriesEvent()170 private static LaunchActivityFailureEvent createExceedMaxNumberLaunchRetriesEvent() { 171 return new LaunchActivityFailureEvent( 172 REASON_EXCEEDED_MAXIMUM_NUMBER_ACTIVITY_LAUNCH_RETRIES); 173 } 174 createWaitingForRetryEvent()175 private static LaunchActivityWaitingForRetryEvent createWaitingForRetryEvent() { 176 return new LaunchActivityWaitingForRetryEvent(); 177 } 178 createLaunchRoleHolderUpdaterEvent()179 private static LaunchActivityEvent createLaunchRoleHolderUpdaterEvent() { 180 return new LaunchActivityEvent(createUpdateDeviceManagementRoleHolderIntent()); 181 } 182 subscribeToViewModelEvents()183 private Queue<ViewModelEvent> subscribeToViewModelEvents() { 184 Queue<ViewModelEvent> capturedViewModelEvents = new ConcurrentLinkedQueue<>(); 185 InstrumentationRegistry.getInstrumentation().runOnMainSync( 186 () -> mViewModel.observeViewModelEvents() 187 .observeForever(capturedViewModelEvents::add)); 188 return capturedViewModelEvents; 189 } 190 blockUntilNextUiThreadCycle()191 private void blockUntilNextUiThreadCycle() { 192 InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {}); 193 } 194 createViewModel()195 private RetryLaunchViewModel createViewModel() { 196 return new RetryLaunchViewModel( 197 (Application) mApplicationContext, 198 createUpdateDeviceManagementRoleHolderIntent(), 199 mHandler, 200 (context, intent) -> mCanLaunchRoleHolderUpdater, 201 mTestConfig); 202 } 203 createUpdateDeviceManagementRoleHolderIntent()204 private static Intent createUpdateDeviceManagementRoleHolderIntent() { 205 return new Intent(DevicePolicyManager.ACTION_UPDATE_DEVICE_POLICY_MANAGEMENT_ROLE_HOLDER) 206 .setPackage(TEST_DEVICE_MANAGEMENT_ROLE_HOLDER_UPDATER_PACKAGE_NAME); 207 } 208 209 private static final class TestConfig implements RetryLaunchViewModel.Config { 210 public int launchRoleHolderUpdaterPeriodMillis; 211 public int launchRoleHolderMaxRetries; 212 public int roleHolderUpdateMaxRetries; 213 TestConfig( int launchRoleHolderUpdaterPeriodMillis, int launchRoleHolderMaxRetries, int roleHolderUpdateMaxRetries)214 TestConfig( 215 int launchRoleHolderUpdaterPeriodMillis, 216 int launchRoleHolderMaxRetries, 217 int roleHolderUpdateMaxRetries) { 218 this.launchRoleHolderUpdaterPeriodMillis = launchRoleHolderUpdaterPeriodMillis; 219 this.launchRoleHolderMaxRetries = launchRoleHolderMaxRetries; 220 this.roleHolderUpdateMaxRetries = roleHolderUpdateMaxRetries; 221 } 222 223 @Override getLaunchActivityRetryMillis()224 public long getLaunchActivityRetryMillis() { 225 return launchRoleHolderUpdaterPeriodMillis; 226 } 227 228 @Override getLaunchActivityMaxRetries()229 public int getLaunchActivityMaxRetries() { 230 return launchRoleHolderMaxRetries; 231 } 232 233 } 234 } 235