Skip to content

Commit

Permalink
Merge c868d56 into 5ba5d22
Browse files Browse the repository at this point in the history
  • Loading branch information
kattrali committed Oct 25, 2018
2 parents 5ba5d22 + c868d56 commit 5a890a9
Show file tree
Hide file tree
Showing 27 changed files with 1,805 additions and 33 deletions.
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,13 @@

## TBD

### Enhancements

* Add a callback to allow modifying reports immediately prior to delivery,
including fatal crashes from native C/C++ code. For more information, see the
[callback reference](https://docs.bugsnag.com/platforms/android/sdk/customizing-error-reports).
[#379](https://github.com/bugsnag/bugsnag-android/pull/379)

### Bug fixes

* [NDK] Improve stack trace quality for signals raised on ARM32 devices
Expand Down
49 changes: 49 additions & 0 deletions features/before_send.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
Feature: Run callbacks before reports delivered

Scenario: Change report before native crash report delivered
When I run "NativeBeforeSendScenario"
And I configure the app to run in the "non-crashy" state
And I relaunch the app
Then I should receive a request
And the request payload contains a completed native report
And the exception "errorClass" equals "SIGSEGV"
And the exception "message" equals "Segmentation violation (invalid memory reference)"
And the event "context" equals "!important"
And the exception "type" equals "c"
And the event "severity" equals "error"
And the event "unhandled" is true

Scenario: Change report before JVM crash report delivered
When I run "BeforeSendScenario"
And I configure the app to run in the "non-crashy" state
And I relaunch the app
Then I should receive a request
And the request payload contains a completed native report
And the exception "errorClass" equals "java.lang.RuntimeException"
And the exception "message" equals "Ruh-roh"
And the event "context" equals "UNSET"
And the exception "type" equals "android"
And the event "severity" equals "error"
And the event "unhandled" is true

Scenario: Change report before native notify() report delivered
When I run "NativeNotifyBeforeSendScenario"
Then I should receive a request
And the request is a valid for the error reporting API
And the exception "errorClass" equals "Ad-hoc"
And the exception "message" equals "Auto-generated issue"
And the event "context" equals "hello"
And the exception "type" equals "c"
And the event "severity" equals "info"
And the event "unhandled" is false

Scenario: Change report before notify() report delivered
When I run "NotifyBeforeSendScenario"
Then I should receive a request
And the request is a valid for the error reporting API
And the exception "errorClass" equals "java.lang.Exception"
And the exception "message" equals "Registration failure"
And the event "context" equals "RESET"
And the exception "type" equals "android"
And the event "severity" equals "error"
And the event "unhandled" is false
7 changes: 7 additions & 0 deletions features/fixtures/mazerunner/src/main/cpp/bugsnags.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,13 @@ Java_com_bugsnag_android_mazerunner_scenarios_CXXExtraordinaryLongStringScenario
return 12062 / value;
}

JNIEXPORT void JNICALL
Java_com_bugsnag_android_mazerunner_scenarios_NativeNotifyBeforeSendScenario_activate(JNIEnv *env,
jobject instance) {
bugsnag_notify_env(env, (char *)"Ad-hoc",
(char *)"Auto-generated issue", BSG_SEVERITY_INFO);
}

JNIEXPORT void JNICALL
Java_com_bugsnag_android_mazerunner_scenarios_CXXNotifyScenario_activate(JNIEnv *env,
jobject instance) {
Expand Down
6 changes: 6 additions & 0 deletions features/fixtures/mazerunner/src/main/cpp/entrypoint.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,12 @@ Java_com_bugsnag_android_mazerunner_scenarios_CXXWriteReadOnlyMemoryScenario_cra
printf("This one here: %d\n", crash_write_read_only_mem(42));
}

JNIEXPORT void JNICALL
Java_com_bugsnag_android_mazerunner_scenarios_NativeBeforeSendScenario_crash(JNIEnv *env,
jobject instance) {
printf("This one here: %s\n", crash_improper_cast(39));
}

JNIEXPORT void JNICALL
Java_com_bugsnag_android_mazerunner_scenarios_CXXImproperTypecastScenario_crash(JNIEnv *env,
jobject instance) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package com.bugsnag.android.mazerunner.scenarios;

import com.bugsnag.android.Bugsnag;
import com.bugsnag.android.BeforeSend;
import com.bugsnag.android.Configuration;
import com.bugsnag.android.Report;

import android.content.Context;
import android.support.annotation.NonNull;


public class BeforeSendScenario extends Scenario {

public BeforeSendScenario(@NonNull Configuration config, @NonNull Context context) {
super(config, context);
config.setAutoCaptureSessions(false);
config.beforeSend(new BeforeSend() {
@Override
public boolean run(Report report) {
report.getError().setContext("UNSET");

return true;
}
});
}

@Override
public void run() {
super.run();
String metadata = getEventMetaData();
if (metadata != null && metadata.equals("non-crashy")) {
return;
}
throw new RuntimeException("Ruh-roh");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package com.bugsnag.android.mazerunner.scenarios;

import com.bugsnag.android.Bugsnag;
import com.bugsnag.android.BeforeSend;
import com.bugsnag.android.Configuration;
import com.bugsnag.android.Report;

import android.content.Context;
import android.support.annotation.NonNull;


public class NativeBeforeSendScenario extends Scenario {

static {
System.loadLibrary("bugsnag-ndk");
System.loadLibrary("entrypoint");
}

public native void crash();

public NativeBeforeSendScenario(@NonNull Configuration config, @NonNull Context context) {
super(config, context);
config.setAutoCaptureSessions(false);
config.beforeSend(new BeforeSend() {
@Override
public boolean run(Report report) {
report.getError().setContext("!important");

return true;
}
});
}

@Override
public void run() {
super.run();
String metadata = getEventMetaData();
if (metadata != null && metadata.equals("non-crashy")) {
return;
}
crash();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package com.bugsnag.android.mazerunner.scenarios;

import com.bugsnag.android.Bugsnag;
import com.bugsnag.android.BeforeSend;
import com.bugsnag.android.Configuration;
import com.bugsnag.android.Report;

import android.content.Context;
import android.support.annotation.NonNull;


public class NativeNotifyBeforeSendScenario extends Scenario {

static {
System.loadLibrary("bugsnag-ndk");
System.loadLibrary("entrypoint");
}

public native void activate();

public NativeNotifyBeforeSendScenario(@NonNull Configuration config, @NonNull Context context) {
super(config, context);
config.setAutoCaptureSessions(false);
config.beforeSend(new BeforeSend() {
@Override
public boolean run(Report report) {
report.getError().setContext("hello");

return true;
}
});
}

@Override
public void run() {
super.run();
activate();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package com.bugsnag.android.mazerunner.scenarios;

import com.bugsnag.android.Bugsnag;
import com.bugsnag.android.BeforeSend;
import com.bugsnag.android.Configuration;
import com.bugsnag.android.Report;
import com.bugsnag.android.Severity;

import android.content.Context;
import android.support.annotation.NonNull;


public class NotifyBeforeSendScenario extends Scenario {

public NotifyBeforeSendScenario(@NonNull Configuration config, @NonNull Context context) {
super(config, context);
config.setAutoCaptureSessions(false);
config.beforeSend(new BeforeSend() {
@Override
public boolean run(Report report) {
report.getError().setContext("RESET");
report.getError().setSeverity(Severity.ERROR);

return true;
}
});
}

@Override
public void run() {
super.run();
Bugsnag.notify(new Exception("Registration failure"));
}
}
114 changes: 114 additions & 0 deletions sdk/src/androidTest/java/com/bugsnag/android/BeforeSendTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
package com.bugsnag.android;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;

import android.support.test.InstrumentationRegistry;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;

@RunWith(AndroidJUnit4.class)
@SmallTest
public class BeforeSendTest {

private Client client;
private Configuration config;
private Report lastReport;
private static String result = "";

/**
* Configures a client
*/
@Before
public void setUp() throws Exception {
result = "";
config = new Configuration("key");
config.setDelivery(new Delivery() {
@Override
public void deliver(SessionTrackingPayload payload,
Configuration config)
throws DeliveryFailureException {}

@Override
public void deliver(Report report,
Configuration config)
throws DeliveryFailureException {
lastReport = report;
}
});
client = new Client(InstrumentationRegistry.getContext(), config);
}

@After
public void tearDown() throws Exception {
lastReport = null;
Async.cancelTasks();
}

@Test
public void testCallbackOrderPreserved() {
config.beforeSend(new BeforeSend() {
@Override
public boolean run(Report report) {
result = result + "a";
return true;
}
});
config.beforeSend(new BeforeSend() {
@Override
public boolean run(Report report) {
result = result + "b";
return true;
}
});
config.beforeSend(new BeforeSend() {
@Override
public boolean run(Report report) {
result = result + "c";
return true;
}
});
client.notifyBlocking(new Exception("womp womp"));
assertEquals("abc", result);
assertNotNull(lastReport);
}

@Test
public void testCancelReport() {
config.beforeSend(new BeforeSend() {
@Override
public boolean run(Report report) {
result = result + "a";
return true;
}
});
config.beforeSend(new BeforeSend() {
@Override
public boolean run(Report report) {
result = result + "b";
return false;
}
});
client.notifyBlocking(new Exception("womp womp"));
assertEquals("ab", result);
assertNull(lastReport);
}

@Test
public void testAlterReport() {
config.beforeSend(new BeforeSend() {
@Override
public boolean run(Report report) {
report.getError().setGroupingHash("123");
return true;
}
});
client.notifyBlocking(new Exception("womp womp"));
assertEquals("123", lastReport.getError().getGroupingHash());
}
}

0 comments on commit 5a890a9

Please sign in to comment.