Commit c5c2eee7 authored by Azhara Assanova's avatar Azhara Assanova Committed by Commit Bot

[AW][Dev-UI] Hide a crash with a missing JSON log file

Hiding a crash sets a boolean field in the JSON log file. When this
file is not found, the method used to do nothing. Now it creates
the JSON log file and writes to it.

run_webview_instrumentation_test_apk -f *CrashReceiverServiceTest*

Bug: 1122058
Test: run_webview_instrumentation_test_apk -f *WebViewCrashInfoCollectorTest*
Change-Id: I3175f09839254b98c0b051873320253103675d15
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2405584Reviewed-by: default avatarHazem Ashmawy <hazems@chromium.org>
Reviewed-by: default avatarRichard Coles <torne@chromium.org>
Reviewed-by: default avatarLaís Minchillo <laisminchillo@chromium.org>
Commit-Queue: Azhara Assanova <azharaa@google.com>
Auto-Submit: Azhara Assanova <azharaa@google.com>
Cr-Commit-Position: refs/heads/master@{#809730}
parent 0beb1151
......@@ -17,6 +17,7 @@ public class SystemWideCrashDirectories {
private static final String WEBVIEW_CRASH_LOG_DIR = "crash_logs";
private static final String WEBVIEW_CRASH_DIR = "WebView_Crashes";
private static final String WEBVIEW_TMP_CRASH_DIR = "WebView_Crashes_Tmp";
private static final String WEBVIEW_CRASH_LOG_SUFFIX = "_log.json";
/**
* Create the directory in which WebView will log crashes info.
......@@ -61,6 +62,14 @@ public class SystemWideCrashDirectories {
return new File(ContextUtils.getApplicationContext().getCacheDir(), WEBVIEW_TMP_CRASH_DIR);
}
/**
* Create Crash json log file in the crash log directory.
* @return a File pointing to the created crash json log file.
*/
public static File createCrashJsonLogFile(String logFileName) {
return new File(getOrCreateWebViewCrashLogDir(), logFileName + WEBVIEW_CRASH_LOG_SUFFIX);
}
private static File getOrCreateDir(File dir) {
// Call mkdir before isDirectory to ensure that if another thread created the directory
// just before the call to mkdir, the current thread fails mkdir, but passes isDirectory.
......
......@@ -268,9 +268,36 @@ public class CrashesListFragmentTest {
*/
private static DataInteraction checkUnknownPackageCrashItemHeader(
DataInteraction headerDataInteraction, CrashInfo crashInfo) {
return checkPackageCrashItemHeader(headerDataInteraction, crashInfo, FAKE_APP_PACKAGE_NAME);
}
/**
* Check that the given crash item header shows the "unknown app" package name, capture date and
* icon for the given {@code crashInfo}.
*
* @param {@link DataInteraction} represents the crash item header.
* @param {@link CrashInfo} to match.
* @return the same {@code headerDataInteraction} passed for the convenience of chaining.
*/
private static DataInteraction checkMissingPackageInfoCrashItemHeader(
DataInteraction headerDataInteraction, CrashInfo crashInfo) {
return checkPackageCrashItemHeader(headerDataInteraction, crashInfo, "unknown app");
}
/**
* Check that the given crash item header shows the given package name, capture date and
* icon for the given {@code crashInfo}.
*
* @param {@link DataInteraction} represents the crash item header.
* @param {@link CrashInfo} to match.
* @param packageName to match.
* @return the same {@code headerDataInteraction} passed for the convenience of chaining.
*/
private static DataInteraction checkPackageCrashItemHeader(
DataInteraction headerDataInteraction, CrashInfo crashInfo, String packageName) {
String captureDate = new Date(crashInfo.captureTime).toString();
headerDataInteraction.onChildView(withId(android.R.id.text1))
.check(matches(withText(FAKE_APP_PACKAGE_NAME)));
.check(matches(withText(packageName)));
headerDataInteraction.onChildView(withId(android.R.id.text2))
.check(matches(withText(captureDate)));
// There should not be an app with FAKE_APP_PACKAGE_NAME so system default icon should be
......@@ -281,6 +308,19 @@ public class CrashesListFragmentTest {
return headerDataInteraction;
}
/**
* Perform click on hide crash button by checking the required conditions for the button.
*
* @param {@link DataInteraction} represents the crash item body.
*/
private static void clickHideCrashButton(DataInteraction bodyDataInteraction) {
bodyDataInteraction.onChildView(withId(R.id.crash_hide_button))
.check(matches(isDisplayed()))
.check(matches(isEnabled()))
.check(matches(withDrawable(R.drawable.ic_delete)))
.perform(click());
}
/**
* Check that the given crash item body shows the correct uploadState, uploadId and uploadDate.
*
......@@ -701,11 +741,44 @@ public class CrashesListFragmentTest {
onView(withId(R.id.crashes_list)).check(matches(withCount(1)));
DataInteraction headerDataInteraction = onData(anything()).atPosition(0);
headerDataInteraction.onChildView(withId(android.R.id.text1))
.check(matches(withText("unknown app")));
headerDataInteraction.onChildView(withId(R.id.crash_package_icon))
.check(matches(withDrawable(android.R.drawable.sym_def_app_icon)));
checkMissingPackageInfoCrashItemHeader(onData(anything()).atPosition(0), crashInfo);
}
@Test
@Feature({"AndroidWebView"})
// Test when crash is missing json, but has upload log file and minidump.
public void testShowingSingleCrashReport_uploaded_missingJson() throws Throwable {
CrashInfo crashInfo = createCrashInfo("123456", -1, null, 1000, null, UploadState.UPLOADED);
assertThat("temp minidump file should exist", createMinidumpFile(crashInfo).exists());
assertThat("upload log file should exist", appendUploadedEntryToLog(crashInfo).exists());
CallbackHelper helper = getCrashListLoadedListener();
int crashListLoadInitCount = helper.getCallCount();
launchCrashesFragment();
helper.waitForCallback(crashListLoadInitCount, 1);
onView(withId(R.id.crashes_list)).check(matches(withCount(1)));
checkMissingPackageInfoCrashItemHeader(onData(anything()).atPosition(0), crashInfo);
}
@Test
@Feature({"AndroidWebView"})
// Test when crash is missing json, but has upload log file and minidump.
public void testShowingSingleCrashReport_pending_missingJson() throws Throwable {
CrashInfo crashInfo = createCrashInfo("123456", -1, null, 1000, null, UploadState.PENDING);
assertThat("temp minidump file should exist", createMinidumpFile(crashInfo).exists());
CallbackHelper helper = getCrashListLoadedListener();
int crashListLoadInitCount = helper.getCallCount();
launchCrashesFragment();
helper.waitForCallback(crashListLoadInitCount, 1);
onView(withId(R.id.crashes_list)).check(matches(withCount(1)));
checkMissingPackageInfoCrashItemHeader(onData(anything()).atPosition(0), crashInfo);
}
@Test
......@@ -766,13 +839,7 @@ public class CrashesListFragmentTest {
DataInteraction bodyDataInteraction = onData(anything()).atPosition(1);
crashListLoadInitCount = helper.getCallCount();
bodyDataInteraction.onChildView(withId(R.id.crash_hide_button))
.check(matches(isDisplayed()))
.check(matches(isEnabled()))
.check(matches(withDrawable(R.drawable.ic_delete)))
.perform(click());
clickHideCrashButton(bodyDataInteraction);
helper.waitForCallback(crashListLoadInitCount, 1);
onView(withId(R.id.crashes_list)).check(matches(withCount(0)));
......@@ -803,13 +870,64 @@ public class CrashesListFragmentTest {
DataInteraction bodyDataInteraction = onData(anything()).atPosition(1);
crashListLoadInitCount = helper.getCallCount();
clickHideCrashButton(bodyDataInteraction);
helper.waitForCallback(crashListLoadInitCount, 1);
bodyDataInteraction.onChildView(withId(R.id.crash_hide_button))
.check(matches(isDisplayed()))
.check(matches(isEnabled()))
.check(matches(withDrawable(R.drawable.ic_delete)))
.perform(click());
onView(withId(R.id.crashes_list)).check(matches(withCount(0)));
}
@Test
@Feature({"AndroidWebView"})
public void testHideCrashButton_uploaded_missingJson() throws Throwable {
CrashInfo crashInfo = createCrashInfo("123456", -1, null, 1000, null, UploadState.UPLOADED);
assertThat("temp minidump file should exist", createMinidumpFile(crashInfo).exists());
assertThat("upload log file should exist", appendUploadedEntryToLog(crashInfo).exists());
CallbackHelper helper = getCrashListLoadedListener();
int crashListLoadInitCount = helper.getCallCount();
launchCrashesFragment();
helper.waitForCallback(crashListLoadInitCount, 1);
onView(withId(R.id.crashes_list)).check(matches(withCount(1)));
// Check crash item header
checkMissingPackageInfoCrashItemHeader(onData(anything()).atPosition(0), crashInfo)
.perform(click()); // click to expand it
// The body is considered item#2 in the list view after expansion
onView(withId(R.id.crashes_list)).check(matches(withCount(2)));
DataInteraction bodyDataInteraction = onData(anything()).atPosition(1);
crashListLoadInitCount = helper.getCallCount();
clickHideCrashButton(bodyDataInteraction);
helper.waitForCallback(crashListLoadInitCount, 1);
onView(withId(R.id.crashes_list)).check(matches(withCount(0)));
}
@Test
@Feature({"AndroidWebView"})
public void testHideCrashButton_pending_missingJson() throws Throwable {
CrashInfo crashInfo = createCrashInfo("123456", -1, null, -1, null, UploadState.PENDING);
assertThat("temp minidump file should exist", createMinidumpFile(crashInfo).exists());
CallbackHelper helper = getCrashListLoadedListener();
int crashListLoadInitCount = helper.getCallCount();
launchCrashesFragment();
helper.waitForCallback(crashListLoadInitCount, 1);
onView(withId(R.id.crashes_list)).check(matches(withCount(1)));
// Check crash item header
checkMissingPackageInfoCrashItemHeader(onData(anything()).atPosition(0), crashInfo)
.perform(click()); // click to expand it
// The body is considered item#2 in the list view after expansion
onView(withId(R.id.crashes_list)).check(matches(withCount(2)));
DataInteraction bodyDataInteraction = onData(anything()).atPosition(1);
crashListLoadInitCount = helper.getCallCount();
clickHideCrashButton(bodyDataInteraction);
helper.waitForCallback(crashListLoadInitCount, 1);
onView(withId(R.id.crashes_list)).check(matches(withCount(0)));
......
......@@ -230,6 +230,6 @@ public class WebViewCrashInfoCollectorTest {
WebViewCrashInfoCollector.updateCrashLogFileWithNewCrashInfo(newCrashInfo);
CrashInfo resultCrashInfo = getCrashFromJsonLogFile("xyz123");
Assert.assertThat(resultCrashInfo, equalsTo(null));
Assert.assertThat(resultCrashInfo, equalsTo(newCrashInfo));
}
}
......@@ -146,28 +146,31 @@ public class WebViewCrashInfoCollector {
* Modify WebView crash JSON log file with the new crash info if the JSON file exists
*/
public static void updateCrashLogFileWithNewCrashInfo(CrashInfo crashInfo) {
File logDir = SystemWideCrashDirectories.getWebViewCrashLogDir();
File logDir = SystemWideCrashDirectories.getOrCreateWebViewCrashLogDir();
File[] logFiles = logDir.listFiles();
if (logFiles == null) {
return;
}
for (File logFile : logFiles) {
// Ignore non-json files.
if (!logFile.isFile() || !logFile.getName().endsWith(".json")) continue;
// Ignore unrelated json files
if (!logFile.getName().contains(crashInfo.localId)) continue;
tryWritingCrashInfoToLogFile(crashInfo, logFile);
return;
}
// logfile does not exist, so creates and writes to a new logfile
File newLogFile = SystemWideCrashDirectories.createCrashJsonLogFile(crashInfo.localId);
tryWritingCrashInfoToLogFile(crashInfo, newLogFile);
}
private static void tryWritingCrashInfoToLogFile(CrashInfo crashInfo, File logFile) {
try {
FileWriter writer = new FileWriter(logFile);
try {
FileWriter writer = new FileWriter(logFile);
try {
writer.write(crashInfo.serializeToJson());
} finally {
writer.close();
}
} catch (IOException e) {
Log.e(TAG, "failed to modify JSON log entry for crash", e);
writer.write(crashInfo.serializeToJson());
} finally {
writer.close();
}
return;
} catch (IOException e) {
Log.e(TAG, "failed to modify JSON log entry for crash", e);
}
// logfile does not exist
}
}
......@@ -30,7 +30,6 @@ import java.util.Map;
*/
public class CrashReceiverService extends Service {
private static final String TAG = "CrashReceiverService";
private static final String WEBVIEW_CRASH_LOG_SUFFIX = "_log.json";
private final Object mCopyingLock = new Object();
private boolean mIsCopying;
......@@ -123,9 +122,8 @@ public class CrashReceiverService extends Service {
copiedAnything = true;
if (crashesInfo != null) {
Map<String, String> crashInfo = crashesInfo.get(i);
File logFile = new File(
SystemWideCrashDirectories.getOrCreateWebViewCrashLogDir(),
copiedFile.getName() + WEBVIEW_CRASH_LOG_SUFFIX);
File logFile = SystemWideCrashDirectories.createCrashJsonLogFile(
copiedFile.getName());
writeCrashInfoToLogFile(logFile, copiedFile, crashInfo);
}
}
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment