Home | History | Annotate | Download | only in cts
      1 /*
      2  * Copyright (C) 2008 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 package android.widget.cts;
     18 
     19 import static org.junit.Assert.assertEquals;
     20 import static org.junit.Assert.assertFalse;
     21 import static org.junit.Assert.assertNotSame;
     22 import static org.junit.Assert.assertNull;
     23 import static org.junit.Assert.assertSame;
     24 import static org.junit.Assert.assertTrue;
     25 import static org.junit.Assert.fail;
     26 import static org.mockito.Mockito.mock;
     27 
     28 import android.app.Activity;
     29 import android.app.AlertDialog;
     30 import android.app.Dialog;
     31 import android.app.Instrumentation;
     32 import android.content.res.Resources;
     33 import android.content.res.Resources.NotFoundException;
     34 import android.content.res.Resources.Theme;
     35 import android.graphics.Color;
     36 import android.graphics.drawable.Drawable;
     37 import android.support.test.InstrumentationRegistry;
     38 import android.support.test.annotation.UiThreadTest;
     39 import android.support.test.filters.MediumTest;
     40 import android.support.test.rule.ActivityTestRule;
     41 import android.support.test.runner.AndroidJUnit4;
     42 import android.view.ContextThemeWrapper;
     43 import android.view.Gravity;
     44 import android.view.KeyEvent;
     45 import android.view.ViewGroup;
     46 import android.widget.ArrayAdapter;
     47 import android.widget.Spinner;
     48 import android.widget.cts.util.TestUtils;
     49 
     50 import com.android.compatibility.common.util.CtsTouchUtils;
     51 import com.android.compatibility.common.util.WidgetTestUtils;
     52 
     53 import org.junit.Before;
     54 import org.junit.Rule;
     55 import org.junit.Test;
     56 import org.junit.runner.RunWith;
     57 
     58 /**
     59  * Test {@link Spinner}.
     60  */
     61 @MediumTest
     62 @RunWith(AndroidJUnit4.class)
     63 public class SpinnerTest {
     64     private Instrumentation mInstrumentation;
     65     private Activity mActivity;
     66     private Spinner mSpinnerDialogMode;
     67     private Spinner mSpinnerDropdownMode;
     68 
     69     @Rule
     70     public ActivityTestRule<SpinnerCtsActivity> mActivityRule =
     71             new ActivityTestRule<>(SpinnerCtsActivity.class);
     72 
     73     @Before
     74     public void setup() {
     75         mInstrumentation = InstrumentationRegistry.getInstrumentation();
     76         mActivity = mActivityRule.getActivity();
     77         mSpinnerDialogMode = (Spinner) mActivity.findViewById(R.id.spinner_dialog_mode);
     78         mSpinnerDropdownMode = (Spinner) mActivity.findViewById(R.id.spinner_dropdown_mode);
     79     }
     80 
     81     @Test
     82     public void testConstructor() {
     83         new Spinner(mActivity);
     84 
     85         new Spinner(mActivity, null);
     86 
     87         new Spinner(mActivity, null, android.R.attr.spinnerStyle);
     88 
     89         new Spinner(mActivity, Spinner.MODE_DIALOG);
     90 
     91         new Spinner(mActivity, Spinner.MODE_DROPDOWN);
     92 
     93         new Spinner(mActivity, null, android.R.attr.spinnerStyle, Spinner.MODE_DIALOG);
     94 
     95         new Spinner(mActivity, null, android.R.attr.spinnerStyle, Spinner.MODE_DROPDOWN);
     96 
     97         new Spinner(mActivity, null, 0, android.R.style.Widget_DeviceDefault_Spinner,
     98                 Spinner.MODE_DIALOG);
     99 
    100         new Spinner(mActivity, null, 0, android.R.style.Widget_DeviceDefault_Spinner,
    101                 Spinner.MODE_DROPDOWN);
    102 
    103         new Spinner(mActivity, null, 0, android.R.style.Widget_DeviceDefault_Light_Spinner,
    104                 Spinner.MODE_DIALOG);
    105 
    106         new Spinner(mActivity, null, 0, android.R.style.Widget_DeviceDefault_Light_Spinner,
    107                 Spinner.MODE_DROPDOWN);
    108 
    109         new Spinner(mActivity, null, 0, android.R.style.Widget_Material_Spinner,
    110                 Spinner.MODE_DIALOG);
    111 
    112         new Spinner(mActivity, null, 0, android.R.style.Widget_Material_Spinner,
    113                 Spinner.MODE_DROPDOWN);
    114 
    115         new Spinner(mActivity, null, 0, android.R.style.Widget_Material_Spinner_Underlined,
    116                 Spinner.MODE_DIALOG);
    117 
    118         new Spinner(mActivity, null, 0, android.R.style.Widget_Material_Spinner_Underlined,
    119                 Spinner.MODE_DROPDOWN);
    120 
    121         new Spinner(mActivity, null, 0, android.R.style.Widget_Material_Light_Spinner,
    122                 Spinner.MODE_DIALOG);
    123 
    124         new Spinner(mActivity, null, 0, android.R.style.Widget_Material_Light_Spinner,
    125                 Spinner.MODE_DROPDOWN);
    126 
    127         new Spinner(mActivity, null, 0, android.R.style.Widget_Material_Light_Spinner_Underlined,
    128                 Spinner.MODE_DIALOG);
    129 
    130         new Spinner(mActivity, null, 0, android.R.style.Widget_Material_Light_Spinner_Underlined,
    131                 Spinner.MODE_DROPDOWN);
    132 
    133         final Resources.Theme popupTheme = mActivity.getResources().newTheme();
    134         popupTheme.applyStyle(android.R.style.Theme_Material, true);
    135 
    136         new Spinner(mActivity, null, android.R.attr.spinnerStyle, 0, Spinner.MODE_DIALOG,
    137                 popupTheme);
    138 
    139         new Spinner(mActivity, null, android.R.attr.spinnerStyle, 0, Spinner.MODE_DROPDOWN,
    140                 popupTheme);
    141     }
    142 
    143     private void verifyGetBaseline(Spinner spinner) throws Throwable {
    144         assertEquals(-1, spinner.getBaseline());
    145 
    146         ArrayAdapter<CharSequence> adapter = ArrayAdapter.createFromResource(mActivity,
    147                 R.array.string, android.R.layout.simple_spinner_item);
    148         adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
    149         mActivityRule.runOnUiThread(() -> {
    150             spinner.setAdapter(adapter);
    151             assertTrue(spinner.getBaseline() > 0);
    152         });
    153     }
    154 
    155     @Test
    156     public void testGetBaseline() throws Throwable {
    157         verifyGetBaseline(mSpinnerDialogMode);
    158         verifyGetBaseline(mSpinnerDropdownMode);
    159     }
    160 
    161     private void verifySetOnItemClickListener(Spinner spinner) {
    162         try {
    163             spinner.setOnItemClickListener(null);
    164             fail("Should throw RuntimeException");
    165         } catch (RuntimeException e) {
    166         }
    167 
    168         try {
    169             spinner.setOnItemClickListener(mock(Spinner.OnItemClickListener.class));
    170             fail("Should throw RuntimeException");
    171         } catch (RuntimeException e) {
    172         }
    173     }
    174 
    175     @Test
    176     public void testSetOnItemClickListener() {
    177         verifySetOnItemClickListener(mSpinnerDialogMode);
    178         verifySetOnItemClickListener(mSpinnerDropdownMode);
    179     }
    180 
    181     private void verifyPerformClick(Spinner spinner) throws Throwable {
    182         mActivityRule.runOnUiThread(() -> assertTrue(spinner.performClick()));
    183     }
    184 
    185     @Test
    186     public void testPerformClick() throws Throwable {
    187         verifyPerformClick(mSpinnerDialogMode);
    188         verifyPerformClick(mSpinnerDropdownMode);
    189     }
    190 
    191     private void verifyOnClick(Spinner spinner) {
    192         // normal value
    193         AlertDialog.Builder builder = new AlertDialog.Builder(mActivity);
    194         AlertDialog alertDialog = builder.show();
    195         assertTrue(alertDialog.isShowing());
    196 
    197         spinner.onClick(alertDialog, 10);
    198         assertEquals(10, spinner.getSelectedItemPosition());
    199         assertFalse(alertDialog.isShowing());
    200 
    201         // exceptional
    202         try {
    203             spinner.onClick(null, 10);
    204             fail("did not throw NullPointerException");
    205         } catch (NullPointerException e) {
    206         }
    207 
    208         Dialog dialog = new Dialog(mActivity);
    209         dialog.show();
    210         assertTrue(dialog.isShowing());
    211 
    212         spinner.onClick(dialog, -10);
    213         assertEquals(-10, spinner.getSelectedItemPosition());
    214         assertFalse(dialog.isShowing());
    215     }
    216 
    217     @UiThreadTest
    218     @Test
    219     public void testOnClick() {
    220         verifyOnClick(mSpinnerDialogMode);
    221         verifyOnClick(mSpinnerDropdownMode);
    222     }
    223 
    224     private void verifyAccessPrompt(Spinner spinner) throws Throwable {
    225         final String initialPrompt = mActivity.getString(R.string.text_view_hello);
    226         assertEquals(initialPrompt, spinner.getPrompt());
    227 
    228         final String promptText = "prompt text";
    229 
    230         mActivityRule.runOnUiThread(() -> spinner.setPrompt(promptText));
    231         assertEquals(promptText, spinner.getPrompt());
    232 
    233         spinner.setPrompt(null);
    234         assertNull(spinner.getPrompt());
    235     }
    236 
    237     @Test
    238     public void testAccessPrompt() throws Throwable {
    239         verifyAccessPrompt(mSpinnerDialogMode);
    240         verifyAccessPrompt(mSpinnerDropdownMode);
    241     }
    242 
    243     private void verifySetPromptId(Spinner spinner) throws Throwable {
    244         mActivityRule.runOnUiThread(() -> spinner.setPromptId(R.string.hello_world));
    245         assertEquals(mActivity.getString(R.string.hello_world), spinner.getPrompt());
    246 
    247         try {
    248             spinner.setPromptId(-1);
    249             fail("Should throw NotFoundException");
    250         } catch (NotFoundException e) {
    251             // issue 1695243, not clear what is supposed to happen if promptId is exceptional.
    252         }
    253 
    254         try {
    255             spinner.setPromptId(Integer.MAX_VALUE);
    256             fail("Should throw NotFoundException");
    257         } catch (NotFoundException e) {
    258             // issue 1695243, not clear what is supposed to happen if promptId is exceptional.
    259         }
    260     }
    261 
    262     @Test
    263     public void testSetPromptId() throws Throwable {
    264         verifySetPromptId(mSpinnerDialogMode);
    265         verifySetPromptId(mSpinnerDropdownMode);
    266     }
    267 
    268     @UiThreadTest
    269     @Test
    270     public void testGetPopupContext() {
    271         Theme theme = mActivity.getResources().newTheme();
    272         Spinner themeSpinner = new Spinner(mActivity, null,
    273                 android.R.attr.spinnerStyle, 0, Spinner.MODE_DIALOG, theme);
    274         assertNotSame(mActivity, themeSpinner.getPopupContext());
    275         assertSame(theme, themeSpinner.getPopupContext().getTheme());
    276 
    277         ContextThemeWrapper context = (ContextThemeWrapper)themeSpinner.getPopupContext();
    278         assertSame(mActivity, context.getBaseContext());
    279     }
    280 
    281     private void verifyGravity(Spinner spinner) throws Throwable {
    282         // Note that here we're using a custom layout for the spinner's selected item
    283         // that doesn't span the whole width of the parent. That way we're exercising the
    284         // relevant path in spinner's layout pass that handles the currently set gravity
    285         ArrayAdapter<CharSequence> adapter = ArrayAdapter.createFromResource(mActivity,
    286                 R.array.string, R.layout.simple_spinner_item_layout);
    287         adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
    288         mActivityRule.runOnUiThread(() -> spinner.setAdapter(adapter));
    289 
    290         WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, spinner, () -> {
    291             spinner.setSelection(1);
    292             spinner.getLayoutParams().width = ViewGroup.LayoutParams.MATCH_PARENT;
    293             spinner.requestLayout();
    294         });
    295 
    296         mActivityRule.runOnUiThread(() -> spinner.setGravity(Gravity.LEFT));
    297         assertEquals(Gravity.LEFT, spinner.getGravity());
    298 
    299         mActivityRule.runOnUiThread(() -> spinner.setGravity(Gravity.CENTER_HORIZONTAL));
    300         assertEquals(Gravity.CENTER_HORIZONTAL, spinner.getGravity());
    301 
    302         mActivityRule.runOnUiThread((() -> spinner.setGravity(Gravity.RIGHT)));
    303         assertEquals(Gravity.RIGHT, spinner.getGravity());
    304 
    305         mActivityRule.runOnUiThread(() -> spinner.setGravity(Gravity.START));
    306         assertEquals(Gravity.START, spinner.getGravity());
    307 
    308         mActivityRule.runOnUiThread(() -> spinner.setGravity(Gravity.END));
    309         assertEquals(Gravity.END, spinner.getGravity());
    310     }
    311 
    312     @Test
    313     public void testGravity() throws Throwable {
    314         verifyGravity(mSpinnerDialogMode);
    315         verifyGravity(mSpinnerDropdownMode);
    316     }
    317 
    318     @Test
    319     public void testDropDownMetricsDropdownMode() throws Throwable {
    320         ArrayAdapter<CharSequence> adapter = ArrayAdapter.createFromResource(mActivity,
    321                 R.array.string, android.R.layout.simple_spinner_item);
    322         adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
    323         mActivityRule.runOnUiThread(() -> mSpinnerDropdownMode.setAdapter(adapter));
    324 
    325         final Resources res = mActivity.getResources();
    326         final int dropDownWidth = res.getDimensionPixelSize(R.dimen.spinner_dropdown_width);
    327         final int dropDownOffsetHorizontal =
    328                 res.getDimensionPixelSize(R.dimen.spinner_dropdown_offset_h);
    329         final int dropDownOffsetVertical =
    330                 res.getDimensionPixelSize(R.dimen.spinner_dropdown_offset_v);
    331 
    332         mActivityRule.runOnUiThread(() -> {
    333             mSpinnerDropdownMode.setDropDownWidth(dropDownWidth);
    334             mSpinnerDropdownMode.setDropDownHorizontalOffset(dropDownOffsetHorizontal);
    335             mSpinnerDropdownMode.setDropDownVerticalOffset(dropDownOffsetVertical);
    336         });
    337 
    338         // Use instrumentation to emulate a tap on the spinner to bring down its popup
    339         CtsTouchUtils.emulateTapOnViewCenter(mInstrumentation, mSpinnerDropdownMode);
    340         // Verify that we're showing the popup
    341         assertTrue(mSpinnerDropdownMode.isPopupShowing());
    342 
    343         // And test its attributes
    344         assertEquals(dropDownWidth, mSpinnerDropdownMode.getDropDownWidth());
    345         // TODO: restore when b/28089349 is addressed
    346         // assertEquals(dropDownOffsetHorizontal,
    347         //      mSpinnerDropdownMode.getDropDownHorizontalOffset());
    348         assertEquals(dropDownOffsetVertical, mSpinnerDropdownMode.getDropDownVerticalOffset());
    349     }
    350 
    351     @Test
    352     public void testDropDownMetricsDialogMode() throws Throwable {
    353         ArrayAdapter<CharSequence> adapter = ArrayAdapter.createFromResource(mActivity,
    354                 R.array.string, android.R.layout.simple_spinner_item);
    355         adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
    356         mActivityRule.runOnUiThread(() -> mSpinnerDialogMode.setAdapter(adapter));
    357 
    358         final Resources res = mActivity.getResources();
    359         final int dropDownWidth = res.getDimensionPixelSize(R.dimen.spinner_dropdown_width);
    360         final int dropDownOffsetHorizontal =
    361                 res.getDimensionPixelSize(R.dimen.spinner_dropdown_offset_h);
    362         final int dropDownOffsetVertical =
    363                 res.getDimensionPixelSize(R.dimen.spinner_dropdown_offset_v);
    364 
    365         mActivityRule.runOnUiThread(() -> {
    366             // These are all expected to be no-ops
    367             mSpinnerDialogMode.setDropDownWidth(dropDownWidth);
    368             mSpinnerDialogMode.setDropDownHorizontalOffset(dropDownOffsetHorizontal);
    369             mSpinnerDialogMode.setDropDownVerticalOffset(dropDownOffsetVertical);
    370         });
    371 
    372         // Use instrumentation to emulate a tap on the spinner to bring down its popup
    373         CtsTouchUtils.emulateTapOnViewCenter(mInstrumentation, mSpinnerDialogMode);
    374         // Verify that we're showing the popup
    375         assertTrue(mSpinnerDialogMode.isPopupShowing());
    376 
    377         // And test its attributes. Note that we are not testing the result of getDropDownWidth
    378         // for this mode
    379         assertEquals(0, mSpinnerDialogMode.getDropDownHorizontalOffset());
    380         assertEquals(0, mSpinnerDialogMode.getDropDownVerticalOffset());
    381     }
    382 
    383     @Test
    384     public void testDropDownBackgroundDropdownMode() throws Throwable {
    385         ArrayAdapter<CharSequence> adapter = ArrayAdapter.createFromResource(mActivity,
    386                 R.array.string, android.R.layout.simple_spinner_item);
    387         adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
    388         mActivityRule.runOnUiThread(() -> mSpinnerDropdownMode.setAdapter(adapter));
    389 
    390         // Set blue background on the popup
    391         mActivityRule.runOnUiThread(() ->
    392                 mSpinnerDropdownMode.setPopupBackgroundResource(R.drawable.blue_fill));
    393 
    394         // Use instrumentation to emulate a tap on the spinner to bring down its popup
    395         CtsTouchUtils.emulateTapOnViewCenter(mInstrumentation, mSpinnerDropdownMode);
    396         // Verify that we're showing the popup
    397         assertTrue(mSpinnerDropdownMode.isPopupShowing());
    398         // And test its fill
    399         Drawable dropDownBackground = mSpinnerDropdownMode.getPopupBackground();
    400         TestUtils.assertAllPixelsOfColor("Drop down should be blue", dropDownBackground,
    401                 dropDownBackground.getBounds().width(), dropDownBackground.getBounds().height(),
    402                 false, Color.BLUE, 1, true);
    403 
    404         // Dismiss the popup with the emulated back key
    405         mInstrumentation.sendKeyDownUpSync(KeyEvent.KEYCODE_BACK);
    406         mInstrumentation.waitForIdleSync();
    407         // Verify that we're not showing the popup
    408         assertFalse(mSpinnerDropdownMode.isPopupShowing());
    409 
    410         // Set yellow background on the popup
    411         mActivityRule.runOnUiThread(() ->
    412                 mSpinnerDropdownMode.setPopupBackgroundDrawable(
    413                         mActivity.getDrawable(R.drawable.yellow_fill)));
    414 
    415         // Use instrumentation to emulate a tap on the spinner to bring down its popup
    416         CtsTouchUtils.emulateTapOnViewCenter(mInstrumentation, mSpinnerDropdownMode);
    417         // Verify that we're showing the popup
    418         assertTrue(mSpinnerDropdownMode.isPopupShowing());
    419         // And test its fill
    420         dropDownBackground = mSpinnerDropdownMode.getPopupBackground();
    421         TestUtils.assertAllPixelsOfColor("Drop down should be yellow", dropDownBackground,
    422                 dropDownBackground.getBounds().width(), dropDownBackground.getBounds().height(),
    423                 false, Color.YELLOW, 1, true);
    424     }
    425 
    426     @Test
    427     public void testDropDownBackgroundDialogMode() throws Throwable {
    428         ArrayAdapter<CharSequence> adapter = ArrayAdapter.createFromResource(mActivity,
    429                 R.array.string, android.R.layout.simple_spinner_item);
    430         adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
    431         mActivityRule.runOnUiThread(() -> mSpinnerDialogMode.setAdapter(adapter));
    432 
    433         // Set blue background on the popup
    434         mActivityRule.runOnUiThread(() ->
    435                 mSpinnerDialogMode.setPopupBackgroundResource(R.drawable.blue_fill));
    436 
    437         // Use instrumentation to emulate a tap on the spinner to bring down its popup
    438         CtsTouchUtils.emulateTapOnViewCenter(mInstrumentation, mSpinnerDialogMode);
    439         // Verify that we're showing the popup
    440         assertTrue(mSpinnerDialogMode.isPopupShowing());
    441         // And test that getPopupBackground returns null
    442         assertNull(mSpinnerDialogMode.getPopupBackground());
    443 
    444         // Dismiss the popup with the emulated back key
    445         mInstrumentation.sendKeyDownUpSync(KeyEvent.KEYCODE_BACK);
    446         mInstrumentation.waitForIdleSync();
    447         // Verify that we're not showing the popup
    448         assertFalse(mSpinnerDialogMode.isPopupShowing());
    449 
    450         // Set yellow background on the popup
    451         mActivityRule.runOnUiThread(() ->
    452                 mSpinnerDialogMode.setPopupBackgroundDrawable(
    453                         mActivity.getDrawable(R.drawable.yellow_fill)));
    454 
    455         // Use instrumentation to emulate a tap on the spinner to bring down its popup
    456         CtsTouchUtils.emulateTapOnViewCenter(mInstrumentation, mSpinnerDialogMode);
    457         // Verify that we're showing the popup
    458         assertTrue(mSpinnerDialogMode.isPopupShowing());
    459         // And test that getPopupBackground returns null
    460         assertNull(mSpinnerDialogMode.getPopupBackground());
    461     }
    462 }
    463