Skip to content

Commit

Permalink
Merge pull request #866 from Microsoft/feature/priority-develop
Browse files Browse the repository at this point in the history
Merge develop to priority
  • Loading branch information
dhei committed Nov 5, 2018
2 parents 47c65bd + 7adc916 commit 862c67d
Show file tree
Hide file tree
Showing 3 changed files with 82 additions and 73 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,14 @@ public void tearDown() {
Thread.setDefaultUncaughtExceptionHandler(sDefaultCrashHandler);
}

private static Error generateStackOverflowError() {
try {
return generateStackOverflowError();
} catch (StackOverflowError error) {
return error;
}
}

private void startFresh(CrashesListener listener) {

/* Configure new instance. */
Expand All @@ -124,6 +132,7 @@ private void startFresh(CrashesListener listener) {

/* Replace channel. */
AppCenter.getInstance().setChannel(mChannel);

/* Set listener. */
Crashes.setListener(listener);

Expand Down Expand Up @@ -204,56 +213,6 @@ public void run() {
assertTrue(Crashes.hasCrashedInLastSession().get());
}

@Test
public void getLastSessionCrashReportExceptionWithHugeFramesAndHugeCauses() throws Exception {

/* Null before start. */
Crashes.unsetInstance();
assertNull(Crashes.getLastSessionCrashReport().get());
assertFalse(Crashes.hasCrashedInLastSession().get());

/* Crash on 1st process. */
Thread.UncaughtExceptionHandler uncaughtExceptionHandler = mock(Thread.UncaughtExceptionHandler.class);
Thread.setDefaultUncaughtExceptionHandler(uncaughtExceptionHandler);
startFresh(null);
assertNull(Crashes.getLastSessionCrashReport().get());
assertFalse(Crashes.hasCrashedInLastSession().get());
final RuntimeException exception = generateHugeException(300, 300);
assertTrue(exception.getStackTrace().length > ErrorLogHelper.FRAME_LIMIT);
final Thread thread = new Thread() {

@Override
public void run() {
throw exception;
}
};
thread.start();
thread.join();

/* Get last session crash on 2nd process. */
startFresh(null);
ErrorReport errorReport = Crashes.getLastSessionCrashReport().get();
assertNotNull(errorReport);

/* The client side throwable failed to save as huge so will be null. */
assertNull(errorReport.getThrowable());
assertTrue(Crashes.hasCrashedInLastSession().get());

/* Check managed error was sent as truncated. */
ArgumentCaptor<ManagedErrorLog> errorLog = ArgumentCaptor.forClass(ManagedErrorLog.class);
verify(mChannel).enqueue(errorLog.capture(), eq(Crashes.ERROR_GROUP), eq(PERSISTENCE_CRITICAL));
assertNotNull(errorLog.getValue());
assertNotNull(errorLog.getValue().getException());
assertNotNull(errorLog.getValue().getException().getFrames());
assertEquals(ErrorLogHelper.FRAME_LIMIT, errorLog.getValue().getException().getFrames().size());
int causesCount = 0;
com.microsoft.appcenter.crashes.ingestion.models.Exception e = errorLog.getValue().getException();
while (e.getInnerExceptions() != null && (e = e.getInnerExceptions().get(0)) != null) {
causesCount++;
}
assertEquals(ErrorLogHelper.CAUSE_LIMIT, causesCount + 1);
}

@Test
public void getLastSessionCrashReportNative() throws Exception {

Expand Down Expand Up @@ -590,27 +549,4 @@ public void run() {
/* Check there are only 2 files: the throwable and the json one. */
assertEquals(2, ErrorLogHelper.getErrorStorageDirectory().listFiles(mMinidumpFilter).length);
}

private static Error generateStackOverflowError() {
try {
return generateStackOverflowError();
} catch (StackOverflowError error) {
return error;
}
}

@SuppressWarnings("SameParameterValue")
private static RuntimeException generateHugeException(int stacktraceIncrease, int causes) {
if (stacktraceIncrease > 0) {
try {
return generateHugeException(stacktraceIncrease - 1, causes);
} catch (StackOverflowError ignore) {
}
}
Exception e = new Exception();
for (int i = 0; i < causes; i++) {
e = new Exception(Integer.valueOf(i).toString(), e);
}
return new RuntimeException(e);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
import org.junit.rules.TemporaryFolder;
import org.mockito.ArgumentCaptor;
import org.mockito.ArgumentMatcher;
import org.mockito.InOrder;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.invocation.InvocationOnMock;
Expand All @@ -55,6 +56,7 @@
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
Expand All @@ -78,12 +80,15 @@
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyBoolean;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.anyLong;
import static org.mockito.Matchers.anyMapOf;
import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.argThat;
import static org.mockito.Matchers.contains;
import static org.mockito.Matchers.eq;
import static org.mockito.Matchers.isA;
import static org.mockito.Matchers.isNull;
import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
Expand All @@ -92,9 +97,11 @@
import static org.mockito.Mockito.verifyZeroInteractions;
import static org.mockito.Mockito.when;
import static org.powermock.api.mockito.PowerMockito.doAnswer;
import static org.powermock.api.mockito.PowerMockito.doThrow;
import static org.powermock.api.mockito.PowerMockito.mockStatic;
import static org.powermock.api.mockito.PowerMockito.verifyNoMoreInteractions;
import static org.powermock.api.mockito.PowerMockito.verifyStatic;
import static org.powermock.api.mockito.PowerMockito.whenNew;

@SuppressWarnings("unused")
@PrepareForTest({ErrorLogHelper.class, SystemClock.class, FileManager.class, SharedPreferencesManager.class, AppCenterLog.class, AppCenter.class, Crashes.class, HandlerUtils.class, Looper.class})
Expand Down Expand Up @@ -1439,4 +1446,56 @@ public void minidumpAppLaunchTimestampFromSessionContextNotFound() throws Except
assertEquals(new Date(crashTime), crashLog.getTimestamp());
assertEquals(new Date(crashTime), crashLog.getAppLaunchTimestamp());
}

@Test
public void stackOverflowOnSavingThrowable() throws Exception {

/* Mock error log utils. */
mockStatic(ErrorLogHelper.class);
when(ErrorLogHelper.getErrorStorageDirectory()).thenReturn(mock(File.class));
when(ErrorLogHelper.getNewMinidumpFiles()).thenReturn(new File[]{});
when(ErrorLogHelper.getStoredErrorLogFiles()).thenReturn(new File[]{});
when(ErrorLogHelper.createErrorLog(any(Context.class), any(Thread.class), any(com.microsoft.appcenter.crashes.ingestion.models.Exception.class), anyMapOf(Thread.class, StackTraceElement[].class), anyLong(), anyBoolean())).thenReturn(mErrorLog);
File throwableFile = mock(File.class);
whenNew(File.class).withParameterTypes(File.class, String.class).withArguments(any(File.class), argThat(new ArgumentMatcher<String>() {

@Override
public boolean matches(Object argument) {
return argument.toString().endsWith(ErrorLogHelper.THROWABLE_FILE_EXTENSION);
}
})).thenReturn(throwableFile);
LogSerializer logSerializer = mock(LogSerializer.class);
String jsonCrash = "{}";
when(logSerializer.serializeLog(any(Log.class))).thenReturn(jsonCrash);

/* Mock storage to fail on stack overflow when saving a Throwable as binary. */
doThrow(new StackOverflowError()).when(FileManager.class);
FileManager.writeObject(any(File.class), any(Serializable.class));

/* Simulate start SDK. */
Crashes crashes = Crashes.getInstance();
crashes.setLogSerializer(logSerializer);
crashes.onStarting(mAppCenterHandler);
crashes.onStarted(mock(Context.class), mock(Channel.class), "", null, true);

/* Simulate crash. */
Throwable throwable = new Throwable();
Crashes.getInstance().saveUncaughtException(Thread.currentThread(), throwable);

/* Verify we gracefully abort saving throwable (no exception) and we created an empty file instead. */
verifyStatic();
FileManager.writeObject(throwableFile, throwable);
assertNotNull(throwableFile);
InOrder inOrder = inOrder(throwableFile);

//noinspection ResultOfMethodCallIgnored
inOrder.verify(throwableFile).delete();

//noinspection ResultOfMethodCallIgnored
inOrder.verify(throwableFile).createNewFile();

/* Verify it didn't prevent saving the JSON file. */
verifyStatic();
FileManager.write(any(File.class), eq(jsonCrash));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -344,4 +344,18 @@ public void validateProperties() {
assertEquals(1, actualProperties.size());
assertEquals(truncatedMapItem, actualProperties.get(truncatedMapItem));
}

@Test
public void truncateCauses() {
RuntimeException e = new RuntimeException();
for (int i = 0; i < 32; i++) {
e = new RuntimeException(Integer.valueOf(i).toString(), e);
}
int depth = 1;
Exception model = ErrorLogHelper.getModelExceptionFromThrowable(e);
while (model.getInnerExceptions() != null && (model = model.getInnerExceptions().get(0)) != null) {
depth++;
}
assertEquals(ErrorLogHelper.CAUSE_LIMIT, depth);
}
}

0 comments on commit 862c67d

Please sign in to comment.