1package com.xtremelabs.robolectric.shadows;
2
3import android.os.Bundle;
4import android.os.Handler;
5import android.os.Looper;
6import android.support.v4.app.Fragment;
7import android.support.v4.app.FragmentActivity;
8import android.support.v4.app.FragmentTransaction;
9import android.view.LayoutInflater;
10import android.view.View;
11import android.view.ViewGroup;
12import android.widget.Button;
13import android.widget.FrameLayout;
14import com.xtremelabs.robolectric.Robolectric;
15import com.xtremelabs.robolectric.WithTestDefaultsRunner;
16import com.xtremelabs.robolectric.tester.android.util.TestFragmentManager;
17import com.xtremelabs.robolectric.util.Scheduler;
18import com.xtremelabs.robolectric.util.TestRunnable;
19import org.junit.Before;
20import org.junit.Test;
21import org.junit.runner.RunWith;
22
23import static com.xtremelabs.robolectric.Robolectric.shadowOf;
24import static org.hamcrest.CoreMatchers.equalTo;
25import static org.hamcrest.CoreMatchers.sameInstance;
26import static org.hamcrest.MatcherAssert.assertThat;
27import static org.junit.Assert.*;
28
29@RunWith(WithTestDefaultsRunner.class)
30public class FragmentManagerTest {
31
32    public static final int CONTAINER_VIEW_ID = 888;
33    private TestFragmentManager manager;
34    private TestFragment fragment;
35    private TestFragmentActivity activity;
36    private ViewGroup containerView;
37
38    @Before
39    public void setUp() throws Exception {
40        activity = new TestFragmentActivity();
41        activity.onCreate(null);
42        manager = (TestFragmentManager) activity.getSupportFragmentManager();
43        fragment = new TestFragment();
44        containerView = (ViewGroup) activity.findViewById(CONTAINER_VIEW_ID);
45    }
46
47    @Test
48    public void shouldFindFragmentById() throws Exception {
49        manager.addFragment(CONTAINER_VIEW_ID, "tag1", fragment, false);
50
51        assertThat(manager.findFragmentById(CONTAINER_VIEW_ID), sameInstance((Fragment) fragment));
52    }
53
54    @Test
55    public void shouldFindFragmentByTag() throws Exception {
56        manager.addFragment(CONTAINER_VIEW_ID, "tag1", fragment, false);
57
58        assertThat(manager.findFragmentByTag("tag1"), sameInstance((Fragment) fragment));
59    }
60
61    @Test
62    public void addFragment_shouldCallLifecycleMethods() throws Exception {
63        manager.addFragment(View.NO_ID, null, fragment, false);
64
65        fragment.transcript.assertEventsSoFar(
66                "onAttach",
67                "onCreate"
68        );
69
70        assertSame(activity, fragment.onAttachActivity);
71        assertSame(activity, fragment.getActivity());
72    }
73
74    @Test
75    public void startFragment_shouldCallLifecycleMethods() throws Exception {
76        manager.addFragment(View.NO_ID, null, fragment, false);
77        fragment.transcript.clear();
78        manager.startFragment(fragment);
79
80        fragment.transcript.assertEventsSoFar(
81                "onCreateView",
82                "onViewCreated",
83                "onActivityCreated",
84                "onStart"
85        );
86
87        assertEquals(fragment.onCreateViewInflater, activity.getLayoutInflater());
88        assertNotNull(fragment.getView());
89    }
90
91    @Test
92    public void addFragment_shouldSetTheFragmentsView() throws Exception {
93        manager.addFragment(View.NO_ID, null, fragment, false);
94
95        assertThat(fragment.getView(), sameInstance(fragment.onCreateViewReturnValue));
96    }
97
98    @Test
99    public void addFragment_shouldSetTheFragmentsTag() throws Exception {
100        manager.addFragment(View.NO_ID, "expected tag", fragment, false);
101
102        assertThat(fragment.getTag(), equalTo("expected tag"));
103    }
104
105    @Test
106    public void startFragment_shouldInsertTheFragmentViewIntoTheContainerView() throws Exception {
107        manager.addFragment(CONTAINER_VIEW_ID, null, fragment, false);
108        manager.startFragment(fragment);
109
110        View fragmentViewParent = (View) activity.findViewById(TestFragment.FRAGMENT_VIEW_ID).getParent();
111        assertThat(activity.findViewById(TestFragment.FRAGMENT_VIEW_ID), sameInstance(fragment.onCreateViewReturnValue));
112        assertThat(fragmentViewParent, sameInstance((View) containerView));
113    }
114
115    @Test
116    public void addFragmentWithReplace_shouldEmptyTheContainer() throws Exception {
117        containerView.addView(new Button(activity));
118        assertEquals(1, containerView.getChildCount());
119
120        manager.addFragment(CONTAINER_VIEW_ID, null, fragment, true);
121
122        assertEquals(1, containerView.getChildCount());
123    }
124
125    @Test
126    public void addFragmentWithReplace_withNoContainer_shouldNotThrow() throws Exception {
127        manager.addFragment(0, null, fragment, true);
128        // pass
129    }
130
131    @Test
132    public void addFragment_shouldSetFragmentsActivity() throws Exception {
133        manager.addFragment(0, null, fragment, false);
134
135        assertSame(activity, fragment.getActivity());
136    }
137
138    @Test
139    public void addFragment_shouldPassTheSavedInstanceStateToOnCreate() throws Exception {
140        Bundle bundle = new Bundle();
141        shadowOf(fragment).setSavedInstanceState(bundle);
142        manager.addFragment(0, null, fragment, false);
143        assertSame(bundle, fragment.onCreateSavedInstanceState);
144    }
145
146    @Test
147    public void getFragment_whenBundleSavedByShadowFragmentActivity_shouldGetFragmentByTagFromBundle() throws Exception {
148        manager.addFragment(CONTAINER_VIEW_ID, "fragment tag", fragment, true);
149
150        Bundle outState = new Bundle();
151        shadowOf(activity).onSaveInstanceState(outState);
152
153        Fragment retrievedFragment = manager.getFragment(outState, "fragment tag");
154        assertEquals(TestFragment.class, retrievedFragment.getClass());
155    }
156
157    @Test
158    public void startFragment_shouldPassSavedInstanceStateToOnCreateMethodOfFragment() throws Exception {
159        shadowOf(fragment).setSavedInstanceState(new Bundle());
160        manager.addFragment(CONTAINER_VIEW_ID, null, fragment, true);
161        manager.startFragment(fragment);
162
163        assertTrue(fragment.onActivityCreated_savedInstanceState != null);
164    }
165
166    @Test
167    public void getCommittedTransactions_shouldReturnListOfOnlyCommittedTransactions() throws Exception {
168        assertTrue(manager.getCommittedTransactions().isEmpty());
169
170        FragmentTransaction transaction = manager.beginTransaction();
171        assertTrue(manager.getCommittedTransactions().isEmpty());
172
173        transaction.add(new Fragment(), "tag");
174        transaction.commit();
175        assertEquals(1, manager.getCommittedTransactions().size());
176        assertSame(transaction, manager.getCommittedTransactions().get(0));
177
178        FragmentTransaction anotherTransaction = manager.beginTransaction();
179        anotherTransaction.add(new Fragment(), "tag");
180        anotherTransaction.commit();
181        assertEquals(2, manager.getCommittedTransactions().size());
182        assertSame(anotherTransaction, manager.getCommittedTransactions().get(1));
183    }
184
185    @Test
186    public void shouldBeAbleToCommitTransactions_whenTheFragmentHasNoView() throws Exception {
187        TestFragment fragment = new TestFragment() {
188            @Override
189            public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
190                return null;
191            }
192        };
193
194        activity.getSupportFragmentManager().beginTransaction().add(CONTAINER_VIEW_ID, fragment).commit();
195
196        assertSame(fragment, activity.getSupportFragmentManager().findFragmentById(CONTAINER_VIEW_ID));
197    }
198
199    @Test
200    public void executePendingTransactions_shouldRunPendingCommitsAsIfTheMainLooperWereNotPaused() throws Exception {
201        Robolectric.pauseMainLooper();
202        Scheduler scheduler = shadowOf(Looper.getMainLooper()).getScheduler();
203        Fragment fragment2 = new Fragment();
204
205        TestRunnable otherEnqueuedTask = new TestRunnable();
206        new Handler(Looper.getMainLooper()).post(otherEnqueuedTask);
207
208        manager.beginTransaction().add(fragment, "fragment1").commit();
209        manager.beginTransaction().add(fragment2, "fragment2").commit();
210
211        assertEquals(3, scheduler.enqueuedTaskCount());
212        assertNull(manager.findFragmentByTag("fragment1"));
213        assertNull(manager.findFragmentByTag("fragment2"));
214
215        boolean ranSomeTransactions = manager.executePendingTransactions();
216        assertTrue(ranSomeTransactions);
217        assertSame(fragment, manager.findFragmentByTag("fragment1"));
218        assertSame(fragment2, manager.findFragmentByTag("fragment2"));
219        assertEquals(1, scheduler.enqueuedTaskCount());
220
221        assertFalse(otherEnqueuedTask.wasRun);
222        Robolectric.unPauseMainLooper();
223        assertTrue(otherEnqueuedTask.wasRun);
224
225        ranSomeTransactions = manager.executePendingTransactions();
226        assertFalse(ranSomeTransactions);
227    }
228
229    @Test
230    public void executePendingTransactions_shouldAvoidRunningTransactionsThatWereAlreadyRun() throws Exception {
231        manager.beginTransaction().add(fragment, "tag").commit();
232        assertEquals(1, manager.getCommittedTransactions().size());
233
234        boolean ranSomeTransactions = manager.executePendingTransactions();
235        assertFalse(ranSomeTransactions);
236        assertEquals(1, manager.getCommittedTransactions().size());
237    }
238
239    private static class TestFragmentActivity extends FragmentActivity {
240        @Override
241        public void onCreate(Bundle savedInstanceState) {
242            super.onCreate(savedInstanceState);
243            View view = new FrameLayout(this);
244            view.setId(CONTAINER_VIEW_ID);
245            setContentView(view);
246        }
247    }
248
249}
250