• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2017 The Android Open Source Project
3  *
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 
15 package android.testing;
16 
17 import static org.junit.Assert.assertNotNull;
18 
19 import android.annotation.Nullable;
20 import android.app.Fragment;
21 import android.app.FragmentController;
22 import android.app.FragmentHostCallback;
23 import android.app.FragmentManagerNonConfig;
24 import android.graphics.PixelFormat;
25 import android.os.Handler;
26 import android.os.Parcelable;
27 import android.support.test.InstrumentationRegistry;
28 import android.view.LayoutInflater;
29 import android.view.View;
30 import android.view.WindowManager;
31 import android.view.WindowManager.LayoutParams;
32 import android.widget.FrameLayout;
33 
34 import org.junit.After;
35 import org.junit.Before;
36 import org.junit.Rule;
37 import org.junit.Test;
38 
39 import java.io.FileDescriptor;
40 import 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  */
48 public abstract class BaseFragmentTest {
49 
50     private static final int VIEW_ID = 42;
51     private final Class<? extends Fragment> mCls;
52     private Handler mHandler;
53     protected FrameLayout mView;
54     protected FragmentController mFragments;
55     protected Fragment mFragment;
56 
57     @Rule
58     public final TestableContext mContext = getContext();
59 
BaseFragmentTest(Class<? extends Fragment> cls)60     public BaseFragmentTest(Class<? extends Fragment> cls) {
61         mCls = cls;
62     }
63 
createRootView()64     protected void createRootView() {
65         mView = new FrameLayout(mContext);
66     }
67 
68     @Before
setupFragment()69     public void setupFragment() throws Exception {
70         createRootView();
71         mView.setId(VIEW_ID);
72 
73         assertNotNull("BaseFragmentTest must be tagged with @RunWithLooper",
74                 TestableLooper.get(this));
75         TestableLooper.get(this).runWithLooper(() -> {
76             mHandler = new Handler();
77 
78             mFragment = mCls.newInstance();
79             mFragments = FragmentController.createController(new HostCallbacks());
80             mFragments.attachHost(null);
81             mFragments.getFragmentManager().beginTransaction()
82                     .replace(VIEW_ID, mFragment)
83                     .commit();
84         });
85     }
86 
getContext()87     protected TestableContext getContext() {
88         return new TestableContext(InstrumentationRegistry.getContext());
89     }
90 
91     @After
tearDown()92     public void tearDown() throws Exception {
93         if (mFragments != null) {
94             // Set mFragments to null to let it know not to destroy.
95             TestableLooper.get(this).runWithLooper(() -> mFragments.dispatchDestroy());
96         }
97     }
98 
99     @Test
testCreateDestroy()100     public void testCreateDestroy() {
101         mFragments.dispatchCreate();
102         processAllMessages();
103         destroyFragments();
104     }
105 
106     @Test
testStartStop()107     public void testStartStop() {
108         mFragments.dispatchStart();
109         processAllMessages();
110         mFragments.dispatchStop();
111         processAllMessages();
112     }
113 
114     @Test
testResumePause()115     public void testResumePause() {
116         mFragments.dispatchResume();
117         processAllMessages();
118         mFragments.dispatchPause();
119         processAllMessages();
120     }
121 
122     @Test
testAttachDetach()123     public void testAttachDetach() {
124         WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
125                 LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT,
126                 LayoutParams.TYPE_SYSTEM_ALERT,
127                 0, PixelFormat.TRANSLUCENT);
128         mFragments.dispatchResume();
129         processAllMessages();
130         attachFragmentToWindow();
131         detachFragmentToWindow();
132         mFragments.dispatchPause();
133         processAllMessages();
134     }
135 
136     @Test
testRecreate()137     public void testRecreate() {
138         mFragments.dispatchResume();
139         processAllMessages();
140         recreateFragment();
141         processAllMessages();
142     }
143 
144     @Test
testMultipleResumes()145     public void testMultipleResumes() {
146         mFragments.dispatchResume();
147         processAllMessages();
148         mFragments.dispatchStop();
149         processAllMessages();
150         mFragments.dispatchResume();
151         processAllMessages();
152     }
153 
recreateFragment()154     protected void recreateFragment() {
155         mFragments.dispatchPause();
156         Parcelable p = mFragments.saveAllState();
157         mFragments.dispatchDestroy();
158 
159         mFragments = FragmentController.createController(new HostCallbacks());
160         mFragments.attachHost(null);
161         mFragments.restoreAllState(p, (FragmentManagerNonConfig) null);
162         mFragments.dispatchResume();
163         mFragment = mFragments.getFragmentManager().findFragmentById(VIEW_ID);
164     }
165 
attachFragmentToWindow()166     protected void attachFragmentToWindow() {
167         ViewUtils.attachView(mView);
168         TestableLooper.get(this).processAllMessages();
169     }
170 
detachFragmentToWindow()171     protected void detachFragmentToWindow() {
172         ViewUtils.detachView(mView);
173         TestableLooper.get(this).processAllMessages();
174     }
175 
destroyFragments()176     protected void destroyFragments() {
177         mFragments.dispatchDestroy();
178         processAllMessages();
179         mFragments = null;
180     }
181 
processAllMessages()182     protected void processAllMessages() {
183         TestableLooper.get(this).processAllMessages();
184     }
185 
findViewById(int id)186     private View findViewById(int id) {
187         return mView.findViewById(id);
188     }
189 
190     private class HostCallbacks extends FragmentHostCallback<BaseFragmentTest> {
HostCallbacks()191         public HostCallbacks() {
192             super(mContext, BaseFragmentTest.this.mHandler, 0);
193         }
194 
195         @Override
onGetHost()196         public BaseFragmentTest onGetHost() {
197             return BaseFragmentTest.this;
198         }
199 
200         @Override
onDump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args)201         public void onDump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
202         }
203 
204         @Override
onShouldSaveFragmentState(Fragment fragment)205         public boolean onShouldSaveFragmentState(Fragment fragment) {
206             return true; // True for now.
207         }
208 
209         @Override
onGetLayoutInflater()210         public LayoutInflater onGetLayoutInflater() {
211             return LayoutInflater.from(mContext);
212         }
213 
214         @Override
onUseFragmentManagerInflaterFactory()215         public boolean onUseFragmentManagerInflaterFactory() {
216             return true;
217         }
218 
219         @Override
onHasWindowAnimations()220         public boolean onHasWindowAnimations() {
221             return false;
222         }
223 
224         @Override
onGetWindowAnimations()225         public int onGetWindowAnimations() {
226             return 0;
227         }
228 
229         @Override
onAttachFragment(Fragment fragment)230         public void onAttachFragment(Fragment fragment) {
231         }
232 
233         @Nullable
234         @Override
onFindViewById(int id)235         public View onFindViewById(int id) {
236             return BaseFragmentTest.this.findViewById(id);
237         }
238 
239         @Override
onHasView()240         public boolean onHasView() {
241             return true;
242         }
243     }
244 }
245