1 /*
2  * Copyright (C) 2015 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.jankmicrobenchmark.janktests;
18 
19 import android.content.Intent;
20 import android.os.Bundle;
21 import android.os.SystemClock;
22 import android.support.test.launcherhelper.LauncherStrategyFactory;
23 import android.support.test.uiautomator.By;
24 import android.support.test.uiautomator.Direction;
25 import android.support.test.uiautomator.UiDevice;
26 import android.support.test.uiautomator.UiObject2;
27 import android.support.test.uiautomator.UiObjectNotFoundException;
28 import android.support.test.uiautomator.Until;
29 import android.widget.Button;
30 
31 import androidx.test.jank.GfxMonitor;
32 import androidx.test.jank.JankTest;
33 import androidx.test.jank.JankTestBase;
34 
35 import junit.framework.Assert;
36 
37 /**
38  * Jank micro benchmark tests
39  * App : ApiDemos
40  */
41 
42 public class ApiDemoJankTests extends JankTestBase {
43     private static final int LONG_TIMEOUT = 5000;
44     private static final int SHORT_TIMEOUT = 500;
45     private static final int INNER_LOOP = 5;
46     private static final int EXPECTED_FRAMES = 100;
47     private static final String PACKAGE_NAME = "com.example.android.apis";
48     private static final String RES_PACKAGE_NAME = "android";
49     private static final String LEANBACK_LAUNCHER = "com.google.android.leanbacklauncher";
50     private UiDevice mDevice;
51     private UiObject2 mListView;
52 
53     @Override
setUp()54     public void setUp() throws Exception {
55         super.setUp();
56         mDevice = UiDevice.getInstance(getInstrumentation());
57         mDevice.setOrientationNatural();
58         LauncherStrategyFactory.getInstance(mDevice).getLauncherStrategy().open();
59     }
60 
61     @Override
tearDown()62     protected void tearDown() throws Exception {
63         mDevice.unfreezeRotation();
64         super.tearDown();
65     }
66 
67     // This method distinguishes between home screen for handheld devices
68     // and home screen for Android TV, both of whom have different Home elements.
getHomeScreen()69     public UiObject2 getHomeScreen() throws UiObjectNotFoundException {
70         if (mDevice.getProductName().equals("fugu")) {
71             return mDevice.wait(Until.findObject(By.res(LEANBACK_LAUNCHER, "main_list_view")),
72                     LONG_TIMEOUT);
73         }
74         else {
75             String launcherPackage = mDevice.getLauncherPackageName();
76             return mDevice.wait(Until.findObject(By.res(launcherPackage,"workspace")),
77                     LONG_TIMEOUT);
78         }
79     }
80 
launchApiDemos()81     public void launchApiDemos() throws UiObjectNotFoundException {
82         UiObject2 homeScreen = getHomeScreen();
83         if (homeScreen == null)
84             navigateToHome();
85         Intent intent = getInstrumentation().getContext().getPackageManager()
86                 .getLaunchIntentForPackage(PACKAGE_NAME);
87         intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
88         getInstrumentation().getContext().startActivity(intent);
89         mDevice.waitForIdle();
90     }
91 
selectAnimation(String optionName)92     public void selectAnimation(String optionName) throws UiObjectNotFoundException {
93         launchApiDemos();
94         UiObject2 animation = mDevice.wait(Until.findObject(
95                 By.res(RES_PACKAGE_NAME, "text1").text("Animation")), LONG_TIMEOUT);
96         Assert.assertNotNull("Animation isn't found in ApiDemos", animation);
97         animation.click();
98         UiObject2 option = mDevice.wait(Until.findObject(
99                 By.res(RES_PACKAGE_NAME, "text1").text(optionName)), LONG_TIMEOUT);
100         int maxAttempt = 3;
101         while (option == null && maxAttempt > 0) {
102             mDevice.wait(Until.findObject(By.res(RES_PACKAGE_NAME, "content")), LONG_TIMEOUT)
103             .scroll(Direction.DOWN, 1.0f);
104             option = mDevice.wait(Until.findObject(By.res(RES_PACKAGE_NAME, "text1")
105                     .text(optionName)), LONG_TIMEOUT);
106             --maxAttempt;
107         }
108         Assert.assertNotNull("Target option in APiDemos animation for test isn't found", option);
109         option.click();
110     }
111 
112     // Since afterTest only runs when the test has passed, there's no way of going
113     // back to the Home Screen if a test fails. This method is a workaround. A feature
114     // request has been filed to have a per test tearDown method - b/25673300
navigateToHome()115     public void navigateToHome() throws UiObjectNotFoundException {
116         UiObject2 homeScreen = getHomeScreen();
117         int count = 0;
118         while (homeScreen == null && count <= 10) {
119             mDevice.pressBack();
120             homeScreen = getHomeScreen();
121             count++;
122         }
123         Assert.assertNotNull("Hit maximum retries and couldn't find Home Screen", homeScreen);
124     }
125 
126     // Since the app doesn't start at the first page when reloaded after the first time,
127     // ensuring that we head back to the first screen before going Home so we're always
128     // on screen one.
goBackHome(Bundle metrics)129     public void goBackHome(Bundle metrics) throws UiObjectNotFoundException {
130         navigateToHome();
131         super.afterTest(metrics);
132     }
133 
134     // Loads the 'activity transition' animation
selectActivityTransitionAnimation()135     public void selectActivityTransitionAnimation() throws UiObjectNotFoundException {
136         selectAnimation("Activity Transition");
137     }
138 
139     // Measures jank for activity transition animation
140     @JankTest(beforeTest="selectActivityTransitionAnimation", afterTest="goBackHome",
141             expectedFrames=EXPECTED_FRAMES)
142     @GfxMonitor(processName=PACKAGE_NAME)
testActivityTransitionAnimation()143     public void testActivityTransitionAnimation() {
144         for (int i = 0; i < INNER_LOOP; i++) {
145             UiObject2 redBallTile = mDevice.wait(Until.findObject(By.res(PACKAGE_NAME, "ball")),
146                     LONG_TIMEOUT);
147             redBallTile.click();
148             SystemClock.sleep(LONG_TIMEOUT);
149             mDevice.pressBack();
150         }
151     }
152 
153     // Loads the 'view flip' animation
selectViewFlipAnimation()154     public void selectViewFlipAnimation() throws UiObjectNotFoundException {
155         selectAnimation("View Flip");
156     }
157 
158     // Measures jank for view flip animation
159     @JankTest(beforeTest="selectViewFlipAnimation", afterTest="goBackHome",
160             expectedFrames=EXPECTED_FRAMES)
161     @GfxMonitor(processName=PACKAGE_NAME)
testViewFlipAnimation()162     public void testViewFlipAnimation() {
163         for (int i = 0; i < INNER_LOOP; i++) {
164             UiObject2 flipButton = mDevice.findObject(By.res(PACKAGE_NAME, "button"));
165             flipButton.click();
166             SystemClock.sleep(LONG_TIMEOUT);
167         }
168     }
169 
170     // Loads the 'cloning' animation
selectCloningAnimation()171     public void selectCloningAnimation() throws UiObjectNotFoundException {
172         selectAnimation("Cloning");
173     }
174 
175     // Measures jank for cloning animation
176     @JankTest(beforeTest="selectCloningAnimation", afterTest="goBackHome",
177             expectedFrames=EXPECTED_FRAMES)
178     @GfxMonitor(processName=PACKAGE_NAME)
testCloningAnimation()179     public void testCloningAnimation() {
180         for (int i = 0; i < INNER_LOOP; i++) {
181             UiObject2 runCloningButton = mDevice.findObject(By.res(PACKAGE_NAME, "startButton"));
182             runCloningButton.click();
183             SystemClock.sleep(LONG_TIMEOUT);
184         }
185     }
186 
187     // Loads the 'loading' animation
selectLoadingOption()188     public void selectLoadingOption() throws UiObjectNotFoundException {
189         selectAnimation("Loading");
190     }
191 
192     // Measures jank for 'loading' animation
193     @JankTest(beforeTest="selectLoadingOption", afterTest="goBackHome",
194             expectedFrames=EXPECTED_FRAMES)
195     @GfxMonitor(processName=PACKAGE_NAME)
testLoadingJank()196     public void testLoadingJank() {
197         UiObject2 runButton = mDevice.wait(Until.findObject(
198                 By.res(PACKAGE_NAME, "startButton").text("RUN")), LONG_TIMEOUT);
199         Assert.assertNotNull("Run button is null", runButton);
200         for (int i = 0; i < INNER_LOOP; i++) {
201             runButton.click();
202             SystemClock.sleep(SHORT_TIMEOUT * 2);
203         }
204     }
205 
206     // Loads the 'simple transition' animation
selectSimpleTransitionOption()207     public void selectSimpleTransitionOption() throws UiObjectNotFoundException {
208         selectAnimation("Simple Transitions");
209     }
210 
211     // Measures jank for 'simple transition' animation
212     @JankTest(beforeTest="selectSimpleTransitionOption", afterTest="goBackHome",
213             expectedFrames=EXPECTED_FRAMES)
214     @GfxMonitor(processName=PACKAGE_NAME)
testSimpleTransitionJank()215     public void testSimpleTransitionJank() {
216         for (int i = 0; i < INNER_LOOP; i++) {
217             UiObject2 scene2 = mDevice.wait(Until.findObject(
218                     By.res(PACKAGE_NAME, "scene2")), LONG_TIMEOUT);
219             Assert.assertNotNull("Scene2 button can't be found", scene2);
220             scene2.click();
221             SystemClock.sleep(SHORT_TIMEOUT);
222 
223             UiObject2 scene1 = mDevice.wait(Until.findObject(
224                     By.res(PACKAGE_NAME, "scene1")), LONG_TIMEOUT);
225             Assert.assertNotNull("Scene1 button can't be found", scene1);
226             scene1.click();
227             SystemClock.sleep(SHORT_TIMEOUT);
228         }
229     }
230 
231     // Loads the 'hide/show' animation
selectHideShowAnimationOption()232     public void selectHideShowAnimationOption() throws UiObjectNotFoundException {
233         selectAnimation("Hide-Show Animations");
234     }
235 
236     // Measures jank for 'hide/show' animation
237     @JankTest(beforeTest="selectHideShowAnimationOption", afterTest="goBackHome",
238             expectedFrames=EXPECTED_FRAMES)
239     @GfxMonitor(processName=PACKAGE_NAME)
testHideShowAnimationJank()240     public void testHideShowAnimationJank() {
241         for (int i = 0; i < INNER_LOOP; i++) {
242             UiObject2 showButton = mDevice.wait(Until.findObject(By.res(
243                     PACKAGE_NAME, "addNewButton").text("SHOW BUTTONS")), LONG_TIMEOUT);
244             Assert.assertNotNull("'Show Buttons' button can't be found", showButton);
245             showButton.click();
246             SystemClock.sleep(SHORT_TIMEOUT);
247 
248             UiObject2 button0 = mDevice.wait(Until.findObject(
249                     By.clazz(Button.class).text("0")), LONG_TIMEOUT);
250             Assert.assertNotNull("Button0 isn't found", button0);
251             button0.click();
252             SystemClock.sleep(SHORT_TIMEOUT);
253 
254             UiObject2 button1 = mDevice.wait(Until.findObject(
255                     By.clazz(Button.class).text("1")), LONG_TIMEOUT);
256             Assert.assertNotNull("Button1 isn't found", button1);
257             button1.click();
258             SystemClock.sleep(SHORT_TIMEOUT);
259 
260             UiObject2 button2 = mDevice.wait(Until.findObject(
261                     By.clazz(Button.class).text("2")), LONG_TIMEOUT);
262             Assert.assertNotNull("Button2 isn't found", button2);
263             button2.click();
264             SystemClock.sleep(SHORT_TIMEOUT);
265 
266             UiObject2 button3 = mDevice.wait(Until.findObject(
267                     By.clazz(Button.class).text("3")), LONG_TIMEOUT);
268             Assert.assertNotNull("Button3 isn't found", button3);
269             button3.click();
270             SystemClock.sleep(SHORT_TIMEOUT);
271         }
272     }
273 
selectViews(String optionName)274     public void selectViews(String optionName) throws UiObjectNotFoundException {
275         launchApiDemos();
276         UiObject2 views = null;
277         short maxAttempt = 4;
278         while (views == null && maxAttempt > 0) {
279             views = mDevice.wait(Until.findObject(By.res(RES_PACKAGE_NAME, "text1")
280                     .text("Views")), LONG_TIMEOUT);
281             if (views == null) {
282                 mDevice.wait(Until.findObject(By.res(RES_PACKAGE_NAME, "content")), LONG_TIMEOUT)
283                 .scroll(Direction.DOWN, 1.0f);
284             }
285             --maxAttempt;
286         }
287         Assert.assertNotNull("Views item can't be found", views);
288         views.click();
289         // Opens selective view (provided as param) from different 'ApiDemos Views' options
290         UiObject2 option = null;
291         maxAttempt = 4;
292         while (option == null && maxAttempt > 0) {
293             option = mDevice.wait(Until.findObject(By.res(RES_PACKAGE_NAME, "text1")
294                     .text(optionName)), LONG_TIMEOUT);
295             if (option == null) {
296                 mDevice.wait(Until.findObject(By.res(RES_PACKAGE_NAME, "content")), LONG_TIMEOUT)
297                 .scroll(Direction.DOWN, 1.0f);
298             }
299             --maxAttempt;
300         }
301         Assert.assertNotNull("Target option to be tested in ApiDemos Views can't be found", option);
302         option.click();
303     }
304 
305     // Loads  simple listview
selectListsArray()306     public void selectListsArray() throws UiObjectNotFoundException {
307         selectViews("Lists");
308         UiObject2 array = mDevice.wait(Until.findObject(
309                 By.res(RES_PACKAGE_NAME, "text1").text("01. Array")), LONG_TIMEOUT);
310         Assert.assertNotNull("Array listview can't be found", array);
311         array.click();
312         mListView = mDevice.wait(Until.findObject(By.res(
313                    RES_PACKAGE_NAME, "content")), LONG_TIMEOUT);
314         Assert.assertNotNull("Content pane isn't found to move up", mListView);
315     }
316 
317     // Measures jank for simple listview fling
318     @JankTest(beforeTest="selectListsArray", afterTest="goBackHome",
319             expectedFrames=EXPECTED_FRAMES)
320     @GfxMonitor(processName=PACKAGE_NAME)
testListViewJank()321     public void testListViewJank() {
322         for (int i = 0; i < INNER_LOOP; i++) {
323             mListView.fling(Direction.DOWN);
324             SystemClock.sleep(SHORT_TIMEOUT);
325             mListView.fling(Direction.UP);
326             SystemClock.sleep(SHORT_TIMEOUT);
327         }
328     }
329 
330     // Loads simple expandable list view
selectExpandableListsSimpleAdapter()331     public void selectExpandableListsSimpleAdapter() throws UiObjectNotFoundException {
332         selectViews("Expandable Lists");
333         UiObject2 simpleAdapter = mDevice.wait(Until.findObject(
334                 By.res(RES_PACKAGE_NAME, "text1").text("3. Simple Adapter")), LONG_TIMEOUT);
335         Assert.assertNotNull("Simple adapter can't be found", simpleAdapter);
336         simpleAdapter.click();
337     }
338 
339     // Measures jank for simple expandable list view expansion
340     // Expansion group1, group3 and group4 arbitrarily selected
341     @JankTest(beforeTest="selectExpandableListsSimpleAdapter", afterTest="goBackHome",
342             expectedFrames=EXPECTED_FRAMES)
343     @GfxMonitor(processName=PACKAGE_NAME)
testExapandableListViewJank()344     public void testExapandableListViewJank() {
345         for (int i = 0; i < INNER_LOOP; i++) {
346             UiObject2 group1 = mDevice.wait(Until.findObject(By.res(
347                     RES_PACKAGE_NAME, "text1").text("Group 1")), LONG_TIMEOUT);
348             Assert.assertNotNull("Group 1 isn't found to be expanded", group1);
349             group1.click();
350             SystemClock.sleep(SHORT_TIMEOUT);
351             group1.click();
352             SystemClock.sleep(SHORT_TIMEOUT);
353             UiObject2 group3 = mDevice.wait(Until.findObject(By.res(
354                     RES_PACKAGE_NAME, "text1").text("Group 3")), LONG_TIMEOUT);
355             Assert.assertNotNull("Group 3 isn't found to be expanded", group3);
356             group3.click();
357             SystemClock.sleep(SHORT_TIMEOUT);
358             group3.click();
359             SystemClock.sleep(SHORT_TIMEOUT);
360             UiObject2 group4 = mDevice.wait(Until.findObject(By.res(
361                     RES_PACKAGE_NAME, "text1").text("Group 4")), LONG_TIMEOUT);
362             Assert.assertNotNull("Group 4 isn't found to be expanded", group4);
363             group4.click();
364             SystemClock.sleep(SHORT_TIMEOUT);
365             group4.click();
366             SystemClock.sleep(SHORT_TIMEOUT);
367             UiObject2 content = mDevice.wait(Until.findObject(By.res(
368                     RES_PACKAGE_NAME, "content")), LONG_TIMEOUT);
369             Assert.assertNotNull("Content pane isn't found to move up", content);
370             content.fling(Direction.UP);
371             SystemClock.sleep(SHORT_TIMEOUT);
372         }
373     }
374 }
375