1 /*
2  * Copyright (C) 2020 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 package com.android.settings.tests.perf;
17 
18 import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
19 
20 import static junit.framework.TestCase.fail;
21 
22 import android.app.Instrumentation;
23 import android.os.Bundle;
24 import android.util.Log;
25 import android.support.test.uiautomator.By;
26 import android.support.test.uiautomator.UiDevice;
27 import android.support.test.uiautomator.UiSelector;
28 import android.support.test.uiautomator.Until;
29 
30 import androidx.test.InstrumentationRegistry;
31 import androidx.test.runner.AndroidJUnit4;
32 
33 import org.junit.After;
34 import org.junit.Before;
35 import org.junit.Test;
36 import org.junit.runner.RunWith;
37 
38 import java.util.ArrayList;
39 import java.util.LinkedHashMap;
40 import java.util.Map;
41 import java.util.Collections;
42 import java.util.regex.Matcher;
43 import java.util.regex.Pattern;
44 
45 @RunWith(AndroidJUnit4.class)
46 public class LaunchSettingsTest {
47     private static class Page {
48         String action;
49         String displayName;
50         String title;
51 
Page(String action, String displayName, String title)52         Page(String action, String displayName, String title) {
53             this.action = action;
54             this.displayName = displayName;
55             this.title = title;
56         }
57     }
58 
59     private static final String SCREEN_TIME_OUT = "7200000";
60     private static final String DEFAULT_SCREEN_TIMEOUT = "15000";
61     private static final int TIME_OUT = 5000;
62     private static final int TEST_TIME = 10;
63     private static final Pattern PATTERN = Pattern.compile("TotalTime:\\s[0-9]*");
64     private static final Page[] PAGES;
65     private static final String TAG = "SettingsPerfTests";
66 
67     static {
68         PAGES = new Page[]{
69                 new Page("android.settings.SETTINGS", "Search settings", "Settings"),
70                 new Page("android.settings.WIFI_SETTINGS", "Use Wi‑Fi", "Wi-Fi"),
71                 new Page("android.settings.BLUETOOTH_SETTINGS", "Connected devices", "BlueTooth"),
72                 new Page("android.settings.APPLICATION_SETTINGS", "App info", "Application"),
73                 new Page("android.intent.action.POWER_USAGE_SUMMARY", "Battery", "Battery"),
74                 new Page("android.settings.INTERNAL_STORAGE_SETTINGS", "Storage", "Storage")
75         };
76     }
77 
78     private Bundle mBundle;
79     private UiDevice mDevice;
80     private Instrumentation mInstrumentation;
81     private Map<String, ArrayList<Integer>> mResult;
82     private String mDefaultScreenTimeout;
83     private String mDefaultAirplaneModeStatus;
84 
85     @Before
setUp()86     public void setUp() throws Exception {
87         mBundle = new Bundle();
88         mDevice = UiDevice.getInstance(getInstrumentation());
89         mInstrumentation = InstrumentationRegistry.getInstrumentation();
90         mResult = new LinkedHashMap<>();
91         mDefaultScreenTimeout = mDevice.executeShellCommand(
92                 "settings get system screen_off_timeout");
93         mDefaultAirplaneModeStatus = getAirplaneModeStatus();
94         setScreenTimeOut(SCREEN_TIME_OUT);
95         setAirplaneMode();
96         mDevice.pressHome();
97         mDevice.waitForIdle(TIME_OUT);
98 
99         for (Page page : PAGES) {
100             mResult.put(page.title, new ArrayList<Integer>());
101         }
102     }
103 
104     @After
tearDown()105     public void tearDown() throws Exception {
106         putResultToBundle();
107         mInstrumentation.sendStatus(0, mBundle);
108         resetScreenTimeout();
109         resetAirplaneMode();
110         closeApp();
111     }
112 
113     @Test
settingsPerformanceTest()114     public void settingsPerformanceTest() throws Exception {
115         for (int i = 0; i < TEST_TIME; i++) {
116             for (Page page : PAGES) {
117                 executePreformanceTest(page.action, page.displayName, page.title);
118             }
119         }
120     }
121 
executePreformanceTest(String action, String displayName, String title)122     private void executePreformanceTest(String action, String displayName, String title)
123             throws Exception {
124         closeApp();
125         mDevice.waitForIdle(TIME_OUT);
126         final String mString = mDevice.executeShellCommand("am start -W -a" + action);
127         mDevice.wait(Until.findObject(By.text(displayName)), TIME_OUT);
128         handleLaunchResult(title, mString);
129     }
130 
handleLaunchResult(String title, String shellCommandResult)131     private void handleLaunchResult(String title, String shellCommandResult) {
132         Matcher mMatcher = PATTERN.matcher(shellCommandResult);
133         if (mMatcher.find()) {
134             mResult.get(title).add(Integer.valueOf(mMatcher.group().split("\\s")[1]));
135         } else {
136             fail(String.format("Not found %s.\n %s", title, shellCommandResult));
137         }
138     }
139 
closeApp()140     private void closeApp() throws Exception {
141         mDevice.executeShellCommand("am force-stop com.android.settings");
142         Thread.sleep(1000);
143     }
144 
putResultToBundle()145     private void putResultToBundle() {
146         for (String string : mResult.keySet()) {
147             mBundle.putString(String.format("LaunchSettingsTest_%s_%s", string, "max"),
148                     getMax(string));
149             mBundle.putString(String.format("LaunchSettingsTest_%s_%s", string, "min"),
150                     getMin(string));
151             mBundle.putString(String.format("LaunchSettingsTest_%s_%s", string, "avg"),
152                     getAvg(string));
153             mBundle.putString(String.format("LaunchSettingsTest_%s_%s", string, "25 Percentile"),
154                     getPercentile(string, 25));
155             mBundle.putString(String.format("LaunchSettingsTest_%s_%s", string, "50 Percentile"),
156                     getPercentile(string, 50));
157             mBundle.putString(String.format("LaunchSettingsTest_%s_%s", string, "75 Percentile"),
158                     getPercentile(string, 75));
159             mBundle.putString(String.format("LaunchSettingsTest_%s_%s", string, "all_results"),
160                     mResult.get(string).toString());
161             mBundle.putString(String.format("LaunchSettingsTest_%s_%s", string, "results_count"),
162                     String.valueOf(mResult.get(string).size()));
163         }
164     }
165 
getMax(String page)166     private String getMax(String page) {
167         if (mResult.get(page).size() == TEST_TIME) {
168             return String.format("%s", Collections.max(mResult.get(page)));
169         }
170         Log.e(TAG, String.format("Fail to get max of %s.", page));
171         return "0";
172 
173     }
174 
getMin(String page)175     private String getMin(String page) {
176         if (mResult.get(page).size() == TEST_TIME) {
177             return String.format("%s", Collections.min(mResult.get(page)));
178         }
179         Log.e(TAG, String.format("Fail to get min of %s.", page));
180         return "0";
181     }
182 
getAvg(String page)183     private String getAvg(String page) {
184         if (mResult.get(page).size() == TEST_TIME) {
185             return String.valueOf((int) mResult.get(page).stream().mapToInt(
186                     i -> i).average().orElse(0));
187         }
188         Log.e(TAG, String.format("Fail to get avg of %s.", page));
189         return "0";
190     }
191 
setScreenTimeOut(String timeout)192     private void setScreenTimeOut(String timeout) throws Exception {
193         mDevice.executeShellCommand("settings put system screen_off_timeout " + timeout);
194     }
195 
resetScreenTimeout()196     private void resetScreenTimeout() throws Exception {
197         String timeout = DEFAULT_SCREEN_TIMEOUT;
198         if (!mDefaultScreenTimeout.isEmpty()) {
199             timeout = mDefaultScreenTimeout;
200         }
201         setScreenTimeOut(timeout);
202     }
203 
setAirplaneMode()204     private void setAirplaneMode() throws Exception {
205         if (mDefaultAirplaneModeStatus.equals("0\n")) {
206             clickAirplaneMode();
207         }
208     }
209 
resetAirplaneMode()210     private void resetAirplaneMode() throws Exception {
211         if (!getAirplaneModeStatus().equals(mDefaultAirplaneModeStatus)) {
212             clickAirplaneMode();
213         }
214     }
215 
clickAirplaneMode()216     private void clickAirplaneMode() throws Exception {
217         mDevice.executeShellCommand("am start -W -a android.settings.AIRPLANE_MODE_SETTINGS");
218         mDevice.waitForIdle(TIME_OUT);
219         mDevice.findObject(By.textContains("Airplane")).click();
220         mDevice.waitForIdle(TIME_OUT);
221     }
222 
getAirplaneModeStatus()223     private String getAirplaneModeStatus() throws Exception {
224         return mDevice.executeShellCommand("settings get global airplane_mode_on");
225     }
226 
getPercentile(String page, double position)227     private String getPercentile(String page, double position) {
228         Collections.sort(mResult.get(page));
229         if (mResult.get(page).size() == TEST_TIME) {
230             return String.valueOf(
231                     mResult.get(page).get((int) (Math.ceil(TEST_TIME * position / 100)) - 1));
232         }
233         Log.e(TAG, String.format("Fail to get percentile of %s.", page));
234         return "0";
235     }
236 }