blob: d18c126a96c1fc2489bdbcdaf6e099af73db3698 [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;
Jason Monk340b0e52017-03-08 14:57:56 -050029import android.support.test.InstrumentationRegistry;
Jason Monk88529052016-11-04 13:29:58 -040030import android.view.LayoutInflater;
31import android.view.View;
Jason Monkde850bb2017-02-01 19:26:30 -050032import android.view.WindowManager;
33import android.view.WindowManager.LayoutParams;
Jason Monk88529052016-11-04 13:29:58 -040034import android.widget.FrameLayout;
35
36import org.junit.After;
37import org.junit.Before;
Jason Monk340b0e52017-03-08 14:57:56 -050038import org.junit.Rule;
Jason Monk88529052016-11-04 13:29:58 -040039import org.junit.Test;
40
41import java.io.FileDescriptor;
42import java.io.PrintWriter;
43
44/**
45 * Base class for fragment class tests. Just adding one for any fragment will push it through
46 * general lifecycle events and ensure no basic leaks are happening. This class also implements
47 * the host for subclasses, so they can push it into desired states and do any unit testing
48 * required.
49 */
Jason Monk340b0e52017-03-08 14:57:56 -050050public abstract class BaseFragmentTest {
Jason Monk88529052016-11-04 13:29:58 -040051
52 private static final int VIEW_ID = 42;
53 private final Class<? extends Fragment> mCls;
Jason Monk88529052016-11-04 13:29:58 -040054 private Handler mHandler;
Siarhei Vishniakou6ad0e392017-06-02 17:20:34 -070055 protected FrameLayout mView;
Jason Monk88529052016-11-04 13:29:58 -040056 protected FragmentController mFragments;
57 protected Fragment mFragment;
58
Jason Monk340b0e52017-03-08 14:57:56 -050059 @Rule
60 public final TestableContext mContext = getContext();
61
62 public BaseFragmentTest(Class<? extends Fragment> cls) {
Jason Monk88529052016-11-04 13:29:58 -040063 mCls = cls;
64 }
65
Siarhei Vishniakou6ad0e392017-06-02 17:20:34 -070066 protected void createRootView() {
67 mView = new FrameLayout(mContext);
68 }
69
Jason Monk88529052016-11-04 13:29:58 -040070 @Before
Jason Monk28d5d222017-02-02 13:08:31 -050071 public void setupFragment() throws Exception {
Siarhei Vishniakou6ad0e392017-06-02 17:20:34 -070072 createRootView();
Jason Monk88529052016-11-04 13:29:58 -040073 mView.setId(VIEW_ID);
Jason Monk28d5d222017-02-02 13:08:31 -050074
Jason Monk340b0e52017-03-08 14:57:56 -050075 assertNotNull("BaseFragmentTest must be tagged with @RunWithLooper",
76 TestableLooper.get(this));
Jason Monk28d5d222017-02-02 13:08:31 -050077 TestableLooper.get(this).runWithLooper(() -> {
78 mHandler = new Handler();
79
Jason Monk9424af72018-12-19 14:17:26 -050080 mFragment = instantiate(mContext, mCls.getName(), null);
Jason Monk88529052016-11-04 13:29:58 -040081 mFragments = FragmentController.createController(new HostCallbacks());
82 mFragments.attachHost(null);
83 mFragments.getFragmentManager().beginTransaction()
84 .replace(VIEW_ID, mFragment)
85 .commit();
86 });
87 }
88
Jason Monk0c408002017-05-03 15:43:52 -040089 /**
90 * Allows tests to sub-class TestableContext if they want to provide any extended functionality
91 * or provide a {@link LeakCheck} to the TestableContext upon instantiation.
92 */
Jason Monk340b0e52017-03-08 14:57:56 -050093 protected TestableContext getContext() {
94 return new TestableContext(InstrumentationRegistry.getContext());
Jason Monk28d5d222017-02-02 13:08:31 -050095 }
96
Jason Monk88529052016-11-04 13:29:58 -040097 @After
Jason Monk28d5d222017-02-02 13:08:31 -050098 public void tearDown() throws Exception {
Jason Monk88529052016-11-04 13:29:58 -040099 if (mFragments != null) {
100 // Set mFragments to null to let it know not to destroy.
Jason Monk28d5d222017-02-02 13:08:31 -0500101 TestableLooper.get(this).runWithLooper(() -> mFragments.dispatchDestroy());
Jason Monk88529052016-11-04 13:29:58 -0400102 }
Jason Monk88529052016-11-04 13:29:58 -0400103 }
104
105 @Test
106 public void testCreateDestroy() {
Jason Monk28d5d222017-02-02 13:08:31 -0500107 mFragments.dispatchCreate();
108 processAllMessages();
Jason Monk88529052016-11-04 13:29:58 -0400109 destroyFragments();
110 }
111
112 @Test
113 public void testStartStop() {
Jason Monk28d5d222017-02-02 13:08:31 -0500114 mFragments.dispatchStart();
115 processAllMessages();
116 mFragments.dispatchStop();
117 processAllMessages();
Jason Monk88529052016-11-04 13:29:58 -0400118 }
119
120 @Test
121 public void testResumePause() {
Jason Monk28d5d222017-02-02 13:08:31 -0500122 mFragments.dispatchResume();
123 processAllMessages();
124 mFragments.dispatchPause();
125 processAllMessages();
Jason Monk88529052016-11-04 13:29:58 -0400126 }
127
128 @Test
Jason Monkde850bb2017-02-01 19:26:30 -0500129 public void testAttachDetach() {
130 WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
131 LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT,
132 LayoutParams.TYPE_SYSTEM_ALERT,
133 0, PixelFormat.TRANSLUCENT);
Jason Monk28d5d222017-02-02 13:08:31 -0500134 mFragments.dispatchResume();
135 processAllMessages();
Jason Monkde850bb2017-02-01 19:26:30 -0500136 attachFragmentToWindow();
137 detachFragmentToWindow();
Jason Monk28d5d222017-02-02 13:08:31 -0500138 mFragments.dispatchPause();
139 processAllMessages();
Jason Monkde850bb2017-02-01 19:26:30 -0500140 }
141
142 @Test
Jason Monk88529052016-11-04 13:29:58 -0400143 public void testRecreate() {
Jason Monk28d5d222017-02-02 13:08:31 -0500144 mFragments.dispatchResume();
145 processAllMessages();
Jason Monk64b214e2017-03-27 13:40:59 -0400146 recreateFragment();
Jason Monk28d5d222017-02-02 13:08:31 -0500147 processAllMessages();
Jason Monk88529052016-11-04 13:29:58 -0400148 }
149
150 @Test
151 public void testMultipleResumes() {
Jason Monk28d5d222017-02-02 13:08:31 -0500152 mFragments.dispatchResume();
153 processAllMessages();
154 mFragments.dispatchStop();
155 processAllMessages();
156 mFragments.dispatchResume();
157 processAllMessages();
158 }
159
Jason Monk64b214e2017-03-27 13:40:59 -0400160 protected void recreateFragment() {
161 mFragments.dispatchPause();
162 Parcelable p = mFragments.saveAllState();
163 mFragments.dispatchDestroy();
164
165 mFragments = FragmentController.createController(new HostCallbacks());
166 mFragments.attachHost(null);
167 mFragments.restoreAllState(p, (FragmentManagerNonConfig) null);
168 mFragments.dispatchResume();
169 mFragment = mFragments.getFragmentManager().findFragmentById(VIEW_ID);
170 }
171
Jason Monk28d5d222017-02-02 13:08:31 -0500172 protected void attachFragmentToWindow() {
173 ViewUtils.attachView(mView);
Jason Monk1660a272017-04-28 15:53:59 -0400174 TestableLooper.get(this).processAllMessages();
Jason Monk28d5d222017-02-02 13:08:31 -0500175 }
176
177 protected void detachFragmentToWindow() {
178 ViewUtils.detachView(mView);
Jason Monk1660a272017-04-28 15:53:59 -0400179 TestableLooper.get(this).processAllMessages();
Jason Monk88529052016-11-04 13:29:58 -0400180 }
181
182 protected void destroyFragments() {
Jason Monk28d5d222017-02-02 13:08:31 -0500183 mFragments.dispatchDestroy();
184 processAllMessages();
Jason Monk88529052016-11-04 13:29:58 -0400185 mFragments = null;
186 }
187
Jason Monk28d5d222017-02-02 13:08:31 -0500188 protected void processAllMessages() {
189 TestableLooper.get(this).processAllMessages();
Jason Monk88529052016-11-04 13:29:58 -0400190 }
191
Jason Monk9424af72018-12-19 14:17:26 -0500192 /**
193 * Method available for override to replace fragment instantiation.
194 */
195 protected Fragment instantiate(Context context, String className, @Nullable Bundle arguments) {
196 return Fragment.instantiate(context, className, arguments);
197 }
198
Jason Monk88529052016-11-04 13:29:58 -0400199 private View findViewById(int id) {
200 return mView.findViewById(id);
201 }
202
Jason Monk340b0e52017-03-08 14:57:56 -0500203 private class HostCallbacks extends FragmentHostCallback<BaseFragmentTest> {
Jason Monk88529052016-11-04 13:29:58 -0400204 public HostCallbacks() {
Jason Monk340b0e52017-03-08 14:57:56 -0500205 super(mContext, BaseFragmentTest.this.mHandler, 0);
Jason Monk88529052016-11-04 13:29:58 -0400206 }
207
208 @Override
Jason Monk340b0e52017-03-08 14:57:56 -0500209 public BaseFragmentTest onGetHost() {
210 return BaseFragmentTest.this;
Jason Monk88529052016-11-04 13:29:58 -0400211 }
212
213 @Override
214 public void onDump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
215 }
216
217 @Override
Jason Monk9424af72018-12-19 14:17:26 -0500218 public Fragment instantiate(Context context, String className, Bundle arguments) {
219 return BaseFragmentTest.this.instantiate(context, className, arguments);
220 }
221
222 @Override
Jason Monk88529052016-11-04 13:29:58 -0400223 public boolean onShouldSaveFragmentState(Fragment fragment) {
224 return true; // True for now.
225 }
226
227 @Override
228 public LayoutInflater onGetLayoutInflater() {
229 return LayoutInflater.from(mContext);
230 }
231
232 @Override
233 public boolean onUseFragmentManagerInflaterFactory() {
234 return true;
235 }
236
237 @Override
238 public boolean onHasWindowAnimations() {
239 return false;
240 }
241
242 @Override
243 public int onGetWindowAnimations() {
244 return 0;
245 }
246
247 @Override
248 public void onAttachFragment(Fragment fragment) {
249 }
250
251 @Nullable
252 @Override
253 public View onFindViewById(int id) {
Jason Monk340b0e52017-03-08 14:57:56 -0500254 return BaseFragmentTest.this.findViewById(id);
Jason Monk88529052016-11-04 13:29:58 -0400255 }
256
257 @Override
258 public boolean onHasView() {
259 return true;
260 }
261 }
262}