Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2015 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 package org.chromium.chrome.browser.crash; | 5 package org.chromium.chrome.browser.crash; |
| 6 | 6 |
| 7 import android.annotation.TargetApi; | |
| 8 import android.app.job.JobInfo; | |
| 9 import android.app.job.JobScheduler; | |
| 7 import android.content.ComponentName; | 10 import android.content.ComponentName; |
| 8 import android.content.Context; | 11 import android.content.Context; |
| 9 import android.content.Intent; | 12 import android.content.Intent; |
| 13 import android.os.Build; | |
| 10 import android.support.test.filters.MediumTest; | 14 import android.support.test.filters.MediumTest; |
| 11 | 15 |
| 12 import org.chromium.base.StreamUtil; | 16 import org.chromium.base.StreamUtil; |
| 13 import org.chromium.base.test.util.AdvancedMockContext; | 17 import org.chromium.base.test.util.AdvancedMockContext; |
| 18 import org.chromium.chrome.browser.ChromeFeatureList; | |
| 19 import org.chromium.components.background_task_scheduler.TaskIds; | |
| 14 import org.chromium.components.minidump_uploader.CrashFileManager; | 20 import org.chromium.components.minidump_uploader.CrashFileManager; |
| 15 import org.chromium.components.minidump_uploader.CrashTestCase; | 21 import org.chromium.components.minidump_uploader.CrashTestCase; |
| 16 | 22 |
| 17 import java.io.BufferedReader; | 23 import java.io.BufferedReader; |
| 18 import java.io.File; | 24 import java.io.File; |
| 19 import java.io.FileReader; | 25 import java.io.FileReader; |
| 20 import java.io.FileWriter; | 26 import java.io.FileWriter; |
| 21 import java.io.IOException; | 27 import java.io.IOException; |
| 22 import java.util.ArrayList; | 28 import java.util.Arrays; |
| 29 import java.util.HashMap; | |
| 23 import java.util.List; | 30 import java.util.List; |
| 24 import java.util.concurrent.atomic.AtomicInteger; | 31 import java.util.Map; |
| 25 | 32 |
| 26 /** | 33 /** |
| 27 * Unittests for {@link LogcatExtractionRunnable}. | 34 * Unittests for {@link LogcatExtractionRunnable}. |
| 28 */ | 35 */ |
| 29 public class LogcatExtractionRunnableTest extends CrashTestCase { | 36 public class LogcatExtractionRunnableTest extends CrashTestCase { |
| 30 private File mCrashDir; | 37 private File mCrashDir; |
| 31 | 38 |
| 39 private static final String BOUNDARY = "boundary"; | |
| 40 private static final String MINIDUMP_CONTENTS = "important minidump contents "; | |
| 41 private static final List<String> LOGCAT = | |
| 42 Arrays.asList("some random log content", "some more deterministic lo g content"); | |
| 43 | |
| 44 private static class TestLogcatExtractionRunnable extends LogcatExtractionRu nnable { | |
| 45 TestLogcatExtractionRunnable(Context context, File minidump) { | |
| 46 super(context, minidump); | |
| 47 } | |
| 48 | |
| 49 @Override | |
| 50 protected List<String> getLogcat() { | |
| 51 return LOGCAT; | |
| 52 } | |
| 53 }; | |
| 54 | |
| 55 @TargetApi(Build.VERSION_CODES.LOLLIPOP) | |
| 56 private static class TestJobScheduler extends JobScheduler { | |
| 57 private final JobScheduler mRealScheduler; | |
|
gsennton
2017/03/15 12:57:43
I don't quite see why you have a real JobScheduler
Ilya Sherman
2017/03/16 01:36:57
Hmm, good point. Simplified.
| |
| 58 | |
| 59 TestJobScheduler(JobScheduler realScheduler) { | |
| 60 mRealScheduler = realScheduler; | |
| 61 } | |
| 62 | |
| 63 @Override | |
| 64 public void cancel(int jobId) { | |
| 65 mRealScheduler.cancel(jobId); | |
| 66 } | |
| 67 | |
| 68 @Override | |
| 69 public void cancelAll() { | |
| 70 mRealScheduler.cancelAll(); | |
| 71 } | |
| 72 | |
| 73 @Override | |
| 74 public List<JobInfo> getAllPendingJobs() { | |
| 75 return mRealScheduler.getAllPendingJobs(); | |
| 76 } | |
| 77 | |
| 78 @Override | |
| 79 public JobInfo getPendingJob(int jobId) { | |
| 80 return mRealScheduler.getPendingJob(jobId); | |
| 81 } | |
| 82 | |
| 83 @Override | |
| 84 public int schedule(JobInfo job) { | |
| 85 assertEquals(TaskIds.CHROME_MINIDUMP_UPLOADING_JOB_ID, job.getId()); | |
| 86 assertEquals(ChromeMinidumpUploadJobService.class.getName(), | |
| 87 job.getService().getClassName()); | |
| 88 return mRealScheduler.schedule(job); | |
| 89 } | |
| 90 }; | |
| 91 | |
| 92 // Responsible for verifying that the correct intent is fired after the logc at is extracted. | |
| 93 private class TestContext extends AdvancedMockContext { | |
| 94 int mNumServiceStarts; | |
| 95 | |
| 96 TestContext(Context realContext) { | |
| 97 super(realContext); | |
| 98 } | |
| 99 | |
| 100 @Override | |
| 101 public ComponentName startService(Intent intent) { | |
| 102 assertFalse("Should only start a service directly when the job sched uler is disabled.", | |
| 103 ChromeFeatureList.isEnabled( | |
| 104 ChromeFeatureList.UPLOAD_CRASH_REPORTS_USING_JOB_SCH EDULER)); | |
| 105 ++mNumServiceStarts; | |
| 106 assertEquals(1, mNumServiceStarts); | |
| 107 assertEquals( | |
| 108 MinidumpUploadService.class.getName(), intent.getComponent() .getClassName()); | |
| 109 assertEquals(MinidumpUploadService.ACTION_UPLOAD, intent.getAction() ); | |
| 110 assertEquals(new File(mCrashDir, "test.dmp.try0").getAbsolutePath(), | |
| 111 intent.getStringExtra(MinidumpUploadService.FILE_TO_UPLOAD_K EY)); | |
| 112 return super.startService(intent); | |
| 113 } | |
| 114 | |
| 115 @Override | |
| 116 public Object getSystemService(String name) { | |
| 117 if (Context.JOB_SCHEDULER_SERVICE.equals(name)) { | |
| 118 assertTrue("Should only access the JobScheduler when it is enabl ed.", | |
| 119 ChromeFeatureList.isEnabled( | |
| 120 ChromeFeatureList.UPLOAD_CRASH_REPORTS_USING_JOB _SCHEDULER)); | |
| 121 return new TestJobScheduler((JobScheduler) super.getSystemServic e(name)); | |
| 122 } | |
| 123 | |
| 124 return super.getSystemService(name); | |
| 125 } | |
| 126 }; | |
| 127 | |
| 32 @Override | 128 @Override |
| 33 protected void setUp() throws Exception { | 129 protected void setUp() throws Exception { |
| 34 super.setUp(); | 130 super.setUp(); |
| 35 mCrashDir = new CrashFileManager(mCacheDir).getCrashDirectory(); | 131 mCrashDir = new CrashFileManager(mCacheDir).getCrashDirectory(); |
| 36 } | 132 } |
| 37 | 133 |
| 38 @MediumTest | 134 @Override |
| 39 public void testSimpleExtraction() { | 135 protected void tearDown() { |
| 40 // Set up the test. | 136 ChromeFeatureList.setTestFeatures(null); |
| 41 final List<String> logcat = new ArrayList<String>(); | 137 } |
| 42 logcat.add("some random log content"); | |
| 43 logcat.add("some more deterministic log content"); | |
| 44 | 138 |
| 45 final File minidump = new File(mCrashDir, "test.dmp"); | 139 /** |
| 46 final String boundary = "boundary"; | 140 * Sets whether to upload minidumps using the JobScheduler API. Minidumps ca n either be uploaded |
| 47 final String minidumpContents = "important minidump contents"; | 141 * via a JobScheduler, or via a direct Intent service. |
| 142 * @param enable Whether to enable the JobScheduler API. | |
| 143 */ | |
| 144 private void setJobSchedulerEnabled(boolean enable) { | |
| 145 Map<String, Boolean> features = new HashMap<>(); | |
| 146 features.put(ChromeFeatureList.UPLOAD_CRASH_REPORTS_USING_JOB_SCHEDULER, enable); | |
| 147 ChromeFeatureList.setTestFeatures(features); | |
| 148 } | |
| 149 | |
| 150 /** | |
| 151 * Creates a simple fake minidump file for testing. | |
| 152 * @param filename The name of the file to create. | |
| 153 */ | |
| 154 private File createMinidump(String filename) throws IOException { | |
| 155 File minidump = new File(mCrashDir, filename); | |
| 48 FileWriter writer = null; | 156 FileWriter writer = null; |
| 49 try { | 157 try { |
| 50 writer = new FileWriter(minidump); | 158 writer = new FileWriter(minidump); |
| 51 writer.write(boundary + "\n"); | 159 writer.write(BOUNDARY + "\n"); |
| 52 writer.write(minidumpContents + "\n"); | 160 writer.write(MINIDUMP_CONTENTS + "\n"); |
| 53 } catch (IOException e) { | |
| 54 fail(e.toString()); | |
| 55 } finally { | 161 } finally { |
| 56 StreamUtil.closeQuietly(writer); | 162 StreamUtil.closeQuietly(writer); |
| 57 } | 163 } |
| 164 return minidump; | |
| 165 } | |
| 58 | 166 |
| 59 // The testContext is responsible for verifying that the correct intent is fired after the | 167 /** |
| 60 // logcat is extracted. | 168 * Verifies that the contents of the {@param filename} are the expected ones . |
| 61 final AtomicInteger numServiceStarts = new AtomicInteger(0); | 169 * @param filename The name of the file containing the concatenated logcat a nd minidump output. |
| 62 Context testContext = new AdvancedMockContext(getInstrumentation().getTa rgetContext()) { | 170 */ |
| 63 @Override | 171 private void verifyMinidumpWithLogcat(String filename) throws IOException { |
| 64 public ComponentName startService(Intent intent) { | |
| 65 assertEquals(1, numServiceStarts.incrementAndGet()); | |
| 66 assertEquals(MinidumpUploadService.class.getName(), | |
| 67 intent.getComponent().getClassName()); | |
| 68 assertEquals(MinidumpUploadService.ACTION_UPLOAD, intent.getActi on()); | |
| 69 assertEquals(new File(mCrashDir, "test.dmp.try0").getAbsolutePat h(), | |
| 70 intent.getStringExtra(MinidumpUploadService.FILE_TO_UPLO AD_KEY)); | |
| 71 return super.startService(intent); | |
| 72 } | |
| 73 }; | |
| 74 | |
| 75 // Extract the logcat! | |
| 76 LogcatExtractionRunnable runnable = new LogcatExtractionRunnable(testCon text, minidump) { | |
| 77 @Override | |
| 78 protected List<String> getLogcat() { | |
| 79 return logcat; | |
| 80 } | |
| 81 }; | |
| 82 runnable.run(); | |
| 83 | |
| 84 // Verify the file contents. | |
| 85 BufferedReader input = null; | 172 BufferedReader input = null; |
| 86 try { | 173 try { |
| 87 File logfile = new File(mCrashDir, "test.dmp.try0"); | 174 File minidumpWithLogcat = new File(mCrashDir, filename); |
| 88 assertTrue("Logfile should exist!", logfile.exists()); | 175 assertTrue("Should have created a file containing the logcat and min idump contents", |
| 176 minidumpWithLogcat.exists()); | |
| 89 | 177 |
| 90 input = new BufferedReader(new FileReader(logfile)); | 178 input = new BufferedReader(new FileReader(minidumpWithLogcat)); |
| 91 assertEquals("The first line should be the boundary line.", boundary , input.readLine()); | 179 assertEquals("The first line should be the boundary line.", BOUNDARY , input.readLine()); |
| 92 assertEquals("The second line should be the content dispoistion.", | 180 assertEquals("The second line should be the content dispoistion.", |
| 93 MinidumpLogcatPrepender.LOGCAT_CONTENT_DISPOSITION, input.re adLine()); | 181 MinidumpLogcatPrepender.LOGCAT_CONTENT_DISPOSITION, input.re adLine()); |
| 94 assertEquals("The third line should be the content type.", | 182 assertEquals("The third line should be the content type.", |
| 95 MinidumpLogcatPrepender.LOGCAT_CONTENT_TYPE, input.readLine( )); | 183 MinidumpLogcatPrepender.LOGCAT_CONTENT_TYPE, input.readLine( )); |
| 96 assertEquals("The fourth line should be blank, for padding.", "", in put.readLine()); | 184 assertEquals("The fourth line should be blank, for padding.", "", in put.readLine()); |
| 97 for (String expected : logcat) { | 185 for (String expected : LOGCAT) { |
| 98 assertEquals(expected, input.readLine()); | 186 assertEquals("The logcat contents should match", expected, input .readLine()); |
| 99 } | 187 } |
| 100 assertEquals("The logcat should be followed by the boundary line.", boundary, | 188 assertEquals("The logcat should be followed by the boundary line.", BOUNDARY, |
| 101 input.readLine()); | 189 input.readLine()); |
| 102 assertEquals( | 190 assertEquals( |
| 103 "The minidump contents should follow.", minidumpContents, in put.readLine()); | 191 "The minidump contents should follow.", MINIDUMP_CONTENTS, i nput.readLine()); |
| 104 assertNull("There should be nothing else in the file", input.readLin e()); | 192 assertNull("There should be nothing else in the file", input.readLin e()); |
| 105 } catch (IOException e) { | |
| 106 fail(e.toString()); | |
| 107 } finally { | 193 } finally { |
| 108 StreamUtil.closeQuietly(input); | 194 StreamUtil.closeQuietly(input); |
| 109 } | 195 } |
| 110 } | 196 } |
| 197 | |
| 198 @MediumTest | |
| 199 public void testSimpleExtraction_SansJobScheduler() throws IOException { | |
| 200 setJobSchedulerEnabled(false); | |
| 201 final File minidump = createMinidump("test.dmp"); | |
| 202 Context testContext = new TestContext(getInstrumentation().getTargetCont ext()); | |
| 203 | |
| 204 LogcatExtractionRunnable runnable = new TestLogcatExtractionRunnable(tes tContext, minidump); | |
| 205 runnable.run(); | |
| 206 | |
| 207 verifyMinidumpWithLogcat("test.dmp.try0"); | |
| 208 } | |
| 209 | |
| 210 @MediumTest | |
| 211 public void testSimpleExtraction_WithJobScheduler() throws IOException { | |
| 212 // The JobScheduler API is only available as of Android L. | |
| 213 if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) return; | |
| 214 | |
| 215 setJobSchedulerEnabled(true); | |
| 216 final File minidump = createMinidump("test.dmp"); | |
| 217 Context testContext = new TestContext(getInstrumentation().getTargetCont ext()); | |
| 218 | |
| 219 LogcatExtractionRunnable runnable = new TestLogcatExtractionRunnable(tes tContext, minidump); | |
| 220 runnable.run(); | |
| 221 | |
| 222 verifyMinidumpWithLogcat("test.dmp.try0"); | |
| 223 } | |
| 111 } | 224 } |
| OLD | NEW |