Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit fbf05e93 authored by Victor Chang's avatar Victor Chang
Browse files

Add UI tests for new time zone picker

- Test selecting 2 time zones in picker
- Device.wait could return null object and cause NPE.
  Add helper function throw UiObjectNotFoundException with selector info

Bug: 62255208
Test: atest SettingsUITests:ZonePickerSettingsTest
Change-Id: Id0439dde25240cf2d3ee834043d1726a2c738d1a
parent 607aa8f5
Loading
Loading
Loading
Loading
+226 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2018 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.ui;

import android.os.RemoteException;
import android.os.SystemProperties;
import android.provider.Settings;
import android.support.test.InstrumentationRegistry;
import android.support.test.filters.MediumTest;
import android.support.test.runner.AndroidJUnit4;
import android.support.test.uiautomator.By;
import android.support.test.uiautomator.BySelector;
import android.support.test.uiautomator.UiDevice;
import android.support.test.uiautomator.UiObject;
import android.support.test.uiautomator.UiObject2;
import android.support.test.uiautomator.UiObjectNotFoundException;
import android.support.test.uiautomator.UiScrollable;
import android.support.test.uiautomator.UiSelector;
import android.support.test.uiautomator.Until;
import android.system.helpers.SettingsHelper;
import android.system.helpers.SettingsHelper.SettingsType;
import android.widget.ListView;
import android.widget.Spinner;

import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;

import java.util.TimeZone;

import static com.android.settings.ui.testutils.SettingsTestUtils.SETTINGS_PACKAGE;
import static com.android.settings.ui.testutils.SettingsTestUtils.TIMEOUT;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;

@MediumTest
@RunWith(AndroidJUnit4.class)
public class ZonePickerSettingsTest {

    private static final BySelector SELECTOR_SELECT_TIME_ZONE =
            By.hasChild(By.text("Select time zone"));

    private UiDevice mDevice;
    private SettingsHelper mHelper;
    private String mIsV2EnabledByDefault;
    private int mIsAutoZoneEnabled;

    @Before
    public void setUp() throws Exception {
        mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
        mHelper = SettingsHelper.getInstance();
        try {
            mDevice.setOrientationNatural();
        } catch (RemoteException e) {
            throw new RuntimeException("failed to freeze device orientation", e);
        }
        mIsV2EnabledByDefault = mHelper.getStringSetting(SettingsType.GLOBAL,
                "settings_zone_picker_v2");
        mHelper.setStringSetting(SettingsType.GLOBAL, "settings_zone_picker_v2", "true");
        mIsAutoZoneEnabled = mHelper.getIntSetting(SettingsType.GLOBAL,
                Settings.Global.AUTO_TIME_ZONE);
    }

    @After
    public void tearDown() throws Exception {
        // Go back to home for next test.
        mDevice.pressBack();
        mDevice.pressBack();
        mDevice.pressHome();
        mDevice.waitForIdle(TIMEOUT * 2);
        mHelper.setStringSetting(SettingsType.GLOBAL, "settings_zone_picker_v2",
                mIsV2EnabledByDefault);
        mHelper.setIntSetting(SettingsType.GLOBAL, Settings.Global.AUTO_TIME_ZONE,
                mIsAutoZoneEnabled);
    }

    @Test
    public void zonePickerDisabled() throws Exception {
        mHelper.setIntSetting(SettingsType.GLOBAL, Settings.Global.AUTO_TIME_ZONE, 1);

        SettingsHelper.launchSettingsPage(
                InstrumentationRegistry.getContext(), Settings.ACTION_DATE_SETTINGS);
        UiObject2 selectTimeZone = wait(SELECTOR_SELECT_TIME_ZONE);
        assertFalse(selectTimeZone.isEnabled());
    }

    // Test 2 time zones with no DST
    @Test
    public void testSelectReykjavik() throws Exception {
        testSelectTimeZone("Iceland", "Reykjavik", "GMT+00:00", "Atlantic/Reykjavik");
    }

    @Test
    public void testSelectPhoenix() throws Exception {
        testSelectTimeZone("United States", "Phoenix", "GMT-07:00", "America/Phoenix");
    }

    private void testSelectTimeZone(String region, String timezone, String expectedTimeZoneOffset,
            String expectedTimeZoneId) throws Exception {
        mHelper.setIntSetting(SettingsType.GLOBAL, Settings.Global.AUTO_TIME_ZONE, 0);

        SettingsHelper.launchSettingsPage(
                InstrumentationRegistry.getContext(), Settings.ACTION_DATE_SETTINGS);

        UiObject2 selectTimeZone = wait(SELECTOR_SELECT_TIME_ZONE);
        assertTrue(selectTimeZone.isEnabled());
        selectTimeZone.click();

        // Select region in the dropdown list
        selectScrollableItem(selectDropDownInSpinner(By.clazz(Spinner.class)),
                new UiSelector().textContains(region))
                .click();

        // Select time zone
        selectScrollableItem(selectTimeZoneList(),
                new UiSelector().textContains(timezone))
                .click();

        // The select button should include the GMT offset in the summary
        BySelector summarySelector = By.res("android:id/summary");
        UiObject2 selectedTimeZone = selectTimeZone.findObject(summarySelector);
        assertUiObjectFound(selectedTimeZone, summarySelector);
        assertTrue("Expect " + expectedTimeZoneOffset + " is shown for " + timezone,
                selectedTimeZone.getText().startsWith(expectedTimeZoneOffset));

        waitAndAssertTimeGetDefault(expectedTimeZoneId);
        assertEquals("Time zone change in Settings should update persist.sys.timezone",
                expectedTimeZoneId, SystemProperties.get("persist.sys.timezone"));
    }

    private static final long CHECK_DEFAULT_TIMEZONE_INTERVAL = 200L;
    private static final long CHECK_DEFAULT_TIMEZONE_TIMEOUT = 3000L;

    /**
     * Wait for the broadcast ACTION_TIMEZONE_CHANGED propagated, and update the default TimeZone
     * by ApplicationThread.
     */
    private static void waitAndAssertTimeGetDefault(String expectedTimeZoneId)
            throws InterruptedException {
        for (int i = 0; i < CHECK_DEFAULT_TIMEZONE_TIMEOUT / CHECK_DEFAULT_TIMEZONE_INTERVAL; i++) {
            if (expectedTimeZoneId.equals(TimeZone.getDefault().getID())) {
                return;
            }
            Thread.sleep(CHECK_DEFAULT_TIMEZONE_INTERVAL);
        }

        assertEquals(expectedTimeZoneId, TimeZone.getDefault().getID());
    }

    /**
     * Perform click on {@link Spinner} and return the pop-up dropdown list.
     * @return UiScrollable representing the pop-up dropdown after clicking on the spinner
     */
    private UiScrollable selectDropDownInSpinner(BySelector spinnerSelector)
            throws UiObjectNotFoundException {
        UiObject2 spinner = wait(spinnerSelector);
        spinner.click();

        UiSelector dropDownSelector = new UiSelector().className(ListView.class);
        return new UiScrollable(dropDownSelector);
    }

    private UiScrollable selectTimeZoneList() {
        return new UiScrollable(new UiSelector().resourceId(SETTINGS_PACKAGE + ":id/tz_list"));
    }

    /**
     * Select the child object in the UiScrollable
     * @throws UiObjectNotFoundException if scrollable or child is not found
     */
    private UiObject selectScrollableItem(UiScrollable scrollable, UiSelector childSelector)
            throws UiObjectNotFoundException {
        if (!scrollable.waitForExists(TIMEOUT)) {
            throw newUiObjectNotFoundException(scrollable.getSelector());
        }
        scrollable.scrollIntoView(childSelector);

        UiObject child = mDevice.findObject(childSelector);
        assertUiObjectFound(child, childSelector);
        return child;
    }

    /**
     * @throws UiObjectNotFoundException if UiDevice.wait returns null
     */
    private UiObject2 wait(BySelector selector) throws UiObjectNotFoundException {
        UiObject2 item = mDevice.wait(Until.findObject(selector), TIMEOUT);
        assertUiObjectFound(item, selector);
        return item;
    }

    private static void assertUiObjectFound(UiObject2 obj, BySelector selector)
            throws UiObjectNotFoundException {
        if (obj == null) {
            throw newUiObjectNotFoundException(selector);
        }
    }


    private static void assertUiObjectFound(UiObject obj, UiSelector selector)
            throws UiObjectNotFoundException {
        if (obj == null) {
            throw newUiObjectNotFoundException(selector);
        }
    }

    private static UiObjectNotFoundException newUiObjectNotFoundException(Object selector) {
        return new UiObjectNotFoundException(
                String.format("UI object not found: %s", selector.toString()));
    }
}