1 /*
2  * Copyright (C) 2017 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 android.server.wm;
18 
19 import static android.server.wm.ComponentNameUtils.getActivityName;
20 import static android.server.wm.profileable.Components.PROFILEABLE_APP_ACTIVITY;
21 import static android.server.wm.profileable.Components.ProfileableAppActivity.COMMAND_WAIT_FOR_PROFILE_OUTPUT;
22 import static android.server.wm.profileable.Components.ProfileableAppActivity.OUTPUT_FILE_PATH;
23 import static android.server.wm.profileable.Components.ProfileableAppActivity.OUTPUT_DIR;
24 
25 import static org.hamcrest.MatcherAssert.assertThat;
26 import static org.hamcrest.Matchers.greaterThanOrEqualTo;
27 import static org.junit.Assert.assertEquals;
28 
29 import android.content.ComponentName;
30 import android.content.Intent;
31 import android.platform.test.annotations.Presubmit;
32 import android.server.wm.CommandSession.ActivitySession;
33 import android.server.wm.CommandSession.DefaultLaunchProxy;
34 
35 import org.junit.AfterClass;
36 import org.junit.BeforeClass;
37 import org.junit.Test;
38 
39 /**
40  * Build/Install/Run:
41  *     atest CtsWindowManagerDeviceTestCases:AmProfileTests
42  *
43  * Please talk to Android Studio team first if you want to modify or delete these tests.
44  */
45 @Presubmit
46 public class AmProfileTests extends ActivityManagerTestBase {
47 
48     private static final String FIRST_WORD_NO_STREAMING = "*version\n";
49     private static final String FIRST_WORD_STREAMING = "SLOW";  // Magic word set by runtime.
50 
51     @BeforeClass
setUpClass()52     public static void setUpClass() {
53         // Allow ProfileableAppActivity to monitor the path.
54         executeShellCommand("mkdir -m 777 -p " + OUTPUT_DIR);
55     }
56 
57     @AfterClass
tearDownClass()58     public static void tearDownClass() {
59         executeShellCommand("rm -rf " + OUTPUT_DIR);
60     }
61 
62     /**
63      * Test am profile functionality with the following 3 configurable options:
64      *    starting the activity before start profiling? yes;
65      *    sampling-based profiling? no;
66      *    using streaming output mode? no.
67      */
68     @Test
testAmProfileStartNoSamplingNoStreaming()69     public void testAmProfileStartNoSamplingNoStreaming() throws Exception {
70         // am profile start ... , and the same to the following 3 test methods.
71         testProfile(true, false, false);
72     }
73 
74     /**
75      * The following tests are similar to testAmProfileStartNoSamplingNoStreaming(),
76      * only different in the three configuration options.
77      */
78     @Test
testAmProfileStartNoSamplingStreaming()79     public void testAmProfileStartNoSamplingStreaming() throws Exception {
80         testProfile(true, false, true);
81     }
82 
83     @Test
testAmProfileStartSamplingNoStreaming()84     public void testAmProfileStartSamplingNoStreaming() throws Exception {
85         testProfile(true, true, false);
86     }
87 
88     @Test
testAmProfileStartSamplingStreaming()89     public void testAmProfileStartSamplingStreaming() throws Exception {
90         testProfile(true, true, true);
91     }
92 
93     @Test
testAmStartStartProfilerNoSamplingNoStreaming()94     public void testAmStartStartProfilerNoSamplingNoStreaming() throws Exception {
95         // am start --start-profiler ..., and the same to the following 3 test methods.
96         testProfile(false, false, false);
97     }
98 
99     @Test
testAmStartStartProfilerNoSamplingStreaming()100     public void testAmStartStartProfilerNoSamplingStreaming() throws Exception {
101         testProfile(false, false, true);
102     }
103 
104     @Test
testAmStartStartProfilerSamplingNoStreaming()105     public void testAmStartStartProfilerSamplingNoStreaming() throws Exception {
106         testProfile(false, true, false);
107     }
108 
109     @Test
testAmStartStartProfilerSamplingStreaming()110     public void testAmStartStartProfilerSamplingStreaming() throws Exception {
111         testProfile(false, true, true);
112     }
113 
testProfile(final boolean startActivityFirst, final boolean sampling, final boolean streaming)114     private void testProfile(final boolean startActivityFirst, final boolean sampling,
115             final boolean streaming) throws Exception {
116         final ActivitySession activitySession;
117         if (startActivityFirst) {
118             activitySession = createManagedActivityClientSession().startActivity(
119                     new Intent().setComponent(PROFILEABLE_APP_ACTIVITY));
120             startProfiling(PROFILEABLE_APP_ACTIVITY.getPackageName(), sampling, streaming);
121         } else {
122             activitySession = startActivityProfiling(PROFILEABLE_APP_ACTIVITY, sampling, streaming);
123         }
124 
125         // Go to home screen and then warm start the activity to generate some interesting trace.
126         launchHomeActivity();
127         launchActivity(PROFILEABLE_APP_ACTIVITY);
128 
129         executeShellCommand(getStopProfileCmd(PROFILEABLE_APP_ACTIVITY));
130 
131         activitySession.sendCommandAndWaitReply(COMMAND_WAIT_FOR_PROFILE_OUTPUT);
132         verifyOutputFileFormat(streaming);
133     }
134 
135     /** Starts profiler on a started process. */
startProfiling(String processName, boolean sampling, boolean streaming)136     private static void startProfiling(String processName, boolean sampling, boolean streaming) {
137         final StringBuilder builder = new StringBuilder("am profile start");
138         appendProfileParameters(builder, sampling, streaming);
139         builder.append(String.format(" %s %s", processName, OUTPUT_FILE_PATH));
140         executeShellCommand(builder.toString());
141     }
142 
143     /** Starts the activity with profiler. */
startActivityProfiling(ComponentName activityName, boolean sampling, boolean streaming)144     private ActivitySession startActivityProfiling(ComponentName activityName, boolean sampling,
145             boolean streaming) {
146         return createManagedActivityClientSession().startActivity(new DefaultLaunchProxy() {
147 
148             @Override
149             public boolean shouldWaitForLaunched() {
150                 // The shell command included "-W".
151                 return false;
152             }
153 
154             @Override
155             public void execute() {
156                 final StringBuilder builder = new StringBuilder();
157                 builder.append(String.format("am start -n %s -W -S --start-profiler %s",
158                         getActivityName(activityName), OUTPUT_FILE_PATH));
159                 appendProfileParameters(builder, sampling, streaming);
160                 mLaunchInjector.setupShellCommand(builder);
161                 executeShellCommand(builder.toString());
162             }
163         });
164     }
165 
166     private static void appendProfileParameters(StringBuilder builder, boolean sampling,
167             boolean streaming) {
168         if (sampling) {
169             builder.append(" --sampling 1000");
170         }
171         if (streaming) {
172             builder.append(" --streaming");
173         }
174     }
175 
176     private static String getStopProfileCmd(final ComponentName activityName) {
177         return "am profile stop " + activityName.getPackageName();
178     }
179 
180     private void verifyOutputFileFormat(final boolean streaming) throws Exception {
181         // This is a hack. The am service has to write to /data/local/tmp because it doesn't have
182         // access to the sdcard. The test cannot read from /data/local/tmp. This allows us to
183         // scan the content to validate what is needed for this test.
184         final String firstLine = executeShellCommand("head -1 " + OUTPUT_FILE_PATH);
185 
186         final String expectedFirstWord = streaming ? FIRST_WORD_STREAMING : FIRST_WORD_NO_STREAMING;
187         assertThat(
188                 "data size", firstLine.length(), greaterThanOrEqualTo(expectedFirstWord.length()));
189         final String actualFirstWord = firstLine.substring(0, expectedFirstWord.length());
190         assertEquals("Unexpected first word", expectedFirstWord, actualFirstWord);
191 
192         // Clean up.
193         executeShellCommand("rm -f " + OUTPUT_FILE_PATH);
194     }
195 }
196