1 /* 2 * Copyright (C) 2018 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 android.autofillservice.cts; 17 18 import android.util.ArraySet; 19 import android.util.Log; 20 21 import androidx.annotation.GuardedBy; 22 import androidx.annotation.NonNull; 23 import androidx.annotation.Nullable; 24 25 import com.android.compatibility.common.util.TestNameUtils; 26 27 import org.junit.rules.TestWatcher; 28 import org.junit.runner.Description; 29 30 import java.util.Set; 31 32 /** 33 * Custom {@link TestWatcher} that's the outer rule of all {@link AutoFillServiceTestCase} tests. 34 * 35 * <p>This class is not thread safe, but should be fine... 36 */ 37 public final class AutofillTestWatcher extends TestWatcher { 38 39 /** 40 * Cleans up all launched activities between the tests and retries. 41 */ cleanAllActivities()42 public void cleanAllActivities() { 43 try { 44 finishActivities(); 45 waitUntilAllDestroyed(); 46 } finally { 47 resetStaticState(); 48 } 49 } 50 51 private static final String TAG = "AutofillTestWatcher"; 52 53 @GuardedBy("sUnfinishedBusiness") 54 private static final Set<AbstractAutoFillActivity> sUnfinishedBusiness = new ArraySet<>(); 55 56 @GuardedBy("sAllActivities") 57 private static final Set<AbstractAutoFillActivity> sAllActivities = new ArraySet<>(); 58 59 @Override starting(Description description)60 protected void starting(Description description) { 61 resetStaticState(); 62 final String testName = description.getDisplayName(); 63 Log.i(TAG, "Starting " + testName); 64 TestNameUtils.setCurrentTestName(testName); 65 } 66 67 @Override finished(Description description)68 protected void finished(Description description) { 69 final String testName = description.getDisplayName(); 70 cleanAllActivities(); 71 Log.i(TAG, "Finished " + testName); 72 TestNameUtils.setCurrentTestName(null); 73 } 74 resetStaticState()75 private void resetStaticState() { 76 synchronized (sUnfinishedBusiness) { 77 sUnfinishedBusiness.clear(); 78 } 79 synchronized (sAllActivities) { 80 sAllActivities.clear(); 81 } 82 } 83 84 /** 85 * Registers an activity so it's automatically finished (if necessary) after the test. 86 */ registerActivity(@onNull String where, @NonNull AbstractAutoFillActivity activity)87 public static void registerActivity(@NonNull String where, 88 @NonNull AbstractAutoFillActivity activity) { 89 synchronized (sUnfinishedBusiness) { 90 if (sUnfinishedBusiness.contains(activity)) { 91 throw new IllegalStateException("Already registered " + activity); 92 } 93 Log.v(TAG, "registering activity on " + where + ": " + activity); 94 sUnfinishedBusiness.add(activity); 95 sAllActivities.add(activity); 96 } 97 synchronized (sAllActivities) { 98 sAllActivities.add(activity); 99 100 } 101 } 102 103 /** 104 * Unregisters an activity so it's not automatically finished after the test. 105 */ unregisterActivity(@onNull String where, @NonNull AbstractAutoFillActivity activity)106 public static void unregisterActivity(@NonNull String where, 107 @NonNull AbstractAutoFillActivity activity) { 108 synchronized (sUnfinishedBusiness) { 109 final boolean unregistered = sUnfinishedBusiness.remove(activity); 110 if (unregistered) { 111 Log.d(TAG, "unregistered activity on " + where + ": " + activity); 112 } else { 113 Log.v(TAG, "ignoring already unregistered activity on " + where + ": " + activity); 114 } 115 } 116 } 117 118 /** 119 * Gets the instance of a previously registered activity. 120 */ 121 @Nullable getActivity(@onNull Class<A> clazz)122 public static <A extends AbstractAutoFillActivity> A getActivity(@NonNull Class<A> clazz) { 123 @SuppressWarnings("unchecked") 124 final A activity = (A) sAllActivities.stream().filter(a -> a.getClass().equals(clazz)) 125 .findFirst() 126 .get(); 127 return activity; 128 } 129 finishActivities()130 private void finishActivities() { 131 synchronized (sUnfinishedBusiness) { 132 if (sUnfinishedBusiness.isEmpty()) { 133 return; 134 } 135 Log.d(TAG, "Manually finishing " + sUnfinishedBusiness.size() + " activities"); 136 for (AbstractAutoFillActivity activity : sUnfinishedBusiness) { 137 if (activity.isFinishing()) { 138 Log.v(TAG, "Ignoring activity that isFinishing(): " + activity); 139 } else { 140 Log.d(TAG, "Finishing activity: " + activity); 141 activity.finishOnly(); 142 } 143 } 144 } 145 } 146 waitUntilAllDestroyed()147 private void waitUntilAllDestroyed() { 148 synchronized (sAllActivities) { 149 if (sAllActivities.isEmpty()) return; 150 151 Log.d(TAG, "Waiting until " + sAllActivities.size() + " activities are destroyed"); 152 for (AbstractAutoFillActivity activity : sAllActivities) { 153 Log.d(TAG, "Waiting for " + activity); 154 try { 155 activity.waintUntilDestroyed(Timeouts.ACTIVITY_RESURRECTION); 156 } catch (InterruptedException e) { 157 Log.e(TAG, "interrupted waiting for " + activity + " to be destroyed"); 158 Thread.currentThread().interrupt(); 159 } 160 } 161 } 162 } 163 } 164