blob: 3cef3bb21a7ff58a72ccb53bc52682794b07e9f9 [file] [log] [blame]
Felipe Lemee53e85f2015-11-17 17:37:53 -08001/*
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
17package com.android.shell;
18
19import static android.test.MoreAsserts.assertContainsRegex;
20import static com.android.shell.ActionSendMultipleConsumerActivity.UI_NAME;
Felipe Lemeb9238b32015-11-24 17:31:47 -080021import static com.android.shell.BugreportProgressService.EXTRA_BUGREPORT;
Felipe Leme85ae3cf2016-02-24 15:36:50 -080022import static com.android.shell.BugreportProgressService.EXTRA_ID;
Felipe Leme69c02922015-11-24 17:48:05 -080023import static com.android.shell.BugreportProgressService.EXTRA_MAX;
24import static com.android.shell.BugreportProgressService.EXTRA_NAME;
25import static com.android.shell.BugreportProgressService.EXTRA_PID;
Felipe Lemeb9238b32015-11-24 17:31:47 -080026import static com.android.shell.BugreportProgressService.EXTRA_SCREENSHOT;
Felipe Leme69c02922015-11-24 17:48:05 -080027import static com.android.shell.BugreportProgressService.INTENT_BUGREPORT_FINISHED;
28import static com.android.shell.BugreportProgressService.INTENT_BUGREPORT_STARTED;
Felipe Lemed1e0f122015-12-18 16:12:41 -080029import static com.android.shell.BugreportProgressService.SCREENSHOT_DELAY_SECONDS;
Felipe Lemee53e85f2015-11-17 17:37:53 -080030
31import java.io.BufferedOutputStream;
32import java.io.BufferedWriter;
33import java.io.ByteArrayOutputStream;
Felipe Lemea0bf0332015-12-11 09:35:03 -080034import java.io.File;
Felipe Lemee53e85f2015-11-17 17:37:53 -080035import java.io.FileOutputStream;
36import java.io.IOException;
37import java.io.InputStream;
38import java.io.OutputStreamWriter;
39import java.io.Writer;
Felipe Lemee86b63b2016-02-08 09:39:50 -080040import java.text.NumberFormat;
Felipe Lemed1e0f122015-12-18 16:12:41 -080041import java.util.ArrayList;
Felipe Lemee53e85f2015-11-17 17:37:53 -080042import java.util.List;
Felipe Lemed1e0f122015-12-18 16:12:41 -080043import java.util.SortedSet;
44import java.util.TreeSet;
Felipe Lemee53e85f2015-11-17 17:37:53 -080045import java.util.zip.ZipEntry;
46import java.util.zip.ZipInputStream;
47import java.util.zip.ZipOutputStream;
48
49import libcore.io.Streams;
Felipe Lemeba477932015-12-09 11:04:59 -080050import android.app.ActivityManager;
51import android.app.ActivityManager.RunningServiceInfo;
Felipe Lemee53e85f2015-11-17 17:37:53 -080052import android.app.Instrumentation;
53import android.app.NotificationManager;
54import android.content.Context;
Felipe Lemea0bf0332015-12-11 09:35:03 -080055import android.content.ContextWrapper;
Felipe Lemee53e85f2015-11-17 17:37:53 -080056import android.content.Intent;
57import android.net.Uri;
58import android.os.Bundle;
59import android.os.SystemProperties;
60import android.service.notification.StatusBarNotification;
61import android.support.test.uiautomator.UiDevice;
Felipe Leme6bbb6b92015-12-07 17:43:55 -080062import android.support.test.uiautomator.UiObject;
Felipe Lemee86b63b2016-02-08 09:39:50 -080063import android.support.test.uiautomator.UiObjectNotFoundException;
Felipe Leme26288782016-02-25 12:10:43 -080064import android.support.test.uiautomator.UiSelector;
Felipe Lemee53e85f2015-11-17 17:37:53 -080065import android.test.InstrumentationTestCase;
Felipe Leme2bfa0852015-12-10 12:43:31 -080066import android.test.suitebuilder.annotation.LargeTest;
Felipe Leme4967f732016-01-06 11:38:53 -080067import android.text.TextUtils;
Felipe Lemed1e0f122015-12-18 16:12:41 -080068import android.text.format.DateUtils;
Felipe Lemee53e85f2015-11-17 17:37:53 -080069import android.util.Log;
70
71import com.android.shell.ActionSendMultipleConsumerActivity.CustomActionSendMultipleListener;
Felipe Lemec4f646772016-01-12 18:12:09 -080072import com.android.shell.BugreportProgressService;
Felipe Lemee53e85f2015-11-17 17:37:53 -080073
74/**
75 * Integration tests for {@link BugreportReceiver}.
76 * <p>
77 * These tests don't mock any component and rely on external UI components (like the notification
78 * bar and activity chooser), which can make them unreliable and slow.
79 * <p>
80 * The general workflow is:
81 * <ul>
82 * <li>creates the bug report files
83 * <li>generates the BUGREPORT_FINISHED intent
84 * <li>emulate user actions to share the intent with a custom activity
85 * <li>asserts the extras received by the custom activity
86 * </ul>
87 * <p>
Felipe Leme6bbb6b92015-12-07 17:43:55 -080088 * <strong>NOTE</strong>: these tests only work if the device is unlocked.
Felipe Lemee53e85f2015-11-17 17:37:53 -080089 */
Felipe Leme2bfa0852015-12-10 12:43:31 -080090@LargeTest
Felipe Lemee53e85f2015-11-17 17:37:53 -080091public class BugreportReceiverTest extends InstrumentationTestCase {
Felipe Lemee53e85f2015-11-17 17:37:53 -080092 private static final String TAG = "BugreportReceiverTest";
93
94 // Timeout for UI operations, in milliseconds.
Felipe Leme46d47912015-12-09 13:03:09 -080095 private static final int TIMEOUT = (int) BugreportProgressService.POLLING_FREQUENCY * 4;
Felipe Lemee53e85f2015-11-17 17:37:53 -080096
Felipe Lemec4f646772016-01-12 18:12:09 -080097 // Timeout for when waiting for a screenshot to finish.
98 private static final int SAFE_SCREENSHOT_DELAY = SCREENSHOT_DELAY_SECONDS + 10;
99
Felipe Lemea0bf0332015-12-11 09:35:03 -0800100 private static final String BUGREPORTS_DIR = "bugreports";
Felipe Lemee53e85f2015-11-17 17:37:53 -0800101 private static final String BUGREPORT_FILE = "test_bugreport.txt";
102 private static final String ZIP_FILE = "test_bugreport.zip";
Felipe Leme26288782016-02-25 12:10:43 -0800103 private static final String ZIP_FILE2 = "test_bugreport2.zip";
Felipe Lemea0bf0332015-12-11 09:35:03 -0800104 private static final String SCREENSHOT_FILE = "test_screenshot.png";
Felipe Lemee53e85f2015-11-17 17:37:53 -0800105
106 private static final String BUGREPORT_CONTENT = "Dump, might as well dump!\n";
107 private static final String SCREENSHOT_CONTENT = "A picture is worth a thousand words!\n";
108
Felipe Leme93702ab2015-12-11 13:06:45 -0800109 private static final int PID = 42;
Felipe Leme26288782016-02-25 12:10:43 -0800110 private static final int PID2 = 24;
Felipe Leme85ae3cf2016-02-24 15:36:50 -0800111 private static final int ID = 108;
Felipe Leme26288782016-02-25 12:10:43 -0800112 private static final int ID2 = 801;
Felipe Leme85ae3cf2016-02-24 15:36:50 -0800113 private static final String PROGRESS_PROPERTY = "dumpstate." + PID + ".progress";
114 private static final String MAX_PROPERTY = "dumpstate." + PID + ".max";
115 private static final String NAME_PROPERTY = "dumpstate." + PID + ".name";
Felipe Leme93702ab2015-12-11 13:06:45 -0800116 private static final String NAME = "BUG, Y U NO REPORT?";
Felipe Leme26288782016-02-25 12:10:43 -0800117 private static final String NAME2 = "A bugreport's life";
Felipe Lemebc73ffc2015-12-11 15:07:14 -0800118 private static final String NEW_NAME = "Bug_Forrest_Bug";
Felipe Leme26288782016-02-25 12:10:43 -0800119 private static final String NEW_NAME2 = "BugsyReportsy";
Felipe Lemebc73ffc2015-12-11 15:07:14 -0800120 private static final String TITLE = "Wimbugdom Champion 2015";
Felipe Leme26288782016-02-25 12:10:43 -0800121 private static final String TITLE2 = "Master of the Universe";
122 private static final String DESCRIPTION = "One's description...";
123 private static final String DESCRIPTION2 = "...is another's treasure.";
Felipe Leme4967f732016-01-06 11:38:53 -0800124
125 private static final String NO_DESCRIPTION = null;
126 private static final String NO_NAME = null;
127 private static final String NO_SCREENSHOT = null;
128 private static final String NO_TITLE = null;
Felipe Leme85ae3cf2016-02-24 15:36:50 -0800129 private static final int NO_ID = 0;
Felipe Lemec8e2b602016-01-29 13:55:35 -0800130 private static final boolean RENAMED_SCREENSHOTS = true;
131 private static final boolean DIDNT_RENAME_SCREENSHOTS = false;
Felipe Leme4967f732016-01-06 11:38:53 -0800132
Felipe Lemebc73ffc2015-12-11 15:07:14 -0800133 private String mDescription;
Felipe Leme93702ab2015-12-11 13:06:45 -0800134
Felipe Lemea0bf0332015-12-11 09:35:03 -0800135 private String mPlainTextPath;
136 private String mZipPath;
Felipe Leme26288782016-02-25 12:10:43 -0800137 private String mZipPath2;
Felipe Lemea0bf0332015-12-11 09:35:03 -0800138 private String mScreenshotPath;
139
Felipe Lemee53e85f2015-11-17 17:37:53 -0800140 private Context mContext;
141 private UiBot mUiBot;
142 private CustomActionSendMultipleListener mListener;
143
144 @Override
145 protected void setUp() throws Exception {
Felipe Lemec4f646772016-01-12 18:12:09 -0800146 Log.i(TAG, "#### setup() on " + getName());
Felipe Lemee53e85f2015-11-17 17:37:53 -0800147 Instrumentation instrumentation = getInstrumentation();
148 mContext = instrumentation.getTargetContext();
149 mUiBot = new UiBot(UiDevice.getInstance(instrumentation), TIMEOUT);
150 mListener = ActionSendMultipleConsumerActivity.getListener(mContext);
Felipe Leme93702ab2015-12-11 13:06:45 -0800151
152 cancelExistingNotifications();
153
Felipe Lemea0bf0332015-12-11 09:35:03 -0800154 mPlainTextPath = getPath(BUGREPORT_FILE);
155 mZipPath = getPath(ZIP_FILE);
Felipe Leme26288782016-02-25 12:10:43 -0800156 mZipPath2 = getPath(ZIP_FILE2);
Felipe Lemea0bf0332015-12-11 09:35:03 -0800157 mScreenshotPath = getPath(SCREENSHOT_FILE);
Felipe Leme93702ab2015-12-11 13:06:45 -0800158 createTextFile(mPlainTextPath, BUGREPORT_CONTENT);
159 createTextFile(mScreenshotPath, SCREENSHOT_CONTENT);
160 createZipFile(mZipPath, BUGREPORT_FILE, BUGREPORT_CONTENT);
Felipe Leme26288782016-02-25 12:10:43 -0800161 createZipFile(mZipPath2, BUGREPORT_FILE, BUGREPORT_CONTENT);
Felipe Leme93702ab2015-12-11 13:06:45 -0800162
Felipe Lemebc73ffc2015-12-11 15:07:14 -0800163 // Creates a multi-line description.
164 StringBuilder sb = new StringBuilder();
165 for (int i = 1; i <= 20; i++) {
166 sb.append("All work and no play makes Shell a dull app!\n");
167 }
168 mDescription = sb.toString();
169
Felipe Leme6bbb6b92015-12-07 17:43:55 -0800170 BugreportPrefs.setWarningState(mContext, BugreportPrefs.STATE_HIDE);
Felipe Lemee53e85f2015-11-17 17:37:53 -0800171 }
172
Felipe Lemebc73ffc2015-12-11 15:07:14 -0800173 public void testProgress() throws Exception {
Felipe Leme93702ab2015-12-11 13:06:45 -0800174 resetProperties();
175 sendBugreportStarted(1000);
Felipe Leme22881292016-01-06 09:57:23 -0800176 waitForScreenshotButtonEnabled(true);
Felipe Leme69c02922015-11-24 17:48:05 -0800177
Felipe Lemee86b63b2016-02-08 09:39:50 -0800178 final NumberFormat nf = NumberFormat.getPercentInstance();
179 nf.setMinimumFractionDigits(2);
180 nf.setMaximumFractionDigits(2);
181
182 assertProgressNotification(NAME, nf.format(0));
Felipe Leme69c02922015-11-24 17:48:05 -0800183
Felipe Leme93702ab2015-12-11 13:06:45 -0800184 SystemProperties.set(PROGRESS_PROPERTY, "108");
Felipe Lemee86b63b2016-02-08 09:39:50 -0800185 assertProgressNotification(NAME, nf.format(0.108));
Felipe Leme69c02922015-11-24 17:48:05 -0800186
Felipe Leme93702ab2015-12-11 13:06:45 -0800187 SystemProperties.set(PROGRESS_PROPERTY, "500");
Felipe Lemee86b63b2016-02-08 09:39:50 -0800188 assertProgressNotification(NAME, nf.format(0.50));
Felipe Leme69c02922015-11-24 17:48:05 -0800189
Felipe Leme93702ab2015-12-11 13:06:45 -0800190 SystemProperties.set(MAX_PROPERTY, "2000");
Felipe Lemee86b63b2016-02-08 09:39:50 -0800191 assertProgressNotification(NAME, nf.format(0.25));
Felipe Leme69c02922015-11-24 17:48:05 -0800192
Felipe Leme93702ab2015-12-11 13:06:45 -0800193 Bundle extras =
Felipe Leme85ae3cf2016-02-24 15:36:50 -0800194 sendBugreportFinishedAndGetSharedIntent(ID, mPlainTextPath, mScreenshotPath);
195 assertActionSendMultiple(extras, BUGREPORT_CONTENT, SCREENSHOT_CONTENT, ID, PID, ZIP_FILE,
Felipe Lemec8e2b602016-01-29 13:55:35 -0800196 NAME, NO_TITLE, NO_DESCRIPTION, 1, RENAMED_SCREENSHOTS);
Felipe Lemed1e0f122015-12-18 16:12:41 -0800197
198 assertServiceNotRunning();
199 }
200
201 public void testProgress_takeExtraScreenshot() throws Exception {
Felipe Lemec4f646772016-01-12 18:12:09 -0800202 takeExtraScreenshotTest(false);
203 }
204
205 public void testProgress_takeExtraScreenshotServiceDiesAfterScreenshotTaken() throws Exception {
206 takeExtraScreenshotTest(true);
207 }
208
209 private void takeExtraScreenshotTest(boolean serviceDies) throws Exception {
Felipe Lemed1e0f122015-12-18 16:12:41 -0800210 resetProperties();
211 sendBugreportStarted(1000);
212
213 waitForScreenshotButtonEnabled(true);
214 takeScreenshot();
215 assertScreenshotButtonEnabled(false);
216 waitForScreenshotButtonEnabled(true);
217
Felipe Leme85ae3cf2016-02-24 15:36:50 -0800218 sendBugreportFinished(ID, mPlainTextPath, mScreenshotPath);
Felipe Lemec4f646772016-01-12 18:12:09 -0800219
220 if (serviceDies) {
Felipe Leme85ae3cf2016-02-24 15:36:50 -0800221 waitShareNotification(ID);
Felipe Lemec4f646772016-01-12 18:12:09 -0800222 killService();
223 }
224
Felipe Leme85ae3cf2016-02-24 15:36:50 -0800225 Bundle extras = acceptBugreportAndGetSharedIntent(ID);
226 assertActionSendMultiple(extras, BUGREPORT_CONTENT, SCREENSHOT_CONTENT, ID, PID, ZIP_FILE,
Felipe Lemec8e2b602016-01-29 13:55:35 -0800227 NAME, NO_TITLE, NO_DESCRIPTION, 2, RENAMED_SCREENSHOTS);
Felipe Leme69c02922015-11-24 17:48:05 -0800228
Felipe Leme93702ab2015-12-11 13:06:45 -0800229 assertServiceNotRunning();
Felipe Leme69c02922015-11-24 17:48:05 -0800230 }
231
Felipe Lemec4f646772016-01-12 18:12:09 -0800232 public void testScreenshotFinishesAfterBugreport() throws Exception {
233 screenshotFinishesAfterBugreportTest(false);
234 }
235
236 public void testScreenshotFinishesAfterBugreportAndServiceDiesBeforeSharing() throws Exception {
237 screenshotFinishesAfterBugreportTest(true);
238 }
239
240 private void screenshotFinishesAfterBugreportTest(boolean serviceDies) throws Exception {
241 resetProperties();
242
243 sendBugreportStarted(1000);
Felipe Leme85ae3cf2016-02-24 15:36:50 -0800244 sendBugreportFinished(ID, mPlainTextPath, NO_SCREENSHOT);
245 waitShareNotification(ID);
Felipe Lemec4f646772016-01-12 18:12:09 -0800246
247 // There's no indication in the UI about the screenshot finish, so just sleep like a baby...
248 Thread.sleep(SAFE_SCREENSHOT_DELAY * DateUtils.SECOND_IN_MILLIS);
249
250 if (serviceDies) {
251 killService();
252 }
253
Felipe Leme85ae3cf2016-02-24 15:36:50 -0800254 Bundle extras = acceptBugreportAndGetSharedIntent(ID);
255 assertActionSendMultiple(extras, BUGREPORT_CONTENT, NO_SCREENSHOT, ID, PID, ZIP_FILE,
Felipe Lemec8e2b602016-01-29 13:55:35 -0800256 NAME, NO_TITLE, NO_DESCRIPTION, 1, RENAMED_SCREENSHOTS);
Felipe Lemec4f646772016-01-12 18:12:09 -0800257
258 assertServiceNotRunning();
259 }
260
Felipe Leme4967f732016-01-06 11:38:53 -0800261 public void testProgress_changeDetailsInvalidInput() throws Exception {
Felipe Lemebc73ffc2015-12-11 15:07:14 -0800262 resetProperties();
263 sendBugreportStarted(1000);
Felipe Leme22881292016-01-06 09:57:23 -0800264 waitForScreenshotButtonEnabled(true);
Felipe Lemebc73ffc2015-12-11 15:07:14 -0800265
Felipe Leme85ae3cf2016-02-24 15:36:50 -0800266 DetailsUi detailsUi = new DetailsUi(mUiBot, ID);
Felipe Lemebc73ffc2015-12-11 15:07:14 -0800267
268 // Check initial name.
Felipe Leme26288782016-02-25 12:10:43 -0800269 detailsUi.assertName(NAME);
Felipe Lemebc73ffc2015-12-11 15:07:14 -0800270
271 // Change name - it should have changed system property once focus is changed.
272 detailsUi.nameField.setText(NEW_NAME);
273 detailsUi.focusAwayFromName();
274 assertPropertyValue(NAME_PROPERTY, NEW_NAME);
275
276 // Cancel the dialog to make sure property was restored.
277 detailsUi.clickCancel();
278 assertPropertyValue(NAME_PROPERTY, NAME);
279
280 // Now try to set an invalid name.
281 detailsUi.reOpen();
282 detailsUi.nameField.setText("/etc/passwd");
283 detailsUi.clickOk();
284 assertPropertyValue(NAME_PROPERTY, "_etc_passwd");
285
286 // Finally, make the real changes.
287 detailsUi.reOpen();
288 detailsUi.nameField.setText(NEW_NAME);
289 detailsUi.titleField.setText(TITLE);
290 detailsUi.descField.setText(mDescription);
291
292 detailsUi.clickOk();
293
294 assertPropertyValue(NAME_PROPERTY, NEW_NAME);
295 assertProgressNotification(NEW_NAME, "0.00%");
296
Felipe Leme85ae3cf2016-02-24 15:36:50 -0800297 Bundle extras = sendBugreportFinishedAndGetSharedIntent(ID, mPlainTextPath,
Felipe Lemebc73ffc2015-12-11 15:07:14 -0800298 mScreenshotPath);
Felipe Leme85ae3cf2016-02-24 15:36:50 -0800299 assertActionSendMultiple(extras, BUGREPORT_CONTENT, SCREENSHOT_CONTENT, ID, PID, TITLE,
Felipe Lemec8e2b602016-01-29 13:55:35 -0800300 NEW_NAME, TITLE, mDescription, 1, RENAMED_SCREENSHOTS);
Felipe Leme4967f732016-01-06 11:38:53 -0800301
302 assertServiceNotRunning();
303 }
304
305 public void testProgress_changeDetailsPlainBugreport() throws Exception {
306 changeDetailsTest(true);
307 }
308
309 public void testProgress_changeDetailsZippedBugreport() throws Exception {
310 changeDetailsTest(false);
311 }
312
313 public void changeDetailsTest(boolean plainText) throws Exception {
Felipe Leme4967f732016-01-06 11:38:53 -0800314 resetProperties();
315 sendBugreportStarted(1000);
316 waitForScreenshotButtonEnabled(true);
317
Felipe Leme85ae3cf2016-02-24 15:36:50 -0800318 DetailsUi detailsUi = new DetailsUi(mUiBot, ID);
Felipe Leme4967f732016-01-06 11:38:53 -0800319
320 // Check initial name.
Felipe Leme26288782016-02-25 12:10:43 -0800321 detailsUi.assertName(NAME);
Felipe Leme4967f732016-01-06 11:38:53 -0800322
323 // Change fields.
324 detailsUi.reOpen();
325 detailsUi.nameField.setText(NEW_NAME);
326 detailsUi.titleField.setText(TITLE);
327 detailsUi.descField.setText(mDescription);
328
329 detailsUi.clickOk();
330
331 assertPropertyValue(NAME_PROPERTY, NEW_NAME);
332 assertProgressNotification(NEW_NAME, "0.00%");
333
Felipe Leme85ae3cf2016-02-24 15:36:50 -0800334 Bundle extras = sendBugreportFinishedAndGetSharedIntent(ID,
Felipe Leme4967f732016-01-06 11:38:53 -0800335 plainText? mPlainTextPath : mZipPath, mScreenshotPath);
Felipe Leme85ae3cf2016-02-24 15:36:50 -0800336 assertActionSendMultiple(extras, BUGREPORT_CONTENT, SCREENSHOT_CONTENT, ID, PID, TITLE,
Felipe Lemec8e2b602016-01-29 13:55:35 -0800337 NEW_NAME, TITLE, mDescription, 1, RENAMED_SCREENSHOTS);
338
339 assertServiceNotRunning();
340 }
341
342 public void testProgress_changeJustDetails() throws Exception {
343 resetProperties();
344 sendBugreportStarted(1000);
345 waitForScreenshotButtonEnabled(true);
346
Felipe Leme85ae3cf2016-02-24 15:36:50 -0800347 DetailsUi detailsUi = new DetailsUi(mUiBot, ID);
Felipe Lemec8e2b602016-01-29 13:55:35 -0800348
349 detailsUi.nameField.setText("");
350 detailsUi.titleField.setText("");
351 detailsUi.descField.setText(mDescription);
352 detailsUi.clickOk();
353
Felipe Leme85ae3cf2016-02-24 15:36:50 -0800354 Bundle extras = sendBugreportFinishedAndGetSharedIntent(ID, mZipPath, mScreenshotPath);
355 assertActionSendMultiple(extras, BUGREPORT_CONTENT, SCREENSHOT_CONTENT, ID, PID, ZIP_FILE,
Felipe Lemec8e2b602016-01-29 13:55:35 -0800356 NO_NAME, NO_TITLE, mDescription, 1, DIDNT_RENAME_SCREENSHOTS);
Felipe Lemebc73ffc2015-12-11 15:07:14 -0800357
358 assertServiceNotRunning();
359 }
360
Felipe Leme26288782016-02-25 12:10:43 -0800361 /*
362 * TODO: this test can be flanky because it relies in the order the notifications are displayed,
363 * since mUiBot gets the first notification.
364 * Ideally, openProgressNotification() should return the whole notification, so DetailsUi
365 * could use it and find children instead, but unfortunately the notification object hierarchy
366 * is too complex and getting it from the notification text object would be to fragile
367 * (for instance, it could require navigating many parents up in the hierarchy).
368 */
369 public void testProgress_changeJustDetailsIsClearedOnSecondBugreport() throws Exception {
370 resetProperties();
371 sendBugreportStarted(ID, PID, NAME, 1000);
372 waitForScreenshotButtonEnabled(true);
373
374 DetailsUi detailsUi = new DetailsUi(mUiBot, ID);
375 detailsUi.assertName(NAME);
376 detailsUi.assertTitle(mContext.getString(R.string.bugreport_info_title));
377 detailsUi.assertDescription(mContext.getString(R.string.bugreport_info_description));
378 detailsUi.nameField.setText(NEW_NAME);
379 detailsUi.titleField.setText(TITLE);
380 detailsUi.descField.setText(DESCRIPTION);
381 detailsUi.clickOk();
382
383 sendBugreportStarted(ID2, PID2, NAME2, 1000);
384
385 Bundle extras = sendBugreportFinishedAndGetSharedIntent(ID, mZipPath, mScreenshotPath);
386 assertActionSendMultiple(extras, BUGREPORT_CONTENT, SCREENSHOT_CONTENT, ID, PID, TITLE,
387 NEW_NAME, TITLE, DESCRIPTION, 1, RENAMED_SCREENSHOTS);
388
389 detailsUi = new DetailsUi(mUiBot, ID2);
390 detailsUi.assertName(NAME2);
391 detailsUi.assertTitle(mContext.getString(R.string.bugreport_info_title));
392 detailsUi.assertDescription(mContext.getString(R.string.bugreport_info_description));
393 detailsUi.nameField.setText(NEW_NAME2);
394 detailsUi.titleField.setText(TITLE2);
395 detailsUi.descField.setText(DESCRIPTION2);
396 detailsUi.clickOk();
397
398 // Must use a different zip file otherwise it will fail because zip already contains
399 // title.txt and description.txt entries.
400 extras = sendBugreportFinishedAndGetSharedIntent(ID2, mZipPath2, NO_SCREENSHOT);
401 assertActionSendMultiple(extras, BUGREPORT_CONTENT, NO_SCREENSHOT, ID2, PID2, TITLE2,
402 NEW_NAME2, TITLE2, DESCRIPTION2, 1, RENAMED_SCREENSHOTS);
403
404 assertServiceNotRunning();
405 }
406
Felipe Leme22881292016-01-06 09:57:23 -0800407 /**
408 * Tests the scenario where the initial screenshot and dumpstate are finished while the user
409 * is changing the info in the details screen.
410 */
411 public void testProgress_bugreportAndScreenshotFinishedWhileChangingDetails() throws Exception {
412 bugreportFinishedWhileChangingDetailsTest(false);
413 }
414
415 /**
416 * Tests the scenario where dumpstate is finished while the user is changing the info in the
417 * details screen, but the initial screenshot finishes afterwards.
418 */
Felipe Lemebc73ffc2015-12-11 15:07:14 -0800419 public void testProgress_bugreportFinishedWhileChangingDetails() throws Exception {
Felipe Leme22881292016-01-06 09:57:23 -0800420 bugreportFinishedWhileChangingDetailsTest(true);
421 }
422
423 private void bugreportFinishedWhileChangingDetailsTest(boolean waitScreenshot) throws Exception {
Felipe Lemebc73ffc2015-12-11 15:07:14 -0800424 resetProperties();
425 sendBugreportStarted(1000);
Felipe Leme22881292016-01-06 09:57:23 -0800426 if (waitScreenshot) {
427 waitForScreenshotButtonEnabled(true);
428 }
Felipe Lemebc73ffc2015-12-11 15:07:14 -0800429
Felipe Leme85ae3cf2016-02-24 15:36:50 -0800430 DetailsUi detailsUi = new DetailsUi(mUiBot, ID);
Felipe Lemebc73ffc2015-12-11 15:07:14 -0800431
432 // Finish the bugreport while user's still typing the name.
433 detailsUi.nameField.setText(NEW_NAME);
Felipe Leme85ae3cf2016-02-24 15:36:50 -0800434 sendBugreportFinished(ID, mPlainTextPath, mScreenshotPath);
Felipe Lemebc73ffc2015-12-11 15:07:14 -0800435
Felipe Leme22881292016-01-06 09:57:23 -0800436 // Wait until the share notification is received...
Felipe Leme85ae3cf2016-02-24 15:36:50 -0800437 waitShareNotification(ID);
Felipe Lemebc73ffc2015-12-11 15:07:14 -0800438 // ...then close notification bar.
439 mContext.sendBroadcast(new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS));
440
441 // Make sure UI was updated properly.
442 assertFalse("didn't disable name on UI", detailsUi.nameField.isEnabled());
443 assertEquals("didn't revert name on UI", NAME, detailsUi.nameField.getText().toString());
444
445 // Finish changing other fields.
446 detailsUi.titleField.setText(TITLE);
447 detailsUi.descField.setText(mDescription);
448 detailsUi.clickOk();
449
450 // Finally, share bugreport.
Felipe Leme85ae3cf2016-02-24 15:36:50 -0800451 Bundle extras = acceptBugreportAndGetSharedIntent(ID);
452 assertActionSendMultiple(extras, BUGREPORT_CONTENT, SCREENSHOT_CONTENT, ID, PID, TITLE,
Felipe Lemec8e2b602016-01-29 13:55:35 -0800453 NAME, TITLE, mDescription, 1, RENAMED_SCREENSHOTS);
Felipe Lemebc73ffc2015-12-11 15:07:14 -0800454
455 assertServiceNotRunning();
456 }
457
Felipe Leme6bbb6b92015-12-07 17:43:55 -0800458 public void testBugreportFinished_withWarning() throws Exception {
459 // Explicitly shows the warning.
460 BugreportPrefs.setWarningState(mContext, BugreportPrefs.STATE_SHOW);
461
462 // Send notification and click on share.
Felipe Leme85ae3cf2016-02-24 15:36:50 -0800463 sendBugreportFinished(NO_ID, mPlainTextPath, null);
464 acceptBugreport(NO_ID);
Felipe Leme6bbb6b92015-12-07 17:43:55 -0800465
466 // Handle the warning
467 mUiBot.getVisibleObject(mContext.getString(R.string.bugreport_confirm));
468 // TODO: get ok and showMessageAgain from the dialog reference above
469 UiObject showMessageAgain =
470 mUiBot.getVisibleObject(mContext.getString(R.string.bugreport_confirm_repeat));
471 mUiBot.click(showMessageAgain, "show-message-again");
472 UiObject ok = mUiBot.getVisibleObject(mContext.getString(com.android.internal.R.string.ok));
473 mUiBot.click(ok, "ok");
474
475 // Share the bugreport.
476 mUiBot.chooseActivity(UI_NAME);
477 Bundle extras = mListener.getExtras();
Felipe Leme4967f732016-01-06 11:38:53 -0800478 assertActionSendMultiple(extras, BUGREPORT_CONTENT, NO_SCREENSHOT);
Felipe Leme6bbb6b92015-12-07 17:43:55 -0800479
480 // Make sure it's hidden now.
481 int newState = BugreportPrefs.getWarningState(mContext, BugreportPrefs.STATE_UNKNOWN);
482 assertEquals("Didn't change state", BugreportPrefs.STATE_HIDE, newState);
483 }
484
Felipe Lemec4f646772016-01-12 18:12:09 -0800485 public void testShareBugreportAfterServiceDies() throws Exception {
Felipe Leme85ae3cf2016-02-24 15:36:50 -0800486 sendBugreportFinished(NO_ID, mPlainTextPath, NO_SCREENSHOT);
Felipe Lemec4f646772016-01-12 18:12:09 -0800487 killService();
Felipe Leme85ae3cf2016-02-24 15:36:50 -0800488 Bundle extras = acceptBugreportAndGetSharedIntent(NO_ID);
Felipe Lemec4f646772016-01-12 18:12:09 -0800489 assertActionSendMultiple(extras, BUGREPORT_CONTENT, NO_SCREENSHOT);
490 }
491
Felipe Lemee53e85f2015-11-17 17:37:53 -0800492 public void testBugreportFinished_plainBugreportAndScreenshot() throws Exception {
Felipe Leme93702ab2015-12-11 13:06:45 -0800493 Bundle extras = sendBugreportFinishedAndGetSharedIntent(mPlainTextPath, mScreenshotPath);
Felipe Lemee53e85f2015-11-17 17:37:53 -0800494 assertActionSendMultiple(extras, BUGREPORT_CONTENT, SCREENSHOT_CONTENT);
495 }
496
497 public void testBugreportFinished_zippedBugreportAndScreenshot() throws Exception {
Felipe Leme93702ab2015-12-11 13:06:45 -0800498 Bundle extras = sendBugreportFinishedAndGetSharedIntent(mZipPath, mScreenshotPath);
Felipe Lemee53e85f2015-11-17 17:37:53 -0800499 assertActionSendMultiple(extras, BUGREPORT_CONTENT, SCREENSHOT_CONTENT);
500 }
501
502 public void testBugreportFinished_plainBugreportAndNoScreenshot() throws Exception {
Felipe Leme4967f732016-01-06 11:38:53 -0800503 Bundle extras = sendBugreportFinishedAndGetSharedIntent(mPlainTextPath, NO_SCREENSHOT);
504 assertActionSendMultiple(extras, BUGREPORT_CONTENT, NO_SCREENSHOT);
Felipe Lemee53e85f2015-11-17 17:37:53 -0800505 }
506
507 public void testBugreportFinished_zippedBugreportAndNoScreenshot() throws Exception {
Felipe Leme4967f732016-01-06 11:38:53 -0800508 Bundle extras = sendBugreportFinishedAndGetSharedIntent(mZipPath, NO_SCREENSHOT);
509 assertActionSendMultiple(extras, BUGREPORT_CONTENT, NO_SCREENSHOT);
Felipe Lemee53e85f2015-11-17 17:37:53 -0800510 }
511
512 private void cancelExistingNotifications() {
513 NotificationManager nm = NotificationManager.from(mContext);
514 for (StatusBarNotification notification : nm.getActiveNotifications()) {
515 int id = notification.getId();
516 Log.i(TAG, "Canceling existing notification (id=" + id + ")");
517 nm.cancel(id);
518 }
519 }
520
Felipe Leme69c02922015-11-24 17:48:05 -0800521 private void assertProgressNotification(String name, String percent) {
Felipe Lemed1e0f122015-12-18 16:12:41 -0800522 // TODO: it currently looks for 3 distinct objects, without taking advantage of their
Felipe Leme69c02922015-11-24 17:48:05 -0800523 // relationship.
Felipe Leme85ae3cf2016-02-24 15:36:50 -0800524 openProgressNotification(ID);
Felipe Leme69c02922015-11-24 17:48:05 -0800525 Log.v(TAG, "Looking for progress notification details: '" + name + "-" + percent + "'");
526 mUiBot.getObject(name);
527 mUiBot.getObject(percent);
528 }
529
Felipe Leme85ae3cf2016-02-24 15:36:50 -0800530 private void openProgressNotification(int id) {
531 String title = mContext.getString(R.string.bugreport_in_progress_title, id);
Felipe Lemebc73ffc2015-12-11 15:07:14 -0800532 Log.v(TAG, "Looking for progress notification title: '" + title + "'");
533 mUiBot.getNotification(title);
534 }
535
Felipe Leme93702ab2015-12-11 13:06:45 -0800536 void resetProperties() {
537 // TODO: call method to remove property instead
Felipe Leme85ae3cf2016-02-24 15:36:50 -0800538 SystemProperties.set(PROGRESS_PROPERTY, "Reset");
539 SystemProperties.set(MAX_PROPERTY, "Reset");
540 SystemProperties.set(NAME_PROPERTY, "Reset");
Felipe Leme93702ab2015-12-11 13:06:45 -0800541 }
542
543 /**
544 * Sends a "bugreport started" intent with the default values.
545 */
Felipe Lemed1e0f122015-12-18 16:12:41 -0800546 private void sendBugreportStarted(int max) throws Exception {
Felipe Leme26288782016-02-25 12:10:43 -0800547 sendBugreportStarted(ID, PID, NAME, max);
548 }
549
550 private void sendBugreportStarted(int id, int pid, String name, int max) throws Exception {
Felipe Leme93702ab2015-12-11 13:06:45 -0800551 Intent intent = new Intent(INTENT_BUGREPORT_STARTED);
Felipe Lemeec175382016-01-26 18:17:15 -0800552 intent.setFlags(Intent.FLAG_RECEIVER_FOREGROUND);
Felipe Leme26288782016-02-25 12:10:43 -0800553 intent.putExtra(EXTRA_ID, id);
554 intent.putExtra(EXTRA_PID, pid);
555 intent.putExtra(EXTRA_NAME, name);
Felipe Leme93702ab2015-12-11 13:06:45 -0800556 intent.putExtra(EXTRA_MAX, max);
557 mContext.sendBroadcast(intent);
558 }
559
Felipe Lemee53e85f2015-11-17 17:37:53 -0800560 /**
561 * Sends a "bugreport finished" intent and waits for the result.
562 *
Felipe Leme93702ab2015-12-11 13:06:45 -0800563 * @return extras sent in the shared intent.
Felipe Lemee53e85f2015-11-17 17:37:53 -0800564 */
Felipe Leme93702ab2015-12-11 13:06:45 -0800565 private Bundle sendBugreportFinishedAndGetSharedIntent(String bugreportPath,
566 String screenshotPath) {
Felipe Leme85ae3cf2016-02-24 15:36:50 -0800567 return sendBugreportFinishedAndGetSharedIntent(NO_ID, bugreportPath, screenshotPath);
Felipe Leme69c02922015-11-24 17:48:05 -0800568 }
569
Felipe Leme93702ab2015-12-11 13:06:45 -0800570 /**
571 * Sends a "bugreport finished" intent and waits for the result.
572 *
573 * @return extras sent in the shared intent.
574 */
Felipe Leme85ae3cf2016-02-24 15:36:50 -0800575 private Bundle sendBugreportFinishedAndGetSharedIntent(int id, String bugreportPath,
Felipe Leme69c02922015-11-24 17:48:05 -0800576 String screenshotPath) {
Felipe Leme85ae3cf2016-02-24 15:36:50 -0800577 sendBugreportFinished(id, bugreportPath, screenshotPath);
578 return acceptBugreportAndGetSharedIntent(id);
Felipe Leme93702ab2015-12-11 13:06:45 -0800579 }
580
581 /**
582 * Accepts the notification to share the finished bugreport and waits for the result.
583 *
584 * @return extras sent in the shared intent.
585 */
Felipe Leme85ae3cf2016-02-24 15:36:50 -0800586 private Bundle acceptBugreportAndGetSharedIntent(int id) {
587 acceptBugreport(id);
Felipe Leme93702ab2015-12-11 13:06:45 -0800588 mUiBot.chooseActivity(UI_NAME);
589 return mListener.getExtras();
590 }
591
592 /**
Felipe Lemec4f646772016-01-12 18:12:09 -0800593 * Waits for the notification to share the finished bugreport.
594 */
Felipe Leme85ae3cf2016-02-24 15:36:50 -0800595 private void waitShareNotification(int id) {
596 mUiBot.getNotification(mContext.getString(R.string.bugreport_finished_title, id));
Felipe Lemec4f646772016-01-12 18:12:09 -0800597 }
598
599 /**
Felipe Leme93702ab2015-12-11 13:06:45 -0800600 * Accepts the notification to share the finished bugreport.
601 */
Felipe Leme85ae3cf2016-02-24 15:36:50 -0800602 private void acceptBugreport(int id) {
603 mUiBot.clickOnNotification(mContext.getString(R.string.bugreport_finished_title, id));
Felipe Leme93702ab2015-12-11 13:06:45 -0800604 }
605
606 /**
607 * Sends a "bugreport finished" intent.
Felipe Leme93702ab2015-12-11 13:06:45 -0800608 */
Felipe Leme85ae3cf2016-02-24 15:36:50 -0800609 private void sendBugreportFinished(int id, String bugreportPath, String screenshotPath) {
Felipe Leme69c02922015-11-24 17:48:05 -0800610 Intent intent = new Intent(INTENT_BUGREPORT_FINISHED);
Felipe Lemeec175382016-01-26 18:17:15 -0800611 intent.setFlags(Intent.FLAG_RECEIVER_FOREGROUND);
Felipe Leme85ae3cf2016-02-24 15:36:50 -0800612 if (id != NO_ID) {
613 intent.putExtra(EXTRA_ID, id);
Felipe Leme69c02922015-11-24 17:48:05 -0800614 }
Felipe Lemee53e85f2015-11-17 17:37:53 -0800615 if (bugreportPath != null) {
616 intent.putExtra(EXTRA_BUGREPORT, bugreportPath);
617 }
618 if (screenshotPath != null) {
619 intent.putExtra(EXTRA_SCREENSHOT, screenshotPath);
620 }
621
622 mContext.sendBroadcast(intent);
Felipe Lemee53e85f2015-11-17 17:37:53 -0800623 }
624
625 /**
Felipe Lemed1e0f122015-12-18 16:12:41 -0800626 * Asserts the proper {@link Intent#ACTION_SEND_MULTIPLE} intent was sent.
Felipe Lemee53e85f2015-11-17 17:37:53 -0800627 */
628 private void assertActionSendMultiple(Bundle extras, String bugreportContent,
629 String screenshotContent) throws IOException {
Felipe Leme85ae3cf2016-02-24 15:36:50 -0800630 assertActionSendMultiple(extras, bugreportContent, screenshotContent, ID, PID, ZIP_FILE,
Felipe Lemec8e2b602016-01-29 13:55:35 -0800631 NO_NAME, NO_TITLE, NO_DESCRIPTION, 0, DIDNT_RENAME_SCREENSHOTS);
Felipe Lemebc73ffc2015-12-11 15:07:14 -0800632 }
633
Felipe Lemed1e0f122015-12-18 16:12:41 -0800634 /**
635 * Asserts the proper {@link Intent#ACTION_SEND_MULTIPLE} intent was sent.
636 *
637 * @param extras extras received in the intent
638 * @param bugreportContent expected content in the bugreport file
639 * @param screenshotContent expected content in the screenshot file (sent by dumpstate), if any
Felipe Leme85ae3cf2016-02-24 15:36:50 -0800640 * @param id emulated dumpstate id
Felipe Lemed1e0f122015-12-18 16:12:41 -0800641 * @param pid emulated dumpstate pid
Felipe Leme4967f732016-01-06 11:38:53 -0800642 * @param name expected subject
643 * @param name bugreport name as provided by the user (or received by dumpstate)
644 * @param title bugreport name as provided by the user
Felipe Lemed1e0f122015-12-18 16:12:41 -0800645 * @param description bugreport description as provided by the user
646 * @param numberScreenshots expected number of screenshots taken by Shell.
Felipe Leme22881292016-01-06 09:57:23 -0800647 * @param renamedScreenshots whether the screenshots are expected to be renamed
Felipe Lemed1e0f122015-12-18 16:12:41 -0800648 */
649 private void assertActionSendMultiple(Bundle extras, String bugreportContent,
Felipe Leme85ae3cf2016-02-24 15:36:50 -0800650 String screenshotContent, int id, int pid, String subject,
Felipe Leme4967f732016-01-06 11:38:53 -0800651 String name, String title, String description,
Felipe Leme22881292016-01-06 09:57:23 -0800652 int numberScreenshots, boolean renamedScreenshots) throws IOException {
Felipe Lemee53e85f2015-11-17 17:37:53 -0800653 String body = extras.getString(Intent.EXTRA_TEXT);
654 assertContainsRegex("missing build info",
655 SystemProperties.get("ro.build.description"), body);
656 assertContainsRegex("missing serial number",
657 SystemProperties.get("ro.serialno"), body);
Felipe Lemebc73ffc2015-12-11 15:07:14 -0800658 if (description != null) {
659 assertContainsRegex("missing description", description, body);
660 }
Felipe Lemee53e85f2015-11-17 17:37:53 -0800661
Felipe Leme4967f732016-01-06 11:38:53 -0800662 assertEquals("wrong subject", subject, extras.getString(Intent.EXTRA_SUBJECT));
Felipe Lemee53e85f2015-11-17 17:37:53 -0800663
664 List<Uri> attachments = extras.getParcelableArrayList(Intent.EXTRA_STREAM);
Felipe Lemed1e0f122015-12-18 16:12:41 -0800665 int expectedNumberScreenshots = numberScreenshots;
666 if (screenshotContent != null) {
667 expectedNumberScreenshots ++; // Add screenshot received by dumpstate
668 }
669 int expectedSize = expectedNumberScreenshots + 1; // All screenshots plus the bugreport file
Felipe Lemec4f646772016-01-12 18:12:09 -0800670 assertEquals("wrong number of attachments (" + attachments + ")",
671 expectedSize, attachments.size());
Felipe Lemee53e85f2015-11-17 17:37:53 -0800672
673 // Need to interact through all attachments, since order is not guaranteed.
Felipe Lemed1e0f122015-12-18 16:12:41 -0800674 Uri zipUri = null;
675 List<Uri> screenshotUris = new ArrayList<>(expectedNumberScreenshots);
Felipe Lemee53e85f2015-11-17 17:37:53 -0800676 for (Uri attachment : attachments) {
677 if (attachment.getPath().endsWith(".zip")) {
678 zipUri = attachment;
679 }
680 if (attachment.getPath().endsWith(".png")) {
Felipe Lemed1e0f122015-12-18 16:12:41 -0800681 screenshotUris.add(attachment);
Felipe Lemee53e85f2015-11-17 17:37:53 -0800682 }
683 }
684 assertNotNull("did not get .zip attachment", zipUri);
685 assertZipContent(zipUri, BUGREPORT_FILE, BUGREPORT_CONTENT);
Felipe Leme4967f732016-01-06 11:38:53 -0800686 if (!TextUtils.isEmpty(title)) {
687 assertZipContent(zipUri, "title.txt", title);
688 }
689 if (!TextUtils.isEmpty(description)) {
690 assertZipContent(zipUri, "description.txt", description);
691 }
Felipe Lemee53e85f2015-11-17 17:37:53 -0800692
Felipe Lemed1e0f122015-12-18 16:12:41 -0800693 // URI of the screenshot taken by dumpstate.
694 Uri externalScreenshotUri = null;
695 SortedSet<String> internalScreenshotNames = new TreeSet<>();
696 for (Uri screenshotUri : screenshotUris) {
697 String screenshotName = screenshotUri.getLastPathSegment();
698 if (screenshotName.endsWith(SCREENSHOT_FILE)) {
699 externalScreenshotUri = screenshotUri;
700 } else {
701 internalScreenshotNames.add(screenshotName);
702 }
Felipe Lemee53e85f2015-11-17 17:37:53 -0800703 }
Felipe Lemed1e0f122015-12-18 16:12:41 -0800704 // Check external screenshot
705 if (screenshotContent != null) {
706 assertNotNull("did not get .png attachment for external screenshot",
707 externalScreenshotUri);
708 assertContent(externalScreenshotUri, SCREENSHOT_CONTENT);
709 } else {
710 assertNull("should not have .png attachment for external screenshot",
711 externalScreenshotUri);
712 }
713 // Check internal screenshots.
714 SortedSet<String> expectedNames = new TreeSet<>();
715 for (int i = 1 ; i <= numberScreenshots; i++) {
Felipe Leme22881292016-01-06 09:57:23 -0800716 String prefix = renamedScreenshots ? name : Integer.toString(pid);
Felipe Lemed1e0f122015-12-18 16:12:41 -0800717 String expectedName = "screenshot-" + prefix + "-" + i + ".png";
718 expectedNames.add(expectedName);
719 }
720 // Ideally we should use MoreAsserts, but the error message in case of failure is not
721 // really useful.
722 assertEquals("wrong names for internal screenshots",
723 expectedNames, internalScreenshotNames);
Felipe Lemee53e85f2015-11-17 17:37:53 -0800724 }
725
726 private void assertContent(Uri uri, String expectedContent) throws IOException {
727 Log.v(TAG, "assertContents(uri=" + uri);
728 try (InputStream is = mContext.getContentResolver().openInputStream(uri)) {
729 String actualContent = new String(Streams.readFully(is));
730 assertEquals("wrong content for '" + uri + "'", expectedContent, actualContent);
731 }
732 }
733
734 private void assertZipContent(Uri uri, String entryName, String expectedContent)
735 throws IOException, IOException {
736 Log.v(TAG, "assertZipEntry(uri=" + uri + ", entryName=" + entryName);
737 try (ZipInputStream zis = new ZipInputStream(mContext.getContentResolver().openInputStream(
738 uri))) {
739 ZipEntry entry;
740 while ((entry = zis.getNextEntry()) != null) {
741 Log.v(TAG, "Zip entry: " + entry.getName());
742 if (entry.getName().equals(entryName)) {
743 ByteArrayOutputStream bos = new ByteArrayOutputStream();
744 Streams.copy(zis, bos);
745 String actualContent = new String(bos.toByteArray(), "UTF-8");
746 bos.close();
747 assertEquals("wrong content for zip entry'" + entryName + "' on '" + uri + "'",
748 expectedContent, actualContent);
749 return;
750 }
751 }
752 }
753 fail("Did not find entry '" + entryName + "' on file '" + uri + "'");
754 }
755
Felipe Lemebc73ffc2015-12-11 15:07:14 -0800756 private void assertPropertyValue(String key, String expectedValue) {
757 String actualValue = SystemProperties.get(key);
758 assertEquals("Wrong value for property '" + key + "'", expectedValue, actualValue);
759 }
760
Felipe Leme93702ab2015-12-11 13:06:45 -0800761 private void assertServiceNotRunning() {
762 String service = BugreportProgressService.class.getName();
763 assertFalse("Service '" + service + "' is still running", isServiceRunning(service));
764 }
765
Felipe Lemec4f646772016-01-12 18:12:09 -0800766 private void killService() {
767 waitForService(true);
768 Log.v(TAG, "Stopping service");
769 boolean stopped = mContext.stopService(new Intent(mContext, BugreportProgressService.class));
770 Log.d(TAG, "stopService returned " + stopped);
771 waitForService(false);
772 assertServiceNotRunning(); // Sanity check.
773 }
774
Felipe Lemeba477932015-12-09 11:04:59 -0800775 private boolean isServiceRunning(String name) {
776 ActivityManager manager = (ActivityManager) mContext
777 .getSystemService(Context.ACTIVITY_SERVICE);
778 for (RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)) {
779 if (service.service.getClassName().equals(name)) {
780 return true;
781 }
782 }
783 return false;
784 }
785
Felipe Lemec4f646772016-01-12 18:12:09 -0800786 private void waitForService(boolean expectRunning) {
787 String service = BugreportProgressService.class.getName();
788 boolean actualRunning;
789 for (int i = 1; i <= 5; i++) {
790 actualRunning = isServiceRunning(service);
791 Log.d(TAG, "Attempt " + i + " to check status of service '"
792 + service + "': expected=" + expectRunning + ", actual= " + actualRunning);
793 if (actualRunning == expectRunning) {
794 return;
795 }
796 try {
797 Thread.sleep(DateUtils.SECOND_IN_MILLIS);
798 } catch (InterruptedException e) {
799 Log.w(TAG, "thread interrupted");
800 Thread.currentThread().interrupt();
801 }
802 }
803 if (!expectRunning) {
804 // Typically happens when service is waiting for a screenshot to finish.
805 Log.w(TAG, "Service didn't stop; try to kill it again");
806 killService();
807 return;
808 }
809
810 fail("Service status didn't change to " + expectRunning);
811 }
812
Felipe Leme26288782016-02-25 12:10:43 -0800813 private void createTextFile(String path, String content) throws IOException {
Felipe Lemee53e85f2015-11-17 17:37:53 -0800814 Log.v(TAG, "createFile(" + path + ")");
815 try (Writer writer = new BufferedWriter(new OutputStreamWriter(
816 new FileOutputStream(path)))) {
817 writer.write(content);
818 }
819 }
820
821 private void createZipFile(String path, String entryName, String content) throws IOException {
822 Log.v(TAG, "createZipFile(" + path + ", " + entryName + ")");
823 try (ZipOutputStream zos = new ZipOutputStream(
824 new BufferedOutputStream(new FileOutputStream(path)))) {
825 ZipEntry entry = new ZipEntry(entryName);
826 zos.putNextEntry(entry);
827 byte[] data = content.getBytes();
828 zos.write(data, 0, data.length);
829 zos.closeEntry();
830 }
831 }
Felipe Lemea0bf0332015-12-11 09:35:03 -0800832
833 private String getPath(String file) {
834 File rootDir = new ContextWrapper(mContext).getFilesDir();
835 File dir = new File(rootDir, BUGREPORTS_DIR);
Felipe Lemee2b4f492015-12-11 18:19:26 -0800836 if (!dir.exists()) {
837 Log.i(TAG, "Creating directory " + dir);
838 assertTrue("Could not create directory " + dir, dir.mkdir());
839 }
Felipe Lemea0bf0332015-12-11 09:35:03 -0800840 String path = new File(dir, file).getAbsolutePath();
841 Log.v(TAG, "Path for '" + file + "': " + path);
842 return path;
843 }
Felipe Lemebc73ffc2015-12-11 15:07:14 -0800844
845 /**
Felipe Lemed1e0f122015-12-18 16:12:41 -0800846 * Gets the notification button used to take a screenshot.
847 */
848 private UiObject getScreenshotButton() {
Felipe Leme85ae3cf2016-02-24 15:36:50 -0800849 openProgressNotification(ID);
Felipe Lemed1e0f122015-12-18 16:12:41 -0800850 return mUiBot.getVisibleObject(
851 mContext.getString(R.string.bugreport_screenshot_action).toUpperCase());
852 }
853
854 /**
855 * Takes a screenshot using the system notification.
856 */
857 private void takeScreenshot() throws Exception {
858 UiObject screenshotButton = getScreenshotButton();
859 mUiBot.click(screenshotButton, "screenshot_button");
860 }
861
862 private UiObject waitForScreenshotButtonEnabled(boolean expectedEnabled) throws Exception {
863 UiObject screenshotButton = getScreenshotButton();
Felipe Lemec4f646772016-01-12 18:12:09 -0800864 int maxAttempts = SAFE_SCREENSHOT_DELAY;
Felipe Lemed1e0f122015-12-18 16:12:41 -0800865 int i = 0;
866 do {
867 boolean enabled = screenshotButton.isEnabled();
868 if (enabled == expectedEnabled) {
869 return screenshotButton;
870 }
871 i++;
872 Log.v(TAG, "Sleeping for 1 second while waiting for screenshot.enable to be "
873 + expectedEnabled + " (attempt " + i + ")");
874 Thread.sleep(DateUtils.SECOND_IN_MILLIS);
875 } while (i <= maxAttempts);
876 fail("screenshot.enable didn't change to " + expectedEnabled + " in " + maxAttempts + "s");
877 return screenshotButton;
878 }
879
880 private void assertScreenshotButtonEnabled(boolean expectedEnabled) throws Exception {
881 UiObject screenshotButton = getScreenshotButton();
882 assertEquals("wrong state for screenshot button ", expectedEnabled,
883 screenshotButton.isEnabled());
884 }
885
886 /**
Felipe Lemebc73ffc2015-12-11 15:07:14 -0800887 * Helper class containing the UiObjects present in the bugreport info dialog.
888 */
889 private final class DetailsUi {
890
891 final UiObject detailsButton;
892 final UiObject nameField;
893 final UiObject titleField;
894 final UiObject descField;
895 final UiObject okButton;
896 final UiObject cancelButton;
897
898 /**
899 * Gets the UI objects by opening the progress notification and clicking DETAILS.
900 */
Felipe Leme85ae3cf2016-02-24 15:36:50 -0800901 DetailsUi(UiBot uiBot, int id) throws UiObjectNotFoundException {
902 openProgressNotification(id);
Felipe Leme26288782016-02-25 12:10:43 -0800903 detailsButton = mUiBot.getVisibleObject(mContext.getString(
904 R.string.bugreport_info_action).toUpperCase());
905
Felipe Lemebc73ffc2015-12-11 15:07:14 -0800906 mUiBot.click(detailsButton, "details_button");
907 // TODO: unhardcode resource ids
Felipe Lemee86b63b2016-02-08 09:39:50 -0800908 UiObject dialogTitle = mUiBot.getVisibleObjectById("android:id/alertTitle");
909 assertEquals("Wrong title", mContext.getString(R.string.bugreport_info_dialog_title,
Felipe Leme85ae3cf2016-02-24 15:36:50 -0800910 id), dialogTitle.getText().toString());
Felipe Lemebc73ffc2015-12-11 15:07:14 -0800911 nameField = mUiBot.getVisibleObjectById("com.android.shell:id/name");
912 titleField = mUiBot.getVisibleObjectById("com.android.shell:id/title");
913 descField = mUiBot.getVisibleObjectById("com.android.shell:id/description");
914 okButton = mUiBot.getObjectById("android:id/button1");
915 cancelButton = mUiBot.getObjectById("android:id/button2");
916 }
917
Felipe Leme26288782016-02-25 12:10:43 -0800918 private void assertField(String name, UiObject field, String expected) {
919 try {
920 String actual = field.getText().toString();
921 assertEquals("Wrong value on field '" + name + "'", expected, actual);
922 } catch (UiObjectNotFoundException e) {
923 // Should not happen...
924 throw new IllegalStateException("field not found: " + name, e);
925 }
926 }
927
928 void assertName(String expected) {
929 assertField("name", nameField, expected);
930 }
931
932 void assertTitle(String expected) {
933 assertField("title", titleField, expected);
934 }
935
936 void assertDescription(String expected) {
937 assertField("description", descField, expected);
938 }
939
Felipe Lemebc73ffc2015-12-11 15:07:14 -0800940 /**
941 * Takes focus away from the name field so it can be validated.
942 */
943 void focusAwayFromName() {
944 mUiBot.click(titleField, "title_field"); // Change focus.
945 mUiBot.pressBack(); // Dismiss keyboard.
946 }
947
948 void reOpen() {
Felipe Leme85ae3cf2016-02-24 15:36:50 -0800949 openProgressNotification(ID);
Felipe Lemebc73ffc2015-12-11 15:07:14 -0800950 mUiBot.click(detailsButton, "details_button");
951
952 }
953
954 void clickOk() {
955 mUiBot.click(okButton, "details_ok_button");
956 }
957
958 void clickCancel() {
959 mUiBot.click(cancelButton, "details_cancel_button");
960 }
961 }
Felipe Lemee53e85f2015-11-17 17:37:53 -0800962}