1 /* 2 * Copyright (C) 2018 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 com.android.settings.ui; 18 19 import android.content.Intent; 20 import android.os.SystemClock; 21 import android.os.storage.DiskInfo; 22 import android.os.storage.VolumeInfo; 23 import android.support.test.InstrumentationRegistry; 24 import android.support.test.runner.AndroidJUnit4; 25 import android.support.test.uiautomator.By; 26 import android.support.test.uiautomator.BySelector; 27 import android.support.test.uiautomator.UiDevice; 28 import android.support.test.uiautomator.UiObject2; 29 import android.support.test.uiautomator.UiObjectNotFoundException; 30 import android.support.test.uiautomator.Until; 31 32 import org.junit.After; 33 import org.junit.Before; 34 import org.junit.Test; 35 import org.junit.runner.RunWith; 36 37 import java.io.IOException; 38 import java.util.regex.Pattern; 39 40 /** 41 * Verify storage wizard flows. Temporarily enables a virtual disk which enables 42 * testing on all devices, regardless of physical SD card support. 43 */ 44 @RunWith(AndroidJUnit4.class) 45 public class StorageWizardTest { 46 private static final String ANDROID_PACKAGE = "android"; 47 private static final String PACKAGE = "com.android.settings"; 48 private static final int TIMEOUT = 5000; 49 private static final int TIMEOUT_LONG = 30000; 50 51 private UiDevice mDevice; 52 53 private String mDisk; 54 private String mVolume; 55 56 @Before 57 public void setUp() throws Exception { 58 mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()); 59 mDevice.executeShellCommand("setprop sys.debug.storage_slow 1"); 60 mDevice.executeShellCommand("sm set-virtual-disk true"); 61 62 mDisk = getAdoptableDisk(); 63 mDevice.executeShellCommand("sm partition " + mDisk + " public"); 64 mVolume = getPublicVolume(); 65 } 66 67 @After 68 public void tearDown() throws Exception { 69 // Go back to home for next test. 70 mDevice.pressBack(); 71 mDevice.pressBack(); 72 mDevice.pressHome(); 73 mDevice.waitForIdle(TIMEOUT); 74 75 mDevice.executeShellCommand("setprop sys.debug.storage_slow 0"); 76 mDevice.executeShellCommand("sm set-virtual-disk false"); 77 mDevice.executeShellCommand("sm forget all"); 78 } 79 80 /** 81 * Test flow for adopting a storage device as internal/adopted. 82 */ 83 @Test 84 public void testInternal() throws Exception { 85 InstrumentationRegistry.getContext().startActivity(buildInitIntent()); 86 87 // Activity: pick option to use as internal 88 waitFor(By.res(PACKAGE, "suw_layout_title").text(containsIgnoringCase("How will you use"))); 89 waitFor(By.res(PACKAGE, "storage_wizard_init_internal")).click(); 90 91 // Dialog: acknowledge that we're formatting the card 92 waitFor(By.res(ANDROID_PACKAGE, "alertTitle").textContains("Format")); 93 waitFor(By.clickable(true).text(containsIgnoringCase("Format"))).click(); 94 95 // Activity: ack storage device is slow 96 waitForLong(By.res(PACKAGE, "suw_layout_title").textContains("Slow")); 97 waitFor(By.res(PACKAGE, "storage_next_button")).click(); 98 99 // Activity: choose to move content 100 waitForLong(By.res(PACKAGE, "suw_layout_title").textContains("Move content")); 101 waitFor(By.res(PACKAGE, "storage_next_button")).click(); 102 103 // Activity: yay, we're done! 104 waitForLong(By.res(PACKAGE, "suw_layout_title").textContains("ready to use")); 105 waitFor(By.res(PACKAGE, "storage_next_button")).click(); 106 } 107 108 /** 109 * Test flow for adopting a storage device as external/portable. 110 */ 111 @Test 112 public void testExternal() throws Exception { 113 InstrumentationRegistry.getContext().startActivity(buildInitIntent()); 114 115 // Activity: pick option to use as external 116 waitFor(By.res(PACKAGE, "suw_layout_title").textContains("How will you use")); 117 waitFor(By.res(PACKAGE, "storage_wizard_init_external")).click(); 118 119 // Activity: yay, we're done! 120 waitFor(By.res(PACKAGE, "suw_layout_title").textContains("ready to use")); 121 waitFor(By.res(PACKAGE, "storage_next_button")).click(); 122 } 123 124 private UiObject2 waitFor(BySelector selector) throws UiObjectNotFoundException { 125 return waitFor(selector, TIMEOUT); 126 } 127 128 private UiObject2 waitForLong(BySelector selector) throws UiObjectNotFoundException { 129 return waitFor(selector, TIMEOUT_LONG); 130 } 131 132 private UiObject2 waitFor(BySelector selector, long timeout) throws UiObjectNotFoundException { 133 final UiObject2 item = mDevice.wait(Until.findObject(selector), timeout); 134 if (item != null) { 135 return item; 136 } else { 137 throw new UiObjectNotFoundException(selector.toString()); 138 } 139 } 140 141 /** 142 * Shamelessly borrowed from AdoptableHostTest in CTS. 143 */ 144 private String getAdoptableDisk() throws IOException { 145 // In the case where we run multiple test we cleanup the state of the device. This 146 // results in the execution of sm forget all which causes the MountService to "reset" 147 // all its knowledge about available drives. This can cause the adoptable drive to 148 // become temporarily unavailable. 149 int attempt = 0; 150 String disks = mDevice.executeShellCommand("sm list-disks adoptable"); 151 while ((disks == null || disks.isEmpty()) && attempt++ < 15) { 152 SystemClock.sleep(1000); 153 disks = mDevice.executeShellCommand("sm list-disks adoptable"); 154 } 155 156 if (disks == null || disks.isEmpty()) { 157 throw new AssertionError("Devices that claim to support adoptable storage must have " 158 + "adoptable media inserted during CTS to verify correct behavior"); 159 } 160 return disks.split("\n")[0].trim(); 161 } 162 163 private String getPublicVolume() throws IOException { 164 int attempt = 0; 165 String volumes = mDevice.executeShellCommand("sm list-volumes public"); 166 while ((volumes == null || volumes.isEmpty() || !volumes.contains("mounted")) 167 && attempt++ < 15) { 168 SystemClock.sleep(1000); 169 volumes = mDevice.executeShellCommand("sm list-volumes public"); 170 } 171 172 if (volumes == null || volumes.isEmpty()) { 173 throw new AssertionError("Devices that claim to support adoptable storage must have " 174 + "adoptable media inserted during CTS to verify correct behavior"); 175 } 176 return volumes.split("[\n ]")[0].trim(); 177 } 178 179 private Intent buildInitIntent() { 180 final Intent intent = new Intent().setClassName(PACKAGE, 181 PACKAGE + ".deviceinfo.StorageWizardInit"); 182 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 183 intent.putExtra(DiskInfo.EXTRA_DISK_ID, mDisk); 184 intent.putExtra(VolumeInfo.EXTRA_VOLUME_ID, mVolume); 185 return intent; 186 } 187 188 private Pattern containsIgnoringCase(String text) { 189 return Pattern.compile("(?i)^.*" + text + ".*$"); 190 } 191 } 192