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 androidx.test.uiautomator;
18 
19 import static org.junit.Assert.assertEquals;
20 import static org.junit.Assert.assertFalse;
21 import static org.junit.Assert.assertNotNull;
22 import static org.junit.Assert.assertThrows;
23 import static org.junit.Assert.assertTrue;
24 import static org.mockito.ArgumentMatchers.anyInt;
25 import static org.mockito.ArgumentMatchers.anyLong;
26 import static org.mockito.ArgumentMatchers.eq;
27 import static org.mockito.Mockito.atLeastOnce;
28 import static org.mockito.Mockito.doReturn;
29 import static org.mockito.Mockito.never;
30 import static org.mockito.Mockito.spy;
31 import static org.mockito.Mockito.times;
32 import static org.mockito.Mockito.verify;
33 
34 import android.app.Instrumentation;
35 import android.app.UiAutomation;
36 import android.content.ContentResolver;
37 import android.graphics.Bitmap;
38 import android.graphics.BitmapFactory;
39 import android.graphics.Point;
40 import android.os.Build;
41 import android.provider.Settings;
42 import android.util.DisplayMetrics;
43 
44 import androidx.test.core.app.ApplicationProvider;
45 import androidx.test.filters.SdkSuppress;
46 import androidx.test.platform.app.InstrumentationRegistry;
47 
48 import org.junit.After;
49 import org.junit.Before;
50 import org.junit.Rule;
51 import org.junit.Test;
52 import org.junit.rules.TemporaryFolder;
53 
54 import java.io.File;
55 import java.io.IOException;
56 import java.util.regex.Matcher;
57 import java.util.regex.Pattern;
58 
59 public class UiDeviceTest {
60 
61     private static final String WATCHER_NAME = "test_watcher";
62 
63     @Rule
64     public TemporaryFolder mTmpDir = new TemporaryFolder();
65 
66     private Instrumentation mInstrumentation;
67     private UiAutomation mUiAutomation;
68     private UiDevice mDevice;
69     private int mDefaultFlags;
70 
71     @Before
setUp()72     public void setUp() {
73         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
74             // Configure spies on API 28+ (min version supported by dexmaker-mockito-inline).
75             mInstrumentation = spy(InstrumentationRegistry.getInstrumentation());
76             mUiAutomation = spy(InstrumentationRegistry.getInstrumentation().getUiAutomation());
77             doReturn(mUiAutomation).when(mInstrumentation).getUiAutomation(anyInt());
78             mDevice = new UiDevice(mInstrumentation);
79         } else {
80             mDevice = new UiDevice(InstrumentationRegistry.getInstrumentation());
81         }
82         mDefaultFlags = Configurator.getInstance().getUiAutomationFlags();
83     }
84 
85     @After
tearDown()86     public void tearDown() {
87         Configurator.getInstance().setUiAutomationFlags(mDefaultFlags);
88     }
89 
90     @Test
testGetDisplayMetrics()91     public void testGetDisplayMetrics() throws IOException {
92         String densityCmdOutput = mDevice.executeShellCommand("wm density");
93         Pattern densityPattern = Pattern.compile("^Physical\\sdensity:\\s(\\d+)\\D+.*");
94         Matcher densityMatcher = densityPattern.matcher(densityCmdOutput);
95         assertTrue(densityMatcher.find());
96         String densityDpi = densityMatcher.group(1);
97         assertNotNull(densityDpi);
98         float density = Float.parseFloat(densityDpi) / DisplayMetrics.DENSITY_DEFAULT;
99 
100         try {
101             int width = 800;
102             int height = 400;
103             mDevice.executeShellCommand(String.format("wm size %dx%d", width, height));
104 
105             Point expectedSizeDp = new Point(Math.round(width / density),
106                     Math.round(height / density));
107 
108             assertEquals(width, mDevice.getDisplayWidth());
109             assertEquals(height, mDevice.getDisplayHeight());
110             assertEquals(expectedSizeDp, mDevice.getDisplaySizeDp());
111         } finally {
112             mDevice.executeShellCommand("wm size reset");
113         }
114     }
115 
116     @Test
testGetProductName()117     public void testGetProductName() {
118         assertEquals(Build.PRODUCT, mDevice.getProductName());
119     }
120 
121     @Test
testRegisterAndRunUiWatcher_conditionMet()122     public void testRegisterAndRunUiWatcher_conditionMet() {
123         // The watcher will return true when its watching condition is met.
124         UiWatcher watcher = () -> true;
125         mDevice.registerWatcher(WATCHER_NAME, watcher);
126 
127         assertFalse(mDevice.hasWatcherTriggered(WATCHER_NAME));
128         assertFalse(mDevice.hasAnyWatcherTriggered());
129         mDevice.runWatchers();
130         assertTrue(mDevice.hasWatcherTriggered(WATCHER_NAME));
131         assertTrue(mDevice.hasAnyWatcherTriggered());
132     }
133 
134     @Test
testRegisterAndRunUiWatcher_conditionNotMet()135     public void testRegisterAndRunUiWatcher_conditionNotMet() {
136         UiWatcher watcher = () -> false;
137         mDevice.registerWatcher(WATCHER_NAME, watcher);
138 
139         assertFalse(mDevice.hasWatcherTriggered(WATCHER_NAME));
140         assertFalse(mDevice.hasAnyWatcherTriggered());
141         mDevice.runWatchers();
142         assertFalse(mDevice.hasWatcherTriggered(WATCHER_NAME));
143         assertFalse(mDevice.hasAnyWatcherTriggered());
144     }
145 
146     @Test
testResetUiWatcher()147     public void testResetUiWatcher() {
148         UiWatcher watcher = () -> true;
149         mDevice.registerWatcher(WATCHER_NAME, watcher);
150         mDevice.runWatchers();
151 
152         assertTrue(mDevice.hasWatcherTriggered(WATCHER_NAME));
153         assertTrue(mDevice.hasAnyWatcherTriggered());
154         mDevice.resetWatcherTriggers();
155         assertFalse(mDevice.hasWatcherTriggered(WATCHER_NAME));
156         assertFalse(mDevice.hasAnyWatcherTriggered());
157     }
158 
159     @Test
testRemoveUiWatcher()160     public void testRemoveUiWatcher() {
161         UiWatcher watcher = () -> true;
162         mDevice.registerWatcher(WATCHER_NAME, watcher);
163         mDevice.removeWatcher(WATCHER_NAME);
164         mDevice.runWatchers();
165 
166         assertFalse(mDevice.hasWatcherTriggered(WATCHER_NAME));
167         assertFalse(mDevice.hasAnyWatcherTriggered());
168     }
169 
170     @Test
testFreezeAndUnfreezeRotation()171     public void testFreezeAndUnfreezeRotation() throws Exception {
172         ContentResolver resolver = ApplicationProvider.getApplicationContext().getContentResolver();
173 
174         mDevice.freezeRotation();
175         // The value of `ACCELEROMETER_ROTATION` will be 0 if the accelerometer is NOT used for
176         // detecting rotation, and 1 otherwise.
177         assertEquals(0,
178                 Settings.System.getInt(resolver, Settings.System.ACCELEROMETER_ROTATION, 0));
179 
180         mDevice.unfreezeRotation();
181         assertEquals(1,
182                 Settings.System.getInt(resolver, Settings.System.ACCELEROMETER_ROTATION, 0));
183     }
184 
185     @Test
186     @SdkSuppress(minSdkVersion = Build.VERSION_CODES.P)
testGetUiAutomation_withDefaultFlags()187     public void testGetUiAutomation_withDefaultFlags() {
188         mDevice.getUiAutomation();
189         // Verify that the UiAutomation instance was obtained with default flags (N+).
190         verify(mInstrumentation, atLeastOnce()).getUiAutomation(eq(mDefaultFlags));
191     }
192 
193     @Test
194     @SdkSuppress(minSdkVersion = Build.VERSION_CODES.P)
testGetUiAutomation_withCustomFlags()195     public void testGetUiAutomation_withCustomFlags() {
196         int customFlags = 5;
197         Configurator.getInstance().setUiAutomationFlags(customFlags);
198         mDevice.getUiAutomation();
199         // Verify that the UiAutomation instance was obtained with custom flags (N+).
200         verify(mInstrumentation, atLeastOnce()).getUiAutomation(eq(customFlags));
201     }
202 
203     @Test
testExecuteShellCommand()204     public void testExecuteShellCommand() throws IOException {
205         String output = mDevice.executeShellCommand("pm list packages");
206         assertTrue(output.contains("package:androidx.test.uiautomator.test"));
207     }
208 
209     @Test
testTakeScreenshot()210     public void testTakeScreenshot() throws Exception {
211         File outFile = mTmpDir.newFile();
212         assertTrue(mDevice.takeScreenshot(outFile));
213         // Verify that a valid screenshot was generated with default scale.
214         Bitmap screenshot = BitmapFactory.decodeFile(outFile.getPath());
215         assertNotNull(screenshot);
216         assertEquals(mDevice.getDisplayWidth(), screenshot.getWidth());
217         assertEquals(mDevice.getDisplayHeight(), screenshot.getHeight());
218     }
219 
220     @Test
testTakeScreenshot_scaled()221     public void testTakeScreenshot_scaled() throws Exception {
222         File outFile = mTmpDir.newFile();
223         assertTrue(mDevice.takeScreenshot(outFile, 0.5f, 100));
224         // Verify that a valid screenshot was generated with 1/2 scale.
225         Bitmap screenshot = BitmapFactory.decodeFile(outFile.getPath());
226         assertNotNull(screenshot);
227         assertEquals(mDevice.getDisplayWidth() / 2, screenshot.getWidth());
228         assertEquals(mDevice.getDisplayHeight() / 2, screenshot.getHeight());
229     }
230 
231     @Test
testInaccessibleDisplay()232     public void testInaccessibleDisplay() {
233         assertThrows(IllegalArgumentException.class, () -> mDevice.getDisplayRotation(1000));
234         assertThrows(IllegalArgumentException.class, () -> mDevice.getDisplaySize(1000));
235     }
236 
237     @Test
238     @SdkSuppress(minSdkVersion = Build.VERSION_CODES.P)
testWaitForIdle()239     public void testWaitForIdle() throws Exception {
240         mDevice.waitForIdle();
241         long defaultTimeout = Configurator.getInstance().getWaitForIdleTimeout();
242         verify(mUiAutomation, times(1)).waitForIdle(anyLong(), eq(defaultTimeout));
243 
244         mDevice.waitForIdle(123L);
245         verify(mUiAutomation, times(1)).waitForIdle(anyLong(), eq(123L));
246     }
247 
248     @Test
249     @SdkSuppress(minSdkVersion = Build.VERSION_CODES.P)
testWaitForIdle_watcher()250     public void testWaitForIdle_watcher() throws Exception {
251         UiWatcher watcher = () -> {
252             mDevice.waitForIdle();
253             return true;
254         };
255         mDevice.registerWatcher(WATCHER_NAME, watcher);
256         mDevice.runWatchers();
257 
258         // Wait for idle skipped during watcher execution.
259         verify(mUiAutomation, never()).waitForIdle(anyLong(), anyLong());
260     }
261 }
262