blob: 9f60cce61bcec372ba2c7835edfa6bfaaa1562a9 [file] [log] [blame]
Jason Monk88529052016-11-04 13:29:58 -04001/*
Jason Monk340b0e52017-03-08 14:57:56 -05002 * Copyright (C) 2017 The Android Open Source Project
Jason Monk88529052016-11-04 13:29:58 -04003 *
4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
5 * except in compliance with the License. You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software distributed under the
10 * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
11 * KIND, either express or implied. See the License for the specific language governing
12 * permissions and limitations under the License.
13 */
14
Jason Monk340b0e52017-03-08 14:57:56 -050015package android.testing;
16
17import static org.junit.Assert.assertNotNull;
Jason Monk88529052016-11-04 13:29:58 -040018
19import android.annotation.Nullable;
20import android.app.Fragment;
21import android.app.FragmentController;
22import android.app.FragmentHostCallback;
23import android.app.FragmentManagerNonConfig;
Jason Monk9424af72018-12-19 14:17:26 -050024import android.content.Context;
Jason Monkde850bb2017-02-01 19:26:30 -050025import android.graphics.PixelFormat;
Jason Monk9424af72018-12-19 14:17:26 -050026import android.os.Bundle;
Jason Monk88529052016-11-04 13:29:58 -040027import android.os.Handler;
Jason Monk88529052016-11-04 13:29:58 -040028import android.os.Parcelable;
29import android.view.LayoutInflater;
30import android.view.View;
Jason Monkde850bb2017-02-01 19:26:30 -050031import android.view.WindowManager;
32import android.view.WindowManager.LayoutParams;
Jason Monk88529052016-11-04 13:29:58 -040033import android.widget.FrameLayout;
34
Brett Chabot84151d92019-02-27 15:37:59 -080035import androidx.test.InstrumentationRegistry;
36
Jason Monk88529052016-11-04 13:29:58 -040037import org.junit.After;
38import org.junit.Before;
Jason Monk340b0e52017-03-08 14:57:56 -050039import org.junit.Rule;
Jason Monk88529052016-11-04 13:29:58 -040040import org.junit.Test;
41
42import java.io.FileDescriptor;
43import java.io.PrintWriter;
44
45/**
46 * Base class for fragment class tests. Just adding one for any fragment will push it through
47 * general lifecycle events and ensure no basic leaks are happening. This class also implements
48 * the host for subclasses, so they can push it into desired states and do any unit testing
49 * required.
50 */
Jason Monk340b0e52017-03-08 14:57:56 -050051public abstract class BaseFragmentTest {
Jason Monk88529052016-11-04 13:29:58 -040052
53 private static final int VIEW_ID = 42;
54 private final Class<? extends Fragment> mCls;
Jason Monk88529052016-11-04 13:29:58 -040055 private Handler mHandler;
Siarhei Vishniakou6ad0e392017-06-02 17:20:34 -070056 protected FrameLayout mView;
Jason Monk88529052016-11-04 13:29:58 -040057 protected FragmentController mFragments;
58 protected Fragment mFragment;
59
Jason Monk340b0e52017-03-08 14:57:56 -050060 @Rule
61 public final TestableContext mContext = getContext();
62
63 public BaseFragmentTest(Class<? extends Fragment> cls) {
Jason Monk88529052016-11-04 13:29:58 -040064 mCls = cls;
65 }
66
Siarhei Vishniakou6ad0e392017-06-02 17:20:34 -070067 protected void createRootView() {
68 mView = new FrameLayout(mContext);
69 }
70
Jason Monk88529052016-11-04 13:29:58 -040071 @Before
Jason Monk28d5d222017-02-02 13:08:31 -050072 public void setupFragment() throws Exception {
Siarhei Vishniakou6ad0e392017-06-02 17:20:34 -070073 createRootView();
Jason Monk88529052016-11-04 13:29:58 -040074 mView.setId(VIEW_ID);
Jason Monk28d5d222017-02-02 13:08:31 -050075
Jason Monk340b0e52017-03-08 14:57:56 -050076 assertNotNull("BaseFragmentTest must be tagged with @RunWithLooper",
77 TestableLooper.get(this));
Jason Monk28d5d222017-02-02 13:08:31 -050078 TestableLooper.get(this).runWithLooper(() -> {
79 mHandler = new Handler();
80
Jason Monk9424af72018-12-19 14:17:26 -050081 mFragment = instantiate(mContext, mCls.getName(), null);
Jason Monk88529052016-11-04 13:29:58 -040082 mFragments = FragmentController.createController(new HostCallbacks());
83 mFragments.attachHost(null);
84 mFragments.getFragmentManager().beginTransaction()
85 .replace(VIEW_ID, mFragment)
86 .commit();
87 });
88 }
89
Jason Monk0c408002017-05-03 15:43:52 -040090 /**
91 * Allows tests to sub-class TestableContext if they want to provide any extended functionality
92 * or provide a {@link LeakCheck} to the TestableContext upon instantiation.
93 */
Jason Monk340b0e52017-03-08 14:57:56 -050094 protected TestableContext getContext() {
95 return new TestableContext(InstrumentationRegistry.getContext());
Jason Monk28d5d222017-02-02 13:08:31 -050096 }
97
Jason Monk88529052016-11-04 13:29:58 -040098 @After
Jason Monk28d5d222017-02-02 13:08:31 -050099 public void tearDown() throws Exception {
Jason Monk88529052016-11-04 13:29:58 -0400100 if (mFragments != null) {
101 // Set mFragments to null to let it know not to destroy.
Jason Monk28d5d222017-02-02 13:08:31 -0500102 TestableLooper.get(this).runWithLooper(() -> mFragments.dispatchDestroy());
Jason Monk88529052016-11-04 13:29:58 -0400103 }
Jason Monk88529052016-11-04 13:29:58 -0400104 }
105
106 @Test
107 public void testCreateDestroy() {
Jason Monk28d5d222017-02-02 13:08:31 -0500108 mFragments.dispatchCreate();
109 processAllMessages();
Jason Monk88529052016-11-04 13:29:58 -0400110 destroyFragments();
111 }
112
113 @Test
114 public void testStartStop() {
Jason Monk28d5d222017-02-02 13:08:31 -0500115 mFragments.dispatchStart();
116 processAllMessages();
117 mFragments.dispatchStop();
118 processAllMessages();
Jason Monk88529052016-11-04 13:29:58 -0400119 }
120
121 @Test
122 public void testResumePause() {
Jason Monk28d5d222017-02-02 13:08:31 -0500123 mFragments.dispatchResume();
124 processAllMessages();
125 mFragments.dispatchPause();
126 processAllMessages();
Jason Monk88529052016-11-04 13:29:58 -0400127 }
128
129 @Test
Jason Monkde850bb2017-02-01 19:26:30 -0500130 public void testAttachDetach() {
131 WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
132 LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT,
133 LayoutParams.TYPE_SYSTEM_ALERT,
134 0, PixelFormat.TRANSLUCENT);
Jason Monk28d5d222017-02-02 13:08:31 -0500135 mFragments.dispatchResume();
136 processAllMessages();
Jason Monkde850bb2017-02-01 19:26:30 -0500137 attachFragmentToWindow();
138 detachFragmentToWindow();
Jason Monk28d5d222017-02-02 13:08:31 -0500139 mFragments.dispatchPause();
140 processAllMessages();
Jason Monkde850bb2017-02-01 19:26:30 -0500141 }
142
143 @Test
Jason Monk88529052016-11-04 13:29:58 -0400144 public void testRecreate() {
Jason Monk28d5d222017-02-02 13:08:31 -0500145 mFragments.dispatchResume();
146 processAllMessages();
Jason Monk64b214e2017-03-27 13:40:59 -0400147 recreateFragment();
Jason Monk28d5d222017-02-02 13:08:31 -0500148 processAllMessages();
Jason Monk88529052016-11-04 13:29:58 -0400149 }
150
151 @Test
152 public void testMultipleResumes() {
Jason Monk28d5d222017-02-02 13:08:31 -0500153 mFragments.dispatchResume();
154 processAllMessages();
155 mFragments.dispatchStop();
156 processAllMessages();
157 mFragments.dispatchResume();
158 processAllMessages();
159 }
160
Jason Monk64b214e2017-03-27 13:40:59 -0400161 protected void recreateFragment() {
162 mFragments.dispatchPause();
163 Parcelable p = mFragments.saveAllState();
164 mFragments.dispatchDestroy();
165
166 mFragments = FragmentController.createController(new HostCallbacks());
167 mFragments.attachHost(null);
168 mFragments.restoreAllState(p, (FragmentManagerNonConfig) null);
169 mFragments.dispatchResume();
170 mFragment = mFragments.getFragmentManager().findFragmentById(VIEW_ID);
171 }
172
Jason Monk28d5d222017-02-02 13:08:31 -0500173 protected void attachFragmentToWindow() {
174 ViewUtils.attachView(mView);
Jason Monk1660a272017-04-28 15:53:59 -0400175 TestableLooper.get(this).processAllMessages();
Jason Monk28d5d222017-02-02 13:08:31 -0500176 }
177
178 protected void detachFragmentToWindow() {
179 ViewUtils.detachView(mView);
Jason Monk1660a272017-04-28 15:53:59 -0400180 TestableLooper.get(this).processAllMessages();
Jason Monk88529052016-11-04 13:29:58 -0400181 }
182
183 protected void destroyFragments() {
Jason Monk28d5d222017-02-02 13:08:31 -0500184 mFragments.dispatchDestroy();
185 processAllMessages();
Jason Monk88529052016-11-04 13:29:58 -0400186 mFragments = null;
187 }
188
Jason Monk28d5d222017-02-02 13:08:31 -0500189 protected void processAllMessages() {
190 TestableLooper.get(this).processAllMessages();
Jason Monk88529052016-11-04 13:29:58 -0400191 }
192
Jason Monk9424af72018-12-19 14:17:26 -0500193 /**
194 * Method available for override to replace fragment instantiation.
195 */
196 protected Fragment instantiate(Context context, String className, @Nullable Bundle arguments) {
197 return Fragment.instantiate(context, className, arguments);
198 }
199
Jason Monk88529052016-11-04 13:29:58 -0400200 private View findViewById(int id) {
201 return mView.findViewById(id);
202 }
203
Jason Monk340b0e52017-03-08 14:57:56 -0500204 private class HostCallbacks extends FragmentHostCallback<BaseFragmentTest> {
Jason Monk88529052016-11-04 13:29:58 -0400205 public HostCallbacks() {
Jason Monk340b0e52017-03-08 14:57:56 -0500206 super(mContext, BaseFragmentTest.this.mHandler, 0);
Jason Monk88529052016-11-04 13:29:58 -0400207 }
208
209 @Override
Jason Monk340b0e52017-03-08 14:57:56 -0500210 public BaseFragmentTest onGetHost() {
211 return BaseFragmentTest.this;
Jason Monk88529052016-11-04 13:29:58 -0400212 }
213
214 @Override
215 public void onDump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
216 }
217
218 @Override
Jason Monk9424af72018-12-19 14:17:26 -0500219 public Fragment instantiate(Context context, String className, Bundle arguments) {
220 return BaseFragmentTest.this.instantiate(context, className, arguments);
221 }
222
223 @Override
Jason Monk88529052016-11-04 13:29:58 -0400224 public boolean onShouldSaveFragmentState(Fragment fragment) {
225 return true; // True for now.
226 }
227
228 @Override
229 public LayoutInflater onGetLayoutInflater() {
230 return LayoutInflater.from(mContext);
231 }
232
233 @Override
234 public boolean onUseFragmentManagerInflaterFactory() {
235 return true;
236 }
237
238 @Override
239 public boolean onHasWindowAnimations() {
240 return false;
241 }
242
243 @Override
244 public int onGetWindowAnimations() {
245 return 0;
246 }
247
248 @Override
249 public void onAttachFragment(Fragment fragment) {
250 }
251
252 @Nullable
253 @Override
254 public View onFindViewById(int id) {
Jason Monk340b0e52017-03-08 14:57:56 -0500255 return BaseFragmentTest.this.findViewById(id);
Jason Monk88529052016-11-04 13:29:58 -0400256 }
257
258 @Override
259 public boolean onHasView() {
260 return true;
261 }
262 }
263}