blob: 5fa065a9135a20a2f37b5c1c0446e4316d9797ce [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 Monkde850bb2017-02-01 19:26:30 -050024import android.graphics.PixelFormat;
Jason Monk88529052016-11-04 13:29:58 -040025import android.os.Handler;
Jason Monk88529052016-11-04 13:29:58 -040026import android.os.Parcelable;
Jason Monk340b0e52017-03-08 14:57:56 -050027import android.support.test.InstrumentationRegistry;
Jason Monk88529052016-11-04 13:29:58 -040028import android.view.LayoutInflater;
29import android.view.View;
Jason Monkde850bb2017-02-01 19:26:30 -050030import android.view.WindowManager;
31import android.view.WindowManager.LayoutParams;
Jason Monk88529052016-11-04 13:29:58 -040032import android.widget.FrameLayout;
33
34import org.junit.After;
35import org.junit.Before;
Jason Monk340b0e52017-03-08 14:57:56 -050036import org.junit.Rule;
Jason Monk88529052016-11-04 13:29:58 -040037import org.junit.Test;
38
39import java.io.FileDescriptor;
40import java.io.PrintWriter;
41
42/**
43 * Base class for fragment class tests. Just adding one for any fragment will push it through
44 * general lifecycle events and ensure no basic leaks are happening. This class also implements
45 * the host for subclasses, so they can push it into desired states and do any unit testing
46 * required.
47 */
Jason Monk340b0e52017-03-08 14:57:56 -050048public abstract class BaseFragmentTest {
Jason Monk88529052016-11-04 13:29:58 -040049
50 private static final int VIEW_ID = 42;
51 private final Class<? extends Fragment> mCls;
Jason Monk88529052016-11-04 13:29:58 -040052 private Handler mHandler;
Siarhei Vishniakou6ad0e392017-06-02 17:20:34 -070053 protected FrameLayout mView;
Jason Monk88529052016-11-04 13:29:58 -040054 protected FragmentController mFragments;
55 protected Fragment mFragment;
56
Jason Monk340b0e52017-03-08 14:57:56 -050057 @Rule
58 public final TestableContext mContext = getContext();
59
60 public BaseFragmentTest(Class<? extends Fragment> cls) {
Jason Monk88529052016-11-04 13:29:58 -040061 mCls = cls;
62 }
63
Siarhei Vishniakou6ad0e392017-06-02 17:20:34 -070064 protected void createRootView() {
65 mView = new FrameLayout(mContext);
66 }
67
Jason Monk88529052016-11-04 13:29:58 -040068 @Before
Jason Monk28d5d222017-02-02 13:08:31 -050069 public void setupFragment() throws Exception {
Siarhei Vishniakou6ad0e392017-06-02 17:20:34 -070070 createRootView();
Jason Monk88529052016-11-04 13:29:58 -040071 mView.setId(VIEW_ID);
Jason Monk28d5d222017-02-02 13:08:31 -050072
Jason Monk340b0e52017-03-08 14:57:56 -050073 assertNotNull("BaseFragmentTest must be tagged with @RunWithLooper",
74 TestableLooper.get(this));
Jason Monk28d5d222017-02-02 13:08:31 -050075 TestableLooper.get(this).runWithLooper(() -> {
76 mHandler = new Handler();
77
78 mFragment = mCls.newInstance();
Jason Monk88529052016-11-04 13:29:58 -040079 mFragments = FragmentController.createController(new HostCallbacks());
80 mFragments.attachHost(null);
81 mFragments.getFragmentManager().beginTransaction()
82 .replace(VIEW_ID, mFragment)
83 .commit();
84 });
85 }
86
Jason Monk0c408002017-05-03 15:43:52 -040087 /**
88 * Allows tests to sub-class TestableContext if they want to provide any extended functionality
89 * or provide a {@link LeakCheck} to the TestableContext upon instantiation.
90 */
Jason Monk340b0e52017-03-08 14:57:56 -050091 protected TestableContext getContext() {
92 return new TestableContext(InstrumentationRegistry.getContext());
Jason Monk28d5d222017-02-02 13:08:31 -050093 }
94
Jason Monk88529052016-11-04 13:29:58 -040095 @After
Jason Monk28d5d222017-02-02 13:08:31 -050096 public void tearDown() throws Exception {
Jason Monk88529052016-11-04 13:29:58 -040097 if (mFragments != null) {
98 // Set mFragments to null to let it know not to destroy.
Jason Monk28d5d222017-02-02 13:08:31 -050099 TestableLooper.get(this).runWithLooper(() -> mFragments.dispatchDestroy());
Jason Monk88529052016-11-04 13:29:58 -0400100 }
Jason Monk88529052016-11-04 13:29:58 -0400101 }
102
103 @Test
104 public void testCreateDestroy() {
Jason Monk28d5d222017-02-02 13:08:31 -0500105 mFragments.dispatchCreate();
106 processAllMessages();
Jason Monk88529052016-11-04 13:29:58 -0400107 destroyFragments();
108 }
109
110 @Test
111 public void testStartStop() {
Jason Monk28d5d222017-02-02 13:08:31 -0500112 mFragments.dispatchStart();
113 processAllMessages();
114 mFragments.dispatchStop();
115 processAllMessages();
Jason Monk88529052016-11-04 13:29:58 -0400116 }
117
118 @Test
119 public void testResumePause() {
Jason Monk28d5d222017-02-02 13:08:31 -0500120 mFragments.dispatchResume();
121 processAllMessages();
122 mFragments.dispatchPause();
123 processAllMessages();
Jason Monk88529052016-11-04 13:29:58 -0400124 }
125
126 @Test
Jason Monkde850bb2017-02-01 19:26:30 -0500127 public void testAttachDetach() {
128 WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
129 LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT,
130 LayoutParams.TYPE_SYSTEM_ALERT,
131 0, PixelFormat.TRANSLUCENT);
Jason Monk28d5d222017-02-02 13:08:31 -0500132 mFragments.dispatchResume();
133 processAllMessages();
Jason Monkde850bb2017-02-01 19:26:30 -0500134 attachFragmentToWindow();
135 detachFragmentToWindow();
Jason Monk28d5d222017-02-02 13:08:31 -0500136 mFragments.dispatchPause();
137 processAllMessages();
Jason Monkde850bb2017-02-01 19:26:30 -0500138 }
139
140 @Test
Jason Monk88529052016-11-04 13:29:58 -0400141 public void testRecreate() {
Jason Monk28d5d222017-02-02 13:08:31 -0500142 mFragments.dispatchResume();
143 processAllMessages();
Jason Monk64b214e2017-03-27 13:40:59 -0400144 recreateFragment();
Jason Monk28d5d222017-02-02 13:08:31 -0500145 processAllMessages();
Jason Monk88529052016-11-04 13:29:58 -0400146 }
147
148 @Test
149 public void testMultipleResumes() {
Jason Monk28d5d222017-02-02 13:08:31 -0500150 mFragments.dispatchResume();
151 processAllMessages();
152 mFragments.dispatchStop();
153 processAllMessages();
154 mFragments.dispatchResume();
155 processAllMessages();
156 }
157
Jason Monk64b214e2017-03-27 13:40:59 -0400158 protected void recreateFragment() {
159 mFragments.dispatchPause();
160 Parcelable p = mFragments.saveAllState();
161 mFragments.dispatchDestroy();
162
163 mFragments = FragmentController.createController(new HostCallbacks());
164 mFragments.attachHost(null);
165 mFragments.restoreAllState(p, (FragmentManagerNonConfig) null);
166 mFragments.dispatchResume();
167 mFragment = mFragments.getFragmentManager().findFragmentById(VIEW_ID);
168 }
169
Jason Monk28d5d222017-02-02 13:08:31 -0500170 protected void attachFragmentToWindow() {
171 ViewUtils.attachView(mView);
Jason Monk1660a272017-04-28 15:53:59 -0400172 TestableLooper.get(this).processAllMessages();
Jason Monk28d5d222017-02-02 13:08:31 -0500173 }
174
175 protected void detachFragmentToWindow() {
176 ViewUtils.detachView(mView);
Jason Monk1660a272017-04-28 15:53:59 -0400177 TestableLooper.get(this).processAllMessages();
Jason Monk88529052016-11-04 13:29:58 -0400178 }
179
180 protected void destroyFragments() {
Jason Monk28d5d222017-02-02 13:08:31 -0500181 mFragments.dispatchDestroy();
182 processAllMessages();
Jason Monk88529052016-11-04 13:29:58 -0400183 mFragments = null;
184 }
185
Jason Monk28d5d222017-02-02 13:08:31 -0500186 protected void processAllMessages() {
187 TestableLooper.get(this).processAllMessages();
Jason Monk88529052016-11-04 13:29:58 -0400188 }
189
190 private View findViewById(int id) {
191 return mView.findViewById(id);
192 }
193
Jason Monk340b0e52017-03-08 14:57:56 -0500194 private class HostCallbacks extends FragmentHostCallback<BaseFragmentTest> {
Jason Monk88529052016-11-04 13:29:58 -0400195 public HostCallbacks() {
Jason Monk340b0e52017-03-08 14:57:56 -0500196 super(mContext, BaseFragmentTest.this.mHandler, 0);
Jason Monk88529052016-11-04 13:29:58 -0400197 }
198
199 @Override
Jason Monk340b0e52017-03-08 14:57:56 -0500200 public BaseFragmentTest onGetHost() {
201 return BaseFragmentTest.this;
Jason Monk88529052016-11-04 13:29:58 -0400202 }
203
204 @Override
205 public void onDump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
206 }
207
208 @Override
209 public boolean onShouldSaveFragmentState(Fragment fragment) {
210 return true; // True for now.
211 }
212
213 @Override
214 public LayoutInflater onGetLayoutInflater() {
215 return LayoutInflater.from(mContext);
216 }
217
218 @Override
219 public boolean onUseFragmentManagerInflaterFactory() {
220 return true;
221 }
222
223 @Override
224 public boolean onHasWindowAnimations() {
225 return false;
226 }
227
228 @Override
229 public int onGetWindowAnimations() {
230 return 0;
231 }
232
233 @Override
234 public void onAttachFragment(Fragment fragment) {
235 }
236
237 @Nullable
238 @Override
239 public View onFindViewById(int id) {
Jason Monk340b0e52017-03-08 14:57:56 -0500240 return BaseFragmentTest.this.findViewById(id);
Jason Monk88529052016-11-04 13:29:58 -0400241 }
242
243 @Override
244 public boolean onHasView() {
245 return true;
246 }
247 }
248}