OLD | NEW |
(Empty) | |
| 1 // Copyright 2015 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. |
| 4 |
| 5 package org.chromium.chrome.browser.crash; |
| 6 |
| 7 import android.content.ComponentName; |
| 8 import android.content.Context; |
| 9 import android.content.Intent; |
| 10 import android.os.Handler; |
| 11 import android.os.HandlerThread; |
| 12 import android.test.suitebuilder.annotation.SmallTest; |
| 13 |
| 14 import org.chromium.base.annotations.SuppressFBWarnings; |
| 15 import org.chromium.base.test.util.AdvancedMockContext; |
| 16 import org.chromium.base.test.util.Feature; |
| 17 import org.chromium.content.browser.test.util.Criteria; |
| 18 import org.chromium.content.browser.test.util.CriteriaHelper; |
| 19 import org.chromium.net.NetworkChangeNotifier; |
| 20 |
| 21 import java.io.File; |
| 22 import java.io.IOException; |
| 23 import java.util.ArrayList; |
| 24 import java.util.List; |
| 25 import java.util.concurrent.atomic.AtomicInteger; |
| 26 |
| 27 /** |
| 28 * Testcase for {@link MinidumpUploadService}. |
| 29 */ |
| 30 public class MinidumpUploadServiceTest extends CrashTestCase { |
| 31 |
| 32 private static final int CHECK_INTERVAL_MS = 250; |
| 33 private static final int MAX_TIMEOUT_MS = 20000; |
| 34 private static final String BOUNDARY = "TESTBOUNDARY"; |
| 35 |
| 36 @SmallTest |
| 37 @Feature({"Android-AppBase"}) |
| 38 public void testFindAndUploadLastCrash() throws IOException { |
| 39 // Setup prerequisites. |
| 40 final AtomicInteger numServiceStarts = new AtomicInteger(0); |
| 41 final File minidumpFile = new File(mCrashDir, "chromium_renderer-123.dmp
"); |
| 42 MinidumpPreparationContext context = new MinidumpPreparationContext( |
| 43 getInstrumentation().getTargetContext()) { |
| 44 @Override |
| 45 public ComponentName startService(Intent intentToCheck) { |
| 46 String filePath = |
| 47 intentToCheck.getStringExtra(MinidumpUploadService.FILE_
TO_UPLOAD_KEY); |
| 48 assertEquals("Minidump path should be the absolute path", |
| 49 minidumpFile.getAbsolutePath(), filePath); |
| 50 assertEquals("Should only call service once", 1, |
| 51 numServiceStarts.incrementAndGet()); |
| 52 assertEquals("Action should be correct", |
| 53 MinidumpUploadService.ACTION_UPLOAD, intentToCheck.getAc
tion()); |
| 54 return new ComponentName(getPackageName(), MinidumpUploadService
.class.getName()); |
| 55 } |
| 56 |
| 57 }; |
| 58 MinidumpUploadService service = new TestMinidumpUploadService(context); |
| 59 setUpMinidumpFile(minidumpFile, BOUNDARY); |
| 60 |
| 61 // Run test. |
| 62 Intent findAndUploadLastCrashIntent = |
| 63 MinidumpUploadService.createFindAndUploadLastCrashIntent(context
); |
| 64 service.onCreate(); |
| 65 service.onHandleIntent(findAndUploadLastCrashIntent); |
| 66 |
| 67 // Verify. |
| 68 assertTrue("Minidump file should exist", minidumpFile.isFile()); |
| 69 assertEquals("Should have called startService() once", 1, numServiceStar
ts.intValue()); |
| 70 } |
| 71 |
| 72 private static class TestMinidumpUploadService extends MinidumpUploadService
{ |
| 73 private TestMinidumpUploadService() {} |
| 74 private TestMinidumpUploadService(Context context) { |
| 75 attachBaseContext(context); |
| 76 } |
| 77 |
| 78 private void attachBaseContextLate(Context base) { |
| 79 super.attachBaseContext(base); |
| 80 } |
| 81 } |
| 82 |
| 83 @SmallTest |
| 84 @Feature({"Android-AppBase"}) |
| 85 public void testFindAndUploadLastCrashNoFile() { |
| 86 // Setup prerequisites. |
| 87 MinidumpPreparationContext context = new MinidumpPreparationContext( |
| 88 getInstrumentation().getTargetContext()) { |
| 89 @Override |
| 90 public ComponentName startService(Intent intentToCheck) { |
| 91 fail("Should not start service"); |
| 92 return new ComponentName(getPackageName(), MinidumpUploadService
.class.getName()); |
| 93 } |
| 94 |
| 95 }; |
| 96 MinidumpUploadService service = new TestMinidumpUploadService(context); |
| 97 |
| 98 // Run test. |
| 99 Intent findAndUploadLastCrashIntent = |
| 100 MinidumpUploadService.createFindAndUploadLastCrashIntent(context
); |
| 101 service.onCreate(); |
| 102 service.onHandleIntent(findAndUploadLastCrashIntent); |
| 103 |
| 104 // Verification is done by the test NOT failing with fail(...) in the |
| 105 // MinidumpPreparationContext startService(...) method. |
| 106 } |
| 107 |
| 108 @SmallTest |
| 109 @Feature({"Android-AppBase"}) |
| 110 public void testFindAndUploadAllCrashes() throws IOException { |
| 111 // Setup prerequisites. |
| 112 final AtomicInteger numServiceStarts = new AtomicInteger(0); |
| 113 final File[] minidumpFiles = { |
| 114 new File(mCrashDir, "chromium_renderer-111.dmp1"), |
| 115 new File(mCrashDir, "chromium_renderer-222.dmp2"), |
| 116 new File(mCrashDir, "chromium_renderer-333.dmp3"), |
| 117 }; |
| 118 MinidumpPreparationContext context = new MinidumpPreparationContext( |
| 119 getInstrumentation().getTargetContext()) { |
| 120 @Override |
| 121 public ComponentName startService(Intent intentToCheck) { |
| 122 String filePath = |
| 123 intentToCheck.getStringExtra(MinidumpUploadService.FILE_
TO_UPLOAD_KEY); |
| 124 // Assuming numServicesStart value corresponds to minidumpFiles
index. |
| 125 assertEquals("Minidump path should be the absolute path", |
| 126 minidumpFiles[numServiceStarts.intValue()].getAbsolutePa
th(), filePath); |
| 127 assertTrue("Should not call service more than number of files", |
| 128 numServiceStarts.incrementAndGet() <= minidumpFiles.leng
th); |
| 129 assertEquals("Action should be correct", |
| 130 MinidumpUploadService.ACTION_UPLOAD, intentToCheck.getAc
tion()); |
| 131 return new ComponentName(getPackageName(), MinidumpUploadService
.class.getName()); |
| 132 } |
| 133 |
| 134 }; |
| 135 MinidumpUploadService service = new TestMinidumpUploadService(context); |
| 136 for (File minidumpFile : minidumpFiles) { |
| 137 setUpMinidumpFile(minidumpFile, BOUNDARY); |
| 138 } |
| 139 |
| 140 // Run test. |
| 141 Intent findAndUploadAllCrashesIntent = |
| 142 MinidumpUploadService.createFindAndUploadAllCrashesIntent(contex
t); |
| 143 service.onCreate(); |
| 144 service.onHandleIntent(findAndUploadAllCrashesIntent); |
| 145 |
| 146 // Verify. |
| 147 for (File minidumpFile : minidumpFiles) { |
| 148 assertTrue("Minidump file should exist: " + minidumpFile, minidumpFi
le.isFile()); |
| 149 } |
| 150 assertEquals("Should have called startService() same number of times as
there are files", |
| 151 minidumpFiles.length, numServiceStarts.intValue()); |
| 152 } |
| 153 |
| 154 @SmallTest |
| 155 @Feature({"Android-AppBase"}) |
| 156 public void testUploadCrash() throws IOException, InterruptedException { |
| 157 List<CountedMinidumpUploadCallable> callables = |
| 158 new ArrayList<CountedMinidumpUploadCallable>(); |
| 159 callables.add(new CountedMinidumpUploadCallable( |
| 160 "chromium_renderer-111.dmp1", true, false)); |
| 161 runUploadCrashTest(callables); |
| 162 } |
| 163 |
| 164 @SmallTest |
| 165 @Feature({"Android-AppBase"}) |
| 166 public void testUploadCrashWithThreeFails() throws IOException, InterruptedE
xception { |
| 167 // Create |MAX_TRIES_ALLOWED| callables. |
| 168 final List<CountedMinidumpUploadCallable> callables = |
| 169 new ArrayList<CountedMinidumpUploadCallable>(); |
| 170 for (int i = 0; i < MinidumpUploadService.MAX_TRIES_ALLOWED; i++) { |
| 171 callables.add(new CountedMinidumpUploadCallable( |
| 172 "chromium_renderer-111.dmp1" + (i > 0 ? ".try" + i : "") , f
alse, true)); |
| 173 } |
| 174 runUploadCrashTest(callables); |
| 175 } |
| 176 |
| 177 @SmallTest |
| 178 @Feature({"Android-AppBase"}) |
| 179 public void testUploadCrashWithOneFailWithNetwork() throws IOException, Inte
rruptedException { |
| 180 List<CountedMinidumpUploadCallable> callables = |
| 181 new ArrayList<CountedMinidumpUploadCallable>(); |
| 182 callables.add(new CountedMinidumpUploadCallable( |
| 183 "chromium_renderer-111.dmp1", false, true)); |
| 184 callables.add(new CountedMinidumpUploadCallable( |
| 185 "chromium_renderer-111.dmp1.try1", true, true)); |
| 186 runUploadCrashTest(callables); |
| 187 } |
| 188 |
| 189 @SmallTest |
| 190 @Feature({"Android-AppBase"}) |
| 191 public void testUploadCrashWithOneFailNoNetwork() throws IOException, Interr
uptedException { |
| 192 List<CountedMinidumpUploadCallable> callables = |
| 193 new ArrayList<CountedMinidumpUploadCallable>(); |
| 194 callables.add(new CountedMinidumpUploadCallable( |
| 195 "chromium_renderer-111.dmp1", false, false)); |
| 196 runUploadCrashTest(callables); |
| 197 } |
| 198 |
| 199 @SuppressFBWarnings("RV_RETURN_VALUE_IGNORED_BAD_PRACTICE") |
| 200 private void runUploadCrashTest(final List<CountedMinidumpUploadCallable> ca
llables) |
| 201 throws IOException, InterruptedException { |
| 202 // Setup prerequisites. |
| 203 // This version of the service overrides the createMinidumpUploadCallabl
e(...) to be able |
| 204 // to return fake ones. It also ensures that the service never tries to
create a callable |
| 205 // too many times. |
| 206 TestMinidumpUploadService service = new TestMinidumpUploadService() { |
| 207 int mIndex = 0; |
| 208 boolean mTriggerNetworkChange = false; |
| 209 |
| 210 @Override |
| 211 MinidumpUploadCallable createMinidumpUploadCallable(File minidumpFil
e, File logfile) { |
| 212 if (mIndex >= callables.size()) { |
| 213 fail("Should not create callable number " + mIndex); |
| 214 } |
| 215 CountedMinidumpUploadCallable callable = callables.get(mIndex++)
; |
| 216 if (callable.mTriggerNetworkChange) { |
| 217 mTriggerNetworkChange = true; |
| 218 } |
| 219 return callable; |
| 220 } |
| 221 |
| 222 @Override |
| 223 protected void onHandleIntent(Intent intent) { |
| 224 try { |
| 225 runTestOnUiThread(new Runnable() { |
| 226 @Override |
| 227 public void run() { |
| 228 // Set up basically a fake. |
| 229 if (!NetworkChangeNotifier.isInitialized()) { |
| 230 NetworkChangeNotifier.init(getApplicationContext
()); |
| 231 } |
| 232 } |
| 233 }); |
| 234 } catch (Throwable t) { |
| 235 t.printStackTrace(); |
| 236 fail("Failed to set up NetworkChangeNotifier"); |
| 237 } |
| 238 |
| 239 super.onHandleIntent(intent); |
| 240 |
| 241 if (mTriggerNetworkChange) { |
| 242 mTriggerNetworkChange = false; |
| 243 try { |
| 244 runTestOnUiThread(new Runnable() { |
| 245 @Override |
| 246 public void run() { |
| 247 NetworkChangeNotifier.setAutoDetectConnectivityS
tate(false); |
| 248 // Quickly force the state to be connected and b
ack to disconnected. |
| 249 // An event should be triggered for retry logics
. |
| 250 NetworkChangeNotifier.forceConnectivityState(fal
se); |
| 251 NetworkChangeNotifier.forceConnectivityState(tru
e); |
| 252 } |
| 253 }); |
| 254 } catch (Throwable t) { |
| 255 t.printStackTrace(); |
| 256 fail("Failed to trigger NetworkChangeNotifier"); |
| 257 } |
| 258 } |
| 259 } |
| 260 }; |
| 261 // Create a context that supports call to startService(...), where it ru
ns the new service |
| 262 // calls on a handler thread. We pass in the MinidumpUploadService as an
argument so we |
| 263 // can call it directly without going through the Android framework. |
| 264 final MinidumpPreparationContext context = new MinidumpPreparationContex
t( |
| 265 getInstrumentation().getTargetContext(), service) { |
| 266 Handler mHandler; |
| 267 { |
| 268 HandlerThread handlerThread = |
| 269 new HandlerThread("MinidumpUploadServiceTest Handler Thr
ead"); |
| 270 handlerThread.start(); |
| 271 mHandler = new Handler(handlerThread.getLooper()); |
| 272 } |
| 273 |
| 274 @Override |
| 275 public ComponentName startService(final Intent intentToCheck) { |
| 276 assertTrue(MinidumpUploadService.ACTION_UPLOAD.equals(intentToCh
eck.getAction()) |
| 277 || MinidumpUploadService.ACTION_FIND_ALL.equals(intentTo
Check.getAction())); |
| 278 // Post to the handler thread to run the retry intent. |
| 279 mHandler.post(new Runnable() { |
| 280 @Override |
| 281 public void run() { |
| 282 mService.onHandleIntent(intentToCheck); |
| 283 } |
| 284 }); |
| 285 return new ComponentName(getPackageName(), MinidumpUploadService
.class.getName()); |
| 286 } |
| 287 |
| 288 }; |
| 289 // We need the context before we can attach it to the service, so since
Context is |
| 290 // dependent on the service, we do this after context creation. |
| 291 service.attachBaseContextLate(context); |
| 292 // Create the file used for uploading. |
| 293 File minidumpFile = new File(mCrashDir, "chromium_renderer-111.dmp1"); |
| 294 minidumpFile.createNewFile(); |
| 295 File logfile = new File(mCacheDir, CrashFileManager.CRASH_DUMP_LOGFILE); |
| 296 setUpMinidumpFile(minidumpFile, BOUNDARY); |
| 297 |
| 298 // Run test. |
| 299 Intent uploadIntent = |
| 300 MinidumpUploadService.createUploadIntent(context, minidumpFile,
logfile); |
| 301 service.onCreate(); |
| 302 service.onHandleIntent(uploadIntent); |
| 303 |
| 304 // Verify asynchronously. |
| 305 assertTrue("All callables should have a call-count of 1", |
| 306 CriteriaHelper.pollForCriteria(new Criteria() { |
| 307 @Override |
| 308 public boolean isSatisfied() { |
| 309 for (CountedMinidumpUploadCallable callable : callables)
{ |
| 310 if (callable.mCalledCount != 1) { |
| 311 return false; |
| 312 } |
| 313 } |
| 314 return true; |
| 315 } |
| 316 }, MAX_TIMEOUT_MS, CHECK_INTERVAL_MS)); |
| 317 } |
| 318 |
| 319 /** |
| 320 * This tests how we instantiate crash uploading from Main, so it is just a
sanity check. |
| 321 */ |
| 322 @SmallTest |
| 323 @Feature({"Android-AppBase"}) |
| 324 public void testTryUploadAllCrashDumpsHelperMethod() { |
| 325 // Setup prerequisites. |
| 326 final String startServiceFlag = "startServiceFlag"; |
| 327 MinidumpPreparationContext context = new MinidumpPreparationContext( |
| 328 getInstrumentation().getTargetContext()) { |
| 329 @Override |
| 330 public ComponentName startService(Intent intentToCheck) { |
| 331 assertEquals(MinidumpUploadService.ACTION_FIND_ALL, intentToChec
k.getAction()); |
| 332 setFlag(startServiceFlag); |
| 333 return new ComponentName(getPackageName(), MinidumpUploadService
.class.getName()); |
| 334 } |
| 335 }; |
| 336 |
| 337 // Run test. |
| 338 MinidumpUploadService.tryUploadAllCrashDumps(context); |
| 339 |
| 340 // Verify. |
| 341 assertTrue("Should have called startService(...)", context.isFlagSet(sta
rtServiceFlag)); |
| 342 } |
| 343 |
| 344 private class MinidumpPreparationContext extends AdvancedMockContext { |
| 345 /** |
| 346 * Field used in overridden versions of startService() so we can support
retries. |
| 347 */ |
| 348 protected MinidumpUploadService mService; |
| 349 |
| 350 public MinidumpPreparationContext(Context targetContext) { |
| 351 this(targetContext, null); |
| 352 } |
| 353 |
| 354 public MinidumpPreparationContext(Context targetContext, MinidumpUploadS
ervice service) { |
| 355 super(targetContext); |
| 356 mService = service; |
| 357 } |
| 358 |
| 359 @Override |
| 360 public File getCacheDir() { |
| 361 return mCacheDir; |
| 362 } |
| 363 } |
| 364 |
| 365 /** |
| 366 * A fake callable, that just counts the number of times it is called. |
| 367 * |
| 368 * It can be constructed with the wanted return-value of the call()-method. |
| 369 */ |
| 370 private static class CountedMinidumpUploadCallable extends MinidumpUploadCal
lable { |
| 371 private int mCalledCount; |
| 372 private final boolean mResult; |
| 373 private final boolean mTriggerNetworkChange; |
| 374 |
| 375 /** |
| 376 * Creates a fake callable, that just counts the number of times it is c
alled. |
| 377 * |
| 378 * @param result the value to return from the call()-method. |
| 379 * @param networkChange Should trigger a network change after this calla
ble is finished. |
| 380 * This essentially triggers a retry if result is set to fail. |
| 381 */ |
| 382 private CountedMinidumpUploadCallable( |
| 383 String fileName, boolean result, boolean networkChange) { |
| 384 super(new File(fileName), null, null, null, null); |
| 385 this.mResult = result; |
| 386 this.mTriggerNetworkChange = networkChange; |
| 387 } |
| 388 |
| 389 @Override |
| 390 public Boolean call() { |
| 391 ++mCalledCount; |
| 392 return mResult; |
| 393 } |
| 394 } |
| 395 } |
OLD | NEW |