Move out test utilities to a Testables library
Test: runtest --path frameworks/base/tests/testablets/tests
&& runtest systemui
Change-Id: Ideef4aef5f26136b1741c556b9be5884f38842a0
diff --git a/tests/testables/src/android/testing/BaseFragmentTest.java b/tests/testables/src/android/testing/BaseFragmentTest.java
new file mode 100644
index 0000000..53841d5
--- /dev/null
+++ b/tests/testables/src/android/testing/BaseFragmentTest.java
@@ -0,0 +1,235 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package android.testing;
+
+import static org.junit.Assert.assertNotNull;
+
+import android.annotation.Nullable;
+import android.app.Fragment;
+import android.app.FragmentController;
+import android.app.FragmentHostCallback;
+import android.app.FragmentManagerNonConfig;
+import android.graphics.PixelFormat;
+import android.os.Handler;
+import android.os.Parcelable;
+import android.support.test.InstrumentationRegistry;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.WindowManager;
+import android.view.WindowManager.LayoutParams;
+import android.widget.FrameLayout;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+
+/**
+ * Base class for fragment class tests. Just adding one for any fragment will push it through
+ * general lifecycle events and ensure no basic leaks are happening. This class also implements
+ * the host for subclasses, so they can push it into desired states and do any unit testing
+ * required.
+ */
+public abstract class BaseFragmentTest {
+
+ private static final int VIEW_ID = 42;
+ private final Class<? extends Fragment> mCls;
+ private Handler mHandler;
+ private FrameLayout mView;
+ protected FragmentController mFragments;
+ protected Fragment mFragment;
+
+ @Rule
+ public final TestableContext mContext = getContext();
+
+ public BaseFragmentTest(Class<? extends Fragment> cls) {
+ mCls = cls;
+ }
+
+ @Before
+ public void setupFragment() throws Exception {
+ mView = new FrameLayout(mContext);
+ mView.setId(VIEW_ID);
+
+ assertNotNull("BaseFragmentTest must be tagged with @RunWithLooper",
+ TestableLooper.get(this));
+ TestableLooper.get(this).runWithLooper(() -> {
+ mHandler = new Handler();
+
+ mFragment = mCls.newInstance();
+ mFragments = FragmentController.createController(new HostCallbacks());
+ mFragments.attachHost(null);
+ mFragments.getFragmentManager().beginTransaction()
+ .replace(VIEW_ID, mFragment)
+ .commit();
+ });
+ }
+
+ protected TestableContext getContext() {
+ return new TestableContext(InstrumentationRegistry.getContext());
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ if (mFragments != null) {
+ // Set mFragments to null to let it know not to destroy.
+ TestableLooper.get(this).runWithLooper(() -> mFragments.dispatchDestroy());
+ }
+ }
+
+ @Test
+ public void testCreateDestroy() {
+ mFragments.dispatchCreate();
+ processAllMessages();
+ destroyFragments();
+ }
+
+ @Test
+ public void testStartStop() {
+ mFragments.dispatchStart();
+ processAllMessages();
+ mFragments.dispatchStop();
+ processAllMessages();
+ }
+
+ @Test
+ public void testResumePause() {
+ mFragments.dispatchResume();
+ processAllMessages();
+ mFragments.dispatchPause();
+ processAllMessages();
+ }
+
+ @Test
+ public void testAttachDetach() {
+ WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
+ LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT,
+ LayoutParams.TYPE_SYSTEM_ALERT,
+ 0, PixelFormat.TRANSLUCENT);
+ mFragments.dispatchResume();
+ processAllMessages();
+ attachFragmentToWindow();
+ detachFragmentToWindow();
+ mFragments.dispatchPause();
+ processAllMessages();
+ }
+
+ @Test
+ public void testRecreate() {
+ mFragments.dispatchResume();
+ processAllMessages();
+ mFragments.dispatchPause();
+ Parcelable p = mFragments.saveAllState();
+ mFragments.dispatchDestroy();
+
+ mFragments = FragmentController.createController(new HostCallbacks());
+ mFragments.attachHost(null);
+ mFragments.restoreAllState(p, (FragmentManagerNonConfig) null);
+ mFragments.dispatchResume();
+ processAllMessages();
+ }
+
+ @Test
+ public void testMultipleResumes() {
+ mFragments.dispatchResume();
+ processAllMessages();
+ mFragments.dispatchStop();
+ processAllMessages();
+ mFragments.dispatchResume();
+ processAllMessages();
+ }
+
+ protected void attachFragmentToWindow() {
+ ViewUtils.attachView(mView);
+ TestableLooper.get(this).processMessages(1);
+ }
+
+ protected void detachFragmentToWindow() {
+ ViewUtils.detachView(mView);
+ TestableLooper.get(this).processMessages(1);
+ }
+
+ protected void destroyFragments() {
+ mFragments.dispatchDestroy();
+ processAllMessages();
+ mFragments = null;
+ }
+
+ protected void processAllMessages() {
+ TestableLooper.get(this).processAllMessages();
+ }
+
+ private View findViewById(int id) {
+ return mView.findViewById(id);
+ }
+
+ private class HostCallbacks extends FragmentHostCallback<BaseFragmentTest> {
+ public HostCallbacks() {
+ super(mContext, BaseFragmentTest.this.mHandler, 0);
+ }
+
+ @Override
+ public BaseFragmentTest onGetHost() {
+ return BaseFragmentTest.this;
+ }
+
+ @Override
+ public void onDump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
+ }
+
+ @Override
+ public boolean onShouldSaveFragmentState(Fragment fragment) {
+ return true; // True for now.
+ }
+
+ @Override
+ public LayoutInflater onGetLayoutInflater() {
+ return LayoutInflater.from(mContext);
+ }
+
+ @Override
+ public boolean onUseFragmentManagerInflaterFactory() {
+ return true;
+ }
+
+ @Override
+ public boolean onHasWindowAnimations() {
+ return false;
+ }
+
+ @Override
+ public int onGetWindowAnimations() {
+ return 0;
+ }
+
+ @Override
+ public void onAttachFragment(Fragment fragment) {
+ }
+
+ @Nullable
+ @Override
+ public View onFindViewById(int id) {
+ return BaseFragmentTest.this.findViewById(id);
+ }
+
+ @Override
+ public boolean onHasView() {
+ return true;
+ }
+ }
+}