diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 2c1143c6ab..60c2a3cea1 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -1,6 +1,6 @@
name: instrumentation_tests
-on: [push, pull_request]
+on: [ push, pull_request ]
env:
GRADLE_OPTS: "-Dorg.gradle.jvmargs=-Xmx4g -Dorg.gradle.daemon=false -Dkotlin.incremental=false -Dorg.gradle.parallel=true"
@@ -16,16 +16,29 @@ jobs:
- 29
steps:
- - uses: actions/checkout@v2.3.5
+ - uses: actions/checkout@v3
with:
submodules: recursive
- - uses: gradle/wrapper-validation-action@v1.0.4
+ - uses: gradle/wrapper-validation-action@v1
- - uses: actions/setup-java@v2
+ - uses: actions/setup-java@v3
with:
distribution: 'zulu'
java-version: 11
+ - name: Gradle cache
+ uses: gradle/gradle-build-action@v2
+
+ - name: Create AVD and generate snapshot for caching
+ if: steps.avd-cache.outputs.cache-hit != 'true'
+ uses: reactivecircus/android-emulator-runner@v2
+ with:
+ api-level: ${{ matrix.api-level }}
+ force-avd-creation: false
+ emulator-options: -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none
+ disable-animations: false
+ script: echo "Generated AVD snapshot for caching."
+
- name: Run Tests
uses: reactivecircus/android-emulator-runner@v2
with:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 703305c4fd..e5cf0cc94b 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,22 @@
# Changelog
+## 5.22.0 (2022-03-31)
+
+### Enhancements
+
+* Added `Bugsnag.isStarted()` to test whether the Bugsnag client is in the middle of initializing. This can be used to guard uses of the Bugsnag API that are either on separate threads early in the app's start-up and so not guaranteed to be executed after `Bugsnag.start` has completed, or where Bugsnag may not have been started at all due to some internal app logic.
+ [slack-jallen](https://github.com/slack-jallen):[#1621](https://github.com/bugsnag/bugsnag-android/pull/1621)
+ [#1640](https://github.com/bugsnag/bugsnag-android/pull/1640)
+
+* Events and Sessions will be discarded if they cannot be uploaded and are older than 60 days or larger than 1MB
+ [#1633](https://github.com/bugsnag/bugsnag-android/pull/1633)
+
+### Bug fixes
+
+* Fixed potentially [thread-unsafe access](https://github.com/bugsnag/bugsnag-android/issues/883) when invoking `Bugsnag` static methods across different threads whilst `Bugsnag.start` is still in-flight. It is now safe to call any `Bugsnag` static method once `Bugsnag.start` has _begun_ executing, as access to the client singleton is controlled by a lock, so the new `isStarted` method (see above) should only be required where it cannot be determined whether the call to `Bugsnag.start` has begun or you do not want to wait. [#1638](https://github.com/bugsnag/bugsnag-android/pull/1638)
+* Calling `bugsnag_event_set_context` with NULL `context` correctly clears the event context again
+ [#1637](https://github.com/bugsnag/bugsnag-android/pull/1637)
+
## 5.21.0 (2022-03-17)
### Enhancements
diff --git a/bugsnag-android-core/detekt-baseline.xml b/bugsnag-android-core/detekt-baseline.xml
index 9070d7bbbe..a671898884 100644
--- a/bugsnag-android-core/detekt-baseline.xml
+++ b/bugsnag-android-core/detekt-baseline.xml
@@ -22,6 +22,7 @@
MagicNumber:DefaultDelivery.kt$DefaultDelivery$429
MagicNumber:DefaultDelivery.kt$DefaultDelivery$499
MagicNumber:LastRunInfoStore.kt$LastRunInfoStore$3
+ MagicNumber:SessionFilenameInfo.kt$SessionFilenameInfo.Companion$35
MaxLineLength:LastRunInfo.kt$LastRunInfo$return "LastRunInfo(consecutiveLaunchCrashes=$consecutiveLaunchCrashes, crashed=$crashed, crashedDuringLaunch=$crashedDuringLaunch)"
MaxLineLength:ThreadState.kt$ThreadState$"[${allThreads.size - maxThreadCount} threads omitted as the maxReportedThreads limit ($maxThreadCount) was exceeded]"
ProtectedMemberInFinalClass:ConfigInternal.kt$ConfigInternal$protected val plugins = HashSet<Plugin>()
@@ -38,7 +39,9 @@
SwallowedException:DeviceDataCollector.kt$DeviceDataCollector$catch (exception: Exception) { logger.w("Could not get battery status") }
SwallowedException:DeviceDataCollector.kt$DeviceDataCollector$catch (exception: Exception) { logger.w("Could not get locationStatus") }
SwallowedException:DeviceIdStore.kt$DeviceIdStore$catch (exc: OverlappingFileLockException) { Thread.sleep(FILE_LOCK_WAIT_MS) }
+ SwallowedException:EventFilenameInfo.kt$EventFilenameInfo.Companion$catch (e: Exception) { return -1 }
SwallowedException:PluginClient.kt$PluginClient$catch (exc: ClassNotFoundException) { logger.d("Plugin '$clz' is not on the classpath - functionality will not be enabled.") null }
+ SwallowedException:SessionFilenameInfo.kt$SessionFilenameInfo.Companion$catch (e: Exception) { return 0 }
TooManyFunctions:ConfigInternal.kt$ConfigInternal : CallbackAwareMetadataAwareUserAwareFeatureFlagAware
TooManyFunctions:EventInternal.kt$EventInternal : FeatureFlagAwareStreamableMetadataAwareUserAware
UnnecessaryAbstractClass:DependencyModule.kt$DependencyModule
diff --git a/bugsnag-android-core/src/main/java/com/bugsnag/android/Bugsnag.java b/bugsnag-android-core/src/main/java/com/bugsnag/android/Bugsnag.java
index f32b49d6ae..11538b580d 100644
--- a/bugsnag-android-core/src/main/java/com/bugsnag/android/Bugsnag.java
+++ b/bugsnag-android-core/src/main/java/com/bugsnag/android/Bugsnag.java
@@ -69,6 +69,15 @@ public static Client start(@NonNull Context androidContext, @NonNull Configurati
return client;
}
+ /**
+ * Returns true if one of the start
methods have been has been called and
+ * so Bugsnag is initialized; false if start
has not been called and the
+ * other methods will throw IllegalStateException.
+ */
+ public static boolean isStarted() {
+ return client != null;
+ }
+
private static void logClientInitWarning() {
getClient().logger.w("Multiple Bugsnag.start calls detected. Ignoring.");
}
@@ -76,18 +85,19 @@ private static void logClientInitWarning() {
/**
* Bugsnag uses the concept of "contexts" to help display and group your errors. Contexts
* represent what was happening in your application at the time an error occurs.
- *
+ *
* In an android app the "context" is automatically set as the foreground Activity.
* If you would like to set this value manually, you should alter this property.
*/
- @Nullable public static String getContext() {
+ @Nullable
+ public static String getContext() {
return getClient().getContext();
}
/**
* Bugsnag uses the concept of "contexts" to help display and group your errors. Contexts
* represent what was happening in your application at the time an error occurs.
- *
+ *
* In an android app the "context" is automatically set as the foreground Activity.
* If you would like to set this value manually, you should alter this property.
*/
@@ -115,15 +125,15 @@ public static User getUser() {
/**
* Add a "on error" callback, to execute code at the point where an error report is
* captured in Bugsnag.
- *
+ *
* You can use this to add or modify information attached to an Event
* before it is sent to your dashboard. You can also return
* false
from any callback to prevent delivery. "on error"
* callbacks do not run before reports generated in the event
* of immediate app termination from crashes in C/C++ code.
- *
+ *
* For example:
- *
+ *
* Bugsnag.addOnError(new OnErrorCallback() {
* public boolean run(Event event) {
* event.setSeverity(Severity.INFO);
@@ -140,6 +150,7 @@ public static void addOnError(@NonNull OnErrorCallback onError) {
/**
* Removes a previously added "on error" callback
+ *
* @param onError the callback to remove
*/
public static void removeOnError(@NonNull OnErrorCallback onError) {
@@ -149,12 +160,12 @@ public static void removeOnError(@NonNull OnErrorCallback onError) {
/**
* Add an "on breadcrumb" callback, to execute code before every
* breadcrumb captured by Bugsnag.
- *
+ *
* You can use this to modify breadcrumbs before they are stored by Bugsnag.
* You can also return false
from any callback to ignore a breadcrumb.
- *
+ *
* For example:
- *
+ *
* Bugsnag.onBreadcrumb(new OnBreadcrumbCallback() {
* public boolean run(Breadcrumb breadcrumb) {
* return false; // ignore the breadcrumb
@@ -170,6 +181,7 @@ public static void addOnBreadcrumb(@NonNull final OnBreadcrumbCallback onBreadcr
/**
* Removes a previously added "on breadcrumb" callback
+ *
* @param onBreadcrumb the callback to remove
*/
public static void removeOnBreadcrumb(@NonNull OnBreadcrumbCallback onBreadcrumb) {
@@ -179,12 +191,12 @@ public static void removeOnBreadcrumb(@NonNull OnBreadcrumbCallback onBreadcrumb
/**
* Add an "on session" callback, to execute code before every
* session captured by Bugsnag.
- *
+ *
* You can use this to modify sessions before they are stored by Bugsnag.
* You can also return false
from any callback to ignore a session.
- *
+ *
* For example:
- *
+ *
* Bugsnag.onSession(new OnSessionCallback() {
* public boolean run(Session session) {
* return false; // ignore the session
@@ -200,6 +212,7 @@ public static void addOnSession(@NonNull OnSessionCallback onSession) {
/**
* Removes a previously added "on session" callback
+ *
* @param onSession the callback to remove
*/
public static void removeOnSession(@NonNull OnSessionCallback onSession) {
@@ -219,7 +232,7 @@ public static void notify(@NonNull final Throwable exception) {
* Notify Bugsnag of a handled exception
*
* @param exception the exception to send to Bugsnag
- * @param onError callback invoked on the generated error report for
+ * @param onError callback invoked on the generated error report for
* additional modification
*/
public static void notify(@NonNull final Throwable exception,
@@ -286,7 +299,8 @@ public static void leaveBreadcrumb(@NonNull String message) {
/**
* Leave a "breadcrumb" log message representing an action or event which
* occurred in your app, to aid with debugging
- * @param message A short label
+ *
+ * @param message A short label
* @param metadata Additional diagnostic information about the app environment
* @param type A category for the breadcrumb
*/
@@ -332,11 +346,10 @@ public static void startSession() {
*
* stability score .
*
+ * @return true if a previous session was resumed, false if a new session was started.
* @see #startSession()
* @see #pauseSession()
* @see Configuration#setAutoTrackSessions(boolean)
- *
- * @return true if a previous session was resumed, false if a new session was started.
*/
public static boolean resumeSession() {
return getClient().resumeSession();
@@ -365,7 +378,7 @@ public static void pauseSession() {
* Returns the current buffer of breadcrumbs that will be sent with captured events. This
* ordered list represents the most recent breadcrumbs to be captured up to the limit
* set in {@link Configuration#getMaxBreadcrumbs()}.
- *
+ *
* The returned collection is readonly and mutating the list will cause no effect on the
* Client's state. If you wish to alter the breadcrumbs collected by the Client then you should
* use {@link Configuration#setEnabledBreadcrumbTypes(Set)} and
@@ -380,7 +393,7 @@ public static List getBreadcrumbs() {
/**
* Retrieves information about the last launch of the application, if it has been run before.
- *
+ *
* For example, this allows checking whether the app crashed on its last launch, which could
* be used to perform conditional behaviour to recover from crashes, such as clearing the
* app data cache.
@@ -394,7 +407,7 @@ public static LastRunInfo getLastRunInfo() {
* Informs Bugsnag that the application has finished launching. Once this has been called
* {@link AppWithState#isLaunching()} will always be false in any new error reports,
* and synchronous delivery will not be attempted on the next launch for any fatal crashes.
- *
+ *
* By default this method will be called after Bugsnag is initialized when
* {@link Configuration#getLaunchDurationMillis()} has elapsed. Invoking this method manually
* has precedence over the value supplied via the launchDurationMillis configuration option.
@@ -462,8 +475,12 @@ public static void clearFeatureFlags() {
@NonNull
public static Client getClient() {
if (client == null) {
- throw new IllegalStateException("You must call Bugsnag.start before any"
- + " other Bugsnag methods");
+ synchronized (lock) {
+ if (client == null) {
+ throw new IllegalStateException("You must call Bugsnag.start before any"
+ + " other Bugsnag methods");
+ }
+ }
}
return client;
diff --git a/bugsnag-android-core/src/main/java/com/bugsnag/android/EventFilenameInfo.kt b/bugsnag-android-core/src/main/java/com/bugsnag/android/EventFilenameInfo.kt
index 6d9ff766c9..f7cfd5f9af 100644
--- a/bugsnag-android-core/src/main/java/com/bugsnag/android/EventFilenameInfo.kt
+++ b/bugsnag-android-core/src/main/java/com/bugsnag/android/EventFilenameInfo.kt
@@ -22,12 +22,8 @@ internal data class EventFilenameInfo(
val errorTypes: Set
) {
- /**
- * Generates a filename for the Event in the format
- * "[timestamp]_[apiKey]_[errorTypes]_[UUID]_[startupcrash|not-jvm].json"
- */
fun encode(): String {
- return "${timestamp}_${apiKey}_${serializeErrorTypeHeader(errorTypes)}_${uuid}_$suffix.json"
+ return toFilename(apiKey, uuid, timestamp, suffix, errorTypes)
}
fun isLaunchCrashReport(): Boolean = suffix == STARTUP_CRASH
@@ -36,7 +32,21 @@ internal data class EventFilenameInfo(
private const val STARTUP_CRASH = "startupcrash"
private const val NON_JVM_CRASH = "not-jvm"
- @JvmOverloads
+ /**
+ * Generates a filename for the Event in the format
+ * "[timestamp]_[apiKey]_[errorTypes]_[UUID]_[startupcrash|not-jvm].json"
+ */
+ fun toFilename(
+ apiKey: String,
+ uuid: String,
+ timestamp: Long,
+ suffix: String,
+ errorTypes: Set
+ ): String {
+ return "${timestamp}_${apiKey}_${serializeErrorTypeHeader(errorTypes)}_${uuid}_$suffix.json"
+ }
+
+ @JvmOverloads @JvmStatic
fun fromEvent(
obj: Any,
uuid: String = UUID.randomUUID().toString(),
@@ -63,11 +73,12 @@ internal data class EventFilenameInfo(
/**
* Reads event information from a filename.
*/
+ @JvmStatic
fun fromFile(file: File, config: ImmutableConfig): EventFilenameInfo {
return EventFilenameInfo(
findApiKeyInFilename(file, config),
"", // ignore UUID field when reading from file as unused
- -1, // ignore timestamp when reading from file as unused
+ findTimestampInFilename(file),
findSuffixInFilename(file),
findErrorTypesInFilename(file)
)
@@ -77,7 +88,7 @@ internal data class EventFilenameInfo(
* Retrieves the api key encoded in the filename, or an empty string if this information
* is not encoded for the given event
*/
- private fun findApiKeyInFilename(file: File, config: ImmutableConfig): String {
+ internal fun findApiKeyInFilename(file: File, config: ImmutableConfig): String {
val name = file.name.removeSuffix("_$STARTUP_CRASH.json")
val start = name.indexOf("_") + 1
val end = name.indexOf("_", start)
@@ -93,7 +104,7 @@ internal data class EventFilenameInfo(
* Retrieves the error types encoded in the filename, or an empty string if this
* information is not encoded for the given event
*/
- private fun findErrorTypesInFilename(eventFile: File): Set {
+ internal fun findErrorTypesInFilename(eventFile: File): Set {
val name = eventFile.name
val end = name.lastIndexOf("_", name.lastIndexOf("_") - 1)
val start = name.lastIndexOf("_", end - 1) + 1
@@ -111,7 +122,7 @@ internal data class EventFilenameInfo(
* Retrieves the error types encoded in the filename, or an empty string if this
* information is not encoded for the given event
*/
- private fun findSuffixInFilename(eventFile: File): String {
+ internal fun findSuffixInFilename(eventFile: File): String {
val name = eventFile.nameWithoutExtension
val suffix = name.substring(name.lastIndexOf("_") + 1)
return when (suffix) {
@@ -120,10 +131,20 @@ internal data class EventFilenameInfo(
}
}
+ /**
+ * Retrieves the error types encoded in the filename, or an empty string if this
+ * information is not encoded for the given event
+ */
+ @JvmStatic
+ fun findTimestampInFilename(eventFile: File): Long {
+ val name = eventFile.nameWithoutExtension
+ return name.substringBefore("_", missingDelimiterValue = "-1").toLongOrNull() ?: -1
+ }
+
/**
* Retrieves the error types for the given event
*/
- private fun findErrorTypesForEvent(obj: Any): Set {
+ internal fun findErrorTypesForEvent(obj: Any): Set {
return when (obj) {
is Event -> obj.impl.getErrorTypesFromStackframes()
else -> setOf(ErrorType.C)
@@ -133,7 +154,7 @@ internal data class EventFilenameInfo(
/**
* Calculates the suffix for the given event
*/
- private fun findSuffixForEvent(obj: Any, launching: Boolean?): String {
+ internal fun findSuffixForEvent(obj: Any, launching: Boolean?): String {
return when {
obj is Event && obj.app.isLaunching == true -> STARTUP_CRASH
launching == true -> STARTUP_CRASH
diff --git a/bugsnag-android-core/src/main/java/com/bugsnag/android/EventStore.java b/bugsnag-android-core/src/main/java/com/bugsnag/android/EventStore.java
index e1365644ae..5ae0c04c50 100644
--- a/bugsnag-android-core/src/main/java/com/bugsnag/android/EventStore.java
+++ b/bugsnag-android-core/src/main/java/com/bugsnag/android/EventStore.java
@@ -7,9 +7,11 @@
import java.io.File;
import java.util.ArrayList;
+import java.util.Calendar;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
+import java.util.Date;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
@@ -119,7 +121,7 @@ File findLaunchCrashReport(Collection storedFiles) {
List launchCrashes = new ArrayList<>();
for (File file : storedFiles) {
- EventFilenameInfo filenameInfo = EventFilenameInfo.Companion.fromFile(file, config);
+ EventFilenameInfo filenameInfo = EventFilenameInfo.fromFile(file, config);
if (filenameInfo.isLaunchCrashReport()) {
launchCrashes.add(file);
}
@@ -163,7 +165,7 @@ void flushReports(Collection storedReports) {
private void flushEventFile(File eventFile) {
try {
- EventFilenameInfo eventInfo = EventFilenameInfo.Companion.fromFile(eventFile, config);
+ EventFilenameInfo eventInfo = EventFilenameInfo.fromFile(eventFile, config);
String apiKey = eventInfo.getApiKey();
EventPayload payload = createEventPayload(eventFile, apiKey);
@@ -188,9 +190,21 @@ private void deliverEventPayload(File eventFile, EventPayload payload) {
logger.i("Deleting sent error file " + eventFile.getName());
break;
case UNDELIVERED:
- cancelQueuedFiles(Collections.singleton(eventFile));
- logger.w("Could not send previously saved error(s)"
- + " to Bugsnag, will try again later");
+ if (isTooBig(eventFile)) {
+ logger.w("Discarding over-sized event ("
+ + eventFile.length()
+ + ") after failed delivery");
+ deleteStoredFiles(Collections.singleton(eventFile));
+ } else if (isTooOld(eventFile)) {
+ logger.w("Discarding historical event (from "
+ + getCreationDate(eventFile)
+ + ") after failed delivery");
+ deleteStoredFiles(Collections.singleton(eventFile));
+ } else {
+ cancelQueuedFiles(Collections.singleton(eventFile));
+ logger.w("Could not send previously saved error(s)"
+ + " to Bugsnag, will try again later");
+ }
break;
case FAILURE:
Exception exc = new RuntimeException("Failed to deliver event payload");
@@ -234,13 +248,29 @@ private void handleEventFlushFailure(Exception exc, File eventFile) {
@Override
String getFilename(Object object) {
EventFilenameInfo eventInfo
- = EventFilenameInfo.Companion.fromEvent(object, null, config);
+ = EventFilenameInfo.fromEvent(object, null, config);
return eventInfo.encode();
}
String getNdkFilename(Object object, String apiKey) {
EventFilenameInfo eventInfo
- = EventFilenameInfo.Companion.fromEvent(object, apiKey, config);
+ = EventFilenameInfo.fromEvent(object, apiKey, config);
return eventInfo.encode();
}
+
+ private static long oneMegabyte = 1024 * 1024;
+
+ public boolean isTooBig(File file) {
+ return file.length() > oneMegabyte;
+ }
+
+ public boolean isTooOld(File file) {
+ Calendar cal = Calendar.getInstance();
+ cal.add(Calendar.DATE, -60);
+ return EventFilenameInfo.findTimestampInFilename(file) < cal.getTimeInMillis();
+ }
+
+ public Date getCreationDate(File file) {
+ return new Date(EventFilenameInfo.findTimestampInFilename(file));
+ }
}
diff --git a/bugsnag-android-core/src/main/java/com/bugsnag/android/FileStore.java b/bugsnag-android-core/src/main/java/com/bugsnag/android/FileStore.java
index cbafdac431..b613cea988 100644
--- a/bugsnag-android-core/src/main/java/com/bugsnag/android/FileStore.java
+++ b/bugsnag-android-core/src/main/java/com/bugsnag/android/FileStore.java
@@ -39,7 +39,7 @@ interface Delegate {
private final Lock lock = new ReentrantLock();
private final Collection queuedFiles = new ConcurrentSkipListSet<>();
- private final Logger logger;
+ protected final Logger logger;
private final EventStore.Delegate delegate;
FileStore(@NonNull File storageDir,
diff --git a/bugsnag-android-core/src/main/java/com/bugsnag/android/Notifier.kt b/bugsnag-android-core/src/main/java/com/bugsnag/android/Notifier.kt
index bfa3ed5d7f..1d6b2e45ed 100644
--- a/bugsnag-android-core/src/main/java/com/bugsnag/android/Notifier.kt
+++ b/bugsnag-android-core/src/main/java/com/bugsnag/android/Notifier.kt
@@ -7,7 +7,7 @@ import java.io.IOException
*/
class Notifier @JvmOverloads constructor(
var name: String = "Android Bugsnag Notifier",
- var version: String = "5.21.0",
+ var version: String = "5.22.0",
var url: String = "https://bugsnag.com"
) : JsonStream.Streamable {
diff --git a/bugsnag-android-core/src/main/java/com/bugsnag/android/SessionFilenameInfo.kt b/bugsnag-android-core/src/main/java/com/bugsnag/android/SessionFilenameInfo.kt
new file mode 100644
index 0000000000..04efa4081b
--- /dev/null
+++ b/bugsnag-android-core/src/main/java/com/bugsnag/android/SessionFilenameInfo.kt
@@ -0,0 +1,55 @@
+package com.bugsnag.android
+
+import java.io.File
+import java.util.UUID
+
+/**
+ * Represents important information about a session filename.
+ * Currently the following information is encoded:
+ *
+ * uuid - to disambiguate stored error reports
+ * timestamp - to sort error reports by time of capture
+ */
+internal data class SessionFilenameInfo(
+ val timestamp: Long,
+ val uuid: String,
+) {
+
+ fun encode(): String {
+ return toFilename(timestamp, uuid)
+ }
+
+ internal companion object {
+
+ const val uuidLength = 36
+
+ /**
+ * Generates a filename for the session in the format
+ * "[UUID][timestamp]_v2.json"
+ */
+ fun toFilename(timestamp: Long, uuid: String): String {
+ return "${uuid}${timestamp}_v2.json"
+ }
+
+ @JvmStatic
+ fun defaultFilename(): String {
+ return toFilename(System.currentTimeMillis(), UUID.randomUUID().toString())
+ }
+
+ fun fromFile(file: File): SessionFilenameInfo {
+ return SessionFilenameInfo(
+ findTimestampInFilename(file),
+ findUuidInFilename(file)
+ )
+ }
+
+ private fun findUuidInFilename(file: File): String {
+ return file.name.substring(0, uuidLength - 1)
+ }
+
+ @JvmStatic
+ fun findTimestampInFilename(file: File): Long {
+ return file.name.substring(uuidLength, file.name.indexOf("_")).toLongOrNull() ?: -1
+ }
+ }
+}
diff --git a/bugsnag-android-core/src/main/java/com/bugsnag/android/SessionStore.java b/bugsnag-android-core/src/main/java/com/bugsnag/android/SessionStore.java
index 0d84d8a677..a0238a5feb 100644
--- a/bugsnag-android-core/src/main/java/com/bugsnag/android/SessionStore.java
+++ b/bugsnag-android-core/src/main/java/com/bugsnag/android/SessionStore.java
@@ -6,7 +6,9 @@
import androidx.annotation.Nullable;
import java.io.File;
+import java.util.Calendar;
import java.util.Comparator;
+import java.util.Date;
import java.util.UUID;
/**
@@ -46,7 +48,16 @@ public int compare(File lhs, File rhs) {
@NonNull
@Override
String getFilename(Object object) {
- return UUID.randomUUID().toString() + System.currentTimeMillis() + "_v2.json";
+ return SessionFilenameInfo.defaultFilename();
}
+ public boolean isTooOld(File file) {
+ Calendar cal = Calendar.getInstance();
+ cal.add(Calendar.DATE, -60);
+ return SessionFilenameInfo.findTimestampInFilename(file) < cal.getTimeInMillis();
+ }
+
+ public Date getCreationDate(File file) {
+ return new Date(SessionFilenameInfo.findTimestampInFilename(file));
+ }
}
diff --git a/bugsnag-android-core/src/main/java/com/bugsnag/android/SessionTracker.java b/bugsnag-android-core/src/main/java/com/bugsnag/android/SessionTracker.java
index 2a5e95c7f1..ddb2f052d5 100644
--- a/bugsnag-android-core/src/main/java/com/bugsnag/android/SessionTracker.java
+++ b/bugsnag-android-core/src/main/java/com/bugsnag/android/SessionTracker.java
@@ -270,8 +270,15 @@ void flushStoredSession(File storedFile) {
logger.d("Sent 1 new session to Bugsnag");
break;
case UNDELIVERED:
- sessionStore.cancelQueuedFiles(Collections.singletonList(storedFile));
- logger.w("Leaving session payload for future delivery");
+ if (sessionStore.isTooOld(storedFile)) {
+ logger.w("Discarding historical session (from {"
+ + sessionStore.getCreationDate(storedFile)
+ + "}) after failed delivery");
+ sessionStore.deleteStoredFiles(Collections.singletonList(storedFile));
+ } else {
+ sessionStore.cancelQueuedFiles(Collections.singletonList(storedFile));
+ logger.w("Leaving session payload for future delivery");
+ }
break;
case FAILURE:
// drop bad data
diff --git a/bugsnag-android-core/src/test/java/com/bugsnag/android/BugsnagApiTest.kt b/bugsnag-android-core/src/test/java/com/bugsnag/android/BugsnagApiTest.kt
index bb1b0b693b..8a4448b017 100644
--- a/bugsnag-android-core/src/test/java/com/bugsnag/android/BugsnagApiTest.kt
+++ b/bugsnag-android-core/src/test/java/com/bugsnag/android/BugsnagApiTest.kt
@@ -37,6 +37,17 @@ class BugsnagApiTest {
verify(client, times(1)).context = "Bar"
}
+ @Test
+ fun isStartedWhenStarted() {
+ assertEquals(true, Bugsnag.isStarted())
+ }
+
+ @Test
+ fun isStartedWhenNotStarted() {
+ Bugsnag.client = null
+ assertEquals(false, Bugsnag.isStarted())
+ }
+
@Test
fun getUser() {
Bugsnag.getUser()
diff --git a/bugsnag-android-core/src/test/java/com/bugsnag/android/BugsnagClientMirrorInterfaceTest.kt b/bugsnag-android-core/src/test/java/com/bugsnag/android/BugsnagClientMirrorInterfaceTest.kt
index 47166196b1..a23892aa54 100644
--- a/bugsnag-android-core/src/test/java/com/bugsnag/android/BugsnagClientMirrorInterfaceTest.kt
+++ b/bugsnag-android-core/src/test/java/com/bugsnag/android/BugsnagClientMirrorInterfaceTest.kt
@@ -9,13 +9,18 @@ import java.lang.reflect.Method
* signatures, and fails if the two are out of sync.
*
* This is intended to catch the case where a new API is added to one class but not the other.
- * If a method genuinely only needs to be present on one of the classes, it can be added to a
- * blacklist via [bugsnagBlacklist] or [clientBlacklist].
+ * If a method genuinely only needs to be present on one of the classes, it should be added to
+ * [bugsnagAllowList] or [clientAllowList].
*/
class BugsnagClientMirrorInterfaceTest {
- private val bugsnagBlacklist = setOf("start", "getClient")
- private val clientBlacklist = setOf(
+ private val bugsnagAllowList = setOf(
+ "start",
+ "getClient",
+ "isStarted"
+ )
+
+ private val clientAllowList = setOf(
"update",
"notifyObservers",
"addObserver",
@@ -35,14 +40,14 @@ class BugsnagClientMirrorInterfaceTest {
@Test
fun bugsnagHasSameMethodsAsClient() {
val methods = clientMethods.subtract(bugsnagMethods)
- .filter { !clientBlacklist.contains(it.name) }
+ .filter { !clientAllowList.contains(it.name) }
assertTrue("Bugsnag is missing the following methods: $methods", methods.isEmpty())
}
@Test
fun clientHasSameMethodsAsBugsnag() {
val methods = bugsnagMethods.subtract(clientMethods)
- .filter { !bugsnagBlacklist.contains(it.name) }
+ .filter { !bugsnagAllowList.contains(it.name) }
assertTrue("Client is missing the following methods: $methods", methods.isEmpty())
}
}
diff --git a/bugsnag-plugin-android-ndk/src/main/jni/bugsnag_ndk.c b/bugsnag-plugin-android-ndk/src/main/jni/bugsnag_ndk.c
index 648a533a73..df219af2ab 100644
--- a/bugsnag-plugin-android-ndk/src/main/jni/bugsnag_ndk.c
+++ b/bugsnag-plugin-android-ndk/src/main/jni/bugsnag_ndk.c
@@ -58,6 +58,17 @@ bool bsg_run_on_error() {
return true;
}
+bool bsg_begin_handling_crash() {
+ static bool expected = false;
+ return atomic_compare_exchange_strong(&bsg_global_env->handling_crash,
+ &expected, true);
+}
+
+void bsg_finish_handling_crash() {
+ bsg_global_env->crash_handled = true;
+ bsg_global_env->handling_crash = false;
+}
+
JNIEXPORT void JNICALL Java_com_bugsnag_android_NdkPlugin_enableCrashReporting(
JNIEnv *env, jobject _this) {
if (bsg_global_env == NULL) {
@@ -143,6 +154,7 @@ JNIEXPORT void JNICALL Java_com_bugsnag_android_ndk_NativeBridge_install(
bugsnag_env->report_header.version = BUGSNAG_EVENT_VERSION;
bugsnag_env->consecutive_launch_crashes = consecutive_launch_crashes;
bugsnag_env->send_threads = send_threads;
+ bugsnag_env->handling_crash = ATOMIC_VAR_INIT(false);
// copy event path to env struct
const char *event_path = bsg_safe_get_string_utf_chars(env, _event_path);
diff --git a/bugsnag-plugin-android-ndk/src/main/jni/bugsnag_ndk.h b/bugsnag-plugin-android-ndk/src/main/jni/bugsnag_ndk.h
index 66506884d4..592c9bb1d6 100644
--- a/bugsnag-plugin-android-ndk/src/main/jni/bugsnag_ndk.h
+++ b/bugsnag-plugin-android-ndk/src/main/jni/bugsnag_ndk.h
@@ -4,6 +4,7 @@
#ifndef BUGSNAG_NDK_H
#define BUGSNAG_NDK_H
+#include
#include
#include "../assets/include/bugsnag.h"
@@ -53,7 +54,7 @@ typedef struct {
* true if a crash is currently being handled. Disallows multiple crashes
* from being processed simultaneously
*/
- bool handling_crash;
+ _Atomic bool handling_crash;
/**
* true if a handler has completed crash handling
*/
@@ -78,6 +79,22 @@ typedef struct {
*/
bool bsg_run_on_error();
+/**
+ * This must be called before handling a native crash to ensure that two crash
+ * handlers don't run simultaneously. If this function returns falls, DO NOT
+ * PROCEED WITH CRASH HANDLING!
+ *
+ * When done handling the crash, you must call bsg_finish_handling_crash()
+ *
+ * @return true if no other crash handler is already running.
+ */
+bool bsg_begin_handling_crash();
+
+/**
+ * Let the system know that you've finished handling the crash.
+ */
+void bsg_finish_handling_crash();
+
#ifdef __cplusplus
}
#endif
diff --git a/bugsnag-plugin-android-ndk/src/main/jni/handlers/cpp_handler.cpp b/bugsnag-plugin-android-ndk/src/main/jni/handlers/cpp_handler.cpp
index 61b1c80646..af8d7bf0f3 100644
--- a/bugsnag-plugin-android-ndk/src/main/jni/handlers/cpp_handler.cpp
+++ b/bugsnag-plugin-android-ndk/src/main/jni/handlers/cpp_handler.cpp
@@ -47,7 +47,10 @@ void bsg_handle_cpp_terminate() {
if (bsg_global_env == NULL || bsg_global_env->handling_crash)
return;
- bsg_global_env->handling_crash = true;
+ if (!bsg_begin_handling_crash()) {
+ return;
+ }
+
bsg_populate_event_as(bsg_global_env);
bsg_global_env->next_event.unhandled = true;
bsg_global_env->next_event.error.frame_count = bsg_unwind_crash_stack(
@@ -72,7 +75,8 @@ void bsg_handle_cpp_terminate() {
bsg_serialize_event_to_file(bsg_global_env);
bsg_serialize_last_run_info_to_file(bsg_global_env);
}
- bsg_global_env->crash_handled = true;
+
+ bsg_finish_handling_crash();
bsg_handler_uninstall_cpp();
if (bsg_global_terminate_previous != NULL) {
bsg_global_terminate_previous();
diff --git a/bugsnag-plugin-android-ndk/src/main/jni/handlers/signal_handler.c b/bugsnag-plugin-android-ndk/src/main/jni/handlers/signal_handler.c
index 5c4f0cc42b..15afe15f70 100644
--- a/bugsnag-plugin-android-ndk/src/main/jni/handlers/signal_handler.c
+++ b/bugsnag-plugin-android-ndk/src/main/jni/handlers/signal_handler.c
@@ -168,18 +168,19 @@ void bsg_handle_signal(int signum, siginfo_t *info,
if (bsg_global_env == NULL) {
return;
}
- if (bsg_global_env->handling_crash) {
- if (bsg_global_env->crash_handled) {
- // The C++ handler default action is to raise a fatal signal once
- // handling is complete. The report is already generated so at this
- // point, the handler only needs to be uninstalled.
- bsg_handler_uninstall_signal();
- bsg_invoke_previous_signal_handler(signum, info, user_context);
- }
+ if (!bsg_begin_handling_crash()) {
+ return;
+ }
+
+ if (bsg_global_env->crash_handled) {
+ // The C++ handler default action is to raise a fatal signal once
+ // handling is complete. The report is already generated so at this
+ // point, the handler only needs to be uninstalled.
+ bsg_handler_uninstall_signal();
+ bsg_invoke_previous_signal_handler(signum, info, user_context);
return;
}
- bsg_global_env->handling_crash = true;
bsg_global_env->next_event.unhandled = true;
bsg_populate_event_as(bsg_global_env);
bsg_global_env->next_event.error.frame_count = bsg_unwind_crash_stack(
@@ -209,6 +210,8 @@ void bsg_handle_signal(int signum, siginfo_t *info,
bsg_serialize_event_to_file(bsg_global_env);
bsg_serialize_last_run_info_to_file(bsg_global_env);
}
+
+ bsg_finish_handling_crash();
bsg_handler_uninstall_signal();
bsg_invoke_previous_signal_handler(signum, info, user_context);
}
diff --git a/bugsnag-plugin-android-ndk/src/main/jni/utils/string.c b/bugsnag-plugin-android-ndk/src/main/jni/utils/string.c
index cb193471f2..e1d853a9e7 100644
--- a/bugsnag-plugin-android-ndk/src/main/jni/utils/string.c
+++ b/bugsnag-plugin-android-ndk/src/main/jni/utils/string.c
@@ -16,9 +16,11 @@ size_t bsg_strlen(const char *str) {
}
void bsg_strncpy(char *dst, const char *src, size_t dst_size) {
- if (src == NULL || dst == NULL || dst_size == 0) {
+ if (dst == NULL || dst_size == 0) {
return;
}
dst[0] = '\0';
- strncat(dst, src, dst_size - 1);
+ if (src != NULL) {
+ strncat(dst, src, dst_size - 1);
+ }
}
diff --git a/bugsnag-plugin-android-ndk/src/test/cpp/test_utils_string.c b/bugsnag-plugin-android-ndk/src/test/cpp/test_utils_string.c
index e8182c9ebf..711025fa71 100644
--- a/bugsnag-plugin-android-ndk/src/test/cpp/test_utils_string.c
+++ b/bugsnag-plugin-android-ndk/src/test/cpp/test_utils_string.c
@@ -12,6 +12,16 @@ TEST test_copy_empty_string(void) {
PASS();
}
+TEST test_copy_null_string(void) {
+ int dst_len = 10;
+ char *dst = calloc(sizeof(char), dst_len);
+ strcpy(dst, "C h a n g e");
+ bsg_strncpy(dst, NULL, dst_len);
+ ASSERT(dst[0] == '\0');
+ free(dst);
+ PASS();
+}
+
TEST test_copy_literal_string(void) {
char *src = "C h a n g e";
int dst_len = 10;
@@ -48,6 +58,7 @@ TEST length_null_string(void) {
SUITE(suite_string_utils) {
RUN_TEST(test_copy_empty_string);
+ RUN_TEST(test_copy_null_string);
RUN_TEST(test_copy_literal_string);
RUN_TEST(length_empty_string);
RUN_TEST(length_literal_string);
diff --git a/examples/sdk-app-example/app/build.gradle b/examples/sdk-app-example/app/build.gradle
index 7ee14b7135..9117ef63d9 100644
--- a/examples/sdk-app-example/app/build.gradle
+++ b/examples/sdk-app-example/app/build.gradle
@@ -38,7 +38,7 @@ android {
}
dependencies {
- implementation "com.bugsnag:bugsnag-android:5.20.0"
+ implementation "com.bugsnag:bugsnag-android:5.21.0"
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
implementation "androidx.appcompat:appcompat:1.4.0"
implementation "com.google.android.material:material:1.4.0"
diff --git a/features/fixtures/mazerunner/app/src/main/java/com/bugsnag/android/mazerunner/MainActivity.kt b/features/fixtures/mazerunner/app/src/main/java/com/bugsnag/android/mazerunner/MainActivity.kt
index 7bdf76f962..e2bdb86e12 100644
--- a/features/fixtures/mazerunner/app/src/main/java/com/bugsnag/android/mazerunner/MainActivity.kt
+++ b/features/fixtures/mazerunner/app/src/main/java/com/bugsnag/android/mazerunner/MainActivity.kt
@@ -10,6 +10,7 @@ import android.widget.Button
import android.widget.EditText
import com.bugsnag.android.Configuration
import com.bugsnag.android.mazerunner.scenarios.Scenario
+import java.io.File
class MainActivity : Activity() {
@@ -30,6 +31,15 @@ class MainActivity : Activity() {
sendBroadcast(closeDialog)
}
+ // Clear persistent data (used to stop scenarios bleeding into each other)
+ findViewById(R.id.clear_persistent_data).setOnClickListener {
+ clearFolder("last-run-info")
+ clearFolder("bugsnag-errors")
+ clearFolder("device-id")
+ clearFolder("user-info")
+ clearFolder("fake")
+ }
+
// load the scenario first, which initialises bugsnag without running any crashy code
findViewById(R.id.start_bugsnag).setOnClickListener {
scenario = loadScenarioFromUi()
@@ -73,6 +83,13 @@ class MainActivity : Activity() {
}
}
+ private fun clearFolder(name: String) {
+ val context = MazerunnerApp.applicationContext()
+ val folder = File(context.cacheDir, name)
+ log("Clearing folder: ${folder.path}")
+ folder.deleteRecursively()
+ }
+
private fun loadScenarioFromUi(): Scenario {
val scenarioPicker = findViewById(R.id.scenario_name)
val eventType = scenarioPicker.text.toString()
diff --git a/features/fixtures/mazerunner/app/src/main/java/com/bugsnag/android/mazerunner/MazerunnerApp.kt b/features/fixtures/mazerunner/app/src/main/java/com/bugsnag/android/mazerunner/MazerunnerApp.kt
index d8788d1a60..91988e93ea 100644
--- a/features/fixtures/mazerunner/app/src/main/java/com/bugsnag/android/mazerunner/MazerunnerApp.kt
+++ b/features/fixtures/mazerunner/app/src/main/java/com/bugsnag/android/mazerunner/MazerunnerApp.kt
@@ -1,11 +1,24 @@
package com.bugsnag.android.mazerunner
import android.app.Application
+import android.content.Context
import android.os.Build
import android.os.StrictMode
class MazerunnerApp : Application() {
+ init {
+ instance = this
+ }
+
+ companion object {
+ private var instance: MazerunnerApp? = null
+
+ fun applicationContext(): Context {
+ return instance!!.applicationContext
+ }
+ }
+
override fun onCreate() {
super.onCreate()
triggerStartupAnrIfRequired()
diff --git a/features/fixtures/mazerunner/app/src/main/res/layout/activity_main.xml b/features/fixtures/mazerunner/app/src/main/res/layout/activity_main.xml
index 639fd4864c..775ae02f47 100644
--- a/features/fixtures/mazerunner/app/src/main/res/layout/activity_main.xml
+++ b/features/fixtures/mazerunner/app/src/main/res/layout/activity_main.xml
@@ -22,6 +22,13 @@
android:hint="Scenario metadata"
android:inputType="text" />
+
+
MagicNumber:AutoDetectAnrsFalseScenario.kt$AutoDetectAnrsFalseScenario$100000
MagicNumber:AutoDetectAnrsTrueScenario.kt$AutoDetectAnrsTrueScenario$100000
MagicNumber:BugsnagInitScenario.kt$BugsnagInitScenario$25
+ MagicNumber:DiscardBigEventsScenario.kt$DiscardBigEventsScenario$100
+ MagicNumber:DiscardBigEventsScenario.kt$DiscardBigEventsScenario$1024
+ MagicNumber:DiscardOldEventsScenario.kt$DiscardOldEventsScenario$100
+ MagicNumber:DiscardOldEventsScenario.kt$DiscardOldEventsScenario$60
+ MagicNumber:DiscardOldSessionScenario.kt$DiscardOldSessionScenario$100
+ MagicNumber:DiscardOldSessionScenario.kt$DiscardOldSessionScenario$35
+ MagicNumber:DiscardOldSessionScenario.kt$DiscardOldSessionScenario$60
+ MagicNumber:DiscardOldSessionScenarioPart2.kt$DiscardOldSessionScenarioPart2$1000
MagicNumber:HandledExceptionMaxThreadsScenario.kt$HandledExceptionMaxThreadsScenario$3
MagicNumber:HandledKotlinSmokeScenario.kt$HandledKotlinSmokeScenario$999
MagicNumber:JvmAnrSleepScenario.kt$JvmAnrSleepScenario$100000
diff --git a/features/fixtures/mazerunner/jvm-scenarios/src/main/java/com/bugsnag/android/mazerunner/scenarios/DiscardBigEventsScenario.kt b/features/fixtures/mazerunner/jvm-scenarios/src/main/java/com/bugsnag/android/mazerunner/scenarios/DiscardBigEventsScenario.kt
new file mode 100644
index 0000000000..34a8ab2d82
--- /dev/null
+++ b/features/fixtures/mazerunner/jvm-scenarios/src/main/java/com/bugsnag/android/mazerunner/scenarios/DiscardBigEventsScenario.kt
@@ -0,0 +1,42 @@
+package com.bugsnag.android.mazerunner.scenarios
+
+import android.content.Context
+import com.bugsnag.android.Bugsnag
+import com.bugsnag.android.Configuration
+import java.io.File
+
+internal class DiscardBigEventsScenario(
+ config: Configuration,
+ context: Context,
+ eventMetadata: String
+) : Scenario(config, context, eventMetadata) {
+
+ init {
+ config.launchDurationMillis = 0
+ config.addOnSend {
+ it.addMetadata("big", "stuff", generateBigText())
+ true
+ }
+ }
+
+ fun generateBigText(): String {
+ return "*".repeat(1024 * 1024)
+ }
+
+ fun waitForEventFile() {
+ val dir = File(context.cacheDir, "bugsnag-errors")
+ while (dir.listFiles()!!.isEmpty()) {
+ Thread.sleep(100)
+ }
+ }
+
+ override fun startScenario() {
+ super.startScenario()
+ Bugsnag.markLaunchCompleted()
+ Bugsnag.notify(MyThrowable("DiscardBigEventsScenario"))
+
+ waitForEventFile()
+
+ Bugsnag.notify(MyThrowable("To keep maze-runner from shutting me down prematurely"))
+ }
+}
diff --git a/features/fixtures/mazerunner/jvm-scenarios/src/main/java/com/bugsnag/android/mazerunner/scenarios/DiscardOldEventsScenario.kt b/features/fixtures/mazerunner/jvm-scenarios/src/main/java/com/bugsnag/android/mazerunner/scenarios/DiscardOldEventsScenario.kt
new file mode 100644
index 0000000000..7262e80389
--- /dev/null
+++ b/features/fixtures/mazerunner/jvm-scenarios/src/main/java/com/bugsnag/android/mazerunner/scenarios/DiscardOldEventsScenario.kt
@@ -0,0 +1,59 @@
+package com.bugsnag.android.mazerunner.scenarios
+
+import android.content.Context
+import com.bugsnag.android.Bugsnag
+import com.bugsnag.android.Configuration
+import java.io.File
+import java.util.Calendar
+
+internal class DiscardOldEventsScenario(
+ config: Configuration,
+ context: Context,
+ eventMetadata: String
+) : Scenario(config, context, eventMetadata) {
+
+ init {
+ config.launchDurationMillis = 0
+ }
+
+ fun setEventFileTimestamp(file: File, timestamp: Long) {
+ val name = file.name
+ val suffix = name.substringAfter("_")
+ val dstFile = File(file.parent, "${timestamp}_$suffix")
+ assert(file.renameTo(dstFile))
+ }
+
+ fun errorsDir(): File {
+ return File(context.cacheDir, "bugsnag-errors")
+ }
+
+ fun waitForEventFile() {
+ val dir = errorsDir()
+ while (dir.listFiles()!!.isEmpty()) {
+ Thread.sleep(100)
+ }
+ }
+
+ fun oldifyEventFiles() {
+ val cal = Calendar.getInstance()
+ cal.add(Calendar.DATE, -60)
+ cal.add(Calendar.MINUTE, -1)
+ val timestamp = cal.timeInMillis
+
+ val files = errorsDir().listFiles()
+ for (file in files!!) {
+ setEventFileTimestamp(file, timestamp)
+ }
+ }
+
+ override fun startScenario() {
+ super.startScenario()
+ Bugsnag.markLaunchCompleted()
+ Bugsnag.notify(MyThrowable("DiscardOldEventsScenario"))
+
+ waitForEventFile()
+ oldifyEventFiles()
+
+ Bugsnag.notify(MyThrowable("To keep maze-runner from shutting me down prematurely"))
+ }
+}
diff --git a/features/fixtures/mazerunner/jvm-scenarios/src/main/java/com/bugsnag/android/mazerunner/scenarios/DiscardOldSessionScenario.kt b/features/fixtures/mazerunner/jvm-scenarios/src/main/java/com/bugsnag/android/mazerunner/scenarios/DiscardOldSessionScenario.kt
new file mode 100644
index 0000000000..eb0900eb30
--- /dev/null
+++ b/features/fixtures/mazerunner/jvm-scenarios/src/main/java/com/bugsnag/android/mazerunner/scenarios/DiscardOldSessionScenario.kt
@@ -0,0 +1,64 @@
+package com.bugsnag.android.mazerunner.scenarios
+
+import android.content.Context
+import com.bugsnag.android.Bugsnag
+import com.bugsnag.android.Configuration
+import com.bugsnag.android.EndpointConfiguration
+import java.io.File
+import java.util.Calendar
+
+internal class DiscardOldSessionScenario(
+ config: Configuration,
+ context: Context,
+ eventMetadata: String
+) : Scenario(config, context, eventMetadata) {
+
+ init {
+ config.launchDurationMillis = 0
+ // We set an endpoint so that attempts to send the session will fail.
+ config.endpoints = EndpointConfiguration(config.endpoints.notify, "https://nonexistent.bugsnag.com")
+ }
+
+ fun setSessionFileTimestamp(file: File, timestamp: Long) {
+ val name = file.name
+ val uuid = name.substring(0, 35)
+ val suffix = name.substringAfter("_")
+ val dstFile = File(file.parent, "${uuid}${timestamp}_$suffix")
+ assert(file.renameTo(dstFile))
+ }
+
+ fun sessionDir(): File {
+ return File(context.cacheDir, "bugsnag-sessions")
+ }
+
+ fun waitForSessionFile() {
+ val dir = sessionDir()
+ while (dir.listFiles()!!.isEmpty()) {
+ Thread.sleep(100)
+ }
+ }
+
+ fun oldifySessionFiles() {
+ val cal = Calendar.getInstance()
+ cal.add(Calendar.DATE, -60)
+ cal.add(Calendar.MINUTE, -1)
+ val timestamp = cal.timeInMillis
+
+ val files = sessionDir().listFiles()
+ for (file in files!!) {
+ setSessionFileTimestamp(file, timestamp)
+ }
+ }
+
+ override fun startScenario() {
+ super.startScenario()
+ Bugsnag.markLaunchCompleted()
+ Bugsnag.startSession()
+
+ waitForSessionFile()
+ oldifySessionFiles()
+
+ System.out.println("DiscardOldSessionScenario: Finished oldifying files; sending placeholder event.")
+ Bugsnag.notify(MyThrowable("To keep maze-runner from shutting me down prematurely"))
+ }
+}
diff --git a/features/fixtures/mazerunner/jvm-scenarios/src/main/java/com/bugsnag/android/mazerunner/scenarios/DiscardOldSessionScenarioPart2.kt b/features/fixtures/mazerunner/jvm-scenarios/src/main/java/com/bugsnag/android/mazerunner/scenarios/DiscardOldSessionScenarioPart2.kt
new file mode 100644
index 0000000000..fe3130849e
--- /dev/null
+++ b/features/fixtures/mazerunner/jvm-scenarios/src/main/java/com/bugsnag/android/mazerunner/scenarios/DiscardOldSessionScenarioPart2.kt
@@ -0,0 +1,28 @@
+package com.bugsnag.android.mazerunner.scenarios
+
+import android.content.Context
+import com.bugsnag.android.Bugsnag
+import com.bugsnag.android.Configuration
+import com.bugsnag.android.EndpointConfiguration
+
+internal class DiscardOldSessionScenarioPart2(
+ config: Configuration,
+ context: Context,
+ eventMetadata: String
+) : Scenario(config, context, eventMetadata) {
+
+ init {
+ config.launchDurationMillis = 0
+ // We set an endpoint so that attempts to send the session will fail.
+ config.endpoints = EndpointConfiguration(config.endpoints.notify, "https://nonexistent.bugsnag.com")
+ }
+
+ override fun startScenario() {
+ super.startScenario()
+
+ // Give Bugsnag time to try sending the serialized session again.
+ Thread.sleep(1000)
+
+ Bugsnag.notify(MyThrowable("To keep maze-runner from shutting me down prematurely"))
+ }
+}
diff --git a/features/fixtures/mazerunner/jvm-scenarios/src/main/java/com/bugsnag/android/mazerunner/scenarios/DiscardOldSessionScenarioPart3.kt b/features/fixtures/mazerunner/jvm-scenarios/src/main/java/com/bugsnag/android/mazerunner/scenarios/DiscardOldSessionScenarioPart3.kt
new file mode 100644
index 0000000000..a0525dbfa1
--- /dev/null
+++ b/features/fixtures/mazerunner/jvm-scenarios/src/main/java/com/bugsnag/android/mazerunner/scenarios/DiscardOldSessionScenarioPart3.kt
@@ -0,0 +1,16 @@
+package com.bugsnag.android.mazerunner.scenarios
+
+import android.content.Context
+import com.bugsnag.android.Configuration
+
+internal class DiscardOldSessionScenarioPart3(
+ config: Configuration,
+ context: Context,
+ eventMetadata: String
+) : Scenario(config, context, eventMetadata) {
+
+ init {
+ config.launchDurationMillis = 0
+ // Note: No impediments to sending.
+ }
+}
diff --git a/features/fixtures/mazerunner/jvm-scenarios/src/main/java/com/bugsnag/android/mazerunner/scenarios/MultiThreadedStartupScenario.kt b/features/fixtures/mazerunner/jvm-scenarios/src/main/java/com/bugsnag/android/mazerunner/scenarios/MultiThreadedStartupScenario.kt
new file mode 100644
index 0000000000..7a81e8dfdb
--- /dev/null
+++ b/features/fixtures/mazerunner/jvm-scenarios/src/main/java/com/bugsnag/android/mazerunner/scenarios/MultiThreadedStartupScenario.kt
@@ -0,0 +1,34 @@
+package com.bugsnag.android.mazerunner.scenarios
+
+import android.content.Context
+import com.bugsnag.android.Bugsnag
+import com.bugsnag.android.Configuration
+import kotlin.concurrent.thread
+
+class MultiThreadedStartupScenario(
+ config: Configuration,
+ context: Context,
+ eventMetadata: String?
+) : Scenario(config, context, eventMetadata) {
+ override fun startBugsnag(startBugsnagOnly: Boolean) {}
+
+ override fun startScenario() {
+ val startThread = thread(name = "AsyncStart") {
+ Bugsnag.start(context, config)
+ }
+
+ thread(name = "leaveBreadcrumb") {
+ // simulate the start of some startup work, but not enough for Bugsnag.start to complete
+ Thread.sleep(1L)
+ try {
+ Bugsnag.leaveBreadcrumb("I'm leaving a breadcrumb on another thread")
+ } catch (e: Exception) {
+ Bugsnag.start(context, config)
+ Bugsnag.notify(e)
+ }
+ }
+
+ // make sure we wait before returning
+ startThread.join()
+ }
+}
diff --git a/features/full_tests/async_error_flush.feature b/features/full_tests/async_error_flush.feature
index 4fe694c632..c8cd76cb2d 100644
--- a/features/full_tests/async_error_flush.feature
+++ b/features/full_tests/async_error_flush.feature
@@ -4,7 +4,7 @@ Feature: Flushing errors does not send duplicates
# Disabled until PLAT-5869 can be actioned to convert these to unit tests.
#Scenario: Only 1 request sent if connectivity change occurs before launch
-# When I run "AsyncErrorConnectivityScenario" and relaunch the app
+# When I run "AsyncErrorConnectivityScenario" and relaunch the crashed app
# And I configure Bugsnag for "AsyncErrorConnectivityScenario"
# Then I wait to receive an error
# And the error is valid for the error reporting API version "4.0" for the "Android Bugsnag Notifier" notifier
diff --git a/features/full_tests/auto_notify.feature b/features/full_tests/auto_notify.feature
index ae401a2754..1dc7eceaee 100644
--- a/features/full_tests/auto_notify.feature
+++ b/features/full_tests/auto_notify.feature
@@ -1,29 +1,32 @@
Feature: Switching automatic error detection on/off for Unity
-Scenario: Handled JVM exceptions are captured with autoNotify=false
+ Background:
+ Given I clear all persistent data
+
+ Scenario: Handled JVM exceptions are captured with autoNotify=false
When I run "HandledJvmAutoNotifyFalseScenario"
Then I wait to receive an error
And the error is valid for the error reporting API version "4.0" for the "Android Bugsnag Notifier" notifier
And the exception "message" equals "HandledJvmAutoNotifyFalseScenario"
-Scenario: JVM exception not captured with autoNotify=false
- When I run "UnhandledJvmAutoNotifyFalseScenario" and relaunch the app
+ Scenario: JVM exception not captured with autoNotify=false
+ When I run "UnhandledJvmAutoNotifyFalseScenario" and relaunch the crashed app
And I configure Bugsnag for "UnhandledJvmAutoNotifyFalseScenario"
Then Bugsnag confirms it has no errors to send
-Scenario: NDK signal not captured with autoNotify=false
- When I run "UnhandledNdkAutoNotifyFalseScenario" and relaunch the app
+ Scenario: NDK signal not captured with autoNotify=false
+ When I run "UnhandledNdkAutoNotifyFalseScenario" and relaunch the crashed app
And I configure Bugsnag for "UnhandledNdkAutoNotifyFalseScenario"
Then Bugsnag confirms it has no errors to send
-@skip_android_8_1
-Scenario: ANR not captured with autoDetectAnrs=false
- When I run "AutoDetectAnrsFalseScenario" and relaunch the app
+ @skip_android_8_1
+ Scenario: ANR not captured with autoDetectAnrs=false
+ When I run "AutoDetectAnrsFalseScenario" and relaunch the crashed app
And I configure Bugsnag for "AutoDetectAnrsFalseScenario"
Then Bugsnag confirms it has no errors to send
-Scenario: JVM exception captured with autoNotify reenabled
- When I run "UnhandledJvmAutoNotifyTrueScenario" and relaunch the app
+ Scenario: JVM exception captured with autoNotify reenabled
+ When I run "UnhandledJvmAutoNotifyTrueScenario" and relaunch the crashed app
And I configure Bugsnag for "UnhandledJvmAutoNotifyTrueScenario"
Then I wait to receive an error
And the error is valid for the error reporting API version "4.0" for the "Android Bugsnag Notifier" notifier
@@ -34,8 +37,8 @@ Scenario: JVM exception captured with autoNotify reenabled
And the event "unhandled" is true
And the event "severity" equals "error"
-Scenario: NDK signal captured with autoNotify reenabled
- When I run "UnhandledNdkAutoNotifyTrueScenario" and relaunch the app
+ Scenario: NDK signal captured with autoNotify reenabled
+ When I run "UnhandledNdkAutoNotifyTrueScenario" and relaunch the crashed app
And I configure Bugsnag for "UnhandledNdkAutoNotifyTrueScenario"
Then I wait to receive an error
And the error is valid for the error reporting API version "4.0" for the "Android Bugsnag Notifier" notifier
@@ -48,8 +51,8 @@ Scenario: NDK signal captured with autoNotify reenabled
And the event "severityReason.unhandledOverridden" is false
# PLAT-6620
-@skip_android_8_1
-Scenario: ANR captured with autoDetectAnrs reenabled
+ @skip_android_8_1
+ Scenario: ANR captured with autoDetectAnrs reenabled
When I run "AutoDetectAnrsTrueScenario"
And I wait for 2 seconds
And I tap the screen 3 times
diff --git a/features/full_tests/breadcrumb.feature b/features/full_tests/breadcrumb.feature
index ba644e69d0..ffda4a7c30 100644
--- a/features/full_tests/breadcrumb.feature
+++ b/features/full_tests/breadcrumb.feature
@@ -1,6 +1,9 @@
Feature: Reporting Breadcrumbs
-Scenario: Manually added breadcrumbs are sent in report
+ Background:
+ Given I clear all persistent data
+
+ Scenario: Manually added breadcrumbs are sent in report
When I run "BreadcrumbScenario"
And I wait to receive an error
Then the error is valid for the error reporting API version "4.0" for the "Android Bugsnag Notifier" notifier
@@ -17,20 +20,20 @@ Scenario: Manually added breadcrumbs are sent in report
And the event "breadcrumbs.0.name" equals "Hello Breadcrumb!"
And the event "breadcrumbs.0.type" equals "manual"
-Scenario: Manually added breadcrumbs are sent in report when auto breadcrumbs are disabled
+ Scenario: Manually added breadcrumbs are sent in report when auto breadcrumbs are disabled
When I run "BreadcrumbDisabledScenario"
And I wait to receive an error
Then the error is valid for the error reporting API version "4.0" for the "Android Bugsnag Notifier" notifier
And the event has 1 breadcrumbs
-Scenario: An automatic breadcrumb is sent in report when the appropriate type is enabled
+ Scenario: An automatic breadcrumb is sent in report when the appropriate type is enabled
When I run "BreadcrumbAutoScenario"
And I wait to receive an error
Then the error is valid for the error reporting API version "4.0" for the "Android Bugsnag Notifier" notifier
And the event has a "state" breadcrumb with the message "Bugsnag loaded"
-Scenario: Error Breadcrumbs appear in subsequent events
- When I run "ErrorBreadcrumbsScenario" and relaunch the app
+ Scenario: Error Breadcrumbs appear in subsequent events
+ When I run "ErrorBreadcrumbsScenario" and relaunch the crashed app
And I configure Bugsnag for "ErrorBreadcrumbsScenario"
Then I wait to receive 2 errors
And the exception "errorClass" equals "java.lang.RuntimeException"
diff --git a/features/full_tests/bugsnag_init.feature b/features/full_tests/bugsnag_init.feature
index 22a6f9ca06..afad8b6d02 100644
--- a/features/full_tests/bugsnag_init.feature
+++ b/features/full_tests/bugsnag_init.feature
@@ -1,6 +1,9 @@
Feature: Verify the Bugsnag Initialization methods
-Scenario: Test Bugsnag initializes correctly
+ Background:
+ Given I clear all persistent data
+
+ Scenario: Test Bugsnag initializes correctly
When I run "BugsnagInitScenario"
And I wait to receive an error
Then the error is valid for the error reporting API version "4.0" for the "Android Bugsnag Notifier" notifier
diff --git a/features/full_tests/crash_handler.feature b/features/full_tests/crash_handler.feature
index 092bb59e14..a401483129 100644
--- a/features/full_tests/crash_handler.feature
+++ b/features/full_tests/crash_handler.feature
@@ -1,7 +1,10 @@
Feature: Reporting with other exception handlers installed
-Scenario: Other uncaught exception handler installed
- When I run "CrashHandlerScenario" and relaunch the app
+ Background:
+ Given I clear all persistent data
+
+ Scenario: Other uncaught exception handler installed
+ When I run "CrashHandlerScenario" and relaunch the crashed app
And I configure Bugsnag for "CrashHandlerScenario"
And I wait to receive an error
Then the error is valid for the error reporting API version "4.0" for the "Android Bugsnag Notifier" notifier
diff --git a/features/full_tests/custom_http_client.feature b/features/full_tests/custom_http_client.feature
index df8e60b627..b1d9c1889c 100644
--- a/features/full_tests/custom_http_client.feature
+++ b/features/full_tests/custom_http_client.feature
@@ -1,8 +1,11 @@
Feature: Using custom API clients for reporting errors
-Scenario: Set a custom HTTP client and flush a stored error + session
+ Background:
+ Given I clear all persistent data
+
+ Scenario: Set a custom HTTP client and flush a stored error + session
When I configure the app to run in the "offline" state
- And I run "CustomHttpClientFlushScenario" and relaunch the app
+ And I run "CustomHttpClientFlushScenario" and relaunch the crashed app
And I configure Bugsnag for "CustomHttpClientFlushScenario"
# error received
@@ -15,7 +18,7 @@ Scenario: Set a custom HTTP client and flush a stored error + session
And the session "Custom-Client" header equals "Hello World"
And the session is valid for the session reporting API version "1.0" for the "Android Bugsnag Notifier" notifier
-Scenario: Set a custom HTTP client and send an error + session
+ Scenario: Set a custom HTTP client and send an error + session
When I run "CustomHttpClientScenario"
# error received
diff --git a/features/full_tests/detect_anr_cxx.feature b/features/full_tests/detect_anr_cxx.feature
index 7bc26a1cb5..0e698e9b78 100644
--- a/features/full_tests/detect_anr_cxx.feature
+++ b/features/full_tests/detect_anr_cxx.feature
@@ -1,6 +1,9 @@
Feature: ANRs triggered in CXX code are captured
-Scenario: ANR triggered in CXX code is captured
+ Background:
+ Given I clear all persistent data
+
+ Scenario: ANR triggered in CXX code is captured
When I run "CXXAnrScenario"
And I wait for 2 seconds
And I tap the screen 3 times
@@ -14,7 +17,7 @@ Scenario: ANR triggered in CXX code is captured
And the error payload field "events.0.threads.0.type" equals "android"
And the error payload field "events.0.threads.0.stacktrace.0.type" is null
-Scenario: ANR triggered in CXX code is captured even when NDK detection is disabled
+ Scenario: ANR triggered in CXX code is captured even when NDK detection is disabled
When I run "CXXAnrNdkDisabledScenario"
And I wait for 2 seconds
And I tap the screen 3 times
diff --git a/features/full_tests/detect_anr_jvm.feature b/features/full_tests/detect_anr_jvm.feature
index a5ae1a286d..4d7c6a83b3 100644
--- a/features/full_tests/detect_anr_jvm.feature
+++ b/features/full_tests/detect_anr_jvm.feature
@@ -1,6 +1,9 @@
Feature: ANRs triggered in JVM code are captured
-Scenario: ANR triggered in JVM loop code is captured
+ Background:
+ Given I clear all persistent data
+
+ Scenario: ANR triggered in JVM loop code is captured
When I run "JvmAnrLoopScenario"
And I wait for 2 seconds
And I tap the screen 3 times
@@ -16,8 +19,8 @@ Scenario: ANR triggered in JVM loop code is captured
# Other scenarios use a deadlock to generate an ANR, which works on Samsung devices. This scenario remains skipped
# on Samsung as it is explicitly design to test ANRs caused by a sleeping thread.
-@skip_samsung
-Scenario: ANR triggered in JVM sleep code is captured
+ @skip_samsung
+ Scenario: ANR triggered in JVM sleep code is captured
When I run "JvmAnrSleepScenario"
And I wait for 2 seconds
And I tap the screen 3 times
@@ -30,7 +33,7 @@ Scenario: ANR triggered in JVM sleep code is captured
And the error "Bugsnag-Stacktrace-Types" header equals "android,c"
And the error payload field "events.0.exceptions.0.type" equals "android"
-Scenario: ANR triggered in JVM code is not captured when detectAnrs = false
+ Scenario: ANR triggered in JVM code is not captured when detectAnrs = false
When I run "JvmAnrDisabledScenario"
And I wait for 2 seconds
And I tap the screen 3 times
@@ -40,7 +43,7 @@ Scenario: ANR triggered in JVM code is not captured when detectAnrs = false
And the exception "errorClass" equals "java.lang.RuntimeException"
And the exception "message" equals "JvmAnrDisabledScenario"
-Scenario: ANR triggered in JVM code is not captured when outside of release stage
+ Scenario: ANR triggered in JVM code is not captured when outside of release stage
When I run "JvmAnrOutsideReleaseStagesScenario"
And I wait for 2 seconds
And I tap the screen 3 times
diff --git a/features/full_tests/detect_ndk_crashes.feature b/features/full_tests/detect_ndk_crashes.feature
index 80a89fbe15..9f9748e2f5 100644
--- a/features/full_tests/detect_ndk_crashes.feature
+++ b/features/full_tests/detect_ndk_crashes.feature
@@ -1,7 +1,10 @@
Feature: Verifies autoDetectNdkCrashes controls when NDK crashes are reported
-Scenario: No crash reported when autoDetectNdkCrashes disabled
- When I run "AutoDetectNdkDisabledScenario" and relaunch the app
+ Background:
+ Given I clear all persistent data
+
+ Scenario: No crash reported when autoDetectNdkCrashes disabled
+ When I run "AutoDetectNdkDisabledScenario" and relaunch the crashed app
And I configure Bugsnag for "AutoDetectNdkDisabledScenario"
And I wait for 2 seconds
Then I should receive no requests
diff --git a/features/full_tests/discarded_events.feature b/features/full_tests/discarded_events.feature
new file mode 100644
index 0000000000..8b461eb887
--- /dev/null
+++ b/features/full_tests/discarded_events.feature
@@ -0,0 +1,49 @@
+Feature: Discarding events
+
+ Scenario: Discard an on-disk error that failed to send and is too old
+ # Fail to send initial handled error. Client stores it to disk.
+ When I set the HTTP status code for the next request to 500
+ And I run "DiscardOldEventsScenario"
+ And I wait to receive an error
+ And the error is valid for the error reporting API version "4.0" for the "Android Bugsnag Notifier" notifier
+
+ # Fail to send event that was reloaded from disk. Event is too old, so the client discards it.
+ Then I discard the oldest error
+ # We send another error to keep maze-runner from shutting down the app prematurely.
+ And I wait to receive an error
+ And I discard the oldest error
+ And I set the HTTP status code for the next request to 500
+ And I close and relaunch the app
+ And I configure Bugsnag for "DiscardOldEventsScenario"
+ And I wait to receive an error
+ And the error is valid for the error reporting API version "4.0" for the "Android Bugsnag Notifier" notifier
+
+ # Now there is no event on disk, so there's nothing to send.
+ Then I discard the oldest error
+ And I close and relaunch the app
+ And I configure Bugsnag for "DiscardOldEventsScenario"
+ Then I should receive no requests
+
+ Scenario: Discard an on-disk error that failed to send and is too big
+ # Fail to send initial handled error. Client stores it to disk.
+ When I set the HTTP status code for the next request to 500
+ And I run "DiscardBigEventsScenario"
+ And I wait to receive an error
+ And the error is valid for the error reporting API version "4.0" for the "Android Bugsnag Notifier" notifier
+
+ # Fail to send event that was reloaded from disk. Event is too old, so the client discards it.
+ Then I discard the oldest error
+ # We send another error to keep maze-runner from shutting down the app prematurely.
+ And I wait to receive an error
+ And I discard the oldest error
+ And I set the HTTP status code for the next request to 500
+ And I close and relaunch the app
+ And I configure Bugsnag for "DiscardBigEventsScenario"
+ And I wait to receive an error
+ And the error is valid for the error reporting API version "4.0" for the "Android Bugsnag Notifier" notifier
+
+ # Now there is no event on disk, so there's nothing to send.
+ Then I discard the oldest error
+ And I close and relaunch the app
+ And I configure Bugsnag for "DiscardBigEventsScenario"
+ Then I should receive no requests
diff --git a/features/full_tests/discarded_session.feature b/features/full_tests/discarded_session.feature
new file mode 100644
index 0000000000..a2cef81777
--- /dev/null
+++ b/features/full_tests/discarded_session.feature
@@ -0,0 +1,33 @@
+Feature: Discarding sessions
+
+ Scenario: Discard an on-disk session that failed to send and is too old
+ # Part 1 sets a bogus session endpoint so that all attempts to send the session fail.
+ # Note: The app will make two attempts.
+ When I run "DiscardOldSessionScenario"
+
+ # Part 1 sleeps to let bg tasks run, then manually renames the session file to oldify it.
+
+ # Send an error to keep maze-runner from shutting down the app prematurely.
+ And I wait to receive an error
+ And the error is valid for the error reporting API version "4.0" for the "Android Bugsnag Notifier" notifier
+ And I discard the oldest error
+
+ # The important part is that no sessions are received at this point.
+ Then I should receive no sessions
+
+ # Part 2 loads the session, fails to send it, then discards it because it's too old.
+ Then I close and relaunch the app
+ And I run "DiscardOldSessionScenarioPart2"
+
+ # Send an error to keep maze-runner from shutting down the app prematurely.
+ And I wait to receive an error
+ And the error is valid for the error reporting API version "4.0" for the "Android Bugsnag Notifier" notifier
+ And I discard the oldest error
+
+ # Part 2 will always fail to send sessions.
+ Then I should receive no sessions
+
+ # Part 3 has no sending impediments, but there are no more session files so nothing to send.
+ Then I close and relaunch the app
+ And I configure Bugsnag for "DiscardOldSessionScenarioPart3"
+ Then I should receive no sessions
diff --git a/features/full_tests/empty_stacktrace.feature b/features/full_tests/empty_stacktrace.feature
index 9d50beee62..6cfd75fb96 100644
--- a/features/full_tests/empty_stacktrace.feature
+++ b/features/full_tests/empty_stacktrace.feature
@@ -1,6 +1,9 @@
Feature: Empty Stacktrace reported
-Scenario: Exceptions with empty stacktraces are sent
+ Background:
+ Given I clear all persistent data
+
+ Scenario: Exceptions with empty stacktraces are sent
When I run "EmptyStacktraceScenario"
Then I wait to receive an error
And the error is valid for the error reporting API version "4.0" for the "Android Bugsnag Notifier" notifier
diff --git a/features/full_tests/event_callback_alters_api_key.feature b/features/full_tests/event_callback_alters_api_key.feature
index 34e6b264f1..cc1217925e 100644
--- a/features/full_tests/event_callback_alters_api_key.feature
+++ b/features/full_tests/event_callback_alters_api_key.feature
@@ -1,6 +1,9 @@
Feature: When the api key is altered in an Event the JSON payload reflects this
-Scenario: Handled exception with altered API key
+ Background:
+ Given I clear all persistent data
+
+ Scenario: Handled exception with altered API key
When I run "HandledExceptionApiKeyChangeScenario"
Then I wait to receive an error
And the error payload field "events" is an array with 1 elements
@@ -8,8 +11,8 @@ Scenario: Handled exception with altered API key
And the error payload field "apiKey" equals "0000111122223333aaaabbbbcccc9999"
And the error "Bugsnag-Api-Key" header equals "0000111122223333aaaabbbbcccc9999"
-Scenario: Unhandled exception with altered API key
- When I run "UnhandledExceptionApiKeyChangeScenario" and relaunch the app
+ Scenario: Unhandled exception with altered API key
+ When I run "UnhandledExceptionApiKeyChangeScenario" and relaunch the crashed app
And I configure Bugsnag for "UnhandledExceptionApiKeyChangeScenario"
And I wait to receive an error
And the error payload field "events" is an array with 1 elements
diff --git a/features/full_tests/feature_flags.feature b/features/full_tests/feature_flags.feature
index bedd18bde3..1873d30109 100644
--- a/features/full_tests/feature_flags.feature
+++ b/features/full_tests/feature_flags.feature
@@ -1,57 +1,60 @@
Feature: Reporting with feature flags
-Scenario: Sends handled exception which includes feature flags
- When I run "FeatureFlagScenario"
- Then I wait to receive an error
- And the exception "errorClass" equals "java.lang.RuntimeException"
- And the event "unhandled" is false
- And event 0 contains the feature flag "demo_mode" with no variant
- And event 0 does not contain the feature flag "should_not_be_reported_1"
- And event 0 does not contain the feature flag "should_not_be_reported_2"
- And event 0 does not contain the feature flag "should_not_be_reported_3"
+ Background:
+ Given I clear all persistent data
-Scenario: Sends handled exception which includes feature flags added in the notify callback
- When I configure the app to run in the "callback" state
- And I run "FeatureFlagScenario"
- Then I wait to receive an error
- And the exception "errorClass" equals "java.lang.RuntimeException"
- And the event "unhandled" is false
- And event 0 contains the feature flag "demo_mode" with no variant
- And event 0 contains the feature flag "sample_group" with variant "a"
- And event 0 does not contain the feature flag "should_not_be_reported_1"
- And event 0 does not contain the feature flag "should_not_be_reported_2"
- And event 0 does not contain the feature flag "should_not_be_reported_3"
+ Scenario: Sends handled exception which includes feature flags
+ When I run "FeatureFlagScenario"
+ Then I wait to receive an error
+ And the exception "errorClass" equals "java.lang.RuntimeException"
+ And the event "unhandled" is false
+ And event 0 contains the feature flag "demo_mode" with no variant
+ And event 0 does not contain the feature flag "should_not_be_reported_1"
+ And event 0 does not contain the feature flag "should_not_be_reported_2"
+ And event 0 does not contain the feature flag "should_not_be_reported_3"
-Scenario: Sends unhandled exception which includes feature flags added in the notify callback
- When I configure the app to run in the "unhandled callback" state
- And I run "FeatureFlagScenario" and relaunch the app
- And I configure Bugsnag for "FeatureFlagScenario"
- Then I wait to receive an error
- And the exception "errorClass" equals "java.lang.RuntimeException"
- And the event "unhandled" is true
- And event 0 contains the feature flag "demo_mode" with no variant
- And event 0 contains the feature flag "sample_group" with variant "a"
- And event 0 does not contain the feature flag "should_not_be_reported_1"
- And event 0 does not contain the feature flag "should_not_be_reported_2"
- And event 0 does not contain the feature flag "should_not_be_reported_3"
+ Scenario: Sends handled exception which includes feature flags added in the notify callback
+ When I configure the app to run in the "callback" state
+ And I run "FeatureFlagScenario"
+ Then I wait to receive an error
+ And the exception "errorClass" equals "java.lang.RuntimeException"
+ And the event "unhandled" is false
+ And event 0 contains the feature flag "demo_mode" with no variant
+ And event 0 contains the feature flag "sample_group" with variant "a"
+ And event 0 does not contain the feature flag "should_not_be_reported_1"
+ And event 0 does not contain the feature flag "should_not_be_reported_2"
+ And event 0 does not contain the feature flag "should_not_be_reported_3"
-Scenario: Sends no feature flags after clearFeatureFlags()
- When I configure the app to run in the "cleared" state
- And I run "FeatureFlagScenario"
- Then I wait to receive an error
- And the exception "errorClass" equals "java.lang.RuntimeException"
- And the event "unhandled" is false
- And event 0 has no feature flags
+ Scenario: Sends unhandled exception which includes feature flags added in the notify callback
+ When I configure the app to run in the "unhandled callback" state
+ And I run "FeatureFlagScenario" and relaunch the crashed app
+ And I configure Bugsnag for "FeatureFlagScenario"
+ Then I wait to receive an error
+ And the exception "errorClass" equals "java.lang.RuntimeException"
+ And the event "unhandled" is true
+ And event 0 contains the feature flag "demo_mode" with no variant
+ And event 0 contains the feature flag "sample_group" with variant "a"
+ And event 0 does not contain the feature flag "should_not_be_reported_1"
+ And event 0 does not contain the feature flag "should_not_be_reported_2"
+ And event 0 does not contain the feature flag "should_not_be_reported_3"
-Scenario: Sends feature flags added in OnSend Callbacks
- When I configure the app to run in the "onsend" state
- And I run "FeatureFlagScenario" and relaunch the app
- And I configure Bugsnag for "FeatureFlagScenario"
- Then I wait to receive an error
- And the exception "errorClass" equals "java.lang.RuntimeException"
- And the event "unhandled" is false
- And event 0 contains the feature flag "demo_mode" with no variant
- And event 0 contains the feature flag "on_send_callback" with no variant
- And event 0 does not contain the feature flag "should_not_be_reported_1"
- And event 0 does not contain the feature flag "should_not_be_reported_2"
- And event 0 does not contain the feature flag "should_not_be_reported_3"
+ Scenario: Sends no feature flags after clearFeatureFlags()
+ When I configure the app to run in the "cleared" state
+ And I run "FeatureFlagScenario"
+ Then I wait to receive an error
+ And the exception "errorClass" equals "java.lang.RuntimeException"
+ And the event "unhandled" is false
+ And event 0 has no feature flags
+
+ Scenario: Sends feature flags added in OnSend Callbacks
+ When I configure the app to run in the "onsend" state
+ And I run "FeatureFlagScenario" and relaunch the crashed app
+ And I configure Bugsnag for "FeatureFlagScenario"
+ Then I wait to receive an error
+ And the exception "errorClass" equals "java.lang.RuntimeException"
+ And the event "unhandled" is false
+ And event 0 contains the feature flag "demo_mode" with no variant
+ And event 0 contains the feature flag "on_send_callback" with no variant
+ And event 0 does not contain the feature flag "should_not_be_reported_1"
+ And event 0 does not contain the feature flag "should_not_be_reported_2"
+ And event 0 does not contain the feature flag "should_not_be_reported_3"
diff --git a/features/full_tests/filtering_metadata.feature b/features/full_tests/filtering_metadata.feature
index 1e2784e1f4..d18d8057d4 100644
--- a/features/full_tests/filtering_metadata.feature
+++ b/features/full_tests/filtering_metadata.feature
@@ -1,6 +1,9 @@
Feature: Metadata is filtered
-Scenario: Using the default metadata filter
+ Background:
+ Given I clear all persistent data
+
+ Scenario: Using the default metadata filter
When I run "AutoRedactKeysScenario"
Then I wait to receive an error
And the error is valid for the error reporting API version "4.0" for the "Android Bugsnag Notifier" notifier
@@ -9,7 +12,7 @@ Scenario: Using the default metadata filter
And the event "metaData.custom.password" equals "[REDACTED]"
And the event "metaData.user.password" equals "[REDACTED]"
-Scenario: Adding a custom metadata filter
+ Scenario: Adding a custom metadata filter
When I run "ManualRedactKeysScenario"
Then I wait to receive an error
And the error is valid for the error reporting API version "4.0" for the "Android Bugsnag Notifier" notifier
diff --git a/features/full_tests/handled_exception.feature b/features/full_tests/handled_exception.feature
index 618d587cec..e7bf0d3f6f 100644
--- a/features/full_tests/handled_exception.feature
+++ b/features/full_tests/handled_exception.feature
@@ -1,10 +1,13 @@
Feature: Reporting handled Exceptions
- Scenario: Report a handled exception without a message
- When I run "HandledExceptionWithoutMessageScenario"
- Then I wait to receive an error
- And the error is valid for the error reporting API version "4.0" for the "Android Bugsnag Notifier" notifier
- And the error payload field "events" is an array with 1 elements
- And the exception "errorClass" equals "com.bugsnag.android.mazerunner.SomeException"
- And the event "exceptions.0.message" is null
- And the error payload field "events.0.device.cpuAbi" is a non-empty array
+ Background:
+ Given I clear all persistent data
+
+ Scenario: Report a handled exception without a message
+ When I run "HandledExceptionWithoutMessageScenario"
+ Then I wait to receive an error
+ And the error is valid for the error reporting API version "4.0" for the "Android Bugsnag Notifier" notifier
+ And the error payload field "events" is an array with 1 elements
+ And the exception "errorClass" equals "com.bugsnag.android.mazerunner.SomeException"
+ And the event "exceptions.0.message" is null
+ And the error payload field "events.0.device.cpuAbi" is a non-empty array
diff --git a/features/full_tests/identify_crashes_on_launch.feature b/features/full_tests/identify_crashes_on_launch.feature
index 70a115f0b5..d1404b2a9a 100644
--- a/features/full_tests/identify_crashes_on_launch.feature
+++ b/features/full_tests/identify_crashes_on_launch.feature
@@ -1,105 +1,108 @@
Feature: Identifying crashes on launch
- Scenario: A JVM error captured after the launch period is false for app.isLaunching
- When I run "JvmDelayedErrorScenario"
- Then I wait to receive 2 errors
- And the error is valid for the error reporting API version "4.0" for the "Android Bugsnag Notifier" notifier
- And the exception "message" equals "first error"
- And the event "app.isLaunching" is true
- Then I discard the oldest error
- And the error is valid for the error reporting API version "4.0" for the "Android Bugsnag Notifier" notifier
- And the exception "message" equals "JvmDelayedErrorScenario"
- And the event "app.isLaunching" is false
+ Background:
+ Given I clear all persistent data
- Scenario: An NDK error captured after the launch period is false for app.isLaunching
- When I run "CXXDelayedErrorScenario" and relaunch the app
- And I configure Bugsnag for "CXXDelayedErrorScenario"
- Then I wait to receive 2 errors
- And the error is valid for the error reporting API version "4.0" for the "Android Bugsnag Notifier" notifier
- And the exception "message" equals "first error"
- And the event "app.isLaunching" is true
- Then I discard the oldest error
- And the error is valid for the error reporting API version "4.0" for the "Android Bugsnag Notifier" notifier
- And the exception "message" equals "Abort program"
- And the event "app.isLaunching" is false
+ Scenario: A JVM error captured after the launch period is false for app.isLaunching
+ When I run "JvmDelayedErrorScenario"
+ Then I wait to receive 2 errors
+ And the error is valid for the error reporting API version "4.0" for the "Android Bugsnag Notifier" notifier
+ And the exception "message" equals "first error"
+ And the event "app.isLaunching" is true
+ Then I discard the oldest error
+ And the error is valid for the error reporting API version "4.0" for the "Android Bugsnag Notifier" notifier
+ And the exception "message" equals "JvmDelayedErrorScenario"
+ And the event "app.isLaunching" is false
- Scenario: A JVM error captured after markLaunchComplete() is false for app.isLaunching
- When I run "JvmMarkLaunchCompletedScenario"
- Then I wait to receive 2 errors
- And the error is valid for the error reporting API version "4.0" for the "Android Bugsnag Notifier" notifier
- And the exception "message" equals "first error"
- And the event "app.isLaunching" is true
- Then I discard the oldest error
- And the error is valid for the error reporting API version "4.0" for the "Android Bugsnag Notifier" notifier
- And the exception "message" equals "JvmMarkLaunchCompletedScenario"
- And the event "app.isLaunching" is false
+ Scenario: An NDK error captured after the launch period is false for app.isLaunching
+ When I run "CXXDelayedErrorScenario" and relaunch the crashed app
+ And I configure Bugsnag for "CXXDelayedErrorScenario"
+ Then I wait to receive 2 errors
+ And the error is valid for the error reporting API version "4.0" for the "Android Bugsnag Notifier" notifier
+ And the exception "message" equals "first error"
+ And the event "app.isLaunching" is true
+ Then I discard the oldest error
+ And the error is valid for the error reporting API version "4.0" for the "Android Bugsnag Notifier" notifier
+ And the exception "message" equals "Abort program"
+ And the event "app.isLaunching" is false
- Scenario: An NDK error captured after markLaunchComplete() is false for app.isLaunching
- When I run "CXXMarkLaunchCompletedScenario" and relaunch the app
- And I configure Bugsnag for "CXXMarkLaunchCompletedScenario"
- Then I wait to receive 2 errors
- And the error is valid for the error reporting API version "4.0" for the "Android Bugsnag Notifier" notifier
- And the exception "message" equals "first error"
- And the event "app.isLaunching" is true
- Then I discard the oldest error
- And the error is valid for the error reporting API version "4.0" for the "Android Bugsnag Notifier" notifier
- And the exception "message" equals "Abort program"
- And the event "app.isLaunching" is false
+ Scenario: A JVM error captured after markLaunchComplete() is false for app.isLaunching
+ When I run "JvmMarkLaunchCompletedScenario"
+ Then I wait to receive 2 errors
+ And the error is valid for the error reporting API version "4.0" for the "Android Bugsnag Notifier" notifier
+ And the exception "message" equals "first error"
+ And the event "app.isLaunching" is true
+ Then I discard the oldest error
+ And the error is valid for the error reporting API version "4.0" for the "Android Bugsnag Notifier" notifier
+ And the exception "message" equals "JvmMarkLaunchCompletedScenario"
+ And the event "app.isLaunching" is false
- Scenario: Escaping from a crash loop by reading LastRunInfo in a JVM error
- When I run "JvmCrashLoopScenario" and relaunch the app
- When I run "JvmCrashLoopScenario" and relaunch the app
- When I run "JvmCrashLoopScenario"
- And I wait to receive 3 errors
+ Scenario: An NDK error captured after markLaunchComplete() is false for app.isLaunching
+ When I run "CXXMarkLaunchCompletedScenario" and relaunch the crashed app
+ And I configure Bugsnag for "CXXMarkLaunchCompletedScenario"
+ Then I wait to receive 2 errors
+ And the error is valid for the error reporting API version "4.0" for the "Android Bugsnag Notifier" notifier
+ And the exception "message" equals "first error"
+ And the event "app.isLaunching" is true
+ Then I discard the oldest error
+ And the error is valid for the error reporting API version "4.0" for the "Android Bugsnag Notifier" notifier
+ And the exception "message" equals "Abort program"
+ And the event "app.isLaunching" is false
+
+ Scenario: Escaping from a crash loop by reading LastRunInfo in a JVM error
+ When I run "JvmCrashLoopScenario" and relaunch the crashed app
+ When I run "JvmCrashLoopScenario" and relaunch the crashed app
+ When I run "JvmCrashLoopScenario"
+ And I wait to receive 3 errors
# JVM crash
- Then the error is valid for the error reporting API version "4.0" for the "Android Bugsnag Notifier" notifier
- And the exception "message" equals "First JVM crash"
- And the event "metaData.LastRunInfo.crashed" is null
- And the event "metaData.LastRunInfo.crashedDuringLaunch" is null
- And the event "metaData.LastRunInfo.consecutiveLaunchCrashes" is null
- And I discard the oldest error
+ Then the error is valid for the error reporting API version "4.0" for the "Android Bugsnag Notifier" notifier
+ And the exception "message" equals "First JVM crash"
+ And the event "metaData.LastRunInfo.crashed" is null
+ And the event "metaData.LastRunInfo.crashedDuringLaunch" is null
+ And the event "metaData.LastRunInfo.consecutiveLaunchCrashes" is null
+ And I discard the oldest error
# JVM crash
- Then the error is valid for the error reporting API version "4.0" for the "Android Bugsnag Notifier" notifier
- And the exception "message" equals "Second JVM crash"
- And the event "metaData.LastRunInfo.crashed" is true
- And the event "metaData.LastRunInfo.crashedDuringLaunch" is true
- And the event "metaData.LastRunInfo.consecutiveLaunchCrashes" equals 1
- And I discard the oldest error
+ Then the error is valid for the error reporting API version "4.0" for the "Android Bugsnag Notifier" notifier
+ And the exception "message" equals "Second JVM crash"
+ And the event "metaData.LastRunInfo.crashed" is true
+ And the event "metaData.LastRunInfo.crashedDuringLaunch" is true
+ And the event "metaData.LastRunInfo.consecutiveLaunchCrashes" equals 1
+ And I discard the oldest error
# Safe mode
- Then the error is valid for the error reporting API version "4.0" for the "Android Bugsnag Notifier" notifier
- And the exception "message" equals "Safe mode enabled"
- And the event "metaData.LastRunInfo.crashed" is true
- And the event "metaData.LastRunInfo.crashedDuringLaunch" is true
- And the event "metaData.LastRunInfo.consecutiveLaunchCrashes" equals 2
+ Then the error is valid for the error reporting API version "4.0" for the "Android Bugsnag Notifier" notifier
+ And the exception "message" equals "Safe mode enabled"
+ And the event "metaData.LastRunInfo.crashed" is true
+ And the event "metaData.LastRunInfo.crashedDuringLaunch" is true
+ And the event "metaData.LastRunInfo.consecutiveLaunchCrashes" equals 2
- Scenario: Escaping from a crash loop by reading LastRunInfo in an NDK error
- When I run "CXXCrashLoopScenario" and relaunch the app
- When I run "CXXCrashLoopScenario" and relaunch the app
- When I run "CXXCrashLoopScenario"
- And I wait to receive 3 errors
+ Scenario: Escaping from a crash loop by reading LastRunInfo in an NDK error
+ When I run "CXXCrashLoopScenario" and relaunch the crashed app
+ When I run "CXXCrashLoopScenario" and relaunch the crashed app
+ When I run "CXXCrashLoopScenario"
+ And I wait to receive 3 errors
# NDK crash
- Then the error is valid for the error reporting API version "4.0" for the "Android Bugsnag Notifier" notifier
- And the exception "message" equals "Abort program"
- And the event "metaData.LastRunInfo.crashed" is null
- And the event "metaData.LastRunInfo.crashedDuringLaunch" is null
- And the event "metaData.LastRunInfo.consecutiveLaunchCrashes" is null
- And I discard the oldest error
+ Then the error is valid for the error reporting API version "4.0" for the "Android Bugsnag Notifier" notifier
+ And the exception "message" equals "Abort program"
+ And the event "metaData.LastRunInfo.crashed" is null
+ And the event "metaData.LastRunInfo.crashedDuringLaunch" is null
+ And the event "metaData.LastRunInfo.consecutiveLaunchCrashes" is null
+ And I discard the oldest error
# NDK crash
- Then the error is valid for the error reporting API version "4.0" for the "Android Bugsnag Notifier" notifier
- And the exception "message" equals "Abort program"
- And the event "metaData.LastRunInfo.crashed" is true
- And the event "metaData.LastRunInfo.crashedDuringLaunch" is true
- And the event "metaData.LastRunInfo.consecutiveLaunchCrashes" equals 1
- And I discard the oldest error
+ Then the error is valid for the error reporting API version "4.0" for the "Android Bugsnag Notifier" notifier
+ And the exception "message" equals "Abort program"
+ And the event "metaData.LastRunInfo.crashed" is true
+ And the event "metaData.LastRunInfo.crashedDuringLaunch" is true
+ And the event "metaData.LastRunInfo.consecutiveLaunchCrashes" equals 1
+ And I discard the oldest error
# Safe mode
- Then the error is valid for the error reporting API version "4.0" for the "Android Bugsnag Notifier" notifier
- And the exception "message" equals "Safe mode enabled"
- And the event "metaData.LastRunInfo.crashed" is true
- And the event "metaData.LastRunInfo.crashedDuringLaunch" is true
- And the event "metaData.LastRunInfo.consecutiveLaunchCrashes" equals 2
+ Then the error is valid for the error reporting API version "4.0" for the "Android Bugsnag Notifier" notifier
+ And the exception "message" equals "Safe mode enabled"
+ And the event "metaData.LastRunInfo.crashed" is true
+ And the event "metaData.LastRunInfo.crashedDuringLaunch" is true
+ And the event "metaData.LastRunInfo.consecutiveLaunchCrashes" equals 2
diff --git a/features/full_tests/ignored_reports.feature b/features/full_tests/ignored_reports.feature
index 4993463d28..1baef7560d 100644
--- a/features/full_tests/ignored_reports.feature
+++ b/features/full_tests/ignored_reports.feature
@@ -1,6 +1,9 @@
Feature: Reports are ignored
-Scenario: Exception classname ignored
+ Background:
+ Given I clear all persistent data
+
+ Scenario: Exception classname ignored
When I run "IgnoredExceptionScenario"
And I wait to receive an error
Then the error is valid for the error reporting API version "4.0" for the "Android Bugsnag Notifier" notifier
@@ -8,17 +11,17 @@ Scenario: Exception classname ignored
And the exception "message" equals "Is it me you're looking for?"
And the event "unhandled" is false
-Scenario: Disabled Exception Handler
- When I run "DisableAutoDetectErrorsScenario" and relaunch the app
+ Scenario: Disabled Exception Handler
+ When I run "DisableAutoDetectErrorsScenario" and relaunch the crashed app
And I configure Bugsnag for "DisableAutoDetectErrorsScenario"
Then Bugsnag confirms it has no errors to send
-Scenario: Changing release stage to exclude the current stage settings before a POSIX signal
- When I run "CXXTrapOutsideReleaseStagesScenario" and relaunch the app
+ Scenario: Changing release stage to exclude the current stage settings before a POSIX signal
+ When I run "CXXTrapOutsideReleaseStagesScenario" and relaunch the crashed app
And I configure Bugsnag for "CXXTrapOutsideReleaseStagesScenario"
Then Bugsnag confirms it has no errors to send
-Scenario: Changing release stage settings to exclude the current stage before a native C++ crash
- When I run "CXXThrowSomethingOutsideReleaseStagesScenario" and relaunch the app
+ Scenario: Changing release stage settings to exclude the current stage before a native C++ crash
+ When I run "CXXThrowSomethingOutsideReleaseStagesScenario" and relaunch the crashed app
And I configure Bugsnag for "CXXThrowSomethingOutsideReleaseStagesScenario"
Then Bugsnag confirms it has no errors to send
diff --git a/features/full_tests/in_foreground.feature b/features/full_tests/in_foreground.feature
index e12a5f504c..d9ac307801 100644
--- a/features/full_tests/in_foreground.feature
+++ b/features/full_tests/in_foreground.feature
@@ -1,6 +1,9 @@
Feature: In foreground field populates correctly
-Scenario: Test handled exception in background
+ Background:
+ Given I clear all persistent data
+
+ Scenario: Test handled exception in background
When I run "InForegroundScenario"
And I send the app to the background for 1 seconds
Then I wait to receive an error
diff --git a/features/full_tests/internal_error_reports.feature b/features/full_tests/internal_error_reports.feature
index 6e10a69962..81788c7d0f 100644
--- a/features/full_tests/internal_error_reports.feature
+++ b/features/full_tests/internal_error_reports.feature
@@ -1,6 +1,9 @@
Feature: Cached Error Reports
-Scenario: If an exception is thrown when sending errors/sessions then internal error reports should be sent
+ Background:
+ Given I clear all persistent data
+
+ Scenario: If an exception is thrown when sending errors/sessions then internal error reports should be sent
When I run "InternalErrorScenario"
And I wait to receive 1 errors
diff --git a/features/full_tests/load_configuration.feature b/features/full_tests/load_configuration.feature
index 6d0df2383d..9730eda3f8 100644
--- a/features/full_tests/load_configuration.feature
+++ b/features/full_tests/load_configuration.feature
@@ -1,6 +1,9 @@
Feature: Loading values into the configuration
-Scenario: Load configuration initialised from the Manifest
+ Background:
+ Given I clear all persistent data
+
+ Scenario: Load configuration initialised from the Manifest
When I run "LoadConfigurationFromManifestScenario"
Then I wait to receive an error
And the error is valid for the error reporting API version "4.0" for the "Android Bugsnag Notifier" notifier with the apiKey "abc12312312312312312312312312312"
@@ -15,7 +18,7 @@ Scenario: Load configuration initialised from the Manifest
And the event "app.type" equals "test"
And the error payload field "events.0.threads" is a non-empty array
-Scenario: Load configuration initialised with Kotlin
+ Scenario: Load configuration initialised with Kotlin
When I run "LoadConfigurationKotlinScenario"
Then I wait to receive an error
And the error is valid for the error reporting API version "4.0" for the "Android Bugsnag Notifier" notifier with the apiKey "45645645645645645645645645645645"
@@ -30,7 +33,7 @@ Scenario: Load configuration initialised with Kotlin
And the event "app.type" equals "kotlin"
And the error payload field "events.0.threads" is an array with 0 elements
-Scenario: Load configuration initialised with nulls
+ Scenario: Load configuration initialised with nulls
When I run "LoadConfigurationNullsScenario"
Then I wait to receive an error
And the error is valid for the error reporting API version "4.0" for the "Android Bugsnag Notifier" notifier with the apiKey "12312312312312312312312312312312"
diff --git a/features/full_tests/max_reported_threads.feature b/features/full_tests/max_reported_threads.feature
index d42e942d82..be7ca18bab 100644
--- a/features/full_tests/max_reported_threads.feature
+++ b/features/full_tests/max_reported_threads.feature
@@ -1,7 +1,10 @@
Feature: Reporting with other exception handlers installed
-Scenario: Unhandled exception with max threads set
- When I run "UnhandledExceptionMaxThreadsScenario" and relaunch the app
+ Background:
+ Given I clear all persistent data
+
+ Scenario: Unhandled exception with max threads set
+ When I run "UnhandledExceptionMaxThreadsScenario" and relaunch the crashed app
And I configure Bugsnag for "UnhandledExceptionMaxThreadsScenario"
And I wait to receive an error
Then the error is valid for the error reporting API version "4.0" for the "Android Bugsnag Notifier" notifier
@@ -12,8 +15,8 @@ Scenario: Unhandled exception with max threads set
And the error payload field "events.0.threads.2.id" equals -1
And the error payload field "events.0.threads.2.name" ends with "threads omitted as the maxReportedThreads limit (2) was exceeded]"
-Scenario: Handled exception with max threads set
- When I run "HandledExceptionMaxThreadsScenario" and relaunch the app
+ Scenario: Handled exception with max threads set
+ When I run "HandledExceptionMaxThreadsScenario" and relaunch the crashed app
And I configure Bugsnag for "HandledExceptionMaxThreadsScenario"
And I wait to receive an error
Then the error is valid for the error reporting API version "4.0" for the "Android Bugsnag Notifier" notifier
diff --git a/features/full_tests/meta_data_scenario.feature b/features/full_tests/meta_data_scenario.feature
index da8abf59a1..41abd4728f 100644
--- a/features/full_tests/meta_data_scenario.feature
+++ b/features/full_tests/meta_data_scenario.feature
@@ -1,12 +1,15 @@
Feature: Reporting metadata
-Scenario: Sends a handled exception which includes custom metadata added in a notify callback
+ Background:
+ Given I clear all persistent data
+
+ Scenario: Sends a handled exception which includes custom metadata added in a notify callback
When I run "MetadataScenario"
Then I wait to receive an error
And the error is valid for the error reporting API version "4.0" for the "Android Bugsnag Notifier" notifier
And the event "metaData.Custom.foo" equals "Hello World!"
-Scenario: Add nested null value to metadata tab
+ Scenario: Add nested null value to metadata tab
When I run "MetadataNestedNullScenario"
Then I wait to receive an error
And the error is valid for the error reporting API version "4.0" for the "Android Bugsnag Notifier" notifier
diff --git a/features/full_tests/multi_process.feature b/features/full_tests/multi_process.feature
index dff55720b4..b8baa1ece7 100644
--- a/features/full_tests/multi_process.feature
+++ b/features/full_tests/multi_process.feature
@@ -1,6 +1,9 @@
Feature: Reporting errors in multi process apps
-Scenario: Handled JVM error
+ Background:
+ Given I clear all persistent data
+
+ Scenario: Handled JVM error
When I run "MultiProcessHandledExceptionScenario"
Then I wait to receive 2 errors
And I sort the errors by "events.0.metaData.app.processName"
@@ -29,11 +32,11 @@ Scenario: Handled JVM error
And the error payload field "events.0.user.name" equals "MultiProcessHandledExceptionScenario"
And the error payload field "events.0.user.email" equals "foreground@example.com"
-Scenario: Unhandled JVM error
+ Scenario: Unhandled JVM error
And I configure the app to run in the "main-activity" state
- When I run "MultiProcessUnhandledExceptionScenario" and relaunch the app
+ When I run "MultiProcessUnhandledExceptionScenario" and relaunch the crashed app
And I configure the app to run in the "multi-process-service" state
- And I run "MultiProcessUnhandledExceptionScenario" and relaunch the app
+ And I run "MultiProcessUnhandledExceptionScenario" and relaunch the crashed app
And I run "MultiProcessUnhandledExceptionScenario"
Then I wait to receive 2 errors
And I sort the errors by "events.0.metaData.app.processName"
@@ -52,7 +55,7 @@ Scenario: Unhandled JVM error
And the event "unhandled" is true
And the error payload field "events.0.metaData.app.processName" equals "com.example.bugsnag.android.mazerunner.multiprocess"
-Scenario: Handled NDK error
+ Scenario: Handled NDK error
When I run "MultiProcessHandledCXXErrorScenario"
Then I wait to receive 2 errors
And I sort the errors by "events.0.metaData.app.processName"
@@ -79,11 +82,11 @@ Scenario: Handled NDK error
And the error payload field "events.0.device.id" equals the stored value "first_device_id"
And the error payload field "events.0.user.id" equals the stored value "first_device_id"
-Scenario: Unhandled NDK error
+ Scenario: Unhandled NDK error
And I configure the app to run in the "main-activity" state
- When I run "MultiProcessUnhandledCXXErrorScenario" and relaunch the app
+ When I run "MultiProcessUnhandledCXXErrorScenario" and relaunch the crashed app
And I configure the app to run in the "multi-process-service" state
- And I run "MultiProcessUnhandledCXXErrorScenario" and relaunch the app
+ And I run "MultiProcessUnhandledCXXErrorScenario" and relaunch the crashed app
And I run "MultiProcessUnhandledCXXErrorScenario"
Then I wait to receive 2 errors
And I sort the errors by "events.0.metaData.app.processName"
@@ -112,7 +115,7 @@ Scenario: Unhandled NDK error
And the error payload field "events.0.user.name" equals "MultiProcessUnhandledCXXErrorScenario"
And the error payload field "events.0.user.email" equals "1@test.com"
-Scenario: User/device information is migrated from SharedPreferences
+ Scenario: User/device information is migrated from SharedPreferences
When I run "SharedPrefMigrationScenario"
Then I wait to receive 2 errors
And I sort the errors by "events.0.metaData.app.processName"
diff --git a/features/full_tests/native_api.feature b/features/full_tests/native_api.feature
index 50253940ea..ffbb704173 100644
--- a/features/full_tests/native_api.feature
+++ b/features/full_tests/native_api.feature
@@ -1,23 +1,26 @@
Feature: Native API
- Scenario: Set extraordinarily long app information
- When I run "CXXExtraordinaryLongStringScenario" and relaunch the app
- And I configure Bugsnag for "CXXExtraordinaryLongStringScenario"
- And I wait to receive an error
- And the error payload contains a completed handled native report
- And the exception "errorClass" equals one of:
- | SIGILL |
- | SIGTRAP |
- And the event "app.version" equals "22.312.749.78.300.810.24.167.32"
- And the event "context" equals "ObservableSessionInitializerStringParserStringSessionProxyGloba"
- And the event "unhandled" is true
+ Background:
+ Given I clear all persistent data
- Scenario: Use the NDK methods without "env" after calling "bugsnag_start"
- When I run "CXXStartScenario"
- And I wait to receive an error
- Then the error payload contains a completed handled native report
- And the event "unhandled" is false
- And the exception "errorClass" equals "Start scenario"
- And the exception "message" equals "Testing env"
- And the event "severity" equals "info"
- And the event has a "log" breadcrumb named "Start scenario crumb"
+ Scenario: Set extraordinarily long app information
+ When I run "CXXExtraordinaryLongStringScenario" and relaunch the crashed app
+ And I configure Bugsnag for "CXXExtraordinaryLongStringScenario"
+ And I wait to receive an error
+ And the error payload contains a completed handled native report
+ And the exception "errorClass" equals one of:
+ | SIGILL |
+ | SIGTRAP |
+ And the event "app.version" equals "22.312.749.78.300.810.24.167.32"
+ And the event "context" equals "ObservableSessionInitializerStringParserStringSessionProxyGloba"
+ And the event "unhandled" is true
+
+ Scenario: Use the NDK methods without "env" after calling "bugsnag_start"
+ When I run "CXXStartScenario"
+ And I wait to receive an error
+ Then the error payload contains a completed handled native report
+ And the event "unhandled" is false
+ And the exception "errorClass" equals "Start scenario"
+ And the exception "message" equals "Testing env"
+ And the event "severity" equals "info"
+ And the event has a "log" breadcrumb named "Start scenario crumb"
diff --git a/features/full_tests/native_breadcrumbs.feature b/features/full_tests/native_breadcrumbs.feature
index aa54ea6b88..14388f3a3d 100644
--- a/features/full_tests/native_breadcrumbs.feature
+++ b/features/full_tests/native_breadcrumbs.feature
@@ -1,22 +1,25 @@
Feature: Native Breadcrumbs API
- Scenario: Leaving breadcrumbs in C followed by a Java crash
- When I run "CXXNativeBreadcrumbJavaCrashScenario" and relaunch the app
- And I configure Bugsnag for "CXXNativeBreadcrumbJavaCrashScenario"
- And I wait to receive an error
- And the error payload contains a completed handled native report
- And the exception "errorClass" equals "java.lang.ArrayIndexOutOfBoundsException"
- And the exception "message" equals "length=2; index=2"
- And the event has a "log" breadcrumb named "Lack of cheese detected"
- And the event "severity" equals "error"
- And the event "unhandled" is true
+ Background:
+ Given I clear all persistent data
- Scenario: Leaving breadcrumbs in C followed by notifying in Java
- When I run "CXXNativeBreadcrumbJavaNotifyScenario"
- And I wait to receive an error
- And the error payload contains a completed handled native report
- And the exception "errorClass" equals "java.lang.Exception"
- And the exception "message" equals "Did not like"
- And the event "severity" equals "warning"
- And the event has a "process" breadcrumb named "Rerun field analysis"
- And the event "unhandled" is false
+ Scenario: Leaving breadcrumbs in C followed by a Java crash
+ When I run "CXXNativeBreadcrumbJavaCrashScenario" and relaunch the crashed app
+ And I configure Bugsnag for "CXXNativeBreadcrumbJavaCrashScenario"
+ And I wait to receive an error
+ And the error payload contains a completed handled native report
+ And the exception "errorClass" equals "java.lang.ArrayIndexOutOfBoundsException"
+ And the exception "message" equals "length=2; index=2"
+ And the event has a "log" breadcrumb named "Lack of cheese detected"
+ And the event "severity" equals "error"
+ And the event "unhandled" is true
+
+ Scenario: Leaving breadcrumbs in C followed by notifying in Java
+ When I run "CXXNativeBreadcrumbJavaNotifyScenario"
+ And I wait to receive an error
+ And the error payload contains a completed handled native report
+ And the exception "errorClass" equals "java.lang.Exception"
+ And the exception "message" equals "Did not like"
+ And the event "severity" equals "warning"
+ And the event has a "process" breadcrumb named "Rerun field analysis"
+ And the event "unhandled" is false
diff --git a/features/full_tests/native_context.feature b/features/full_tests/native_context.feature
index f01106acd1..516f3f5f6b 100644
--- a/features/full_tests/native_context.feature
+++ b/features/full_tests/native_context.feature
@@ -1,17 +1,20 @@
Feature: Native Context API
- Scenario: Changing intents followed by notifying in C
- When I run "CXXAutoContextScenario"
- Then I wait to receive 2 errors
+ Background:
+ Given I clear all persistent data
- Then the error payload contains a completed handled native report
- And the event "severity" equals "info"
- And the event "context" equals "SecondActivity"
- And the exception "errorClass" equals "Hello hello"
- And the exception "message" equals "This is a new world"
- And the event "unhandled" is false
- And I discard the oldest error
+ Scenario: Changing intents followed by notifying in C
+ When I run "CXXAutoContextScenario"
+ Then I wait to receive 2 errors
- And the error is valid for the error reporting API version "4.0" for the "Android Bugsnag Notifier" notifier
- And the exception "message" equals "CXXAutoContextScenario"
- And the event "context" equals "SecondActivity"
+ Then the error payload contains a completed handled native report
+ And the event "severity" equals "info"
+ And the event "context" equals "SecondActivity"
+ And the exception "errorClass" equals "Hello hello"
+ And the exception "message" equals "This is a new world"
+ And the event "unhandled" is false
+ And I discard the oldest error
+
+ And the error is valid for the error reporting API version "4.0" for the "Android Bugsnag Notifier" notifier
+ And the exception "message" equals "CXXAutoContextScenario"
+ And the event "context" equals "SecondActivity"
diff --git a/features/full_tests/native_crash_handling.feature b/features/full_tests/native_crash_handling.feature
index a591be2f3c..4ea365067e 100644
--- a/features/full_tests/native_crash_handling.feature
+++ b/features/full_tests/native_crash_handling.feature
@@ -1,7 +1,10 @@
Feature: Native crash reporting
+ Background:
+ Given I clear all persistent data
+
Scenario: Dereference a null pointer
- When I run "CXXDereferenceNullScenario" and relaunch the app
+ When I run "CXXDereferenceNullScenario" and relaunch the crashed app
And I configure Bugsnag for "CXXDereferenceNullScenario"
And I wait to receive an error
And the error payload contains a completed unhandled native report
@@ -27,7 +30,7 @@ Feature: Native crash reporting
# https://android.googlesource.com/platform/bionic/+/fb7eb5e07f43587c2bedf2aaa53b21fa002417bb
@skip_below_api18
Scenario: Stack buffer overflow
- When I run "CXXStackoverflowScenario" and relaunch the app
+ When I run "CXXStackoverflowScenario" and relaunch the crashed app
And I configure Bugsnag for "CXXStackoverflowScenario"
And I wait to receive an error
And the error payload contains a completed unhandled native report
@@ -39,12 +42,12 @@ Feature: Native crash reporting
| crash_stack_overflow | CXXStackoverflowScenario.cpp |
Scenario: Program trap()
- When I run "CXXTrapScenario" and relaunch the app
+ When I run "CXXTrapScenario" and relaunch the crashed app
And I configure Bugsnag for "CXXTrapScenario"
And I wait to receive an error
And the error payload contains a completed unhandled native report
And the exception "errorClass" equals one of:
- | SIGILL |
+ | SIGILL |
| SIGTRAP |
And the exception "message" equals one of:
| Illegal instruction |
@@ -56,7 +59,7 @@ Feature: Native crash reporting
| trap_it() | CXXTrapScenario.cpp | 10 |
Scenario: Write to read-only memory
- When I run "CXXWriteReadOnlyMemoryScenario" and relaunch the app
+ When I run "CXXWriteReadOnlyMemoryScenario" and relaunch the crashed app
And I configure Bugsnag for "CXXWriteReadOnlyMemoryScenario"
And I wait to receive an error
And the exception "errorClass" equals "SIGSEGV"
@@ -65,11 +68,11 @@ Feature: Native crash reporting
And the event "severity" equals "error"
And the event "unhandled" is true
And the first significant stack frames match:
- | crash_write_read_only_mem(int) | CXXWriteReadOnlyMemoryScenario.cpp | 12 |
+ | crash_write_read_only_mem(int) | CXXWriteReadOnlyMemoryScenario.cpp | 12 |
| Java_com_bugsnag_android_mazerunner_scenarios_CXXWriteReadOnlyMemoryScenario_crash | CXXWriteReadOnlyMemoryScenario.cpp | 22 |
Scenario: Improper object type cast
- When I run "CXXImproperTypecastScenario" and relaunch the app
+ When I run "CXXImproperTypecastScenario" and relaunch the crashed app
And I configure Bugsnag for "CXXImproperTypecastScenario"
And I wait to receive an error
And the exception "errorClass" equals "SIGSEGV"
@@ -78,11 +81,11 @@ Feature: Native crash reporting
And the event "severity" equals "error"
And the event "unhandled" is true
And the first significant stack frames match:
- | crash_improper_cast(void*) | CXXImproperTypecastScenario.cpp | 12 |
+ | crash_improper_cast(void*) | CXXImproperTypecastScenario.cpp | 12 |
| Java_com_bugsnag_android_mazerunner_scenarios_CXXImproperTypecastScenario_crash | CXXImproperTypecastScenario.cpp | 20 |
Scenario: Program abort()
- When I run "CXXAbortScenario" and relaunch the app
+ When I run "CXXAbortScenario" and relaunch the crashed app
And I configure Bugsnag for "CXXAbortScenario"
And I wait to receive an error
And the error payload contains a completed unhandled native report
@@ -90,17 +93,17 @@ Feature: Native crash reporting
| SIGABRT |
| SIGSEGV |
And the exception "message" equals one of:
- | Abort program |
+ | Abort program |
| Segmentation violation (invalid memory reference) |
And the exception "type" equals "c"
And the event "severity" equals "error"
And the event "unhandled" is true
And the first significant stack frames match:
- | evictor::exit_with_style() | CXXAbortScenario.cpp | 5 |
+ | evictor::exit_with_style() | CXXAbortScenario.cpp | 5 |
| Java_com_bugsnag_android_mazerunner_scenarios_CXXAbortScenario_crash | CXXAbortScenario.cpp | 13 |
Scenario: Undefined JNI method
- When I run "UnsatisfiedLinkErrorScenario" and relaunch the app
+ When I run "UnsatisfiedLinkErrorScenario" and relaunch the crashed app
And I configure Bugsnag for "UnsatisfiedLinkErrorScenario"
And I wait to receive an error
And the report contains the required fields
@@ -110,7 +113,7 @@ Feature: Native crash reporting
And the event "unhandled" is true
Scenario: Causing a crash in a separate library
- When I run "CXXExternalStackElementScenario" and relaunch the app
+ When I run "CXXExternalStackElementScenario" and relaunch the crashed app
And I configure Bugsnag for "CXXExternalStackElementScenario"
And I wait to receive an error
And the error payload contains a completed unhandled native report
@@ -124,14 +127,14 @@ Feature: Native crash reporting
| Trace/breakpoint trap |
And the exception "type" equals "c"
And the first significant stack frames match:
- | something_innocuous | libmonochrome.so | (ignore) |
- | Java_com_bugsnag_android_mazerunner_scenarios_CXXExternalStackElementScenario_crash | CXXExternalStackElementScenario.cpp | 14 |
+ | something_innocuous | libmonochrome.so | (ignore) |
+ | Java_com_bugsnag_android_mazerunner_scenarios_CXXExternalStackElementScenario_crash | CXXExternalStackElementScenario.cpp | 14 |
Scenario: Call null function pointer
- A null pointer should be the first element of a stack trace,
- followed by the calling function
+ A null pointer should be the first element of a stack trace,
+ followed by the calling function
- When I run "CXXCallNullFunctionPointerScenario" and relaunch the app
+ When I run "CXXCallNullFunctionPointerScenario" and relaunch the crashed app
And I configure Bugsnag for "CXXCallNullFunctionPointerScenario"
And I wait to receive an error
And the error payload contains a completed unhandled native report
diff --git a/features/full_tests/native_event_tracking.feature b/features/full_tests/native_event_tracking.feature
index 47a20c357d..7bb8e78859 100644
--- a/features/full_tests/native_event_tracking.feature
+++ b/features/full_tests/native_event_tracking.feature
@@ -1,43 +1,46 @@
Feature: Synchronizing app/device metadata in the native layer
- Scenario: Capture foreground state while in the foreground
- When I run "CXXDelayedNotifyScenario"
- And I wait to receive an error
- Then the error payload contains a completed handled native report
- And the event "app.inForeground" is true
- And the event "app.duration" is greater than 0
- And the event "unhandled" is false
+ Background:
+ Given I clear all persistent data
- Scenario: Capture foreground state while in the background
- When I run "CXXBackgroundNotifyScenario"
- And I send the app to the background for 5 seconds
- And I wait to receive an error
- Then the error payload contains a completed handled native report
- And the event "app.inForeground" is false
- And the event "app.durationInForeground" equals 0
- And the event "app.duration" is greater than 0
- And the event "context" string is empty
- And the event "unhandled" is false
+ Scenario: Capture foreground state while in the foreground
+ When I run "CXXDelayedNotifyScenario"
+ And I wait to receive an error
+ Then the error payload contains a completed handled native report
+ And the event "app.inForeground" is true
+ And the event "app.duration" is greater than 0
+ And the event "unhandled" is false
- Scenario: Capture foreground state while in a foreground crash
- When I run "CXXTrapScenario" and relaunch the app
- And I configure Bugsnag for "CXXStartSessionScenario"
- And I wait to receive an error
- Then the error payload contains a completed handled native report
- And the event "app.inForeground" is true
- And the event "app.durationInForeground" is not null
- And the event "app.duration" is not null
- And the event "unhandled" is true
+ Scenario: Capture foreground state while in the background
+ When I run "CXXBackgroundNotifyScenario"
+ And I send the app to the background for 5 seconds
+ And I wait to receive an error
+ Then the error payload contains a completed handled native report
+ And the event "app.inForeground" is false
+ And the event "app.durationInForeground" equals 0
+ And the event "app.duration" is greater than 0
+ And the event "context" string is empty
+ And the event "unhandled" is false
- Scenario: Capture foreground state while in a background crash
- When I run "CXXDelayedCrashScenario"
- And I send the app to the background for 10 seconds
- And I clear any error dialogue
- And I relaunch the app after a crash
- And I configure Bugsnag for "CXXDelayedCrashScenario"
- And I wait to receive an error
- Then the error payload contains a completed handled native report
- And the event "app.inForeground" is false
- And the event "app.duration" is greater than 0
- And the event "context" string is empty
- And the event "unhandled" is true
+ Scenario: Capture foreground state while in a foreground crash
+ When I run "CXXTrapScenario" and relaunch the crashed app
+ And I configure Bugsnag for "CXXStartSessionScenario"
+ And I wait to receive an error
+ Then the error payload contains a completed handled native report
+ And the event "app.inForeground" is true
+ And the event "app.durationInForeground" is not null
+ And the event "app.duration" is not null
+ And the event "unhandled" is true
+
+ Scenario: Capture foreground state while in a background crash
+ When I run "CXXDelayedCrashScenario"
+ And I send the app to the background for 10 seconds
+ And I clear any error dialogue
+ And I relaunch the app after a crash
+ And I configure Bugsnag for "CXXDelayedCrashScenario"
+ And I wait to receive an error
+ Then the error payload contains a completed handled native report
+ And the event "app.inForeground" is false
+ And the event "app.duration" is greater than 0
+ And the event "context" string is empty
+ And the event "unhandled" is true
diff --git a/features/full_tests/native_feature_flags.feature b/features/full_tests/native_feature_flags.feature
index 5e481c2da2..edc3e4a7ff 100644
--- a/features/full_tests/native_feature_flags.feature
+++ b/features/full_tests/native_feature_flags.feature
@@ -1,12 +1,15 @@
Feature: Synchronizing feature flags to the native layer
+ Background:
+ Given I clear all persistent data
+
Scenario: Feature flags are synchronized to the native layer
- When I run "CXXFeatureFlagNativeCrashScenario" and relaunch the app
+ When I run "CXXFeatureFlagNativeCrashScenario" and relaunch the crashed app
And I configure Bugsnag for "CXXFeatureFlagNativeCrashScenario"
And I wait to receive an error
And the error payload contains a completed unhandled native report
And the exception "errorClass" equals one of:
- | SIGILL |
+ | SIGILL |
| SIGTRAP |
And event 0 contains the feature flag "demo_mode" with no variant
And event 0 contains the feature flag "sample_group" with variant "a"
@@ -16,12 +19,12 @@ Feature: Synchronizing feature flags to the native layer
Scenario: clearFeatureFlags() is synchronized to the native layer
When I configure the app to run in the "cleared" state
- And I run "CXXFeatureFlagNativeCrashScenario" and relaunch the app
+ And I run "CXXFeatureFlagNativeCrashScenario" and relaunch the crashed app
And I configure Bugsnag for "CXXFeatureFlagNativeCrashScenario"
And I wait to receive an error
And the error payload contains a completed unhandled native report
And the exception "errorClass" equals one of:
- | SIGILL |
+ | SIGILL |
| SIGTRAP |
And event 0 has no feature flags
@@ -32,7 +35,7 @@ Feature: Synchronizing feature flags to the native layer
And I configure Bugsnag for "CXXFeatureFlagNativeCrashScenario"
Then I wait to receive an error
And the exception "errorClass" equals one of:
- | SIGILL |
+ | SIGILL |
| SIGTRAP |
And the event "unhandled" is true
And event 0 contains the feature flag "demo_mode" with no variant
diff --git a/features/full_tests/native_metadata.feature b/features/full_tests/native_metadata.feature
index 3cf8cbe16c..06d220eae4 100644
--- a/features/full_tests/native_metadata.feature
+++ b/features/full_tests/native_metadata.feature
@@ -1,52 +1,55 @@
Feature: Native Metadata API
- Scenario: Add custom metadata to configuration followed by a C crash
- When I run "CXXConfigurationMetadataNativeCrashScenario" and relaunch the app
- And I configure the app to run in the "non-metadata" state
- And I configure Bugsnag for "CXXConfigurationMetadataNativeCrashScenario"
- And I wait to receive an error
- And the error payload contains a completed handled native report
- And the exception "errorClass" equals one of:
- | SIGILL |
- | SIGTRAP |
- And the event "severity" equals "error"
- And the event "metaData.fruit.apple" equals "gala"
- And the event "metaData.fruit.ripe" is true
- And the event "metaData.fruit.counters" equals 47
- And the event "unhandled" is true
+ Background:
+ Given I clear all persistent data
+
+ Scenario: Add custom metadata to configuration followed by a C crash
+ When I run "CXXConfigurationMetadataNativeCrashScenario" and relaunch the crashed app
+ And I configure the app to run in the "non-metadata" state
+ And I configure Bugsnag for "CXXConfigurationMetadataNativeCrashScenario"
+ And I wait to receive an error
+ And the error payload contains a completed handled native report
+ And the exception "errorClass" equals one of:
+ | SIGILL |
+ | SIGTRAP |
+ And the event "severity" equals "error"
+ And the event "metaData.fruit.apple" equals "gala"
+ And the event "metaData.fruit.ripe" is true
+ And the event "metaData.fruit.counters" equals 47
+ And the event "unhandled" is true
#TODO up to here
- Scenario: Remove MetaData from the NDK layer
- When I run "CXXRemoveDataScenario"
- And I wait to receive 2 errors
- Then the error payload contains a completed handled native report
- And the event "unhandled" is false
- And the exception "errorClass" equals "RemoveDataScenario"
- And the exception "message" equals "oh no"
- And the event "metaData.persist.keep" equals "foo"
- And the event "metaData.persist.remove" equals "bar"
- And the event "metaData.remove.foo" equals "bar"
- Then I discard the oldest error
- And the error payload contains a completed handled native report
- And the event "unhandled" is false
- And the exception "errorClass" equals "RemoveDataScenario"
- And the exception "message" equals "oh no"
- And the event "metaData.persist.keep" equals "foo"
- And the event "metaData.persist.remove" is null
- And the event "metaData.remove" is null
+ Scenario: Remove MetaData from the NDK layer
+ When I run "CXXRemoveDataScenario"
+ And I wait to receive 2 errors
+ Then the error payload contains a completed handled native report
+ And the event "unhandled" is false
+ And the exception "errorClass" equals "RemoveDataScenario"
+ And the exception "message" equals "oh no"
+ And the event "metaData.persist.keep" equals "foo"
+ And the event "metaData.persist.remove" equals "bar"
+ And the event "metaData.remove.foo" equals "bar"
+ Then I discard the oldest error
+ And the error payload contains a completed handled native report
+ And the event "unhandled" is false
+ And the exception "errorClass" equals "RemoveDataScenario"
+ And the exception "message" equals "oh no"
+ And the event "metaData.persist.keep" equals "foo"
+ And the event "metaData.persist.remove" is null
+ And the event "metaData.remove" is null
- Scenario: Get Java data in the Native layer
- When I run "CXXGetJavaDataScenario" and relaunch the app
- And I configure Bugsnag for "CXXGetJavaDataScenario"
- And I wait to receive an error
- Then the error payload contains a completed unhandled native report
- And the event "unhandled" is true
- And the event "metaData.data.context" equals "passContext"
- And the event "metaData.data.appVersion" equals "passAppVersion"
- And the event "metaData.data.userName" equals "passUserName"
- And the event "metaData.data.userEmail" equals "passUserEmail"
- And the event "metaData.data.userId" equals "passUserId"
- And the event "metaData.data.metadata" equals "passMetaData"
- And the event "metaData.data.device" is not null
+ Scenario: Get Java data in the Native layer
+ When I run "CXXGetJavaDataScenario" and relaunch the crashed app
+ And I configure Bugsnag for "CXXGetJavaDataScenario"
+ And I wait to receive an error
+ Then the error payload contains a completed unhandled native report
+ And the event "unhandled" is true
+ And the event "metaData.data.context" equals "passContext"
+ And the event "metaData.data.appVersion" equals "passAppVersion"
+ And the event "metaData.data.userName" equals "passUserName"
+ And the event "metaData.data.userEmail" equals "passUserEmail"
+ And the event "metaData.data.userId" equals "passUserId"
+ And the event "metaData.data.metadata" equals "passMetaData"
+ And the event "metaData.data.device" is not null
# TODO: Skipped pending PLAT-6176
# And the event "metaData.data.password" equals "[REDACTED]"
diff --git a/features/full_tests/native_on_error.feature b/features/full_tests/native_on_error.feature
index 5b944549b4..56f1155f2c 100644
--- a/features/full_tests/native_on_error.feature
+++ b/features/full_tests/native_on_error.feature
@@ -1,20 +1,23 @@
Feature: Native on error callbacks are invoked
- Scenario: on_error returning false prevents C signal being reported
- When I run "CXXSignalOnErrorFalseScenario" and relaunch the app
- And I configure Bugsnag for "CXXSignalOnErrorFalseScenario"
- Then Bugsnag confirms it has no errors to send
+ Background:
+ Given I clear all persistent data
- Scenario: on_error returning false prevents C++ exception being reported
- When I run "CXXExceptionOnErrorFalseScenario" and relaunch the app
- And I configure Bugsnag for "CXXExceptionOnErrorFalseScenario"
- Then Bugsnag confirms it has no errors to send
+ Scenario: on_error returning false prevents C signal being reported
+ When I run "CXXSignalOnErrorFalseScenario" and relaunch the crashed app
+ And I configure Bugsnag for "CXXSignalOnErrorFalseScenario"
+ Then Bugsnag confirms it has no errors to send
- Scenario: Removing on_error callback
- When I run "CXXRemoveOnErrorScenario" and relaunch the app
- And I configure Bugsnag for "CXXRemoveOnErrorScenario"
- And I wait to receive an error
- Then the error payload contains a completed handled native report
- And the event "user.id" equals "default"
- And the event "user.email" equals "default@default.df"
- And the event "user.name" equals "default"
+ Scenario: on_error returning false prevents C++ exception being reported
+ When I run "CXXExceptionOnErrorFalseScenario" and relaunch the crashed app
+ And I configure Bugsnag for "CXXExceptionOnErrorFalseScenario"
+ Then Bugsnag confirms it has no errors to send
+
+ Scenario: Removing on_error callback
+ When I run "CXXRemoveOnErrorScenario" and relaunch the crashed app
+ And I configure Bugsnag for "CXXRemoveOnErrorScenario"
+ And I wait to receive an error
+ Then the error payload contains a completed handled native report
+ And the event "user.id" equals "default"
+ And the event "user.email" equals "default@default.df"
+ And the event "user.name" equals "default"
diff --git a/features/full_tests/native_session_tracking.feature b/features/full_tests/native_session_tracking.feature
index 050e17e701..fc93d3ec16 100644
--- a/features/full_tests/native_session_tracking.feature
+++ b/features/full_tests/native_session_tracking.feature
@@ -1,7 +1,10 @@
Feature: NDK Session Tracking
-Scenario: Paused session is not in payload of unhandled NDK error
- And I run "CXXPausedSessionScenario" and relaunch the app
+ Background:
+ Given I clear all persistent data
+
+ Scenario: Paused session is not in payload of unhandled NDK error
+ And I run "CXXPausedSessionScenario" and relaunch the crashed app
And I configure Bugsnag for "CXXPausedSessionScenario"
And I wait to receive a session
Then the session is valid for the session reporting API version "1.0" for the "Android Bugsnag Notifier" notifier
@@ -10,8 +13,8 @@ Scenario: Paused session is not in payload of unhandled NDK error
And the error is valid for the error reporting API version "4.0" for the "Android Bugsnag Notifier" notifier
And the error payload field "events.0.session" is null
-Scenario: Started session is in payload of unhandled NDK error
- And I run "CXXStartSessionScenario" and relaunch the app
+ Scenario: Started session is in payload of unhandled NDK error
+ And I run "CXXStartSessionScenario" and relaunch the crashed app
And I configure Bugsnag for "CXXStartSessionScenario"
And I wait to receive a session
Then the session is valid for the session reporting API version "1.0" for the "Android Bugsnag Notifier" notifier
@@ -20,8 +23,8 @@ Scenario: Started session is in payload of unhandled NDK error
And the error is valid for the error reporting API version "4.0" for the "Android Bugsnag Notifier" notifier
And the error payload field "events.0.session.events.unhandled" equals 1
-Scenario: Starting a session, notifying, followed by a C crash
- When I run "CXXSessionInfoCrashScenario" and relaunch the app
+ Scenario: Starting a session, notifying, followed by a C crash
+ When I run "CXXSessionInfoCrashScenario" and relaunch the crashed app
And I configure Bugsnag for "CXXSessionInfoCrashScenario"
And I wait to receive a session
And I wait to receive 3 errors
diff --git a/features/full_tests/native_signal_raise.feature b/features/full_tests/native_signal_raise.feature
index 5278b116fb..6b477a9f6c 100644
--- a/features/full_tests/native_signal_raise.feature
+++ b/features/full_tests/native_signal_raise.feature
@@ -1,69 +1,72 @@
Feature: Raising native signals
- Scenario: Raise SIGILL
- When I run "CXXSigillScenario" and relaunch the app
- And I configure Bugsnag for "CXXSigillScenario"
- And I wait to receive an error
- And the error payload contains a completed unhandled native report
- And the exception "errorClass" equals "SIGILL"
- And the exception "message" equals one of:
- | Illegal instruction |
- | Trace/breakpoint trap |
- And the exception "type" equals "c"
- And the event "severity" equals "error"
- And the event "unhandled" is true
+ Background:
+ Given I clear all persistent data
- Scenario: Raise SIGSEGV
- When I run "CXXSigsegvScenario" and relaunch the app
- And I configure Bugsnag for "CXXSigsegvScenario"
- And I wait to receive an error
- And the error payload contains a completed unhandled native report
- And the exception "errorClass" equals "SIGSEGV"
- And the exception "message" equals "Segmentation violation (invalid memory reference)"
- And the exception "type" equals "c"
- And the event "severity" equals "error"
- And the event "unhandled" is true
+ Scenario: Raise SIGILL
+ When I run "CXXSigillScenario" and relaunch the crashed app
+ And I configure Bugsnag for "CXXSigillScenario"
+ And I wait to receive an error
+ And the error payload contains a completed unhandled native report
+ And the exception "errorClass" equals "SIGILL"
+ And the exception "message" equals one of:
+ | Illegal instruction |
+ | Trace/breakpoint trap |
+ And the exception "type" equals "c"
+ And the event "severity" equals "error"
+ And the event "unhandled" is true
- Scenario: Raise SIGABRT
- When I run "CXXSigabrtScenario" and relaunch the app
- And I configure Bugsnag for "CXXSigabrtScenario"
- And I wait to receive an error
- And the error payload contains a completed unhandled native report
- And the exception "errorClass" equals "SIGABRT"
- And the exception "message" equals "Abort program"
- And the exception "type" equals "c"
- And the event "severity" equals "error"
- And the event "unhandled" is true
+ Scenario: Raise SIGSEGV
+ When I run "CXXSigsegvScenario" and relaunch the crashed app
+ And I configure Bugsnag for "CXXSigsegvScenario"
+ And I wait to receive an error
+ And the error payload contains a completed unhandled native report
+ And the exception "errorClass" equals "SIGSEGV"
+ And the exception "message" equals "Segmentation violation (invalid memory reference)"
+ And the exception "type" equals "c"
+ And the event "severity" equals "error"
+ And the event "unhandled" is true
- Scenario: Raise SIGBUS
- When I run "CXXSigbusScenario" and relaunch the app
- And I configure Bugsnag for "CXXSigbusScenario"
- And I wait to receive an error
- And the error payload contains a completed unhandled native report
- And the exception "errorClass" equals "SIGBUS"
- And the exception "message" equals "Bus error (bad memory access)"
- And the exception "type" equals "c"
- And the event "severity" equals "error"
- And the event "unhandled" is true
+ Scenario: Raise SIGABRT
+ When I run "CXXSigabrtScenario" and relaunch the crashed app
+ And I configure Bugsnag for "CXXSigabrtScenario"
+ And I wait to receive an error
+ And the error payload contains a completed unhandled native report
+ And the exception "errorClass" equals "SIGABRT"
+ And the exception "message" equals "Abort program"
+ And the exception "type" equals "c"
+ And the event "severity" equals "error"
+ And the event "unhandled" is true
- Scenario: Raise SIGFPE
- When I run "CXXSigfpeScenario" and relaunch the app
- And I configure Bugsnag for "CXXSigfpeScenario"
- And I wait to receive an error
- And the error payload contains a completed unhandled native report
- And the exception "errorClass" equals "SIGFPE"
- And the exception "message" equals "Floating-point exception"
- And the exception "type" equals "c"
- And the event "severity" equals "error"
- And the event "unhandled" is true
+ Scenario: Raise SIGBUS
+ When I run "CXXSigbusScenario" and relaunch the crashed app
+ And I configure Bugsnag for "CXXSigbusScenario"
+ And I wait to receive an error
+ And the error payload contains a completed unhandled native report
+ And the exception "errorClass" equals "SIGBUS"
+ And the exception "message" equals "Bus error (bad memory access)"
+ And the exception "type" equals "c"
+ And the event "severity" equals "error"
+ And the event "unhandled" is true
- Scenario: Raise SIGTRAP
- When I run "CXXSigtrapScenario" and relaunch the app
- And I configure Bugsnag for "CXXSigtrapScenario"
- And I wait to receive an error
- And the error payload contains a completed unhandled native report
- And the exception "errorClass" equals "SIGTRAP"
- And the exception "message" equals "Trace/breakpoint trap"
- And the exception "type" equals "c"
- And the event "severity" equals "error"
- And the event "unhandled" is true
+ Scenario: Raise SIGFPE
+ When I run "CXXSigfpeScenario" and relaunch the crashed app
+ And I configure Bugsnag for "CXXSigfpeScenario"
+ And I wait to receive an error
+ And the error payload contains a completed unhandled native report
+ And the exception "errorClass" equals "SIGFPE"
+ And the exception "message" equals "Floating-point exception"
+ And the exception "type" equals "c"
+ And the event "severity" equals "error"
+ And the event "unhandled" is true
+
+ Scenario: Raise SIGTRAP
+ When I run "CXXSigtrapScenario" and relaunch the crashed app
+ And I configure Bugsnag for "CXXSigtrapScenario"
+ And I wait to receive an error
+ And the error payload contains a completed unhandled native report
+ And the exception "errorClass" equals "SIGTRAP"
+ And the exception "message" equals "Trace/breakpoint trap"
+ And the exception "type" equals "c"
+ And the event "severity" equals "error"
+ And the event "unhandled" is true
diff --git a/features/full_tests/native_threads.feature b/features/full_tests/native_threads.feature
index 865d303890..a55b11161c 100644
--- a/features/full_tests/native_threads.feature
+++ b/features/full_tests/native_threads.feature
@@ -1,7 +1,10 @@
Feature: Capture native threads
+ Background:
+ Given I clear all persistent data
+
Scenario: Reports native threads for Unhandled errors
- When I run "CXXCaptureThreadsScenario" and relaunch the app
+ When I run "CXXCaptureThreadsScenario" and relaunch the crashed app
And I configure Bugsnag for "CXXCaptureThreadsScenario"
And I wait to receive an error
Then the error payload contains a completed unhandled native report
@@ -12,7 +15,7 @@ Feature: Capture native threads
Scenario: Reports native threads for Unhandled errors where sendThreads is UNHANDLED_ONLY
When I configure the app to run in the "UNHANDLED_ONLY" state
- And I run "CXXCaptureThreadsScenario" and relaunch the app
+ And I run "CXXCaptureThreadsScenario" and relaunch the crashed app
And I configure Bugsnag for "CXXCaptureThreadsScenario"
And I wait to receive an error
Then the error payload contains a completed unhandled native report
@@ -23,7 +26,7 @@ Feature: Capture native threads
Scenario: No threads are reported when sendThreads is NEVER
When I configure the app to run in the "NEVER" state
- And I run "CXXCaptureThreadsScenario" and relaunch the app
+ And I run "CXXCaptureThreadsScenario" and relaunch the crashed app
And I configure Bugsnag for "CXXCaptureThreadsScenario"
And I wait to receive an error
Then the error payload contains a completed handled native report
diff --git a/features/full_tests/native_throw_crash.feature b/features/full_tests/native_throw_crash.feature
index 4c5ba720f6..bfe83249d8 100644
--- a/features/full_tests/native_throw_crash.feature
+++ b/features/full_tests/native_throw_crash.feature
@@ -1,7 +1,10 @@
Feature: Native crash reporting with thrown objects
+ Background:
+ Given I clear all persistent data
+
Scenario: Throwing an exception in C++
- When I run "CXXExceptionScenario" and relaunch the app
+ When I run "CXXExceptionScenario" and relaunch the crashed app
And I configure Bugsnag for "CXXExceptionScenario"
And I wait to receive an error
And the error payload contains a completed unhandled native report
@@ -11,7 +14,7 @@ Feature: Native crash reporting with thrown objects
And the exception "message" equals "Abort program"
Scenario: Throwing an object in C++
- When I run "CXXThrowSomethingScenario" and relaunch the app
+ When I run "CXXThrowSomethingScenario" and relaunch the crashed app
And I configure Bugsnag for "CXXThrowSomethingScenario"
And I wait to receive an error
And the error payload contains a completed unhandled native report
@@ -21,7 +24,7 @@ Feature: Native crash reporting with thrown objects
And the exception "message" equals "Abort program"
Scenario: Rethrow in C++ without initial exception
- When I run "CXXInvalidRethrow" and relaunch the app
+ When I run "CXXInvalidRethrow" and relaunch the crashed app
And I configure Bugsnag for "CXXInvalidRethrow"
And I wait to receive an error
And the error payload contains a completed unhandled native report
@@ -33,7 +36,7 @@ Feature: Native crash reporting with thrown objects
| print_last_exception() | CXXInvalidRethrow.cpp | 7 |
Scenario: Throw from C++ noexcept function
- When I run "CXXThrowFromNoexcept" and relaunch the app
+ When I run "CXXThrowFromNoexcept" and relaunch the crashed app
And I configure Bugsnag for "CXXThrowFromNoexcept"
And I wait to receive an error
And the error payload contains a completed unhandled native report
@@ -42,6 +45,6 @@ Feature: Native crash reporting with thrown objects
And the event "severity" equals "error"
And the event "unhandled" is true
And on arm64, the first significant stack frames match:
- | FunkError::what() const | CXXThrowFromNoexcept.cpp | 13 |
+ | FunkError::what() const | CXXThrowFromNoexcept.cpp | 13 |
| Java_com_bugsnag_android_mazerunner_scenarios_CXXThrowFromNoexcept_crash | CXXThrowFromNoexcept.cpp | 21 |
diff --git a/features/full_tests/native_user.feature b/features/full_tests/native_user.feature
index 531cca3608..ccd18402b8 100644
--- a/features/full_tests/native_user.feature
+++ b/features/full_tests/native_user.feature
@@ -1,25 +1,28 @@
Feature: Native User API
- Scenario: Adding user information in Java followed by a C crash
- And I run "CXXJavaUserInfoNativeCrashScenario" and relaunch the app
- And I configure Bugsnag for "CXXJavaUserInfoNativeCrashScenario"
- And I wait to receive an error
- Then the error payload contains a completed handled native report
- And the exception "errorClass" equals one of:
- | SIGILL |
- | SIGTRAP |
- And the event "severity" equals "error"
- And the event "user.name" equals "Strulyegha Ghaumon Rabelban Snefkal Angengtai Samperris D"
- And the event "user.id" equals "9816734"
- And the event "user.email" equals "j@example.com"
- And the event "unhandled" is true
+ Background:
+ Given I clear all persistent data
- Scenario: Set user in Native layer followed by a Java crash
- When I run "CXXNativeUserInfoJavaCrashScenario" and relaunch the app
- And I configure Bugsnag for "CXXNativeUserInfoJavaCrashScenario"
- And I wait to receive an error
- Then the error is valid for the error reporting API version "4.0" for the "Android Bugsnag Notifier" notifier
- And the error payload field "events" is an array with 1 elements
- And the event "user.id" equals "24601"
- And the event "user.email" equals "test@test.test"
- And the event "user.name" equals "test user"
+ Scenario: Adding user information in Java followed by a C crash
+ And I run "CXXJavaUserInfoNativeCrashScenario" and relaunch the crashed app
+ And I configure Bugsnag for "CXXJavaUserInfoNativeCrashScenario"
+ And I wait to receive an error
+ Then the error payload contains a completed handled native report
+ And the exception "errorClass" equals one of:
+ | SIGILL |
+ | SIGTRAP |
+ And the event "severity" equals "error"
+ And the event "user.name" equals "Strulyegha Ghaumon Rabelban Snefkal Angengtai Samperris D"
+ And the event "user.id" equals "9816734"
+ And the event "user.email" equals "j@example.com"
+ And the event "unhandled" is true
+
+ Scenario: Set user in Native layer followed by a Java crash
+ When I run "CXXNativeUserInfoJavaCrashScenario" and relaunch the crashed app
+ And I configure Bugsnag for "CXXNativeUserInfoJavaCrashScenario"
+ And I wait to receive an error
+ Then the error is valid for the error reporting API version "4.0" for the "Android Bugsnag Notifier" notifier
+ And the error payload field "events" is an array with 1 elements
+ And the event "user.id" equals "24601"
+ And the event "user.email" equals "test@test.test"
+ And the event "user.name" equals "test user"
diff --git a/features/full_tests/naughty_strings.feature b/features/full_tests/naughty_strings.feature
index 32c0f25ac9..843e1124aa 100644
--- a/features/full_tests/naughty_strings.feature
+++ b/features/full_tests/naughty_strings.feature
@@ -1,6 +1,9 @@
Feature: The notifier handles user data containing unusual strings
-Scenario: Test handled JVM error
+ Background:
+ Given I clear all persistent data
+
+ Scenario: Test handled JVM error
When I run "NaughtyStringScenario"
Then I wait to receive an error
And the error is valid for the error reporting API version "4.0" for the "Android Bugsnag Notifier" notifier
@@ -24,9 +27,9 @@ Scenario: Test handled JVM error
And the error payload field "events.0.metaData.custom.val_14" equals "گچپژ"
# commented out some failing unicode assertions and skipped Android <6 until PLAT-5606 is addressed
-@skip_below_android_6
-Scenario: Test unhandled NDK error
- When I run "CXXNaughtyStringsScenario" and relaunch the app
+ @skip_below_android_6
+ Scenario: Test unhandled NDK error
+ When I run "CXXNaughtyStringsScenario" and relaunch the crashed app
And I configure Bugsnag for "CXXNaughtyStringsScenario"
Then I wait to receive an error
And the error is valid for the error reporting API version "4.0" for the "Android Bugsnag Notifier" notifier
diff --git a/features/full_tests/network_breadcrumbs.feature b/features/full_tests/network_breadcrumbs.feature
index 1a9d244b43..e3a389f913 100644
--- a/features/full_tests/network_breadcrumbs.feature
+++ b/features/full_tests/network_breadcrumbs.feature
@@ -1,6 +1,9 @@
Feature: Capturing network breadcrumbs
-Scenario: Breadcrumbs are captured for OkHttp network requests
+ Background:
+ Given I clear all persistent data
+
+ Scenario: Breadcrumbs are captured for OkHttp network requests
When I run "NetworkBreadcrumbScenario"
And I wait to receive an error
Then the error is valid for the error reporting API version "4.0" for the "Android Bugsnag Notifier" notifier
diff --git a/features/full_tests/null_stacktrace.feature b/features/full_tests/null_stacktrace.feature
index 35a28dbdfe..d4d5d32771 100644
--- a/features/full_tests/null_stacktrace.feature
+++ b/features/full_tests/null_stacktrace.feature
@@ -1,6 +1,9 @@
Feature: Null Stacktrace reported
-Scenario: Exceptions with null stacktraces are sent
+ Background:
+ Given I clear all persistent data
+
+ Scenario: Exceptions with null stacktraces are sent
When I run "NullStackTraceScenario"
Then I wait to receive an error
And the error is valid for the error reporting API version "4.0" for the "Android Bugsnag Notifier" notifier
diff --git a/features/full_tests/onsend_callback.feature b/features/full_tests/onsend_callback.feature
index b26756007c..72fd0776b2 100644
--- a/features/full_tests/onsend_callback.feature
+++ b/features/full_tests/onsend_callback.feature
@@ -1,7 +1,10 @@
Feature: OnSend Callbacks can alter Events before upload
+ Background:
+ Given I clear all persistent data
+
Scenario: Unhandled exception altered by OnSendCallback
- When I run "OnSendCallbackScenario" and relaunch the app
+ When I run "OnSendCallbackScenario" and relaunch the crashed app
And I configure the app to run in the "start-only" state
And I configure Bugsnag for "OnSendCallbackScenario"
Then I wait to receive an error
diff --git a/features/full_tests/oom.feature b/features/full_tests/oom.feature
index fb03de3b5d..ee89168e27 100644
--- a/features/full_tests/oom.feature
+++ b/features/full_tests/oom.feature
@@ -1,7 +1,10 @@
Feature: Reporting OOMs
-Scenario: Out of Memory Error captured
- When I run "OomScenario" and relaunch the app
+ Background:
+ Given I clear all persistent data
+
+ Scenario: Out of Memory Error captured
+ When I run "OomScenario" and relaunch the crashed app
And I configure Bugsnag for "OomScenario"
Then I wait to receive an error
And the error is valid for the error reporting API version "4.0" for the "Android Bugsnag Notifier" notifier
diff --git a/features/full_tests/override_unhandled.feature b/features/full_tests/override_unhandled.feature
index 9525ab3056..bcd6a16c34 100644
--- a/features/full_tests/override_unhandled.feature
+++ b/features/full_tests/override_unhandled.feature
@@ -1,6 +1,9 @@
Feature: Overriding unhandled state
-Scenario: Non-fatal exception overridden to unhandled
+ Background:
+ Given I clear all persistent data
+
+ Scenario: Non-fatal exception overridden to unhandled
When I run "OverrideToUnhandledExceptionScenario"
Then I wait to receive an error
And I wait to receive a session
@@ -14,8 +17,8 @@ Scenario: Non-fatal exception overridden to unhandled
And the event "session.events.handled" equals 0
And the event "session.events.unhandled" equals 1
-Scenario: Fatal exception overridden to handled
- When I run "OverrideToHandledExceptionScenario" and relaunch the app
+ Scenario: Fatal exception overridden to handled
+ When I run "OverrideToHandledExceptionScenario" and relaunch the crashed app
And I configure Bugsnag for "OverrideToHandledExceptionScenario"
And I wait to receive an error
And I wait to receive a session
@@ -29,8 +32,8 @@ Scenario: Fatal exception overridden to handled
And the event "session.events.handled" equals 1
And the event "session.events.unhandled" equals 0
-Scenario: CXX error overridden to handled
- When I run "CXXHandledOverrideScenario" and relaunch the app
+ Scenario: CXX error overridden to handled
+ When I run "CXXHandledOverrideScenario" and relaunch the crashed app
And I configure Bugsnag for "CXXHandledOverrideScenario"
And I wait to receive an error
And the error is valid for the error reporting API version "4.0" for the "Android Bugsnag Notifier" notifier
diff --git a/features/full_tests/plugin_interface.feature b/features/full_tests/plugin_interface.feature
index 969587cbd3..c9d1c38bcf 100644
--- a/features/full_tests/plugin_interface.feature
+++ b/features/full_tests/plugin_interface.feature
@@ -1,14 +1,17 @@
Feature: Add custom behavior through a plugin interface
- Some internal libraries may build on top of the Bugsnag Android library and
- require custom behavior prior to the library being fully initialized. This
- interface allows for installing that behavior before calling the regular
- initialization process.
-
- Scenario: Changing payload notifier description
- When I run "CustomPluginNotifierDescriptionScenario"
- Then I wait to receive an error
- Then the event "context" equals "Foo Handler Library"
- And the error payload field "events" is an array with 1 elements
- And the exception "errorClass" equals "java.lang.RuntimeException"
+ Some internal libraries may build on top of the Bugsnag Android library and
+ require custom behavior prior to the library being fully initialized. This
+ interface allows for installing that behavior before calling the regular
+ initialization process.
+
+ Background:
+ Given I clear all persistent data
+
+ Scenario: Changing payload notifier description
+ When I run "CustomPluginNotifierDescriptionScenario"
+ Then I wait to receive an error
+ Then the event "context" equals "Foo Handler Library"
+ And the error payload field "events" is an array with 1 elements
+ And the exception "errorClass" equals "java.lang.RuntimeException"
diff --git a/features/full_tests/release_stage.feature b/features/full_tests/release_stage.feature
index 93ff6454a3..c1faa43e47 100644
--- a/features/full_tests/release_stage.feature
+++ b/features/full_tests/release_stage.feature
@@ -1,21 +1,24 @@
Feature: Reporting exceptions with release stages
-Scenario: Exception not reported when outside release stage
+ Background:
+ Given I clear all persistent data
+
+ Scenario: Exception not reported when outside release stage
When I run "OutsideReleaseStageScenario"
And I wait to receive 1 logs
Then the "debug" level log message equals "Bugsnag loaded"
-Scenario: Exception not reported when release stage null
+ Scenario: Exception not reported when release stage null
When I run "NullReleaseStageScenario"
And I wait to receive 1 logs
Then the "debug" level log message equals "Bugsnag loaded"
-Scenario: Exception reported when release stages empty
+ Scenario: Exception reported when release stages empty
When I run "EmptyEnabledReleaseStageScenario"
And I wait to receive 1 logs
Then the "debug" level log message equals "Bugsnag loaded"
-Scenario: Exception reported when inside Notify release stage array
+ Scenario: Exception reported when inside Notify release stage array
When I run "ArrayEnabledReleaseStageScenario"
Then I wait to receive an error
And the error is valid for the error reporting API version "4.0" for the "Android Bugsnag Notifier" notifier
diff --git a/features/full_tests/session_stopping.feature b/features/full_tests/session_stopping.feature
index 661ab31f16..5e44cf9780 100644
--- a/features/full_tests/session_stopping.feature
+++ b/features/full_tests/session_stopping.feature
@@ -1,6 +1,9 @@
Feature: Pausing and resuming sessions
-Scenario: Stopping, resuming, and starting sessions are reflected in error and session payloads
+ Background:
+ Given I clear all persistent data
+
+ Scenario: Stopping, resuming, and starting sessions are reflected in error and session payloads
When I run "SessionStoppingScenario"
# 2 sessions are received
diff --git a/features/full_tests/session_tracking.feature b/features/full_tests/session_tracking.feature
index 23f2d2582e..20bcfd676c 100644
--- a/features/full_tests/session_tracking.feature
+++ b/features/full_tests/session_tracking.feature
@@ -1,6 +1,9 @@
Feature: Session Tracking
-Scenario: User is persisted between sessions
+ Background:
+ Given I clear all persistent data
+
+ Scenario: User is persisted between sessions
When I run "SessionPersistUserScenario"
And I wait to receive a session
Then the session is valid for the session reporting API version "1.0" for the "Android Bugsnag Notifier" notifier
@@ -20,7 +23,7 @@ Scenario: User is persisted between sessions
And the session "user.email" equals "test@test.test"
And the session "user.name" equals "test user"
-Scenario: User is not persisted between sessions
+ Scenario: User is not persisted between sessions
When I run "SessionPersistUserDisabledScenario"
And I wait to receive a session
Then the session is valid for the session reporting API version "1.0" for the "Android Bugsnag Notifier" notifier
diff --git a/features/full_tests/stackoverflow.feature b/features/full_tests/stackoverflow.feature
index 8e6e383ba1..96eb016537 100644
--- a/features/full_tests/stackoverflow.feature
+++ b/features/full_tests/stackoverflow.feature
@@ -1,7 +1,10 @@
Feature: Reporting Stack overflow
-Scenario: Stack Overflow sends
- When I run "StackOverflowScenario" and relaunch the app
+ Background:
+ Given I clear all persistent data
+
+ Scenario: Stack Overflow sends
+ When I run "StackOverflowScenario" and relaunch the crashed app
And I configure Bugsnag for "StackOverflowScenario"
And I wait to receive an error
And the error is valid for the error reporting API version "4.0" for the "Android Bugsnag Notifier" notifier
diff --git a/features/full_tests/startup_anr.feature b/features/full_tests/startup_anr.feature
index 3c60663a06..895bf99709 100644
--- a/features/full_tests/startup_anr.feature
+++ b/features/full_tests/startup_anr.feature
@@ -1,13 +1,16 @@
Feature: onCreate ANR
+ Background:
+ Given I clear all persistent data
+
# Android 10 Note: Android 10 devices appear to allow much longer times for startup without an ANR
# and then terminate the "misbehaving" app with a KILL (9) signal (almost like the ANR code doesn't
# fire at all). Since we can't cover a KILL signal in a test, we skip Android 10.
-@skip_android_10
-Scenario: onCreate ANR is reported
- When I run "ConfigureStartupAnrScenario"
- And I relaunch the app after a crash
- And I wait for 30 seconds
- And I clear any error dialogue
- Then I wait to receive an error
- And the exception "errorClass" equals "ANR"
+ @skip_android_10
+ Scenario: onCreate ANR is reported
+ When I run "ConfigureStartupAnrScenario"
+ And I relaunch the app after a crash
+ And I wait for 30 seconds
+ And I clear any error dialogue
+ Then I wait to receive an error
+ And the exception "errorClass" equals "ANR"
diff --git a/features/full_tests/strict_mode_legacy.feature b/features/full_tests/strict_mode_legacy.feature
index 9b91f9ff23..938f5564d8 100644
--- a/features/full_tests/strict_mode_legacy.feature
+++ b/features/full_tests/strict_mode_legacy.feature
@@ -1,8 +1,11 @@
Feature: Reporting Strict Mode Violations
-@skip_above_android_8
-Scenario: StrictMode DiscWrite violation
- When I run "StrictModeDiscScenario" and relaunch the app
+ Background:
+ Given I clear all persistent data
+
+ @skip_above_android_8
+ Scenario: StrictMode DiscWrite violation
+ When I run "StrictModeDiscScenario" and relaunch the crashed app
And I configure Bugsnag for "StrictModeDiscScenario"
And I wait to receive an error
Then the error is valid for the error reporting API version "4.0" for the "Android Bugsnag Notifier" notifier
@@ -10,9 +13,9 @@ Scenario: StrictMode DiscWrite violation
And the event "metaData.StrictMode.Violation" equals "DiskWrite"
And the event "severityReason.type" equals "strictMode"
-@skip_above_android_8
-Scenario: StrictMode Network on Main Thread violation
- When I run "StrictModeNetworkScenario" and relaunch the app
+ @skip_above_android_8
+ Scenario: StrictMode Network on Main Thread violation
+ When I run "StrictModeNetworkScenario" and relaunch the crashed app
And I configure Bugsnag for "StrictModeNetworkScenario"
And I wait to receive an error
Then the error is valid for the error reporting API version "4.0" for the "Android Bugsnag Notifier" notifier
@@ -21,8 +24,8 @@ Scenario: StrictMode Network on Main Thread violation
And the event "severityReason.type" equals "strictMode"
# In Android <9 StrictMode kills VM policy violations with SIGKILL, so no requests are received.
-@skip_above_android_8
-Scenario: StrictMode Activity leak violation
- When I run "StrictModeFileUriExposeScenario" and relaunch the app
+ @skip_above_android_8
+ Scenario: StrictMode Activity leak violation
+ When I run "StrictModeFileUriExposeScenario" and relaunch the crashed app
And I configure Bugsnag for "StrictModeFileUriExposeScenario"
Then I should receive no requests
diff --git a/features/full_tests/strict_mode_violations.feature b/features/full_tests/strict_mode_violations.feature
index 924901252e..a486ae116c 100644
--- a/features/full_tests/strict_mode_violations.feature
+++ b/features/full_tests/strict_mode_violations.feature
@@ -1,8 +1,11 @@
Feature: Reporting Strict Mode Violations
-@skip_below_android_9
-Scenario: StrictMode Exposed File URI violation
- When I run "StrictModeFileUriExposeScenario" and relaunch the app
+ Background:
+ Given I clear all persistent data
+
+ @skip_below_android_9
+ Scenario: StrictMode Exposed File URI violation
+ When I run "StrictModeFileUriExposeScenario" and relaunch the crashed app
And I configure Bugsnag for "StrictModeFileUriExposeScenario"
And I wait to receive an error
Then the error is valid for the error reporting API version "4.0" for the "Android Bugsnag Notifier" notifier
@@ -15,9 +18,9 @@ Scenario: StrictMode Exposed File URI violation
And the event "exceptions.0.stacktrace.0.method" equals "android.os.StrictMode.onFileUriExposed"
And the event "exceptions.0.stacktrace.0.file" equals "StrictMode.java"
-@skip_below_android_9
-Scenario: StrictMode DiscWrite violation
- When I run "StrictModeDiscScenario" and relaunch the app
+ @skip_below_android_9
+ Scenario: StrictMode DiscWrite violation
+ When I run "StrictModeDiscScenario" and relaunch the crashed app
And I configure Bugsnag for "StrictModeDiscScenario"
And I wait to receive 2 errors
@@ -31,12 +34,12 @@ Scenario: StrictMode DiscWrite violation
And the error payload field "events.0.exceptions.0.stacktrace" is a non-empty array
And the exception "stacktrace.1.method" equals one of:
- | libcore.io.BlockGuardOs.open |
- | java.io.FileOutputStream. |
+ | libcore.io.BlockGuardOs.open |
+ | java.io.FileOutputStream. |
And the exception "stacktrace.1.file" equals one of:
- | BlockGuardOs.java |
- | FileOutputStream.java |
+ | BlockGuardOs.java |
+ | FileOutputStream.java |
# Second violation (triggered by writing to FileOutputStream)
And I discard the oldest error
diff --git a/features/full_tests/threaded_startup.feature b/features/full_tests/threaded_startup.feature
new file mode 100644
index 0000000000..d75afd5034
--- /dev/null
+++ b/features/full_tests/threaded_startup.feature
@@ -0,0 +1,10 @@
+Feature: Switching automatic error detection on/off for Unity
+
+ Background:
+ Given I clear all persistent data
+
+ Scenario: Starting Bugsnag & calling it on separate threads
+ When I run "MultiThreadedStartupScenario" and relaunch the crashed app
+ And I configure Bugsnag for "MultiThreadedStartupScenario"
+ And I wait for 2 seconds
+ Then I should receive no error
diff --git a/features/full_tests/trimmed_stacktrace.feature b/features/full_tests/trimmed_stacktrace.feature
index 021bc3263d..dc43c4c1f8 100644
--- a/features/full_tests/trimmed_stacktrace.feature
+++ b/features/full_tests/trimmed_stacktrace.feature
@@ -1,6 +1,9 @@
Feature: Reporting large stacktrace
-Scenario: A large stacktrace should have its frames trimmed to a reasonable number
+ Background:
+ Given I clear all persistent data
+
+ Scenario: A large stacktrace should have its frames trimmed to a reasonable number
When I run "TrimmedStacktraceScenario"
And I wait to receive an error
And the error is valid for the error reporting API version "4.0" for the "Android Bugsnag Notifier" notifier
diff --git a/features/full_tests/user.feature b/features/full_tests/user.feature
index 3a39bc0b47..cfadce4a02 100644
--- a/features/full_tests/user.feature
+++ b/features/full_tests/user.feature
@@ -1,28 +1,31 @@
Feature: Reporting User Information
- Scenario: User fields set as null
- When I run "UserDisabledScenario"
- And I wait to receive an error
- And the error is valid for the error reporting API version "4.0" for the "Android Bugsnag Notifier" notifier
- And the exception "message" equals "UserDisabledScenario"
- And the event "user.id" is null
- And the event "user.email" is null
- And the event "user.name" is null
+ Background:
+ Given I clear all persistent data
- Scenario: Only User ID field set
- When I run "UserIdScenario"
- And I wait to receive an error
- And the error is valid for the error reporting API version "4.0" for the "Android Bugsnag Notifier" notifier
- And the exception "message" equals "UserIdScenario"
- And the event "user.id" equals "abc"
- And the event "user.email" is null
- And the event "user.name" is null
+ Scenario: User fields set as null
+ When I run "UserDisabledScenario"
+ And I wait to receive an error
+ And the error is valid for the error reporting API version "4.0" for the "Android Bugsnag Notifier" notifier
+ And the exception "message" equals "UserDisabledScenario"
+ And the event "user.id" is null
+ And the event "user.email" is null
+ And the event "user.name" is null
- Scenario: Override user details in callback
- When I run "UserCallbackScenario"
- And I wait to receive an error
- And the error is valid for the error reporting API version "4.0" for the "Android Bugsnag Notifier" notifier
- And the exception "message" equals "UserCallbackScenario"
- And the event "user.id" equals "Agent Pink"
- And the event "user.email" equals "bob@example.com"
- And the event "user.name" equals "Zebedee"
+ Scenario: Only User ID field set
+ When I run "UserIdScenario"
+ And I wait to receive an error
+ And the error is valid for the error reporting API version "4.0" for the "Android Bugsnag Notifier" notifier
+ And the exception "message" equals "UserIdScenario"
+ And the event "user.id" equals "abc"
+ And the event "user.email" is null
+ And the event "user.name" is null
+
+ Scenario: Override user details in callback
+ When I run "UserCallbackScenario"
+ And I wait to receive an error
+ And the error is valid for the error reporting API version "4.0" for the "Android Bugsnag Notifier" notifier
+ And the exception "message" equals "UserCallbackScenario"
+ And the event "user.id" equals "Agent Pink"
+ And the event "user.email" equals "bob@example.com"
+ And the event "user.name" equals "Zebedee"
diff --git a/features/minimal/detect_anr_minimal.feature b/features/minimal/detect_anr_minimal.feature
index 6677ba3f87..2da3156130 100644
--- a/features/minimal/detect_anr_minimal.feature
+++ b/features/minimal/detect_anr_minimal.feature
@@ -1,6 +1,9 @@
Feature: ANRs triggered in a fixture with only bugsnag-android-core are captured
-Scenario: Triggering ANR does not crash the minimal app
+ Background:
+ Given I clear all persistent data
+
+ Scenario: Triggering ANR does not crash the minimal app
When I run "JvmAnrMinimalFixtureScenario"
And I wait for 2 seconds
And I tap the screen 3 times
diff --git a/features/smoke_tests/handled.feature b/features/smoke_tests/handled.feature
index 650ee67bfe..b48f041b7b 100644
--- a/features/smoke_tests/handled.feature
+++ b/features/smoke_tests/handled.feature
@@ -1,6 +1,9 @@
Feature: Handled smoke tests
-Scenario: Notify caught Java exception with default configuration
+ Background:
+ Given I clear all persistent data
+
+ Scenario: Notify caught Java exception with default configuration
When I run "HandledJavaSmokeScenario"
And I wait to receive an error
Then the error is valid for the error reporting API version "4.0" for the "Android Bugsnag Notifier" notifier
@@ -109,7 +112,7 @@ Scenario: Notify caught Java exception with default configuration
And the event "threads.0.stacktrace.0.file" is not null
And the event "threads.0.stacktrace.0.lineNumber" is not null
-Scenario: Notify Kotlin exception with overwritten configuration
+ Scenario: Notify Kotlin exception with overwritten configuration
When I run "HandledKotlinSmokeScenario"
Then I wait to receive an error
And the error is valid for the error reporting API version "4.0" for the "Android Bugsnag Notifier" notifier
@@ -165,8 +168,8 @@ Scenario: Notify Kotlin exception with overwritten configuration
And the event "threads.0.stacktrace.0.file" is not null
And the event "threads.0.stacktrace.0.lineNumber" is not null
-@debug-safe
-Scenario: Handled C functionality
+ @debug-safe
+ Scenario: Handled C functionality
When I run "CXXNotifySmokeScenario"
And I wait to receive an error
diff --git a/features/smoke_tests/sessions.feature b/features/smoke_tests/sessions.feature
index 50face6c38..15e23b7a70 100644
--- a/features/smoke_tests/sessions.feature
+++ b/features/smoke_tests/sessions.feature
@@ -1,7 +1,10 @@
Feature: Session functionality smoke tests
-@debug-safe
-Scenario: Automated sessions send
+ Background:
+ Given I clear all persistent data
+
+ @debug-safe
+ Scenario: Automated sessions send
When I run "AutoSessionSmokeScenario"
And I wait to receive a session
@@ -52,9 +55,9 @@ Scenario: Automated sessions send
And the event "session.events.unhandled" equals 0
And the event "severityReason.unhandledOverridden" is false
-@debug-safe
-Scenario: Manual session control works
- When I run "ManualSessionSmokeScenario" and relaunch the app
+ @debug-safe
+ Scenario: Manual session control works
+ When I run "ManualSessionSmokeScenario" and relaunch the crashed app
And I configure Bugsnag for "ManualSessionSmokeScenario"
And I wait to receive a session
diff --git a/features/smoke_tests/unhandled.feature b/features/smoke_tests/unhandled.feature
index 72a3d5d884..d32135e470 100644
--- a/features/smoke_tests/unhandled.feature
+++ b/features/smoke_tests/unhandled.feature
@@ -1,7 +1,10 @@
Feature: Unhandled smoke tests
-Scenario: Unhandled Java Exception with loaded configuration
- When I run "UnhandledJavaLoadedConfigScenario" and relaunch the app
+ Background:
+ Given I clear all persistent data
+
+ Scenario: Unhandled Java Exception with loaded configuration
+ When I run "UnhandledJavaLoadedConfigScenario" and relaunch the crashed app
And I configure Bugsnag for "UnhandledJavaLoadedConfigScenario"
And I wait to receive an error
Then the error is valid for the error reporting API version "4.0" for the "Android Bugsnag Notifier" notifier
@@ -95,8 +98,8 @@ Scenario: Unhandled Java Exception with loaded configuration
And the event "threads.0.stacktrace.0.file" is not null
And the event "threads.0.stacktrace.0.lineNumber" is not null
-Scenario: Signal raised with overwritten config
- When I run "CXXSignalSmokeScenario" and relaunch the app
+ Scenario: Signal raised with overwritten config
+ When I run "CXXSignalSmokeScenario" and relaunch the crashed app
And I configure Bugsnag for "CXXSignalSmokeScenario"
And I wait to receive an error
Then the error is valid for the error reporting API version "4.0" for the "Android Bugsnag Notifier" notifier
@@ -181,9 +184,9 @@ Scenario: Signal raised with overwritten config
And the event "metaData.fruit.ripe" is true
And the event "metaData.fruit.counters" equals 47
-@debug-safe
-Scenario: C++ exception thrown with overwritten config
- When I run "CXXExceptionSmokeScenario" and relaunch the app
+ @debug-safe
+ Scenario: C++ exception thrown with overwritten config
+ When I run "CXXExceptionSmokeScenario" and relaunch the crashed app
And I configure Bugsnag for "CXXExceptionSmokeScenario"
And I wait to receive an error
Then the error is valid for the error reporting API version "4.0" for the "Android Bugsnag Notifier" notifier
@@ -203,10 +206,10 @@ Scenario: C++ exception thrown with overwritten config
And the error payload field "events.0.exceptions.0.stacktrace" is a non-empty array
And the event stacktrace identifies the program counter
And the first significant stack frames match:
- | magicstacks::top() | CXXExceptionSmokeScenario.cpp | 13 |
- | magicstacks::middle() | CXXExceptionSmokeScenario.cpp | 16 |
- | magicstacks::start() | CXXExceptionSmokeScenario.cpp | 18 |
- | Java_com_bugsnag_android_mazerunner_scenarios_CXXExceptionSmokeScenario_crash | CXXExceptionSmokeScenario.cpp | 25 |
+ | magicstacks::top() | CXXExceptionSmokeScenario.cpp | 13 |
+ | magicstacks::middle() | CXXExceptionSmokeScenario.cpp | 16 |
+ | magicstacks::start() | CXXExceptionSmokeScenario.cpp | 18 |
+ | Java_com_bugsnag_android_mazerunner_scenarios_CXXExceptionSmokeScenario_crash | CXXExceptionSmokeScenario.cpp | 25 |
# App data
And the event binary arch field is valid
@@ -258,8 +261,8 @@ Scenario: C++ exception thrown with overwritten config
# Breadcrumbs
And the event has a "manual" breadcrumb named "CXXExceptionSmokeScenario"
-@skip_android_8_1
-Scenario: ANR detection
+ @skip_android_8_1
+ Scenario: ANR detection
When I run "JvmAnrLoopScenario"
And I wait for 2 seconds
And I tap the screen 3 times
diff --git a/features/steps/android_steps.rb b/features/steps/android_steps.rb
index ae95d9a8ed..eb90755e3b 100644
--- a/features/steps/android_steps.rb
+++ b/features/steps/android_steps.rb
@@ -1,3 +1,7 @@
+When('I clear all persistent data') do
+ step 'I click the element "clear_persistent_data"'
+end
+
# Waits 5s for an element to be present. If it isn't assume a system error dialog is
# blocking its view and dismiss it before trying once more.
#
@@ -27,7 +31,7 @@
}
end
-When("I run {string} and relaunch the app") do |event_type|
+When("I run {string} and relaunch the crashed app") do |event_type|
steps %Q{
When I run "#{event_type}"
And I relaunch the app after a crash
diff --git a/gradle.properties b/gradle.properties
index 1e1329ff98..863565b984 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -11,7 +11,7 @@ org.gradle.jvmargs=-Xmx4096m
# This option should only be used with decoupled projects. More details, visit
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
org.gradle.parallel=true
-VERSION_NAME=5.21.0
+VERSION_NAME=5.22.0
GROUP=com.bugsnag
POM_SCM_URL=https://github.com/bugsnag/bugsnag-android
POM_SCM_CONNECTION=scm:git@github.com:bugsnag/bugsnag-android.git