Robotests for BluetoothPairingDialog

Created some tests to protect some basic bluetooth
pairing dialogs features from regressing. Most of the
tests in this CL ensure that the view is properly
created and that it is properly updating the
associated controller when a relevant action occurs.

Test: make RunSettingsRoboTests
Bug: 32180625
Change-Id: I2f4103a39ffced52353712f952e8ff3d26590169
diff --git a/src/com/android/settings/bluetooth/BluetoothPairingController.java b/src/com/android/settings/bluetooth/BluetoothPairingController.java
index 38b30a8..ce82612 100644
--- a/src/com/android/settings/bluetooth/BluetoothPairingController.java
+++ b/src/com/android/settings/bluetooth/BluetoothPairingController.java
@@ -188,7 +188,7 @@
      *
      * @return - The message ID to show the user.
      */
-    public int getDeviceVariantMessageID() {
+    public int getDeviceVariantMessageId() {
         switch (mType) {
             case BluetoothDevice.PAIRING_VARIANT_PIN_16_DIGITS:
             case BluetoothDevice.PAIRING_VARIANT_PIN:
@@ -198,7 +198,7 @@
                 return R.string.bluetooth_enter_passkey_other_device;
 
             default:
-                return -1;
+                return INVALID_DIALOG_TYPE;
         }
     }
 
@@ -208,7 +208,7 @@
      *
      * @return - The message ID to show the user.
      */
-    public int getDeviceVariantMessageHint() {
+    public int getDeviceVariantMessageHintId() {
         switch (mType) {
             case BluetoothDevice.PAIRING_VARIANT_PIN_16_DIGITS:
                 return R.string.bluetooth_pin_values_hint_16_digits;
@@ -218,7 +218,7 @@
                 return R.string.bluetooth_pin_values_hint;
 
             default:
-                return -1;
+                return INVALID_DIALOG_TYPE;
         }
     }
 
diff --git a/src/com/android/settings/bluetooth/BluetoothPairingDialogFragment.java b/src/com/android/settings/bluetooth/BluetoothPairingDialogFragment.java
index d4247c0..abeb862 100644
--- a/src/com/android/settings/bluetooth/BluetoothPairingDialogFragment.java
+++ b/src/com/android/settings/bluetooth/BluetoothPairingDialogFragment.java
@@ -45,10 +45,9 @@
     private static final String TAG = "BTPairingDialogFragment";
 
     private AlertDialog.Builder mBuilder;
-    private BluetoothPairingController mPairingController;
     private AlertDialog mDialog;
+    private BluetoothPairingController mPairingController;
     private EditText mPairingView;
-
     /**
      * The interface we expect a listener to implement. Typically this should be done by
      * the controller.
@@ -106,11 +105,25 @@
     }
 
     /**
+     * Used in testing to get a reference to the dialog.
+     * @return - The fragments current dialog
+     */
+    protected AlertDialog getmDialog() {
+        return mDialog;
+    }
+
+    /**
      * Sets the controller that the fragment should use. this method MUST be called
      * before you try to show the dialog or an error will be thrown. An implementation
-     * of a pairing controller can be found at {@link BluetoothPairingController}.
+     * of a pairing controller can be found at {@link BluetoothPairingController}. A
+     * controller may not be substituted once it is assigned. Forcibly switching a
+     * controller for a new one will lead to undefined behavior.
      */
     public void setPairingController(BluetoothPairingController pairingController) {
+        if (mPairingController != null) {
+            throw new IllegalStateException("The controller can only be set once. "
+                    + "Forcibly replacing it will lead to undefined behavior");
+        }
         mPairingController = pairingController;
     }
 
@@ -146,7 +159,7 @@
         mBuilder.setPositiveButton(getString(android.R.string.ok), this);
         mBuilder.setNegativeButton(getString(android.R.string.cancel), this);
         AlertDialog dialog = mBuilder.create();
-        dialog.getButton(Dialog.BUTTON_POSITIVE).setEnabled(false);
+        dialog.setOnShowListener(d -> mDialog.getButton(Dialog.BUTTON_POSITIVE).setEnabled(false));
         return dialog;
     }
 
@@ -171,6 +184,7 @@
 
         mPairingView = pairingView;
 
+        pairingView.setInputType(InputType.TYPE_CLASS_NUMBER);
         pairingView.addTextChangedListener(this);
         alphanumericPin.setOnCheckedChangeListener((buttonView, isChecked) -> {
             // change input type for soft keyboard to numeric or alphanumeric
@@ -181,15 +195,21 @@
             }
         });
 
-        int messageId = mPairingController.getDeviceVariantMessageID();
-        int messageIdHint = mPairingController.getDeviceVariantMessageHint();
+        int messageId = mPairingController.getDeviceVariantMessageId();
+        int messageIdHint = mPairingController.getDeviceVariantMessageHintId();
         int maxLength = mPairingController.getDeviceMaxPasskeyLength();
         alphanumericPin.setVisibility(mPairingController.pairingCodeIsAlphanumeric()
                 ? View.VISIBLE : View.GONE);
-
-        messageViewCaptionHint.setText(messageIdHint);
-        messageView2.setText(messageId);
-        pairingView.setInputType(InputType.TYPE_CLASS_NUMBER);
+        if (messageId != BluetoothPairingController.INVALID_DIALOG_TYPE) {
+            messageView2.setText(messageId);
+        } else {
+            messageView2.setVisibility(View.GONE);
+        }
+        if (messageIdHint != BluetoothPairingController.INVALID_DIALOG_TYPE) {
+            messageViewCaptionHint.setText(messageIdHint);
+        } else {
+            messageViewCaptionHint.setVisibility(View.GONE);
+        }
         pairingView.setFilters(new InputFilter[]{
                 new LengthFilter(maxLength)});
 
@@ -203,10 +223,8 @@
         mBuilder.setTitle(getString(R.string.bluetooth_pairing_request,
                 mPairingController.getDeviceName()));
         mBuilder.setView(createView());
-        mBuilder.setPositiveButton(getString(R.string.bluetooth_pairing_accept),
-                this);
-        mBuilder.setNegativeButton(getString(R.string.bluetooth_pairing_decline),
-                this);
+        mBuilder.setPositiveButton(getString(R.string.bluetooth_pairing_accept), this);
+        mBuilder.setNegativeButton(getString(R.string.bluetooth_pairing_decline), this);
         AlertDialog dialog = mBuilder.create();
         return dialog;
     }
diff --git a/tests/robotests/src/com/android/settings/SettingsRobolectricTestRunner.java b/tests/robotests/src/com/android/settings/SettingsRobolectricTestRunner.java
new file mode 100644
index 0000000..e156716
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/SettingsRobolectricTestRunner.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2016 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 com.android.settings;
+
+import org.junit.runners.model.InitializationError;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.annotation.Config;
+import org.robolectric.manifest.AndroidManifest;
+import org.robolectric.res.Fs;
+import org.robolectric.res.ResourcePath;
+
+import java.util.List;
+
+/**
+ * Custom test runner for the testing of BluetoothPairingDialogs. This is needed because the
+ * default behavior for robolectric is just to grab the resource directory in the target package.
+ * We want to override this to add several spanning different projects.
+ */
+public class SettingsRobolectricTestRunner extends RobolectricTestRunner {
+
+    /**
+     * We don't actually want to change this behavior, so we just call super.
+     */
+    public SettingsRobolectricTestRunner(Class<?> testClass) throws InitializationError {
+        super(testClass);
+    }
+
+    /**
+     * We are going to create our own custom manifest so that we can add multiple resource
+     * paths to it. This lets us access resources in both Settings and SettingsLib in our tests.
+     */
+    @Override
+    protected AndroidManifest getAppManifest(Config config) {
+        // Using the manifest file's relative path, we can figure out the application directory.
+        final String appRoot = "packages/apps/Settings";
+        final String manifestPath = appRoot + "/AndroidManifest.xml";
+        final String resDir = appRoot + "/res";
+        final String assetsDir = appRoot + "/assets";
+
+        // By adding any resources from libraries we need to the AndroidManifest, we can access
+        // them from within the parallel universe's resource loader.
+        final AndroidManifest manifest = new AndroidManifest(Fs.fileFromPath(manifestPath),
+                Fs.fileFromPath(resDir), Fs.fileFromPath(assetsDir)) {
+            @Override
+            public List<ResourcePath> getIncludedResourcePaths() {
+                List<ResourcePath> paths = super.getIncludedResourcePaths();
+                paths.add(new ResourcePath(
+                        getPackageName(),
+                        Fs.fileFromPath("./packages/apps/Settings/res"),
+                        null));
+                paths.add(new ResourcePath(
+                        getPackageName(),
+                        Fs.fileFromPath("./frameworks/base/packages/SettingsLib/res"),
+                        null));
+                return paths;
+            }
+        };
+
+        // Set the package name to the renamed one
+        manifest.setPackageName("com.android.settings");
+        return manifest;
+    }
+}
\ No newline at end of file
diff --git a/tests/robotests/src/com/android/settings/bluetooth/BluetoothPairingDialogTest.java b/tests/robotests/src/com/android/settings/bluetooth/BluetoothPairingDialogTest.java
new file mode 100644
index 0000000..004e4f9
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/bluetooth/BluetoothPairingDialogTest.java
@@ -0,0 +1,340 @@
+/*
+ * Copyright (C) 2016 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 com.android.settings.bluetooth;
+
+import static com.google.common.truth.Truth.assertThat;
+import static org.junit.Assert.fail;
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.AlertDialog;
+import android.text.SpannableStringBuilder;
+import android.text.TextUtils;
+import android.view.View;
+import android.widget.CheckBox;
+import android.widget.TextView;
+import com.android.settings.SettingsRobolectricTestRunner;
+import com.android.settings.R;
+import com.android.settings.TestConfig;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.annotation.Config;
+import org.robolectric.util.FragmentTestUtil;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
+public class BluetoothPairingDialogTest {
+
+    private static final String FILLER = "text that goes in a view";
+    private static final String FAKE_DEVICE_NAME = "Fake Bluetooth Device";
+
+    @Mock
+    private BluetoothPairingController controller;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+    }
+
+    @Test
+    public void dialogUpdatesControllerWithUserInput() {
+        // set the correct dialog type
+        when(controller.getDialogType()).thenReturn(BluetoothPairingController.USER_ENTRY_DIALOG);
+
+        // we don't care about these for this test
+        when(controller.getDeviceVariantMessageId())
+                .thenReturn(BluetoothPairingController.INVALID_DIALOG_TYPE);
+        when(controller.getDeviceVariantMessageHintId())
+                .thenReturn(BluetoothPairingController.INVALID_DIALOG_TYPE);
+
+        // build fragment
+        BluetoothPairingDialogFragment frag = makeFragment();
+
+        // test that controller is updated on text change
+        frag.afterTextChanged(new SpannableStringBuilder(FILLER));
+        verify(controller, times(1)).updateUserInput(any());
+    }
+
+    @Test
+    public void dialogEnablesSubmitButtonOnValidationFromController() {
+        // set the correct dialog type
+        when(controller.getDialogType()).thenReturn(BluetoothPairingController.USER_ENTRY_DIALOG);
+
+        // we don't care about these for this test
+        when(controller.getDeviceVariantMessageId())
+                .thenReturn(BluetoothPairingController.INVALID_DIALOG_TYPE);
+        when(controller.getDeviceVariantMessageHintId())
+                .thenReturn(BluetoothPairingController.INVALID_DIALOG_TYPE);
+
+        // force the controller to say that any passkey is valid
+        when(controller.isPasskeyValid(any())).thenReturn(true);
+
+        // build fragment
+        BluetoothPairingDialogFragment frag = makeFragment();
+
+        // test that the positive button is enabled when passkey is valid
+        frag.afterTextChanged(new SpannableStringBuilder(FILLER));
+        View button = frag.getmDialog().getButton(AlertDialog.BUTTON_POSITIVE);
+        assertThat(button).isNotNull();
+        assertThat(button.getVisibility()).isEqualTo(View.VISIBLE);
+    }
+
+    @Test
+    public void dialogDoesNotAskForPairCodeOnConsentVariant() {
+        // set the dialog variant to confirmation/consent
+        when(controller.getDialogType()).thenReturn(BluetoothPairingController.CONFIRMATION_DIALOG);
+
+        // build the fragment
+        BluetoothPairingDialogFragment frag = makeFragment();
+
+        // check that the input field used by the entry dialog fragment does not exist
+        View view = frag.getmDialog().findViewById(R.id.text);
+        assertThat(view).isNull();
+    }
+
+    @Test
+    public void dialogAsksForPairCodeOnUserEntryVariant() {
+        // set the dialog variant to user entry
+        when(controller.getDialogType()).thenReturn(BluetoothPairingController.USER_ENTRY_DIALOG);
+
+        // we don't care about these for this test
+        when(controller.getDeviceVariantMessageId())
+                .thenReturn(BluetoothPairingController.INVALID_DIALOG_TYPE);
+        when(controller.getDeviceVariantMessageHintId())
+                .thenReturn(BluetoothPairingController.INVALID_DIALOG_TYPE);
+
+        // build the fragment
+        BluetoothPairingDialogFragment frag = makeFragment();
+
+        // check that the pin/passkey input field is visible to the user
+        View view = frag.getmDialog().findViewById(R.id.text);
+        assertThat(view.getVisibility()).isEqualTo(View.VISIBLE);
+    }
+
+    @Test
+    public void dialogDisplaysPairCodeOnDisplayPasskeyVariant() {
+        // set the dialog variant to display passkey
+        when(controller.getDialogType())
+                .thenReturn(BluetoothPairingController.DISPLAY_PASSKEY_DIALOG);
+
+        // ensure that the controller returns good values to indicate a passkey needs to be shown
+        when(controller.isDisplayPairingKeyVariant()).thenReturn(true);
+        when(controller.hasPairingContent()).thenReturn(true);
+        when(controller.getPairingContent()).thenReturn(FILLER);
+
+        // build the fragment
+        BluetoothPairingDialogFragment frag = makeFragment();
+
+        // get the relevant views
+        View messagePairing = frag.getmDialog().findViewById(R.id.pairing_code_message);
+        TextView pairingViewContent =
+                (TextView) frag.getmDialog().findViewById(R.id.pairing_subhead);
+        View pairingViewCaption = frag.getmDialog().findViewById(R.id.pairing_caption);
+
+        // check that the relevant views are visible and that the passkey is shown
+        assertThat(messagePairing.getVisibility()).isEqualTo(View.VISIBLE);
+        assertThat(pairingViewCaption.getVisibility()).isEqualTo(View.VISIBLE);
+        assertThat(pairingViewContent.getVisibility()).isEqualTo(View.VISIBLE);
+        assertThat(TextUtils.equals(FILLER, pairingViewContent.getText())).isTrue();
+    }
+
+    @Test(expected = IllegalStateException.class)
+    public void dialogThrowsExceptionIfNoControllerSet() {
+        // instantiate a fragment
+        BluetoothPairingDialogFragment frag = new BluetoothPairingDialogFragment();
+
+        // this should throw an error
+        FragmentTestUtil.startFragment(frag);
+        fail("Starting the fragment with no controller set should have thrown an exception.");
+    }
+
+    @Test
+    public void dialogCallsHookOnPositiveButtonPress() {
+        // set the dialog variant to confirmation/consent
+        when(controller.getDialogType()).thenReturn(BluetoothPairingController.CONFIRMATION_DIALOG);
+
+        // we don't care what this does, just that it is called
+        doNothing().when(controller).onDialogPositiveClick(any());
+
+        // build the fragment
+        BluetoothPairingDialogFragment frag = makeFragment();
+
+        // click the button and verify that the controller hook was called
+        frag.onClick(frag.getmDialog(), AlertDialog.BUTTON_POSITIVE);
+        verify(controller, times(1)).onDialogPositiveClick(any());
+    }
+
+    @Test
+    public void dialogCallsHookOnNegativeButtonPress() {
+        // set the dialog variant to confirmation/consent
+        when(controller.getDialogType()).thenReturn(BluetoothPairingController.CONFIRMATION_DIALOG);
+
+        // we don't care what this does, just that it is called
+        doNothing().when(controller).onDialogNegativeClick(any());
+
+        // build the fragment
+        BluetoothPairingDialogFragment frag = makeFragment();
+
+        // click the button and verify that the controller hook was called
+        frag.onClick(frag.getmDialog(), AlertDialog.BUTTON_NEGATIVE);
+        verify(controller, times(1)).onDialogNegativeClick(any());
+    }
+
+    @Test(expected = IllegalStateException.class)
+    public void dialogDoesNotAllowSwappingController() {
+        // instantiate a fragment
+        BluetoothPairingDialogFragment frag = new BluetoothPairingDialogFragment();
+        frag.setPairingController(controller);
+
+        // this should throw an error
+        frag.setPairingController(controller);
+        fail("Setting the controller multiple times should throw an exception.");
+    }
+
+    @Test
+    public void dialogPositiveButtonDisabledWhenUserInputInvalid() {
+        // set the correct dialog type
+        when(controller.getDialogType()).thenReturn(BluetoothPairingController.USER_ENTRY_DIALOG);
+
+        // we don't care about these for this test
+        when(controller.getDeviceVariantMessageId())
+                .thenReturn(BluetoothPairingController.INVALID_DIALOG_TYPE);
+        when(controller.getDeviceVariantMessageHintId())
+                .thenReturn(BluetoothPairingController.INVALID_DIALOG_TYPE);
+
+        // force the controller to say that any passkey is valid
+        when(controller.isPasskeyValid(any())).thenReturn(false);
+
+        // build fragment
+        BluetoothPairingDialogFragment frag = makeFragment();
+
+        // test that the positive button is enabled when passkey is valid
+        frag.afterTextChanged(new SpannableStringBuilder(FILLER));
+        View button = frag.getmDialog().getButton(AlertDialog.BUTTON_POSITIVE);
+        assertThat(button).isNotNull();
+        assertThat(button.isEnabled()).isFalse();
+    }
+
+    @Test
+    public void dialogShowsContactSharingCheckboxWhenBluetoothProfileNotReady() {
+        // set the dialog variant to confirmation/consent
+        when(controller.getDialogType()).thenReturn(BluetoothPairingController.CONFIRMATION_DIALOG);
+
+        // set a fake device name and pretend the profile has not been set up for it
+        when(controller.getDeviceName()).thenReturn(FAKE_DEVICE_NAME);
+        when(controller.isProfileReady()).thenReturn(false);
+
+        // build the fragment
+        BluetoothPairingDialogFragment frag = makeFragment();
+
+        // verify that the checkbox is visible and that the device name is correct
+        CheckBox sharingCheckbox = (CheckBox) frag.getmDialog()
+                .findViewById(R.id.phonebook_sharing_message_confirm_pin);
+        assertThat(sharingCheckbox.getVisibility()).isEqualTo(View.VISIBLE);
+        assertThat(sharingCheckbox.getText().toString().contains(FAKE_DEVICE_NAME)).isTrue();
+    }
+
+    @Test
+    public void dialogHidesContactSharingCheckboxWhenBluetoothProfileIsReady() {
+        // set the dialog variant to confirmation/consent
+        when(controller.getDialogType()).thenReturn(BluetoothPairingController.CONFIRMATION_DIALOG);
+
+        // set a fake device name and pretend the profile has been set up for it
+        when(controller.getDeviceName()).thenReturn(FAKE_DEVICE_NAME);
+        when(controller.isProfileReady()).thenReturn(true);
+
+        // build the fragment
+        BluetoothPairingDialogFragment frag = makeFragment();
+
+        // verify that the checkbox is gone
+        CheckBox sharingCheckbox = (CheckBox) frag.getmDialog()
+                .findViewById(R.id.phonebook_sharing_message_confirm_pin);
+        assertThat(sharingCheckbox.getVisibility()).isEqualTo(View.GONE);
+    }
+
+    @Test
+    public void dialogShowsMessageOnPinEntryView() {
+        // set the correct dialog type
+        when(controller.getDialogType()).thenReturn(BluetoothPairingController.USER_ENTRY_DIALOG);
+
+        // Set the message id to something specific to verify later
+        when(controller.getDeviceVariantMessageId()).thenReturn(R.string.cancel);
+        when(controller.getDeviceVariantMessageHintId())
+                .thenReturn(BluetoothPairingController.INVALID_DIALOG_TYPE);
+
+        // build the fragment
+        BluetoothPairingDialogFragment frag = makeFragment();
+
+        // verify message is what we expect it to be and is visible
+        TextView message = (TextView) frag.getmDialog().findViewById(R.id.message_below_pin);
+        assertThat(message.getVisibility()).isEqualTo(View.VISIBLE);
+        assertThat(TextUtils.equals(frag.getString(R.string.cancel), message.getText())).isTrue();
+    }
+
+    @Test
+    public void dialogShowsMessageHintOnPinEntryView() {
+        // set the correct dialog type
+        when(controller.getDialogType()).thenReturn(BluetoothPairingController.USER_ENTRY_DIALOG);
+
+        // Set the message id hint to something specific to verify later
+        when(controller.getDeviceVariantMessageHintId()).thenReturn(R.string.cancel);
+        when(controller.getDeviceVariantMessageId())
+                .thenReturn(BluetoothPairingController.INVALID_DIALOG_TYPE);
+
+        // build the fragment
+        BluetoothPairingDialogFragment frag = makeFragment();
+
+        // verify message is what we expect it to be and is visible
+        TextView hint = (TextView) frag.getmDialog().findViewById(R.id.pin_values_hint);
+        assertThat(hint.getVisibility()).isEqualTo(View.VISIBLE);
+        assertThat(TextUtils.equals(frag.getString(R.string.cancel), hint.getText())).isTrue();
+    }
+
+    @Test
+    public void dialogHidesMessageAndHintWhenNotProvidedOnPinEntryView() {
+        // set the correct dialog type
+        when(controller.getDialogType()).thenReturn(BluetoothPairingController.USER_ENTRY_DIALOG);
+
+        // Set the id's to what is returned when it is not provided
+        when(controller.getDeviceVariantMessageHintId())
+                .thenReturn(BluetoothPairingController.INVALID_DIALOG_TYPE);
+        when(controller.getDeviceVariantMessageId())
+                .thenReturn(BluetoothPairingController.INVALID_DIALOG_TYPE);
+
+        // build the fragment
+        BluetoothPairingDialogFragment frag = makeFragment();
+
+        // verify message is what we expect it to be and is visible
+        TextView hint = (TextView) frag.getmDialog().findViewById(R.id.pin_values_hint);
+        assertThat(hint.getVisibility()).isEqualTo(View.GONE);
+        TextView message = (TextView) frag.getmDialog().findViewById(R.id.message_below_pin);
+        assertThat(message.getVisibility()).isEqualTo(View.GONE);
+    }
+
+    private BluetoothPairingDialogFragment makeFragment() {
+        BluetoothPairingDialogFragment frag = new BluetoothPairingDialogFragment();
+        frag.setPairingController(controller);
+        FragmentTestUtil.startFragment(frag);
+        assertThat(frag.getmDialog()).isNotNull();
+        return frag;
+    }
+}