/* * 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 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", true); } @Test public void testSelectPhoenix() throws Exception { testSelectTimeZone("United States", "Phoenix", "GMT-07:00", "America/Phoenix", false); } private void testSelectTimeZone(String region, String timezone, String expectedTimeZoneOffset, String expectedTimeZoneId, boolean assumeOneTimeZoneInRegion) 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(); wait(By.text("Region")).click(); // Speed-up the test by searching with the first 2 characters of the region name wait(By.res("android", "search_src_text")).setText(region.substring(0, 2)); // Select region in the list selectItemInList(new UiSelector().textContains(region)) .click(); // Only select time zone explicitly if there are more than one time zones in a region if (!assumeOneTimeZoneInRegion) { wait(By.text("Time zone")); selectItemInList(new UiSelector().textContains(timezone)) .click(); } mDevice.pressBack(); // 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()); } private UiObject selectItemInList(UiSelector childSelector) throws UiObjectNotFoundException { UiScrollable recyclerView = new UiScrollable( new UiSelector().resourceId(SETTINGS_PACKAGE + ":id/recycler_view")); return selectScrollableItem(recyclerView, childSelector); } /** * 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())); } }