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