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