• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2018 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.traceur.uitest;
18 
19 import static org.junit.Assert.assertNotNull;
20 import static org.junit.Assert.assertTrue;
21 
22 import android.content.Context;
23 import android.content.Intent;
24 import android.content.pm.PackageManager;
25 import android.os.RemoteException;
26 import android.os.SystemClock;
27 import android.platform.test.annotations.Presubmit;
28 import android.support.test.uiautomator.By;
29 import android.support.test.uiautomator.UiDevice;
30 import android.support.test.uiautomator.UiObject2;
31 import android.support.test.uiautomator.UiObjectNotFoundException;
32 import android.support.test.uiautomator.UiSelector;
33 import android.support.test.uiautomator.UiScrollable;
34 import android.support.test.uiautomator.Until;
35 
36 import androidx.test.InstrumentationRegistry;
37 import androidx.test.runner.AndroidJUnit4;
38 
39 import org.junit.After;
40 import org.junit.Before;
41 import org.junit.Test;
42 import org.junit.runner.RunWith;
43 
44 import java.util.List;
45 import java.util.regex.Pattern;
46 
47 @RunWith(AndroidJUnit4.class)
48 public class TraceurAppTests {
49 
50     private static final String TRACEUR_PACKAGE = "com.android.traceur";
51     private static final int LAUNCH_TIMEOUT_MS = 10000;
52     private static final int UI_TIMEOUT_MS = 7500;
53     private static final int SHORT_PAUSE_MS = 1000;
54 
55     private UiDevice mDevice;
56 
57     @Before
setUp()58     public void setUp() throws Exception {
59         mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
60 
61         try {
62             if (!mDevice.isScreenOn()) {
63                 mDevice.wakeUp();
64             }
65 
66             // Press Menu to skip the lock screen.
67             // In case we weren't on the lock screen, press Home to return to a clean launcher.
68             mDevice.pressMenu();
69             mDevice.pressHome();
70 
71             mDevice.setOrientationNatural();
72         } catch (RemoteException e) {
73             throw new RuntimeException("Failed to freeze device orientation.", e);
74         }
75 
76         mDevice.waitForIdle();
77 
78         Context context = InstrumentationRegistry.getContext();
79         Intent intent = context.getPackageManager().getLaunchIntentForPackage(TRACEUR_PACKAGE);
80         intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);    // Clear out any previous instances
81         context.startActivity(intent);
82 
83         // Wait for the app to appear.
84         assertTrue(mDevice.wait(Until.hasObject(By.pkg(TRACEUR_PACKAGE).depth(0)),
85                   LAUNCH_TIMEOUT_MS));
86         // Default trace categories are restored in case a previous test modified them and
87         // terminated early.
88         restoreDefaultCategories();
89     }
90 
91     @After
tearDown()92     public void tearDown() throws Exception {
93         mDevice.unfreezeRotation();
94         // Finish Traceur activity.
95         mDevice.pressBack();
96         mDevice.pressHome();
97     }
98 
99     /**
100      * Verifies that the main page contains the correct UI elements.
101      * If the main page is scrollable, the test checks that all expected elements are found while
102      * scrolling. Otherwise, it checks that the expected elements are already on the page.
103      */
104     @Presubmit
105     @Test
testElementsOnMainScreen()106     public void testElementsOnMainScreen() throws Exception {
107         UiScrollable scrollableMainScreen = new UiScrollable(new UiSelector().scrollable(true));
108 
109         if (scrollableMainScreen.exists()) {
110             scrollableMainScreen.setAsVerticalList();
111             scrollableMainScreen.setMaxSearchSwipes(10);
112 
113             boolean recordFound = scrollableMainScreen.scrollTextIntoView("Record trace");
114             assertTrue("Record trace switch not found.", recordFound);
115 
116             boolean applicationsFound =
117                     scrollableMainScreen.scrollTextIntoView("Trace debuggable applications");
118             assertTrue("Applications element not found.", applicationsFound);
119 
120             boolean categoriesFound = scrollableMainScreen.scrollTextIntoView("Categories");
121             assertTrue("Categories element not found.", categoriesFound);
122 
123             boolean restoreFound = scrollableMainScreen.scrollTextIntoView("Restore default categories");
124             assertTrue("Restore default categories element not found.", restoreFound);
125 
126             boolean bufferSizeFound = scrollableMainScreen.scrollTextIntoView("Per-CPU buffer size");
127             assertTrue("Per-CPU buffer size element not found.", bufferSizeFound);
128 
129             boolean clearFound = scrollableMainScreen.scrollTextIntoView("Clear saved traces");
130             assertTrue("Clear saved traces element not found.", clearFound);
131 
132             boolean longTraceFound = scrollableMainScreen.scrollTextIntoView("Long traces");
133             assertTrue("Long traces element not found.", longTraceFound);
134 
135             boolean maxTraceSizeFound = scrollableMainScreen.scrollTextIntoView("Maximum long trace size");
136             assertTrue("Maximum long trace size element not found.", maxTraceSizeFound);
137 
138             boolean maxTraceDurationFound =
139                     scrollableMainScreen.scrollTextIntoView("Maximum long trace duration");
140             assertTrue("Maximum long trace duration element not found.", maxTraceDurationFound);
141 
142             boolean quickSettingsFound = scrollableMainScreen.scrollTextIntoView("Show Quick Settings tile");
143             assertTrue("Show Quick Settings tile switch not found.", quickSettingsFound);
144         } else {
145             assertNotNull("Record trace switch not found.",
146                     mDevice.wait(Until.findObject(By.text("Record trace")),
147                     UI_TIMEOUT_MS));
148             assertNotNull("Applications element not found.",
149                     mDevice.wait(Until.findObject(By.text("Trace debuggable applications")),
150                     UI_TIMEOUT_MS));
151             assertNotNull("Categories element not found.",
152                     mDevice.wait(Until.findObject(By.text("Categories")),
153                     UI_TIMEOUT_MS));
154             assertNotNull("Restore default categories element not found.",
155                     mDevice.wait(Until.findObject(By.text("Restore default categories")),
156                     UI_TIMEOUT_MS));
157             assertNotNull("Per-CPU buffer size element not found.",
158                     mDevice.wait(Until.findObject(By.text("Per-CPU buffer size")),
159                     UI_TIMEOUT_MS));
160             assertNotNull("Clear saved traces element not found.",
161                     mDevice.wait(Until.findObject(By.text("Clear saved traces")),
162                     UI_TIMEOUT_MS));
163             assertNotNull("Long traces element not found.",
164                     mDevice.wait(Until.findObject(By.text("Long traces")),
165                     UI_TIMEOUT_MS));
166             assertNotNull("Maximum long trace size element not found.",
167                     mDevice.wait(Until.findObject(By.text("Maximum long trace size")),
168                     UI_TIMEOUT_MS));
169             assertNotNull("Maximum long trace duration element not found.",
170                     mDevice.wait(Until.findObject(By.text("Maximum long trace duration")),
171                     UI_TIMEOUT_MS));
172             assertNotNull("Show Quick Settings tile switch not found.",
173                     mDevice.wait(Until.findObject(By.text("Show Quick Settings tile")),
174                     UI_TIMEOUT_MS));
175         }
176     }
177 
178     /**
179      * Checks that a trace can be recorded and shared.
180      * This test records a trace by toggling 'Record trace' in the UI, taps on the share
181      * notification once the trace is saved, then (on non-AOSP) verifies that a share dialog
182      * appears.
183      */
184     @Presubmit
185     @Test
testSuccessfulTracing()186     public void testSuccessfulTracing() throws Exception {
187         UiObject2 recordTraceSwitch = mDevice.wait(Until.findObject(By.text("Record trace")),
188                 UI_TIMEOUT_MS);
189         assertNotNull("Record trace switch not found.", recordTraceSwitch);
190         recordTraceSwitch.click();
191 
192         mDevice.waitForIdle();
193 
194         mDevice.wait(Until.hasObject(By.text("Trace is being recorded")), UI_TIMEOUT_MS);
195         mDevice.wait(Until.gone(By.text("Trace is being recorded")), UI_TIMEOUT_MS);
196 
197         recordTraceSwitch = mDevice.wait(Until.findObject(By.text("Record trace")), UI_TIMEOUT_MS);
198         assertNotNull("Record trace switch not found.", recordTraceSwitch);
199         recordTraceSwitch.click();
200 
201         mDevice.waitForIdle();
202 
203         // Wait for the popover notification to appear and then disappear,
204         // so we can reliably click the notification in the notification shade.
205         mDevice.wait(Until.hasObject(By.text("Tap to share your trace")), UI_TIMEOUT_MS);
206         mDevice.wait(Until.gone(By.text("Tap to share your trace")), UI_TIMEOUT_MS);
207 
208         mDevice.openNotification();
209         UiObject2 shareNotification = mDevice.wait(Until.findObject(
210                 By.text("Tap to share your trace")),
211                 UI_TIMEOUT_MS);
212         assertNotNull("Share notification not found.", shareNotification);
213         shareNotification.click();
214 
215         mDevice.waitForIdle();
216 
217         UiObject2 shareDialog = mDevice.wait(Until.findObject(
218                 By.textContains("Only share system traces with people and apps you trust.")),
219                 UI_TIMEOUT_MS);
220         assertNotNull("Share dialog not found.", shareDialog);
221 
222         // The buttons on dialogs sometimes have their capitalization manipulated by themes.
223         UiObject2 shareButton = mDevice.wait(Until.findObject(
224                 By.text(Pattern.compile("share", Pattern.CASE_INSENSITIVE))), UI_TIMEOUT_MS);
225         assertNotNull("Share button not found.", shareButton);
226         shareButton.click();
227 
228         // The share sheet will not appear on AOSP builds, as there are no apps available to share
229         // traces with. This checks if Gmail is installed (i.e. if the build is non-AOSP) before
230         // verifying that the share sheet exists.
231         try {
232             Context context = InstrumentationRegistry.getContext();
233             context.getPackageManager().getApplicationInfo("com.google.android.gm", 0);
234             UiObject2 shareSheet = mDevice.wait(Until.findObject(
235                     By.res("android:id/profile_tabhost")), UI_TIMEOUT_MS);
236             assertNotNull("Share sheet not found.", shareSheet);
237         } catch (PackageManager.NameNotFoundException e) {
238             // Gmail is not installed, so the device is on an AOSP build.
239         }
240     }
241 
242     /**
243      * Checks that trace categories are displayed after tapping on the 'Categories' button.
244      */
245     @Presubmit
246     @Test
testTraceCategoriesExist()247     public void testTraceCategoriesExist() {
248         openTraceCategories();
249         List<UiObject2> categories = getTraceCategories();
250         assertNotNull("List of categories not found.", categories);
251         assertTrue("No available trace categories.", categories.size() > 0);
252     }
253 
254     /**
255      * Checks that the 'Categories' summary updates when trace categories are selected.
256      * This test checks that the summary for the 'Categories' button changes from 'Default' to 'N
257      * selected' when a trace category is clicked, then back to 'Default' when the same category is
258      * clicked again.
259      */
260     @Presubmit
261     @Test
testCorrectCategoriesSummary()262     public void testCorrectCategoriesSummary() {
263         UiObject2 summary = getCategoriesSummary();
264         assertTrue("Expected 'Default' summary not found on startup.",
265                 summary.getText().contains("Default"));
266 
267         openTraceCategories();
268         toggleFirstTraceCategory();
269 
270         // The summary must be reset after each toggle because the reference will be stale.
271         summary = getCategoriesSummary();
272         assertTrue("Expected 'N selected' summary not found.",
273                 summary.getText().contains("selected"));
274 
275         openTraceCategories();
276         toggleFirstTraceCategory();
277 
278         summary = getCategoriesSummary();
279         assertTrue("Expected 'Default' summary not found after changing categories.",
280                 summary.getText().contains("Default"));
281     }
282 
283     /**
284      * Checks that the 'Restore default categories' button resets the trace categories summary.
285      * This test changes the set of selected trace categories from the default, then checks that the
286      * 'Categories' summary resets to 'Default' when the restore button is clicked.
287      */
288     @Presubmit
289     @Test
testRestoreDefaultCategories()290     public void testRestoreDefaultCategories() {
291         openTraceCategories();
292         toggleFirstTraceCategory();
293 
294         UiObject2 summary = getCategoriesSummary();
295         assertTrue("Expected 'N selected' summary not found.",
296                 summary.getText().contains("selected"));
297 
298         restoreDefaultCategories();
299 
300         // The summary must be reset after the toggle because the reference will be stale.
301         summary = getCategoriesSummary();
302         assertTrue("Expected 'Default' summary not found after restoring categories.",
303                 summary.getText().contains("Default"));
304     }
305 
306     /**
307      * Taps on the 'Categories' button.
308      */
openTraceCategories()309     private void openTraceCategories() {
310         UiObject2 categoriesButton = mDevice.wait(Until.findObject(
311                 By.text("Categories")), UI_TIMEOUT_MS);
312         assertNotNull("Categories button not found.", categoriesButton);
313         categoriesButton.click();
314 
315         mDevice.waitForIdle();
316     }
317 
318     /**
319      * Taps on the 'Restore default categories' button.
320      */
restoreDefaultCategories()321     private void restoreDefaultCategories() {
322         UiObject2 restoreButton = mDevice.wait(Until.findObject(
323                 By.text("Restore default categories")), UI_TIMEOUT_MS);
324         assertNotNull("'Restore default categories' button not found.", restoreButton);
325         restoreButton.click();
326 
327         mDevice.waitForIdle();
328         // This pause is necessary because the trace category restoration takes time to propagate to
329         // the main page.
330         SystemClock.sleep(SHORT_PAUSE_MS);
331     }
332 
333     /**
334      * Returns the UiObject2 of the summary for 'Categories'.
335      * This must only be used on Traceur's main page.
336      */
getCategoriesSummary()337     private UiObject2 getCategoriesSummary() {
338         UiObject2 categoriesButton = mDevice.wait(Until.findObject(
339                 By.text("Categories")), UI_TIMEOUT_MS);
340         assertNotNull("Categories button not found.", categoriesButton);
341         // The summary text is a sibling view of 'Categories' and can be found through their parent.
342         UiObject2 categoriesSummary = categoriesButton.getParent().wait(Until.findObject(
343                 By.res("android:id/summary")), UI_TIMEOUT_MS);
344         assertNotNull("Categories summary not found.", categoriesSummary);
345         return categoriesSummary;
346     }
347 
348     /**
349      * Returns the list of available trace categories.
350      * This must only be used after openTraceCategories() has been called.
351      */
getTraceCategories()352     private List<UiObject2> getTraceCategories() {
353         UiObject2 categoriesListView = mDevice.wait(Until.findObject(
354                 By.res("android:id/select_dialog_listview")), UI_TIMEOUT_MS);
355         assertNotNull("List of categories not found.", categoriesListView);
356         return categoriesListView.getChildren();
357     }
358 
359     /**
360      * Toggles the first checkbox in the list of trace categories.
361      * This must only be used after openTraceCategories() has been called.
362      */
toggleFirstTraceCategory()363     private void toggleFirstTraceCategory() {
364         getTraceCategories().get(0).click();
365 
366         mDevice.waitForIdle();
367 
368         UiObject2 confirmButton = mDevice.wait(Until.findObject(
369                 By.res("android:id/button1")), UI_TIMEOUT_MS);
370         assertNotNull("'OK' button not found under trace categories list.", confirmButton);
371         confirmButton.click();
372 
373         mDevice.waitForIdle();
374         // This pause is necessary because the trace category selection takes time to propagate to
375         // the main page.
376         SystemClock.sleep(SHORT_PAUSE_MS);
377     }
378 
379 }
380