blob: c83e7988dcc649dbebc9754d14110d3735ba5bc1 [file] [log] [blame]
Adam Powell180202f2016-09-21 15:41:47 -07001/*
2 * Copyright (C) 2016 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17
18package android.app;
19
George Mount4034eae2017-05-16 13:07:51 -070020import static junit.framework.TestCase.assertNotNull;
21import static junit.framework.TestCase.assertNotSame;
22import static junit.framework.TestCase.assertSame;
23
24import static org.junit.Assert.assertTrue;
25import static org.junit.Assert.fail;
26
Adam Powell180202f2016-09-21 15:41:47 -070027import android.content.Context;
28import android.os.Handler;
29import android.os.Parcelable;
30import android.support.test.filters.MediumTest;
31import android.support.test.rule.ActivityTestRule;
32import android.support.test.runner.AndroidJUnit4;
33import android.util.ArrayMap;
George Mount4034eae2017-05-16 13:07:51 -070034
Adam Powell180202f2016-09-21 15:41:47 -070035import org.junit.Rule;
36import org.junit.Test;
37import org.junit.runner.RunWith;
38
Adam Powell180202f2016-09-21 15:41:47 -070039@RunWith(AndroidJUnit4.class)
40public class LoaderLifecycleTest {
41 @Rule
42 public ActivityTestRule<EmptyActivity> mActivityRule =
43 new ActivityTestRule<>(EmptyActivity.class);
44 @Test
45 @MediumTest
46 public void loaderIdentityTest() throws Throwable{
47 mActivityRule.runOnUiThread(() -> {
48 final Handler h = new Handler();
49 final FragmentController fc1 = FragmentController.createController(
50 new TestFragmentHostCallback(mActivityRule.getActivity(), h, 0));
51
52 fc1.attachHost(null);
53 fc1.dispatchCreate();
54
55 final FragmentManager fm1 = fc1.getFragmentManager();
56
57 final Fragment f1 = new Fragment();
58 fm1.beginTransaction().add(f1, "one").commitNow();
59
60 // Removing and re-adding a fragment completely will destroy its LoaderManager.
61 // Keep the first one here to confirm this later.
62 final LoaderManager lm1 = f1.getLoaderManager();
63
64 // Remove the fragment, add a second one, and re-add the first to
65 // force its internal index to change. The tests below should still remain consistent.
66 final Fragment f2 = new Fragment();
67 fm1.beginTransaction().remove(f1).commitNow();
68 fm1.beginTransaction().add(f2, "two").commitNow();
69 fm1.beginTransaction().add(f1, "one").commitNow();
70
71 // We'll check this to see if we get the same instance back later
72 // as passed through NonConfigurationInstance. If the keys stay consistent
73 // across fragment remove/re-add, this will be consistent.
74 final LoaderManager lm12 = f1.getLoaderManager();
75
76 assertNotSame("fully removed and re-added fragment got same LoaderManager", lm1, lm12);
77
78 fc1.dispatchActivityCreated();
79 fc1.noteStateNotSaved();
80 fc1.execPendingActions();
81 fc1.doLoaderStart();
82 fc1.dispatchStart();
83 fc1.reportLoaderStart();
84 fc1.dispatchResume();
85 fc1.execPendingActions();
86
87 // Bring the state back down to destroyed, simulating an activity restart
88 fc1.dispatchPause();
89 final Parcelable savedState = fc1.saveAllState();
90 fc1.doLoaderStop(true);
91 fc1.dispatchStop();
92 final FragmentManagerNonConfig nonconf = fc1.retainNestedNonConfig();
93
94 final ArrayMap<String, LoaderManager> loaderNonConfig = fc1.retainLoaderNonConfig();
95 assertNotNull("loaderNonConfig was null", loaderNonConfig);
96
97 fc1.dispatchDestroy();
98
99 // Create the new controller and restore state
100 final FragmentController fc2 = FragmentController.createController(
101 new TestFragmentHostCallback(mActivityRule.getActivity(), h, 0));
102
103 final FragmentManager fm2 = fc2.getFragmentManager();
104
105 fc2.attachHost(null);
Adam Powell100ba762016-09-22 10:47:40 -0700106 // Make sure nothing blows up on a null here
107 fc2.restoreLoaderNonConfig(null);
108 // for real this time
Adam Powell180202f2016-09-21 15:41:47 -0700109 fc2.restoreLoaderNonConfig(loaderNonConfig);
110 fc2.restoreAllState(savedState, nonconf);
111 fc2.dispatchCreate();
112
113
114 fc2.dispatchActivityCreated();
115 fc2.noteStateNotSaved();
116 fc2.execPendingActions();
117 fc2.doLoaderStart();
118 fc2.dispatchStart();
119 fc2.reportLoaderStart();
120 fc2.dispatchResume();
121 fc2.execPendingActions();
122
123 // Test that the fragments are in the configuration we expect
124 final Fragment restoredOne = fm2.findFragmentByTag("one");
125 final LoaderManager lm2 = restoredOne.getLoaderManager();
126
127 assertSame("didn't get same LoaderManager instance back", lm2, lm12);
128
129 // Bring the state back down to destroyed before we finish the test
130 fc2.dispatchPause();
131 fc2.saveAllState();
132 fc2.dispatchStop();
133 fc2.dispatchDestroy();
134 });
135 }
136
137 @Test
138 @MediumTest
139 public void backStackLoaderIdentityTest() throws Throwable{
140 mActivityRule.runOnUiThread(() -> {
141 final Handler h = new Handler();
142 final FragmentHostCallback host1 =
143 new TestFragmentHostCallback(mActivityRule.getActivity(), h, 0);
144 final FragmentController fc1 = FragmentController.createController(host1);
145
146 fc1.attachHost(null);
147 fc1.dispatchCreate();
148
149 final FragmentManager fm1 = fc1.getFragmentManager();
150
151 final Fragment f1 = new Fragment();
152 fm1.beginTransaction().add(f1, "one").commitNow();
153
154 final LoaderManager lm1 = f1.getLoaderManager();
155
156 // Put the fragment on the back stack.
157 fm1.beginTransaction().remove(f1).addToBackStack("backentry").commit();
158 fm1.executePendingTransactions();
159
160 fc1.dispatchActivityCreated();
161 fc1.noteStateNotSaved();
162 fc1.execPendingActions();
163 fc1.doLoaderStart();
164 fc1.dispatchStart();
165 fc1.reportLoaderStart();
166 fc1.dispatchResume();
167 fc1.execPendingActions();
168
169 // Bring the state back down to destroyed, simulating an activity restart
170 fc1.dispatchPause();
171 final Parcelable savedState = fc1.saveAllState();
172 fc1.doLoaderStop(true);
173 fc1.dispatchStop();
174 final FragmentManagerNonConfig nonconf = fc1.retainNestedNonConfig();
175
176 final ArrayMap<String, LoaderManager> loaderNonConfig = fc1.retainLoaderNonConfig();
177 assertNotNull("loaderNonConfig was null", loaderNonConfig);
178
179 fc1.dispatchDestroy();
180
181 // Create the new controller and restore state
182 final FragmentHostCallback host2 =
183 new TestFragmentHostCallback(mActivityRule.getActivity(), h, 0);
184 final FragmentController fc2 = FragmentController.createController(host2);
185
186 final FragmentManager fm2 = fc2.getFragmentManager();
187
188 fc2.attachHost(null);
189 fc2.restoreLoaderNonConfig(loaderNonConfig);
190 fc2.restoreAllState(savedState, nonconf);
191 fc2.dispatchCreate();
192
193
194 fc2.dispatchActivityCreated();
195 fc2.noteStateNotSaved();
196 fc2.execPendingActions();
197 fc2.doLoaderStart();
198 fc2.dispatchStart();
199 fc2.reportLoaderStart();
200 fc2.dispatchResume();
201 fc2.execPendingActions();
202
203 assertNotSame("LoaderManager kept reference to old FragmentHostCallback",
204 host1, lm1.getFragmentHostCallback());
205 assertSame("LoaderManager did not refrence new FragmentHostCallback",
206 host2, lm1.getFragmentHostCallback());
207
208 // Test that the fragments are in the configuration we expect
209 final Fragment restoredOne = fm2.findFragmentByTag("one");
George Mount4034eae2017-05-16 13:07:51 -0700210 try {
211 restoredOne.getLoaderManager();
212 fail("A restored fragment on the back stack doesn't have a host, so it should "
213 + "throw an exception");
214 } catch (IllegalStateException e) {
215 // expected
216 }
217 fm2.popBackStackImmediate();
218 // Now restoredOne should be added and should be in a good state.
219 assertTrue(restoredOne.isAdded());
Adam Powell180202f2016-09-21 15:41:47 -0700220 final LoaderManager lm2 = restoredOne.getLoaderManager();
221
222 assertSame("didn't get same LoaderManager instance back", lm2, lm1);
223
224 // Bring the state back down to destroyed before we finish the test
225 fc2.dispatchPause();
226 fc2.saveAllState();
227 fc2.dispatchStop();
228 fc2.dispatchDestroy();
229 });
230 }
231
232 public class TestFragmentHostCallback extends FragmentHostCallback<LoaderLifecycleTest> {
233 public TestFragmentHostCallback(Context context, Handler handler, int windowAnimations) {
234 super(context, handler, windowAnimations);
235 }
236
237 @Override
238 public LoaderLifecycleTest onGetHost() {
239 return LoaderLifecycleTest.this;
240 }
241 }
242}