1 /* 2 * Copyright (C) 2016 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.fragment.cts; 17 18 import static org.junit.Assert.assertEquals; 19 import static org.junit.Assert.assertTrue; 20 21 import android.app.Activity; 22 import android.app.Fragment; 23 import android.app.FragmentController; 24 import android.app.FragmentManager; 25 import android.app.FragmentManagerNonConfig; 26 import android.os.Looper; 27 import android.os.Parcelable; 28 import android.util.Pair; 29 import android.view.View; 30 import android.view.ViewGroup; 31 import android.view.accessibility.AccessibilityNodeInfo; 32 33 import androidx.test.rule.ActivityTestRule; 34 35 import java.util.concurrent.CountDownLatch; 36 import java.util.concurrent.TimeUnit; 37 38 public class FragmentTestUtil { waitForExecution(final ActivityTestRule<? extends Activity> rule)39 public static void waitForExecution(final ActivityTestRule<? extends Activity> rule) { 40 // Wait for two cycles. When starting a postponed transition, it will post to 41 // the UI thread and then the execution will be added onto the queue after that. 42 // The two-cycle wait makes sure fragments have the opportunity to complete both 43 // before returning. 44 try { 45 rule.runOnUiThread(() -> { 46 }); 47 rule.runOnUiThread(() -> { 48 }); 49 } catch (Throwable t) { 50 throw new RuntimeException(t); 51 } 52 } 53 runOnUiThreadRethrow(ActivityTestRule<? extends Activity> rule, Runnable r)54 private static void runOnUiThreadRethrow(ActivityTestRule<? extends Activity> rule, 55 Runnable r) { 56 if (Looper.getMainLooper() == Looper.myLooper()) { 57 r.run(); 58 } else { 59 try { 60 rule.runOnUiThread(r); 61 } catch (Throwable t) { 62 throw new RuntimeException(t); 63 } 64 } 65 } 66 executePendingTransactions( final ActivityTestRule<? extends Activity> rule)67 public static boolean executePendingTransactions( 68 final ActivityTestRule<? extends Activity> rule) { 69 return executePendingTransactions(rule, rule.getActivity().getFragmentManager()); 70 } 71 executePendingTransactions( final ActivityTestRule<? extends Activity> rule, final FragmentManager fm)72 public static boolean executePendingTransactions( 73 final ActivityTestRule<? extends Activity> rule, final FragmentManager fm) { 74 final boolean[] ret = new boolean[1]; 75 runOnUiThreadRethrow(rule, new Runnable() { 76 @Override 77 public void run() { 78 ret[0] = fm.executePendingTransactions(); 79 } 80 }); 81 return ret[0]; 82 } 83 popBackStackImmediate(final ActivityTestRule<? extends Activity> rule)84 public static boolean popBackStackImmediate(final ActivityTestRule<? extends Activity> rule) { 85 return popBackStackImmediate(rule, rule.getActivity().getFragmentManager()); 86 } 87 popBackStackImmediate(final ActivityTestRule<? extends Activity> rule, final FragmentManager fm)88 public static boolean popBackStackImmediate(final ActivityTestRule<? extends Activity> rule, 89 final FragmentManager fm) { 90 final boolean[] ret = new boolean[1]; 91 runOnUiThreadRethrow(rule, new Runnable() { 92 @Override 93 public void run() { 94 ret[0] = fm.popBackStackImmediate(); 95 } 96 }); 97 return ret[0]; 98 } 99 popBackStackImmediate(final ActivityTestRule<FragmentTestActivity> rule, final int id, final int flags)100 public static boolean popBackStackImmediate(final ActivityTestRule<FragmentTestActivity> rule, 101 final int id, final int flags) { 102 return popBackStackImmediate(rule, rule.getActivity().getFragmentManager(), id, flags); 103 } 104 popBackStackImmediate(final ActivityTestRule<FragmentTestActivity> rule, final FragmentManager fm, final int id, final int flags)105 public static boolean popBackStackImmediate(final ActivityTestRule<FragmentTestActivity> rule, 106 final FragmentManager fm, final int id, final int flags) { 107 final boolean[] ret = new boolean[1]; 108 runOnUiThreadRethrow(rule, new Runnable() { 109 @Override 110 public void run() { 111 ret[0] = fm.popBackStackImmediate(id, flags); 112 } 113 }); 114 return ret[0]; 115 } 116 popBackStackImmediate(final ActivityTestRule<FragmentTestActivity> rule, final String name, final int flags)117 public static boolean popBackStackImmediate(final ActivityTestRule<FragmentTestActivity> rule, 118 final String name, final int flags) { 119 return popBackStackImmediate(rule, rule.getActivity().getFragmentManager(), name, flags); 120 } 121 popBackStackImmediate(final ActivityTestRule<FragmentTestActivity> rule, final FragmentManager fm, final String name, final int flags)122 public static boolean popBackStackImmediate(final ActivityTestRule<FragmentTestActivity> rule, 123 final FragmentManager fm, final String name, final int flags) { 124 final boolean[] ret = new boolean[1]; 125 runOnUiThreadRethrow(rule, new Runnable() { 126 @Override 127 public void run() { 128 ret[0] = fm.popBackStackImmediate(name, flags); 129 } 130 }); 131 return ret[0]; 132 } 133 setContentView(final ActivityTestRule<FragmentTestActivity> rule, final int layoutId)134 public static void setContentView(final ActivityTestRule<FragmentTestActivity> rule, 135 final int layoutId) { 136 final Activity activity = rule.getActivity(); 137 runOnUiThreadRethrow(rule, new Runnable() { 138 @Override 139 public void run() { 140 activity.setContentView(layoutId); 141 } 142 }); 143 } 144 assertChildren(ViewGroup container, Fragment... fragments)145 public static void assertChildren(ViewGroup container, Fragment... fragments) { 146 final int numFragments = fragments == null ? 0 : fragments.length; 147 assertEquals("There aren't the correct number of fragment Views in its container", 148 numFragments, container.getChildCount()); 149 for (int i = 0; i < numFragments; i++) { 150 assertEquals("Wrong Fragment View order for [" + i + "]", container.getChildAt(i), 151 fragments[i].getView()); 152 } 153 } 154 createController(ActivityTestRule<FragmentTestActivity> rule)155 public static FragmentController createController(ActivityTestRule<FragmentTestActivity> rule) { 156 final FragmentController[] controller = new FragmentController[1]; 157 final FragmentTestActivity activity = rule.getActivity(); 158 runOnUiThreadRethrow(rule, () -> { 159 HostCallbacks hostCallbacks = new HostCallbacks(activity, null, 0); 160 controller[0] = FragmentController.createController(hostCallbacks); 161 }); 162 return controller[0]; 163 } 164 165 resume(ActivityTestRule<FragmentTestActivity> rule, FragmentController fragmentController, Pair<Parcelable, FragmentManagerNonConfig> savedState)166 public static void resume(ActivityTestRule<FragmentTestActivity> rule, 167 FragmentController fragmentController, 168 Pair<Parcelable, FragmentManagerNonConfig> savedState) { 169 runOnUiThreadRethrow(rule, () -> { 170 fragmentController.attachHost(null); 171 if (savedState != null) { 172 fragmentController.restoreAllState(savedState.first, savedState.second); 173 } 174 fragmentController.dispatchCreate(); 175 fragmentController.dispatchActivityCreated(); 176 fragmentController.noteStateNotSaved(); 177 fragmentController.execPendingActions(); 178 fragmentController.dispatchStart(); 179 fragmentController.reportLoaderStart(); 180 fragmentController.dispatchResume(); 181 fragmentController.execPendingActions(); 182 }); 183 } 184 destroy( ActivityTestRule<FragmentTestActivity> rule, FragmentController fragmentController)185 public static Pair<Parcelable, FragmentManagerNonConfig> destroy( 186 ActivityTestRule<FragmentTestActivity> rule, FragmentController fragmentController) { 187 final Pair<Parcelable, FragmentManagerNonConfig>[] result = new Pair[1]; 188 runOnUiThreadRethrow(rule, () -> { 189 fragmentController.dispatchPause(); 190 final Parcelable savedState = fragmentController.saveAllState(); 191 final FragmentManagerNonConfig nonConfig = fragmentController.retainNestedNonConfig(); 192 fragmentController.dispatchStop(); 193 fragmentController.doLoaderStop(false); 194 fragmentController.dispatchDestroy(); 195 fragmentController.doLoaderDestroy(); 196 result[0] = Pair.create(savedState, nonConfig); 197 }); 198 return result[0]; 199 } 200 isVisible(Fragment fragment)201 public static boolean isVisible(Fragment fragment) { 202 View view = fragment.getView(); 203 AccessibilityNodeInfo accessibilityNodeInfo = view.createAccessibilityNodeInfo(); 204 boolean isVisible = accessibilityNodeInfo.isVisibleToUser(); 205 accessibilityNodeInfo.recycle(); 206 return isVisible; 207 } 208 209 /** 210 * Allocates until a garbage collection occurs. 211 */ forceGC()212 public static void forceGC() { 213 // This works on ART: 214 Runtime.getRuntime().gc(); 215 Runtime.getRuntime().runFinalization(); 216 Runtime.getRuntime().gc(); 217 Runtime.getRuntime().runFinalization(); 218 } 219 220 /** 221 * Restarts the RecreatedActivity and waits for the new activity to be resumed. 222 * 223 * @return The newly-restarted Activity 224 */ recreateActivity( ActivityTestRule<? extends Activity> rule, T activity)225 public static <T extends RecreatedActivity> T recreateActivity( 226 ActivityTestRule<? extends Activity> rule, T activity) throws InterruptedException { 227 // Now switch the orientation 228 RecreatedActivity.sResumed = new CountDownLatch(1); 229 RecreatedActivity.sDestroyed = new CountDownLatch(1); 230 231 runOnUiThreadRethrow(rule, () -> { 232 activity.recreate(); 233 }); 234 assertTrue(RecreatedActivity.sResumed.await(1, TimeUnit.SECONDS)); 235 assertTrue(RecreatedActivity.sDestroyed.await(1, TimeUnit.SECONDS)); 236 T newActivity = (T) RecreatedActivity.sActivity; 237 238 waitForExecution(rule); 239 240 RecreatedActivity.clearState(); 241 return newActivity; 242 } 243 } 244 245 246