1 /* 2 * Copyright (C) 2016 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.documentsui; 18 19 import static java.util.Objects.requireNonNull; 20 21 import android.app.Activity; 22 import android.app.UiAutomation; 23 import android.app.UiModeManager; 24 import android.content.ContentProviderClient; 25 import android.content.ContentResolver; 26 import android.content.Context; 27 import android.content.Intent; 28 import android.content.res.Configuration; 29 import android.os.Bundle; 30 import android.os.RemoteException; 31 import android.provider.DocumentsContract; 32 import android.provider.DocumentsContract.Document; 33 import android.test.ActivityInstrumentationTestCase2; 34 import android.view.KeyEvent; 35 import android.view.MotionEvent; 36 37 import androidx.test.uiautomator.Configurator; 38 import androidx.test.uiautomator.UiDevice; 39 import androidx.test.uiautomator.UiObjectNotFoundException; 40 41 import com.android.documentsui.base.Features; 42 import com.android.documentsui.base.RootInfo; 43 import com.android.documentsui.base.UserId; 44 import com.android.documentsui.bots.Bots; 45 import com.android.documentsui.files.FilesActivity; 46 47 import java.io.IOException; 48 49 import javax.annotation.Nullable; 50 51 /** 52 * Provides basic test environment for UI tests: 53 * - Launches activity 54 * - Creates and gives access to test root directories and test files 55 * - Cleans up the test environment 56 */ 57 public abstract class ActivityTest<T extends Activity> extends ActivityInstrumentationTestCase2<T> { 58 59 static final int TIMEOUT = 5000; 60 static final int NIGHT_MODE_CHANGE_WAIT_TIME = 1000; 61 62 // Testing files. For custom ones, override initTestFiles(). 63 public static final String dirName1 = "Dir1"; 64 public static final String childDir1 = "ChildDir1"; 65 public static final String fileName1 = "file1.log"; 66 public static final String fileName2 = "file12.png"; 67 public static final String fileName3 = "anotherFile0.log"; 68 public static final String fileName4 = "poodles.text"; 69 public static final String fileNameNoRename = "NO_RENAMEfile.txt"; 70 71 public Bots bots; 72 public UiDevice device; 73 public Context context; 74 public UserId userId; 75 public UiAutomation automation; 76 77 public Features features; 78 public RootInfo rootDir0; 79 public RootInfo rootDir1; 80 protected ContentResolver mResolver; 81 protected DocumentsProviderHelper mDocsHelper; 82 protected ContentProviderClient mClient; 83 protected UiModeManager mUiModeManager; 84 85 private String initialScreenOffTimeoutValue = null; 86 private String initialSleepTimeoutValue = null; 87 ActivityTest(Class<T> activityClass)88 public ActivityTest(Class<T> activityClass) { 89 super(activityClass); 90 } 91 92 /* 93 * Returns the root that will be opened within the activity. 94 * By default tests are started with one of the test roots. 95 * Override the method if you want to open different root on start. 96 * @return Root that will be opened. Return null if you want to open activity's default root. 97 */ getInitialRoot()98 protected @Nullable RootInfo getInitialRoot() { 99 return rootDir0; 100 } 101 102 /** 103 * Returns the authority of the testing provider begin used. 104 * By default it's StubProvider's authority. 105 * @return Authority of the provider. 106 */ getTestingProviderAuthority()107 protected String getTestingProviderAuthority() { 108 return StubProvider.DEFAULT_AUTHORITY; 109 } 110 111 /** 112 * Resolves testing roots. 113 */ setupTestingRoots()114 protected void setupTestingRoots() throws RemoteException { 115 rootDir0 = mDocsHelper.getRoot(StubProvider.ROOT_0_ID); 116 rootDir1 = mDocsHelper.getRoot(StubProvider.ROOT_1_ID); 117 } 118 119 @Override setUp()120 public void setUp() throws Exception { 121 device = UiDevice.getInstance(getInstrumentation()); 122 // NOTE: Must be the "target" context, else security checks in content provider will fail. 123 context = getInstrumentation().getTargetContext(); 124 userId = UserId.DEFAULT_USER; 125 automation = getInstrumentation().getUiAutomation(); 126 features = new Features.RuntimeFeatures(context.getResources(), null); 127 128 bots = new Bots(device, automation, context, TIMEOUT); 129 130 Configurator.getInstance().setToolType(MotionEvent.TOOL_TYPE_MOUSE); 131 132 mResolver = context.getContentResolver(); 133 mDocsHelper = new DocumentsProviderHelper(userId, getTestingProviderAuthority(), context, 134 getTestingProviderAuthority()); 135 136 device.setOrientationNatural(); 137 device.pressKeyCode(KeyEvent.KEYCODE_WAKEUP); 138 device.pressKeyCode(KeyEvent.KEYCODE_MENU); 139 140 disableScreenOffAndSleepTimeouts(); 141 142 setupTestingRoots(); 143 144 launchActivity(); 145 resetStorage(); 146 147 // Since at the launch of activity, ROOT_0 and ROOT_1 have no files, drawer will 148 // automatically open for phone devices. Espresso register click() as (x, y) MotionEvents, 149 // so if a drawer is on top of a file we want to select, it will actually click the drawer. 150 // Thus to start a clean state, we always try to close first. 151 bots.roots.closeDrawer(); 152 153 // Configure the provider back to default. 154 mDocsHelper.configure(null, Bundle.EMPTY); 155 } 156 157 @Override tearDown()158 public void tearDown() throws Exception { 159 device.unfreezeRotation(); 160 mDocsHelper.cleanUp(); 161 restoreScreenOffAndSleepTimeouts(); 162 super.tearDown(); 163 } 164 launchActivity()165 protected void launchActivity() { 166 final Intent intent = new Intent(context, FilesActivity.class); 167 intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK); 168 if (getInitialRoot() != null) { 169 intent.setAction(Intent.ACTION_VIEW); 170 intent.setDataAndType(getInitialRoot().getUri(), DocumentsContract.Root.MIME_TYPE_ITEM); 171 } 172 setActivityIntent(intent); 173 getActivity(); // Launch the activity. 174 } 175 resetStorage()176 protected void resetStorage() throws RemoteException { 177 mDocsHelper.clear(null, null); 178 device.waitForIdle(); 179 } 180 initTestFiles()181 protected void initTestFiles() throws RemoteException { 182 mDocsHelper.createFolder(rootDir0, dirName1); 183 mDocsHelper.createDocument(rootDir0, "text/plain", fileName1); 184 mDocsHelper.createDocument(rootDir0, "image/png", fileName2); 185 mDocsHelper.createDocumentWithFlags(rootDir0.documentId, "text/plain", fileNameNoRename, 186 Document.FLAG_SUPPORTS_WRITE); 187 188 mDocsHelper.createDocument(rootDir1, "text/plain", fileName3); 189 mDocsHelper.createDocument(rootDir1, "text/plain", fileName4); 190 } 191 assertDefaultContentOfTestDir0()192 void assertDefaultContentOfTestDir0() throws UiObjectNotFoundException { 193 bots.directory.waitForDocument(fileName1); 194 bots.directory.waitForDocument(fileName2); 195 bots.directory.waitForDocument(dirName1); 196 bots.directory.waitForDocument(fileNameNoRename); 197 bots.directory.assertDocumentsCount(4); 198 } 199 assertDefaultContentOfTestDir1()200 void assertDefaultContentOfTestDir1() throws UiObjectNotFoundException { 201 bots.directory.waitForDocument(fileName3); 202 bots.directory.waitForDocument(fileName4); 203 bots.directory.assertDocumentsCount(2); 204 } 205 206 /** 207 * Setup test Activity UI Mode YES or not(AUTO/YES/NO) before start to testing 208 * @param uiModeNight Constant for {@link #setNightMode(int)} 209 * 0 - MODE_NIGHT_AUTO 210 * 1 - MODE_NIGHT_NO 211 * 2 - MODE_NIGHT_YES 212 */ setSystemUiModeNight(int uiModeNight)213 protected void setSystemUiModeNight(int uiModeNight) { 214 int systemUiMode = getActivity().getResources().getConfiguration().uiMode 215 & Configuration.UI_MODE_NIGHT_MASK; 216 if(uiModeNight != systemUiMode) { 217 /* TODO since ag/4947691 enable config_lockDayNightMode to block app setNightMode() 218 create b/115315612 to handle the UiModeManager permission deny problem */ 219 mUiModeManager = (UiModeManager) getActivity() 220 .getSystemService(Context.UI_MODE_SERVICE); 221 mUiModeManager.setNightMode(uiModeNight); 222 device.waitForIdle(NIGHT_MODE_CHANGE_WAIT_TIME); 223 } 224 } 225 disableScreenOffAndSleepTimeouts()226 private void disableScreenOffAndSleepTimeouts() throws IOException { 227 initialScreenOffTimeoutValue = device.executeShellCommand( 228 "settings get system screen_off_timeout"); 229 initialSleepTimeoutValue = device.executeShellCommand( 230 "settings get secure sleep_timeout"); 231 device.executeShellCommand("settings put system screen_off_timeout -1"); 232 device.executeShellCommand("settings put secure sleep_timeout -1"); 233 } 234 restoreScreenOffAndSleepTimeouts()235 private void restoreScreenOffAndSleepTimeouts() throws IOException { 236 requireNonNull(initialScreenOffTimeoutValue); 237 requireNonNull(initialSleepTimeoutValue); 238 try { 239 device.executeShellCommand( 240 "settings put system screen_off_timeout " + initialScreenOffTimeoutValue); 241 device.executeShellCommand( 242 "settings put secure sleep_timeout " + initialSleepTimeoutValue); 243 } finally { 244 initialScreenOffTimeoutValue = null; 245 initialSleepTimeoutValue = null; 246 } 247 } 248 } 249