• 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 
87     /**
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      */
getContext()91     protected TestableContext getContext() {
92         return new TestableContext(InstrumentationRegistry.getContext());
93     }
94 
95     @After
tearDown()96     public void tearDown() throws Exception {
97         if (mFragments != null) {
98             // Set mFragments to null to let it know not to destroy.
99             TestableLooper.get(this).runWithLooper(() -> mFragments.dispatchDestroy());
100         }
101     }
102 
103     @Test
testCreateDestroy()104     public void testCreateDestroy() {
105         mFragments.dispatchCreate();
106         processAllMessages();
107         destroyFragments();
108     }
109 
110     @Test
testStartStop()111     public void testStartStop() {
112         mFragments.dispatchStart();
113         processAllMessages();
114         mFragments.dispatchStop();
115         processAllMessages();
116     }
117 
118     @Test
testResumePause()119     public void testResumePause() {
120         mFragments.dispatchResume();
121         processAllMessages();
122         mFragments.dispatchPause();
123         processAllMessages();
124     }
125 
126     @Test
testAttachDetach()127     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);
132         mFragments.dispatchResume();
133         processAllMessages();
134         attachFragmentToWindow();
135         detachFragmentToWindow();
136         mFragments.dispatchPause();
137         processAllMessages();
138     }
139 
140     @Test
testRecreate()141     public void testRecreate() {
142         mFragments.dispatchResume();
143         processAllMessages();
144         recreateFragment();
145         processAllMessages();
146     }
147 
148     @Test
testMultipleResumes()149     public void testMultipleResumes() {
150         mFragments.dispatchResume();
151         processAllMessages();
152         mFragments.dispatchStop();
153         processAllMessages();
154         mFragments.dispatchResume();
155         processAllMessages();
156     }
157 
recreateFragment()158     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 
attachFragmentToWindow()170     protected void attachFragmentToWindow() {
171         ViewUtils.attachView(mView);
172         TestableLooper.get(this).processAllMessages();
173     }
174 
detachFragmentToWindow()175     protected void detachFragmentToWindow() {
176         ViewUtils.detachView(mView);
177         TestableLooper.get(this).processAllMessages();
178     }
179 
destroyFragments()180     protected void destroyFragments() {
181         mFragments.dispatchDestroy();
182         processAllMessages();
183         mFragments = null;
184     }
185 
processAllMessages()186     protected void processAllMessages() {
187         TestableLooper.get(this).processAllMessages();
188     }
189 
findViewById(int id)190     private View findViewById(int id) {
191         return mView.findViewById(id);
192     }
193 
194     private class HostCallbacks extends FragmentHostCallback<BaseFragmentTest> {
HostCallbacks()195         public HostCallbacks() {
196             super(mContext, BaseFragmentTest.this.mHandler, 0);
197         }
198 
199         @Override
onGetHost()200         public BaseFragmentTest onGetHost() {
201             return BaseFragmentTest.this;
202         }
203 
204         @Override
onDump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args)205         public void onDump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
206         }
207 
208         @Override
onShouldSaveFragmentState(Fragment fragment)209         public boolean onShouldSaveFragmentState(Fragment fragment) {
210             return true; // True for now.
211         }
212 
213         @Override
onGetLayoutInflater()214         public LayoutInflater onGetLayoutInflater() {
215             return LayoutInflater.from(mContext);
216         }
217 
218         @Override
onUseFragmentManagerInflaterFactory()219         public boolean onUseFragmentManagerInflaterFactory() {
220             return true;
221         }
222 
223         @Override
onHasWindowAnimations()224         public boolean onHasWindowAnimations() {
225             return false;
226         }
227 
228         @Override
onGetWindowAnimations()229         public int onGetWindowAnimations() {
230             return 0;
231         }
232 
233         @Override
onAttachFragment(Fragment fragment)234         public void onAttachFragment(Fragment fragment) {
235         }
236 
237         @Nullable
238         @Override
onFindViewById(int id)239         public View onFindViewById(int id) {
240             return BaseFragmentTest.this.findViewById(id);
241         }
242 
243         @Override
onHasView()244         public boolean onHasView() {
245             return true;
246         }
247     }
248 }
249