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