18852905b0b3837e326127c6dfef6f699124ce715Jason Monk/*
2340b0e5216b4fcc435e0459b1ca46155a572100dJason Monk * Copyright (C) 2017 The Android Open Source Project
38852905b0b3837e326127c6dfef6f699124ce715Jason Monk *
48852905b0b3837e326127c6dfef6f699124ce715Jason Monk * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
58852905b0b3837e326127c6dfef6f699124ce715Jason Monk * except in compliance with the License. You may obtain a copy of the License at
68852905b0b3837e326127c6dfef6f699124ce715Jason Monk *
78852905b0b3837e326127c6dfef6f699124ce715Jason Monk *      http://www.apache.org/licenses/LICENSE-2.0
88852905b0b3837e326127c6dfef6f699124ce715Jason Monk *
98852905b0b3837e326127c6dfef6f699124ce715Jason Monk * Unless required by applicable law or agreed to in writing, software distributed under the
108852905b0b3837e326127c6dfef6f699124ce715Jason Monk * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
118852905b0b3837e326127c6dfef6f699124ce715Jason Monk * KIND, either express or implied. See the License for the specific language governing
128852905b0b3837e326127c6dfef6f699124ce715Jason Monk * permissions and limitations under the License.
138852905b0b3837e326127c6dfef6f699124ce715Jason Monk */
148852905b0b3837e326127c6dfef6f699124ce715Jason Monk
15340b0e5216b4fcc435e0459b1ca46155a572100dJason Monkpackage android.testing;
16340b0e5216b4fcc435e0459b1ca46155a572100dJason Monk
17340b0e5216b4fcc435e0459b1ca46155a572100dJason Monkimport static org.junit.Assert.assertNotNull;
188852905b0b3837e326127c6dfef6f699124ce715Jason Monk
198852905b0b3837e326127c6dfef6f699124ce715Jason Monkimport android.annotation.Nullable;
208852905b0b3837e326127c6dfef6f699124ce715Jason Monkimport android.app.Fragment;
218852905b0b3837e326127c6dfef6f699124ce715Jason Monkimport android.app.FragmentController;
228852905b0b3837e326127c6dfef6f699124ce715Jason Monkimport android.app.FragmentHostCallback;
238852905b0b3837e326127c6dfef6f699124ce715Jason Monkimport android.app.FragmentManagerNonConfig;
24de850bbcaa61c1874b803f2086443febbafd81a4Jason Monkimport android.graphics.PixelFormat;
258852905b0b3837e326127c6dfef6f699124ce715Jason Monkimport android.os.Handler;
268852905b0b3837e326127c6dfef6f699124ce715Jason Monkimport android.os.Parcelable;
27340b0e5216b4fcc435e0459b1ca46155a572100dJason Monkimport android.support.test.InstrumentationRegistry;
288852905b0b3837e326127c6dfef6f699124ce715Jason Monkimport android.view.LayoutInflater;
298852905b0b3837e326127c6dfef6f699124ce715Jason Monkimport android.view.View;
30de850bbcaa61c1874b803f2086443febbafd81a4Jason Monkimport android.view.WindowManager;
31de850bbcaa61c1874b803f2086443febbafd81a4Jason Monkimport android.view.WindowManager.LayoutParams;
328852905b0b3837e326127c6dfef6f699124ce715Jason Monkimport android.widget.FrameLayout;
338852905b0b3837e326127c6dfef6f699124ce715Jason Monk
348852905b0b3837e326127c6dfef6f699124ce715Jason Monkimport org.junit.After;
358852905b0b3837e326127c6dfef6f699124ce715Jason Monkimport org.junit.Before;
36340b0e5216b4fcc435e0459b1ca46155a572100dJason Monkimport org.junit.Rule;
378852905b0b3837e326127c6dfef6f699124ce715Jason Monkimport org.junit.Test;
388852905b0b3837e326127c6dfef6f699124ce715Jason Monk
398852905b0b3837e326127c6dfef6f699124ce715Jason Monkimport java.io.FileDescriptor;
408852905b0b3837e326127c6dfef6f699124ce715Jason Monkimport java.io.PrintWriter;
418852905b0b3837e326127c6dfef6f699124ce715Jason Monk
428852905b0b3837e326127c6dfef6f699124ce715Jason Monk/**
438852905b0b3837e326127c6dfef6f699124ce715Jason Monk * Base class for fragment class tests.  Just adding one for any fragment will push it through
448852905b0b3837e326127c6dfef6f699124ce715Jason Monk * general lifecycle events and ensure no basic leaks are happening.  This class also implements
458852905b0b3837e326127c6dfef6f699124ce715Jason Monk * the host for subclasses, so they can push it into desired states and do any unit testing
468852905b0b3837e326127c6dfef6f699124ce715Jason Monk * required.
478852905b0b3837e326127c6dfef6f699124ce715Jason Monk */
48340b0e5216b4fcc435e0459b1ca46155a572100dJason Monkpublic abstract class BaseFragmentTest {
498852905b0b3837e326127c6dfef6f699124ce715Jason Monk
508852905b0b3837e326127c6dfef6f699124ce715Jason Monk    private static final int VIEW_ID = 42;
518852905b0b3837e326127c6dfef6f699124ce715Jason Monk    private final Class<? extends Fragment> mCls;
528852905b0b3837e326127c6dfef6f699124ce715Jason Monk    private Handler mHandler;
536ad0e39f338d47e75bd1b964259ba21c00043f45Siarhei Vishniakou    protected FrameLayout mView;
548852905b0b3837e326127c6dfef6f699124ce715Jason Monk    protected FragmentController mFragments;
558852905b0b3837e326127c6dfef6f699124ce715Jason Monk    protected Fragment mFragment;
568852905b0b3837e326127c6dfef6f699124ce715Jason Monk
57340b0e5216b4fcc435e0459b1ca46155a572100dJason Monk    @Rule
58340b0e5216b4fcc435e0459b1ca46155a572100dJason Monk    public final TestableContext mContext = getContext();
59340b0e5216b4fcc435e0459b1ca46155a572100dJason Monk
60340b0e5216b4fcc435e0459b1ca46155a572100dJason Monk    public BaseFragmentTest(Class<? extends Fragment> cls) {
618852905b0b3837e326127c6dfef6f699124ce715Jason Monk        mCls = cls;
628852905b0b3837e326127c6dfef6f699124ce715Jason Monk    }
638852905b0b3837e326127c6dfef6f699124ce715Jason Monk
646ad0e39f338d47e75bd1b964259ba21c00043f45Siarhei Vishniakou    protected void createRootView() {
656ad0e39f338d47e75bd1b964259ba21c00043f45Siarhei Vishniakou        mView = new FrameLayout(mContext);
666ad0e39f338d47e75bd1b964259ba21c00043f45Siarhei Vishniakou    }
676ad0e39f338d47e75bd1b964259ba21c00043f45Siarhei Vishniakou
688852905b0b3837e326127c6dfef6f699124ce715Jason Monk    @Before
6928d5d220cc66eb4263aaae083a8496950258642bJason Monk    public void setupFragment() throws Exception {
706ad0e39f338d47e75bd1b964259ba21c00043f45Siarhei Vishniakou        createRootView();
718852905b0b3837e326127c6dfef6f699124ce715Jason Monk        mView.setId(VIEW_ID);
7228d5d220cc66eb4263aaae083a8496950258642bJason Monk
73340b0e5216b4fcc435e0459b1ca46155a572100dJason Monk        assertNotNull("BaseFragmentTest must be tagged with @RunWithLooper",
74340b0e5216b4fcc435e0459b1ca46155a572100dJason Monk                TestableLooper.get(this));
7528d5d220cc66eb4263aaae083a8496950258642bJason Monk        TestableLooper.get(this).runWithLooper(() -> {
7628d5d220cc66eb4263aaae083a8496950258642bJason Monk            mHandler = new Handler();
7728d5d220cc66eb4263aaae083a8496950258642bJason Monk
7828d5d220cc66eb4263aaae083a8496950258642bJason Monk            mFragment = mCls.newInstance();
798852905b0b3837e326127c6dfef6f699124ce715Jason Monk            mFragments = FragmentController.createController(new HostCallbacks());
808852905b0b3837e326127c6dfef6f699124ce715Jason Monk            mFragments.attachHost(null);
818852905b0b3837e326127c6dfef6f699124ce715Jason Monk            mFragments.getFragmentManager().beginTransaction()
828852905b0b3837e326127c6dfef6f699124ce715Jason Monk                    .replace(VIEW_ID, mFragment)
838852905b0b3837e326127c6dfef6f699124ce715Jason Monk                    .commit();
848852905b0b3837e326127c6dfef6f699124ce715Jason Monk        });
858852905b0b3837e326127c6dfef6f699124ce715Jason Monk    }
868852905b0b3837e326127c6dfef6f699124ce715Jason Monk
87340b0e5216b4fcc435e0459b1ca46155a572100dJason Monk    protected TestableContext getContext() {
88340b0e5216b4fcc435e0459b1ca46155a572100dJason Monk        return new TestableContext(InstrumentationRegistry.getContext());
8928d5d220cc66eb4263aaae083a8496950258642bJason Monk    }
9028d5d220cc66eb4263aaae083a8496950258642bJason Monk
918852905b0b3837e326127c6dfef6f699124ce715Jason Monk    @After
9228d5d220cc66eb4263aaae083a8496950258642bJason Monk    public void tearDown() throws Exception {
938852905b0b3837e326127c6dfef6f699124ce715Jason Monk        if (mFragments != null) {
948852905b0b3837e326127c6dfef6f699124ce715Jason Monk            // Set mFragments to null to let it know not to destroy.
9528d5d220cc66eb4263aaae083a8496950258642bJason Monk            TestableLooper.get(this).runWithLooper(() -> mFragments.dispatchDestroy());
968852905b0b3837e326127c6dfef6f699124ce715Jason Monk        }
978852905b0b3837e326127c6dfef6f699124ce715Jason Monk    }
988852905b0b3837e326127c6dfef6f699124ce715Jason Monk
998852905b0b3837e326127c6dfef6f699124ce715Jason Monk    @Test
1008852905b0b3837e326127c6dfef6f699124ce715Jason Monk    public void testCreateDestroy() {
10128d5d220cc66eb4263aaae083a8496950258642bJason Monk        mFragments.dispatchCreate();
10228d5d220cc66eb4263aaae083a8496950258642bJason Monk        processAllMessages();
1038852905b0b3837e326127c6dfef6f699124ce715Jason Monk        destroyFragments();
1048852905b0b3837e326127c6dfef6f699124ce715Jason Monk    }
1058852905b0b3837e326127c6dfef6f699124ce715Jason Monk
1068852905b0b3837e326127c6dfef6f699124ce715Jason Monk    @Test
1078852905b0b3837e326127c6dfef6f699124ce715Jason Monk    public void testStartStop() {
10828d5d220cc66eb4263aaae083a8496950258642bJason Monk        mFragments.dispatchStart();
10928d5d220cc66eb4263aaae083a8496950258642bJason Monk        processAllMessages();
11028d5d220cc66eb4263aaae083a8496950258642bJason Monk        mFragments.dispatchStop();
11128d5d220cc66eb4263aaae083a8496950258642bJason Monk        processAllMessages();
1128852905b0b3837e326127c6dfef6f699124ce715Jason Monk    }
1138852905b0b3837e326127c6dfef6f699124ce715Jason Monk
1148852905b0b3837e326127c6dfef6f699124ce715Jason Monk    @Test
1158852905b0b3837e326127c6dfef6f699124ce715Jason Monk    public void testResumePause() {
11628d5d220cc66eb4263aaae083a8496950258642bJason Monk        mFragments.dispatchResume();
11728d5d220cc66eb4263aaae083a8496950258642bJason Monk        processAllMessages();
11828d5d220cc66eb4263aaae083a8496950258642bJason Monk        mFragments.dispatchPause();
11928d5d220cc66eb4263aaae083a8496950258642bJason Monk        processAllMessages();
1208852905b0b3837e326127c6dfef6f699124ce715Jason Monk    }
1218852905b0b3837e326127c6dfef6f699124ce715Jason Monk
1228852905b0b3837e326127c6dfef6f699124ce715Jason Monk    @Test
123de850bbcaa61c1874b803f2086443febbafd81a4Jason Monk    public void testAttachDetach() {
124de850bbcaa61c1874b803f2086443febbafd81a4Jason Monk        WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
125de850bbcaa61c1874b803f2086443febbafd81a4Jason Monk                LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT,
126de850bbcaa61c1874b803f2086443febbafd81a4Jason Monk                LayoutParams.TYPE_SYSTEM_ALERT,
127de850bbcaa61c1874b803f2086443febbafd81a4Jason Monk                0, PixelFormat.TRANSLUCENT);
12828d5d220cc66eb4263aaae083a8496950258642bJason Monk        mFragments.dispatchResume();
12928d5d220cc66eb4263aaae083a8496950258642bJason Monk        processAllMessages();
130de850bbcaa61c1874b803f2086443febbafd81a4Jason Monk        attachFragmentToWindow();
131de850bbcaa61c1874b803f2086443febbafd81a4Jason Monk        detachFragmentToWindow();
13228d5d220cc66eb4263aaae083a8496950258642bJason Monk        mFragments.dispatchPause();
13328d5d220cc66eb4263aaae083a8496950258642bJason Monk        processAllMessages();
134de850bbcaa61c1874b803f2086443febbafd81a4Jason Monk    }
135de850bbcaa61c1874b803f2086443febbafd81a4Jason Monk
136de850bbcaa61c1874b803f2086443febbafd81a4Jason Monk    @Test
1378852905b0b3837e326127c6dfef6f699124ce715Jason Monk    public void testRecreate() {
13828d5d220cc66eb4263aaae083a8496950258642bJason Monk        mFragments.dispatchResume();
13928d5d220cc66eb4263aaae083a8496950258642bJason Monk        processAllMessages();
14064b214ea16c787fdcf2bb44ad81b1336cafebafeJason Monk        recreateFragment();
14128d5d220cc66eb4263aaae083a8496950258642bJason Monk        processAllMessages();
1428852905b0b3837e326127c6dfef6f699124ce715Jason Monk    }
1438852905b0b3837e326127c6dfef6f699124ce715Jason Monk
1448852905b0b3837e326127c6dfef6f699124ce715Jason Monk    @Test
1458852905b0b3837e326127c6dfef6f699124ce715Jason Monk    public void testMultipleResumes() {
14628d5d220cc66eb4263aaae083a8496950258642bJason Monk        mFragments.dispatchResume();
14728d5d220cc66eb4263aaae083a8496950258642bJason Monk        processAllMessages();
14828d5d220cc66eb4263aaae083a8496950258642bJason Monk        mFragments.dispatchStop();
14928d5d220cc66eb4263aaae083a8496950258642bJason Monk        processAllMessages();
15028d5d220cc66eb4263aaae083a8496950258642bJason Monk        mFragments.dispatchResume();
15128d5d220cc66eb4263aaae083a8496950258642bJason Monk        processAllMessages();
1528852905b0b3837e326127c6dfef6f699124ce715Jason Monk    }
1538852905b0b3837e326127c6dfef6f699124ce715Jason Monk
15464b214ea16c787fdcf2bb44ad81b1336cafebafeJason Monk    protected void recreateFragment() {
15564b214ea16c787fdcf2bb44ad81b1336cafebafeJason Monk        mFragments.dispatchPause();
15664b214ea16c787fdcf2bb44ad81b1336cafebafeJason Monk        Parcelable p = mFragments.saveAllState();
15764b214ea16c787fdcf2bb44ad81b1336cafebafeJason Monk        mFragments.dispatchDestroy();
15864b214ea16c787fdcf2bb44ad81b1336cafebafeJason Monk
15964b214ea16c787fdcf2bb44ad81b1336cafebafeJason Monk        mFragments = FragmentController.createController(new HostCallbacks());
16064b214ea16c787fdcf2bb44ad81b1336cafebafeJason Monk        mFragments.attachHost(null);
16164b214ea16c787fdcf2bb44ad81b1336cafebafeJason Monk        mFragments.restoreAllState(p, (FragmentManagerNonConfig) null);
16264b214ea16c787fdcf2bb44ad81b1336cafebafeJason Monk        mFragments.dispatchResume();
16364b214ea16c787fdcf2bb44ad81b1336cafebafeJason Monk        mFragment = mFragments.getFragmentManager().findFragmentById(VIEW_ID);
16464b214ea16c787fdcf2bb44ad81b1336cafebafeJason Monk    }
16564b214ea16c787fdcf2bb44ad81b1336cafebafeJason Monk
16628d5d220cc66eb4263aaae083a8496950258642bJason Monk    protected void attachFragmentToWindow() {
16728d5d220cc66eb4263aaae083a8496950258642bJason Monk        ViewUtils.attachView(mView);
1681660a27b5d682cbb0bd9e2ce66e5ca39d6ab7816Jason Monk        TestableLooper.get(this).processAllMessages();
1698852905b0b3837e326127c6dfef6f699124ce715Jason Monk    }
1708852905b0b3837e326127c6dfef6f699124ce715Jason Monk
17128d5d220cc66eb4263aaae083a8496950258642bJason Monk    protected void detachFragmentToWindow() {
17228d5d220cc66eb4263aaae083a8496950258642bJason Monk        ViewUtils.detachView(mView);
1731660a27b5d682cbb0bd9e2ce66e5ca39d6ab7816Jason Monk        TestableLooper.get(this).processAllMessages();
17428d5d220cc66eb4263aaae083a8496950258642bJason Monk    }
17528d5d220cc66eb4263aaae083a8496950258642bJason Monk
17628d5d220cc66eb4263aaae083a8496950258642bJason Monk    protected void destroyFragments() {
17728d5d220cc66eb4263aaae083a8496950258642bJason Monk        mFragments.dispatchDestroy();
17828d5d220cc66eb4263aaae083a8496950258642bJason Monk        processAllMessages();
17928d5d220cc66eb4263aaae083a8496950258642bJason Monk        mFragments = null;
1808852905b0b3837e326127c6dfef6f699124ce715Jason Monk    }
1818852905b0b3837e326127c6dfef6f699124ce715Jason Monk
18228d5d220cc66eb4263aaae083a8496950258642bJason Monk    protected void processAllMessages() {
18328d5d220cc66eb4263aaae083a8496950258642bJason Monk        TestableLooper.get(this).processAllMessages();
1848852905b0b3837e326127c6dfef6f699124ce715Jason Monk    }
1858852905b0b3837e326127c6dfef6f699124ce715Jason Monk
1868852905b0b3837e326127c6dfef6f699124ce715Jason Monk    private View findViewById(int id) {
1878852905b0b3837e326127c6dfef6f699124ce715Jason Monk        return mView.findViewById(id);
1888852905b0b3837e326127c6dfef6f699124ce715Jason Monk    }
1898852905b0b3837e326127c6dfef6f699124ce715Jason Monk
190340b0e5216b4fcc435e0459b1ca46155a572100dJason Monk    private class HostCallbacks extends FragmentHostCallback<BaseFragmentTest> {
1918852905b0b3837e326127c6dfef6f699124ce715Jason Monk        public HostCallbacks() {
192340b0e5216b4fcc435e0459b1ca46155a572100dJason Monk            super(mContext, BaseFragmentTest.this.mHandler, 0);
1938852905b0b3837e326127c6dfef6f699124ce715Jason Monk        }
1948852905b0b3837e326127c6dfef6f699124ce715Jason Monk
1958852905b0b3837e326127c6dfef6f699124ce715Jason Monk        @Override
196340b0e5216b4fcc435e0459b1ca46155a572100dJason Monk        public BaseFragmentTest onGetHost() {
197340b0e5216b4fcc435e0459b1ca46155a572100dJason Monk            return BaseFragmentTest.this;
1988852905b0b3837e326127c6dfef6f699124ce715Jason Monk        }
1998852905b0b3837e326127c6dfef6f699124ce715Jason Monk
2008852905b0b3837e326127c6dfef6f699124ce715Jason Monk        @Override
2018852905b0b3837e326127c6dfef6f699124ce715Jason Monk        public void onDump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
2028852905b0b3837e326127c6dfef6f699124ce715Jason Monk        }
2038852905b0b3837e326127c6dfef6f699124ce715Jason Monk
2048852905b0b3837e326127c6dfef6f699124ce715Jason Monk        @Override
2058852905b0b3837e326127c6dfef6f699124ce715Jason Monk        public boolean onShouldSaveFragmentState(Fragment fragment) {
2068852905b0b3837e326127c6dfef6f699124ce715Jason Monk            return true; // True for now.
2078852905b0b3837e326127c6dfef6f699124ce715Jason Monk        }
2088852905b0b3837e326127c6dfef6f699124ce715Jason Monk
2098852905b0b3837e326127c6dfef6f699124ce715Jason Monk        @Override
2108852905b0b3837e326127c6dfef6f699124ce715Jason Monk        public LayoutInflater onGetLayoutInflater() {
2118852905b0b3837e326127c6dfef6f699124ce715Jason Monk            return LayoutInflater.from(mContext);
2128852905b0b3837e326127c6dfef6f699124ce715Jason Monk        }
2138852905b0b3837e326127c6dfef6f699124ce715Jason Monk
2148852905b0b3837e326127c6dfef6f699124ce715Jason Monk        @Override
2158852905b0b3837e326127c6dfef6f699124ce715Jason Monk        public boolean onUseFragmentManagerInflaterFactory() {
2168852905b0b3837e326127c6dfef6f699124ce715Jason Monk            return true;
2178852905b0b3837e326127c6dfef6f699124ce715Jason Monk        }
2188852905b0b3837e326127c6dfef6f699124ce715Jason Monk
2198852905b0b3837e326127c6dfef6f699124ce715Jason Monk        @Override
2208852905b0b3837e326127c6dfef6f699124ce715Jason Monk        public boolean onHasWindowAnimations() {
2218852905b0b3837e326127c6dfef6f699124ce715Jason Monk            return false;
2228852905b0b3837e326127c6dfef6f699124ce715Jason Monk        }
2238852905b0b3837e326127c6dfef6f699124ce715Jason Monk
2248852905b0b3837e326127c6dfef6f699124ce715Jason Monk        @Override
2258852905b0b3837e326127c6dfef6f699124ce715Jason Monk        public int onGetWindowAnimations() {
2268852905b0b3837e326127c6dfef6f699124ce715Jason Monk            return 0;
2278852905b0b3837e326127c6dfef6f699124ce715Jason Monk        }
2288852905b0b3837e326127c6dfef6f699124ce715Jason Monk
2298852905b0b3837e326127c6dfef6f699124ce715Jason Monk        @Override
2308852905b0b3837e326127c6dfef6f699124ce715Jason Monk        public void onAttachFragment(Fragment fragment) {
2318852905b0b3837e326127c6dfef6f699124ce715Jason Monk        }
2328852905b0b3837e326127c6dfef6f699124ce715Jason Monk
2338852905b0b3837e326127c6dfef6f699124ce715Jason Monk        @Nullable
2348852905b0b3837e326127c6dfef6f699124ce715Jason Monk        @Override
2358852905b0b3837e326127c6dfef6f699124ce715Jason Monk        public View onFindViewById(int id) {
236340b0e5216b4fcc435e0459b1ca46155a572100dJason Monk            return BaseFragmentTest.this.findViewById(id);
2378852905b0b3837e326127c6dfef6f699124ce715Jason Monk        }
2388852905b0b3837e326127c6dfef6f699124ce715Jason Monk
2398852905b0b3837e326127c6dfef6f699124ce715Jason Monk        @Override
2408852905b0b3837e326127c6dfef6f699124ce715Jason Monk        public boolean onHasView() {
2418852905b0b3837e326127c6dfef6f699124ce715Jason Monk            return true;
2428852905b0b3837e326127c6dfef6f699124ce715Jason Monk        }
2438852905b0b3837e326127c6dfef6f699124ce715Jason Monk    }
2448852905b0b3837e326127c6dfef6f699124ce715Jason Monk}
245