From 593c29e04e231520c5a9a8807386f949edff1aa0 Mon Sep 17 00:00:00 2001 From: Sebastian Kaspari Date: Wed, 22 Apr 2020 12:27:16 +0200 Subject: [PATCH] Issue #1705: Allow CrashReporterService to return a unique identifier for reported crashes. --- .../lib/crash/service/CrashReporterService.kt | 15 +- .../service/GleanCrashReporterService.kt | 9 +- .../crash/service/MozillaSocorroService.kt | 70 +- .../lib/crash/service/SentryService.kt | 22 +- .../components/lib/crash/CrashReporterTest.kt | 36 +- .../lib/crash/handler/ExceptionHandlerTest.kt | 9 +- .../service/MozillaSocorroServiceTest.kt | 814 ++++++++++-------- .../service/SendCrashReportServiceTest.kt | 9 +- .../service/SendCrashTelemetryServiceTest.kt | 9 +- .../lib/crash/service/SentryServiceTest.kt | 8 +- .../mozilla/samples/crash/CrashApplication.kt | 9 +- 11 files changed, 608 insertions(+), 402 deletions(-) diff --git a/components/lib/crash/src/main/java/mozilla/components/lib/crash/service/CrashReporterService.kt b/components/lib/crash/src/main/java/mozilla/components/lib/crash/service/CrashReporterService.kt index 95cff5bc757..5845dd8a95f 100644 --- a/components/lib/crash/src/main/java/mozilla/components/lib/crash/service/CrashReporterService.kt +++ b/components/lib/crash/src/main/java/mozilla/components/lib/crash/service/CrashReporterService.kt @@ -14,16 +14,25 @@ internal const val INFO_PREFIX = "[INFO]" interface CrashReporterService { /** * Submits a crash report for this [Crash.UncaughtExceptionCrash]. + * + * @return Unique identifier that can be used by/with this crash reporter service to find this + * crash - or null if no identifier can be provided. */ - fun report(crash: Crash.UncaughtExceptionCrash) + fun report(crash: Crash.UncaughtExceptionCrash): String? /** * Submits a crash report for this [Crash.NativeCodeCrash]. + * + * @return Unique identifier that can be used by/with this crash reporter service to find this + * crash - or null if no identifier can be provided. */ - fun report(crash: Crash.NativeCodeCrash) + fun report(crash: Crash.NativeCodeCrash): String? /** * Submits a caught exception report for this [Throwable]. + * + * @return Unique identifier that can be used by/with this crash reporter service to find this + * crash - or null if no identifier can be provided. */ - fun report(throwable: Throwable) + fun report(throwable: Throwable): String? } diff --git a/components/lib/crash/src/main/java/mozilla/components/lib/crash/service/GleanCrashReporterService.kt b/components/lib/crash/src/main/java/mozilla/components/lib/crash/service/GleanCrashReporterService.kt index 94a9584ff6a..9411ba838bd 100644 --- a/components/lib/crash/src/main/java/mozilla/components/lib/crash/service/GleanCrashReporterService.kt +++ b/components/lib/crash/src/main/java/mozilla/components/lib/crash/service/GleanCrashReporterService.kt @@ -173,19 +173,22 @@ class GleanCrashReporterService( } } - override fun report(crash: Crash.UncaughtExceptionCrash) { + override fun report(crash: Crash.UncaughtExceptionCrash): String? { reportCrash(UNCAUGHT_EXCEPTION_KEY) + return null } - override fun report(crash: Crash.NativeCodeCrash) { + override fun report(crash: Crash.NativeCodeCrash): String? { if (crash.isFatal) { reportCrash(FATAL_NATIVE_CODE_CRASH_KEY) } else { reportCrash(NONFATAL_NATIVE_CODE_CRASH_KEY) } + return null } - override fun report(throwable: Throwable) { + override fun report(throwable: Throwable): String? { reportCrash(CAUGHT_EXCEPTION_KEY) + return null } } diff --git a/components/lib/crash/src/main/java/mozilla/components/lib/crash/service/MozillaSocorroService.kt b/components/lib/crash/src/main/java/mozilla/components/lib/crash/service/MozillaSocorroService.kt index 0ced823026d..4b6a6f363bf 100644 --- a/components/lib/crash/src/main/java/mozilla/components/lib/crash/service/MozillaSocorroService.kt +++ b/components/lib/crash/src/main/java/mozilla/components/lib/crash/service/MozillaSocorroService.kt @@ -9,7 +9,6 @@ import android.content.Context import android.content.pm.PackageManager import android.os.Build import androidx.annotation.VisibleForTesting -import mozilla.components.Build as AcBuild import mozilla.components.lib.crash.Crash import mozilla.components.support.base.log.logger.Logger import org.json.JSONException @@ -30,8 +29,8 @@ import java.net.URL import java.nio.channels.Channels import java.util.concurrent.TimeUnit import java.util.zip.GZIPOutputStream -import kotlin.collections.HashMap import kotlin.random.Random +import mozilla.components.Build as AcBuild /* This ID is used for all Mozilla products. Setting as default if no ID is passed in */ private const val MOZILLA_PRODUCT_ID = "{eeb82917-e434-4870-8148-5c03d4caa81b}" @@ -43,6 +42,9 @@ internal const val FATAL_NATIVE_CRASH_TYPE = "fatal native crash" internal const val NON_FATAL_NATIVE_CRASH_TYPE = "non-fatal native crash" internal const val DEFAULT_VERSION_NAME = "N/A" + +private const val KEY_CRASH_ID = "CrashID" + /** * A [CrashReporterService] implementation uploading crash reports to crash-stats.mozilla.com. * @@ -91,16 +93,34 @@ class MozillaSocorroService( } } - override fun report(crash: Crash.UncaughtExceptionCrash) { - sendReport(crash.throwable, null, null, isNativeCodeCrash = false, isFatalCrash = true) + override fun report(crash: Crash.UncaughtExceptionCrash): String? { + return sendReport( + crash.throwable, + miniDumpFilePath = null, + extrasFilePath = null, + isNativeCodeCrash = false, + isFatalCrash = true + ) } - override fun report(crash: Crash.NativeCodeCrash) { - sendReport(null, crash.minidumpPath, crash.extrasPath, isNativeCodeCrash = true, isFatalCrash = crash.isFatal) + override fun report(crash: Crash.NativeCodeCrash): String? { + return sendReport( + throwable = null, + miniDumpFilePath = crash.minidumpPath, + extrasFilePath = crash.extrasPath, + isNativeCodeCrash = true, + isFatalCrash = crash.isFatal + ) } - override fun report(throwable: Throwable) { - sendReport(throwable, null, null, isNativeCodeCrash = false, isFatalCrash = false) + override fun report(throwable: Throwable): String? { + return sendReport( + throwable, + miniDumpFilePath = null, + extrasFilePath = null, + isNativeCodeCrash = false, + isFatalCrash = false + ) } @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE) @@ -110,7 +130,7 @@ class MozillaSocorroService( extrasFilePath: String?, isNativeCodeCrash: Boolean, isFatalCrash: Boolean - ) { + ): String? { val url = URL(serverUrl) val boundary = generateBoundary() var conn: HttpURLConnection? = null @@ -125,23 +145,41 @@ class MozillaSocorroService( sendCrashData(conn.outputStream, boundary, throwable, miniDumpFilePath, extrasFilePath, isNativeCodeCrash, isFatalCrash) - BufferedReader(InputStreamReader(conn.inputStream)).use { - val response = StringBuffer() - var inputLine = it.readLine() - while (inputLine != null) { - response.append(inputLine) - inputLine = it.readLine() + BufferedReader(InputStreamReader(conn.inputStream)).use { reader -> + val map = parseResponse(reader) + + val id = map?.get(KEY_CRASH_ID) + if (id != null) { + Logger.info("Crash reported to Socorro: $id") + } else { + Logger.info("Server rejected crash report") } - Logger.info("Crash reported to Socorro: $response") + return id } } catch (e: IOException) { Logger.error("failed to send report to Socorro", e) + return null } finally { conn?.disconnect() } } + private fun parseResponse(reader: BufferedReader): Map? { + val map = mutableMapOf() + + reader.readLines().forEach { line -> + val position = line.indexOf("=") + if (position != -1) { + val key = line.substring(0, position) + val value = unescape(line.substring(position + 1)) + map[key] = value + } + } + + return map + } + @Suppress("LongParameterList", "LongMethod") private fun sendCrashData( os: OutputStream, diff --git a/components/lib/crash/src/main/java/mozilla/components/lib/crash/service/SentryService.kt b/components/lib/crash/src/main/java/mozilla/components/lib/crash/service/SentryService.kt index 71a8153e809..6d2da14ba2e 100644 --- a/components/lib/crash/src/main/java/mozilla/components/lib/crash/service/SentryService.kt +++ b/components/lib/crash/src/main/java/mozilla/components/lib/crash/service/SentryService.kt @@ -64,7 +64,7 @@ class SentryService( } } - override fun report(crash: Crash.UncaughtExceptionCrash) { + override fun report(crash: Crash.UncaughtExceptionCrash): String? { crash.breadcrumbs.forEach { client.context.recordBreadcrumb(it.toSentryBreadcrumb()) } @@ -73,22 +73,36 @@ class SentryService( .withLevel(Event.Level.FATAL) .withSentryInterface(ExceptionInterface(crash.throwable)) client.sendEvent(eventBuilder) + + return eventBuilder.event.id.toString() } - override fun report(crash: Crash.NativeCodeCrash) { + override fun report(crash: Crash.NativeCodeCrash): String? { if (sendEventForNativeCrashes) { crash.breadcrumbs.forEach { client.context.recordBreadcrumb(it.toSentryBreadcrumb()) } - client.sendMessage(createMessage(crash)) + + val eventBuilder = EventBuilder() + .withMessage(createMessage(crash)) + .withLevel(Event.Level.INFO) + + client.sendEvent(eventBuilder) + + return eventBuilder.event.id.toString() } + + return null } - override fun report(throwable: Throwable) { + override fun report(throwable: Throwable): String? { val eventBuilder = EventBuilder().withMessage(createMessage(throwable)) .withLevel(Event.Level.INFO) .withSentryInterface(ExceptionInterface(throwable)) + client.sendEvent(eventBuilder) + + return eventBuilder.event.id.toString() } @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE) diff --git a/components/lib/crash/src/test/java/mozilla/components/lib/crash/CrashReporterTest.kt b/components/lib/crash/src/test/java/mozilla/components/lib/crash/CrashReporterTest.kt index e9b66f3b39d..8bff58a8efd 100644 --- a/components/lib/crash/src/test/java/mozilla/components/lib/crash/CrashReporterTest.kt +++ b/components/lib/crash/src/test/java/mozilla/components/lib/crash/CrashReporterTest.kt @@ -303,15 +303,14 @@ class CrashReporterTest { var exceptionCrash = false val service = object : CrashReporterService { - override fun report(crash: Crash.UncaughtExceptionCrash) { + override fun report(crash: Crash.UncaughtExceptionCrash): String? { exceptionCrash = true + return null } - override fun report(crash: Crash.NativeCodeCrash) { - } + override fun report(crash: Crash.NativeCodeCrash): String? = null - override fun report(throwable: Throwable) { - } + override fun report(throwable: Throwable): String? = null } val reporter = spy(CrashReporter( @@ -330,15 +329,14 @@ class CrashReporterTest { var nativeCrash = false val service = object : CrashReporterService { - override fun report(crash: Crash.UncaughtExceptionCrash) { - } + override fun report(crash: Crash.UncaughtExceptionCrash): String? = null - override fun report(crash: Crash.NativeCodeCrash) { + override fun report(crash: Crash.NativeCodeCrash): String? { nativeCrash = true + return null } - override fun report(throwable: Throwable) { - } + override fun report(throwable: Throwable): String? = null } val reporter = spy(CrashReporter( @@ -357,14 +355,13 @@ class CrashReporterTest { var exceptionCrash = false val service = object : CrashReporterService { - override fun report(crash: Crash.UncaughtExceptionCrash) { - } + override fun report(crash: Crash.UncaughtExceptionCrash): String? = null - override fun report(crash: Crash.NativeCodeCrash) { - } + override fun report(crash: Crash.NativeCodeCrash): String? = null - override fun report(throwable: Throwable) { + override fun report(throwable: Throwable): String? { exceptionCrash = true + return null } } @@ -385,15 +382,14 @@ class CrashReporterTest { var nativeCrash = false val telemetryService = object : CrashReporterService { - override fun report(crash: Crash.UncaughtExceptionCrash) { - } + override fun report(crash: Crash.UncaughtExceptionCrash): String? = null - override fun report(crash: Crash.NativeCodeCrash) { + override fun report(crash: Crash.NativeCodeCrash): String? { nativeCrash = true + return null } - override fun report(throwable: Throwable) { - } + override fun report(throwable: Throwable): String? = null } val reporter = spy(CrashReporter( diff --git a/components/lib/crash/src/test/java/mozilla/components/lib/crash/handler/ExceptionHandlerTest.kt b/components/lib/crash/src/test/java/mozilla/components/lib/crash/handler/ExceptionHandlerTest.kt index c2efecc9a71..da22e21042b 100644 --- a/components/lib/crash/src/test/java/mozilla/components/lib/crash/handler/ExceptionHandlerTest.kt +++ b/components/lib/crash/src/test/java/mozilla/components/lib/crash/handler/ExceptionHandlerTest.kt @@ -60,14 +60,11 @@ class ExceptionHandlerTest { val crashReporter = CrashReporter( shouldPrompt = CrashReporter.Prompt.NEVER, services = listOf(object : CrashReporterService { - override fun report(crash: Crash.UncaughtExceptionCrash) { - } + override fun report(crash: Crash.UncaughtExceptionCrash): String? = null - override fun report(crash: Crash.NativeCodeCrash) { - } + override fun report(crash: Crash.NativeCodeCrash): String? = null - override fun report(throwable: Throwable) { - } + override fun report(throwable: Throwable): String? = null }), scope = scope ).install(testContext) diff --git a/components/lib/crash/src/test/java/mozilla/components/lib/crash/service/MozillaSocorroServiceTest.kt b/components/lib/crash/src/test/java/mozilla/components/lib/crash/service/MozillaSocorroServiceTest.kt index d6021cdff9a..2231d4c7617 100644 --- a/components/lib/crash/src/test/java/mozilla/components/lib/crash/service/MozillaSocorroServiceTest.kt +++ b/components/lib/crash/src/test/java/mozilla/components/lib/crash/service/MozillaSocorroServiceTest.kt @@ -17,7 +17,7 @@ import org.junit.Assert.assertFalse import org.junit.Test import org.junit.runner.RunWith import org.mockito.ArgumentMatchers.anyBoolean -import org.mockito.Mockito.doNothing +import org.mockito.Mockito.doReturn import org.mockito.Mockito.spy import org.mockito.Mockito.verify import java.io.BufferedReader @@ -35,7 +35,7 @@ class MozillaSocorroServiceTest { testContext, "Test App" )) - doNothing().`when`(service).sendReport(any(), any(), any(), anyBoolean(), anyBoolean()) + doReturn("").`when`(service).sendReport(any(), any(), any(), anyBoolean(), anyBoolean()) val crash = Crash.NativeCodeCrash("", true, "", false, arrayListOf()) service.report(crash) @@ -50,7 +50,7 @@ class MozillaSocorroServiceTest { testContext, "Test App" )) - doNothing().`when`(service).sendReport(any(), any(), any(), anyBoolean(), anyBoolean()) + doReturn("").`when`(service).sendReport(any(), any(), any(), anyBoolean(), anyBoolean()) val crash = Crash.UncaughtExceptionCrash(RuntimeException("Test"), arrayListOf()) service.report(crash) @@ -65,7 +65,7 @@ class MozillaSocorroServiceTest { testContext, "Test App" )) - doNothing().`when`(service).sendReport(any(), any(), any(), anyBoolean(), anyBoolean()) + doReturn("").`when`(service).sendReport(any(), any(), any(), anyBoolean(), anyBoolean()) val throwable = RuntimeException("Test") service.report(throwable) @@ -77,321 +77,409 @@ class MozillaSocorroServiceTest { @Test fun `MozillaSocorroService native fatal crash request is correct`() { val mockWebServer = MockWebServer() - mockWebServer.enqueue(MockResponse().setResponseCode(200) - .setBody("CrashID=bp-924121d3-4de3-4b32-ab12-026fc0190928")) - mockWebServer.start() - val serverUrl = mockWebServer.url("/") - val service = spy(MozillaSocorroService( - testContext, - "Test App", - appId = "{aa3c5121-dab2-40e2-81ca-7ea25febc110}", - serverUrl = serverUrl.toString() - )) - - val crash = Crash.NativeCodeCrash( - "dump.path", - true, - "extras.path", - isFatal = true, - breadcrumbs = arrayListOf()) - service.report(crash) - - val fileInputStream = ByteArrayInputStream(mockWebServer.takeRequest().body.inputStream().readBytes()) - val inputStream = GZIPInputStream(fileInputStream) - val reader = InputStreamReader(inputStream) - val bufferedReader = BufferedReader(reader) - val request = bufferedReader.readText() - - assert(request.contains("name=Android_ProcessName\r\n\r\nmozilla.components.lib.crash.test")) - assert(request.contains("name=ProductID\r\n\r\n{aa3c5121-dab2-40e2-81ca-7ea25febc110}")) - assert(request.contains("name=Vendor\r\n\r\nMozilla")) - assert(request.contains("name=ReleaseChannel\r\n\r\nnightly")) - assert(request.contains("name=Android_PackageName\r\n\r\nmozilla.components.lib.crash.test")) - assert(request.contains("name=Android_Device\r\n\r\nrobolectric")) - assert(request.contains("name=CrashType\r\n\r\n$FATAL_NATIVE_CRASH_TYPE")) - verify(service).report(crash) - verify(service).sendReport(null, "dump.path", "extras.path", true, true) + try { + mockWebServer.enqueue( + MockResponse().setResponseCode(200) + .setBody("CrashID=bp-924121d3-4de3-4b32-ab12-026fc0190928") + ) + mockWebServer.start() + val serverUrl = mockWebServer.url("/") + val service = spy( + MozillaSocorroService( + testContext, + "Test App", + appId = "{aa3c5121-dab2-40e2-81ca-7ea25febc110}", + serverUrl = serverUrl.toString() + ) + ) + + val crash = Crash.NativeCodeCrash( + "dump.path", + true, + "extras.path", + isFatal = true, + breadcrumbs = arrayListOf() + ) + service.report(crash) + + val fileInputStream = + ByteArrayInputStream(mockWebServer.takeRequest().body.inputStream().readBytes()) + val inputStream = GZIPInputStream(fileInputStream) + val reader = InputStreamReader(inputStream) + val bufferedReader = BufferedReader(reader) + val request = bufferedReader.readText() + + assert(request.contains("name=Android_ProcessName\r\n\r\nmozilla.components.lib.crash.test")) + assert(request.contains("name=ProductID\r\n\r\n{aa3c5121-dab2-40e2-81ca-7ea25febc110}")) + assert(request.contains("name=Vendor\r\n\r\nMozilla")) + assert(request.contains("name=ReleaseChannel\r\n\r\nnightly")) + assert(request.contains("name=Android_PackageName\r\n\r\nmozilla.components.lib.crash.test")) + assert(request.contains("name=Android_Device\r\n\r\nrobolectric")) + assert(request.contains("name=CrashType\r\n\r\n$FATAL_NATIVE_CRASH_TYPE")) + + verify(service).report(crash) + verify(service).sendReport(null, "dump.path", "extras.path", true, true) + } finally { + mockWebServer.shutdown() + } } @Test fun `MozillaSocorroService native non-fatal crash request is correct`() { val mockWebServer = MockWebServer() - mockWebServer.enqueue(MockResponse().setResponseCode(200) - .setBody("CrashID=bp-924121d3-4de3-4b32-ab12-026fc0190928")) - mockWebServer.start() - val serverUrl = mockWebServer.url("/") - val service = spy(MozillaSocorroService( - testContext, - "Test App", - appId = "{aa3c5121-dab2-40e2-81ca-7ea25febc110}", - serverUrl = serverUrl.toString() - )) - - val crash = Crash.NativeCodeCrash( - "dump.path", - true, - "extras.path", - isFatal = false, - breadcrumbs = arrayListOf()) - service.report(crash) - - val fileInputStream = ByteArrayInputStream(mockWebServer.takeRequest().body.inputStream().readBytes()) - val inputStream = GZIPInputStream(fileInputStream) - val reader = InputStreamReader(inputStream) - val bufferedReader = BufferedReader(reader) - val request = bufferedReader.readText() - assert(request.contains("name=Android_ProcessName\r\n\r\nmozilla.components.lib.crash.test")) - assert(request.contains("name=ProductID\r\n\r\n{aa3c5121-dab2-40e2-81ca-7ea25febc110}")) - assert(request.contains("name=Vendor\r\n\r\nMozilla")) - assert(request.contains("name=ReleaseChannel\r\n\r\nnightly")) - assert(request.contains("name=Android_PackageName\r\n\r\nmozilla.components.lib.crash.test")) - assert(request.contains("name=Android_Device\r\n\r\nrobolectric")) - assert(request.contains("name=CrashType\r\n\r\n$NON_FATAL_NATIVE_CRASH_TYPE")) - - verify(service).report(crash) - verify(service).sendReport(null, "dump.path", "extras.path", true, false) + try { + mockWebServer.enqueue( + MockResponse().setResponseCode(200) + .setBody("CrashID=bp-924121d3-4de3-4b32-ab12-026fc0190928") + ) + mockWebServer.start() + val serverUrl = mockWebServer.url("/") + val service = spy( + MozillaSocorroService( + testContext, + "Test App", + appId = "{aa3c5121-dab2-40e2-81ca-7ea25febc110}", + serverUrl = serverUrl.toString() + ) + ) + + val crash = Crash.NativeCodeCrash( + "dump.path", + true, + "extras.path", + isFatal = false, + breadcrumbs = arrayListOf() + ) + service.report(crash) + + val fileInputStream = + ByteArrayInputStream(mockWebServer.takeRequest().body.inputStream().readBytes()) + val inputStream = GZIPInputStream(fileInputStream) + val reader = InputStreamReader(inputStream) + val bufferedReader = BufferedReader(reader) + val request = bufferedReader.readText() + + assert(request.contains("name=Android_ProcessName\r\n\r\nmozilla.components.lib.crash.test")) + assert(request.contains("name=ProductID\r\n\r\n{aa3c5121-dab2-40e2-81ca-7ea25febc110}")) + assert(request.contains("name=Vendor\r\n\r\nMozilla")) + assert(request.contains("name=ReleaseChannel\r\n\r\nnightly")) + assert(request.contains("name=Android_PackageName\r\n\r\nmozilla.components.lib.crash.test")) + assert(request.contains("name=Android_Device\r\n\r\nrobolectric")) + assert(request.contains("name=CrashType\r\n\r\n$NON_FATAL_NATIVE_CRASH_TYPE")) + + verify(service).report(crash) + verify(service).sendReport(null, "dump.path", "extras.path", true, false) + } finally { + mockWebServer.shutdown() + } } @Test fun `MozillaSocorroService uncaught exception request is correct`() { val mockWebServer = MockWebServer() - mockWebServer.enqueue(MockResponse().setResponseCode(200) - .setBody("CrashID=bp-924121d3-4de3-4b32-ab12-026fc0190928")) - mockWebServer.start() - val serverUrl = mockWebServer.url("/") - val service = spy(MozillaSocorroService( - testContext, - "Test App", - appId = "{aa3c5121-dab2-40e2-81ca-7ea25febc110}", - serverUrl = serverUrl.toString() - )) - - val crash = Crash.UncaughtExceptionCrash(RuntimeException("Test"), arrayListOf()) - service.report(crash) - val fileInputStream = ByteArrayInputStream(mockWebServer.takeRequest().body.inputStream().readBytes()) - val inputStream = GZIPInputStream(fileInputStream) - val reader = InputStreamReader(inputStream) - val bufferedReader = BufferedReader(reader) - val request = bufferedReader.readText() - - assert(request.contains("name=JavaStackTrace\r\n\r\njava.lang.RuntimeException: Test")) - assert(request.contains("name=Android_ProcessName\r\n\r\nmozilla.components.lib.crash.test")) - assert(request.contains("name=ProductID\r\n\r\n{aa3c5121-dab2-40e2-81ca-7ea25febc110}")) - assert(request.contains("name=Vendor\r\n\r\nMozilla")) - assert(request.contains("name=ReleaseChannel\r\n\r\nnightly")) - assert(request.contains("name=Android_PackageName\r\n\r\nmozilla.components.lib.crash.test")) - assert(request.contains("name=Android_Device\r\n\r\nrobolectric")) - assert(request.contains("name=CrashType\r\n\r\n$UNCAUGHT_EXCEPTION_TYPE")) - - verify(service).report(crash) - verify(service).sendReport(crash.throwable, null, null, false, true) + try { + mockWebServer.enqueue( + MockResponse().setResponseCode(200) + .setBody("CrashID=bp-924121d3-4de3-4b32-ab12-026fc0190928") + ) + mockWebServer.start() + val serverUrl = mockWebServer.url("/") + val service = spy( + MozillaSocorroService( + testContext, + "Test App", + appId = "{aa3c5121-dab2-40e2-81ca-7ea25febc110}", + serverUrl = serverUrl.toString() + ) + ) + + val crash = Crash.UncaughtExceptionCrash(RuntimeException("Test"), arrayListOf()) + service.report(crash) + + val fileInputStream = + ByteArrayInputStream(mockWebServer.takeRequest().body.inputStream().readBytes()) + val inputStream = GZIPInputStream(fileInputStream) + val reader = InputStreamReader(inputStream) + val bufferedReader = BufferedReader(reader) + val request = bufferedReader.readText() + + assert(request.contains("name=JavaStackTrace\r\n\r\njava.lang.RuntimeException: Test")) + assert(request.contains("name=Android_ProcessName\r\n\r\nmozilla.components.lib.crash.test")) + assert(request.contains("name=ProductID\r\n\r\n{aa3c5121-dab2-40e2-81ca-7ea25febc110}")) + assert(request.contains("name=Vendor\r\n\r\nMozilla")) + assert(request.contains("name=ReleaseChannel\r\n\r\nnightly")) + assert(request.contains("name=Android_PackageName\r\n\r\nmozilla.components.lib.crash.test")) + assert(request.contains("name=Android_Device\r\n\r\nrobolectric")) + assert(request.contains("name=CrashType\r\n\r\n$UNCAUGHT_EXCEPTION_TYPE")) + + verify(service).report(crash) + verify(service).sendReport(crash.throwable, null, null, false, true) + } finally { + mockWebServer.shutdown() + } } @Test fun `MozillaSocorroService caught exception request is correct`() { val mockWebServer = MockWebServer() - mockWebServer.enqueue(MockResponse().setResponseCode(200) - .setBody("CrashID=bp-924121d3-4de3-4b32-ab12-026fc0190928")) - mockWebServer.start() - val serverUrl = mockWebServer.url("/") - val service = spy(MozillaSocorroService( - testContext, - "Test App", - serverUrl = serverUrl.toString() - )) - val throwable = RuntimeException("Test") - service.report(throwable) - - val fileInputStream = ByteArrayInputStream(mockWebServer.takeRequest().body.inputStream().readBytes()) - val inputStream = GZIPInputStream(fileInputStream) - val reader = InputStreamReader(inputStream) - val bufferedReader = BufferedReader(reader) - val request = bufferedReader.readText() - - assert(request.contains("name=JavaStackTrace\r\n\r\n$INFO_PREFIX java.lang.RuntimeException: Test")) - assert(request.contains("name=Android_ProcessName\r\n\r\nmozilla.components.lib.crash.test")) - assert(request.contains("name=ProductID\r\n\r\n{eeb82917-e434-4870-8148-5c03d4caa81b}")) - assert(request.contains("name=Vendor\r\n\r\nMozilla")) - assert(request.contains("name=ReleaseChannel\r\n\r\nnightly")) - assert(request.contains("name=Android_PackageName\r\n\r\nmozilla.components.lib.crash.test")) - assert(request.contains("name=Android_Device\r\n\r\nrobolectric")) - assert(request.contains("name=CrashType\r\n\r\n$CAUGHT_EXCEPTION_TYPE")) - - verify(service).report(throwable) - verify(service).sendReport(throwable, null, null, false, false) + try { + mockWebServer.enqueue( + MockResponse().setResponseCode(200) + .setBody("CrashID=bp-924121d3-4de3-4b32-ab12-026fc0190928") + ) + mockWebServer.start() + val serverUrl = mockWebServer.url("/") + val service = spy( + MozillaSocorroService( + testContext, + "Test App", + serverUrl = serverUrl.toString() + ) + ) + + val throwable = RuntimeException("Test") + service.report(throwable) + + val fileInputStream = + ByteArrayInputStream(mockWebServer.takeRequest().body.inputStream().readBytes()) + val inputStream = GZIPInputStream(fileInputStream) + val reader = InputStreamReader(inputStream) + val bufferedReader = BufferedReader(reader) + val request = bufferedReader.readText() + + assert(request.contains("name=JavaStackTrace\r\n\r\n$INFO_PREFIX java.lang.RuntimeException: Test")) + assert(request.contains("name=Android_ProcessName\r\n\r\nmozilla.components.lib.crash.test")) + assert(request.contains("name=ProductID\r\n\r\n{eeb82917-e434-4870-8148-5c03d4caa81b}")) + assert(request.contains("name=Vendor\r\n\r\nMozilla")) + assert(request.contains("name=ReleaseChannel\r\n\r\nnightly")) + assert(request.contains("name=Android_PackageName\r\n\r\nmozilla.components.lib.crash.test")) + assert(request.contains("name=Android_Device\r\n\r\nrobolectric")) + assert(request.contains("name=CrashType\r\n\r\n$CAUGHT_EXCEPTION_TYPE")) + + verify(service).report(throwable) + verify(service).sendReport(throwable, null, null, false, false) + } finally { + mockWebServer.shutdown() + } } @Test fun `MozillaSocorroService caught exception request app details are correct`() { val mockWebServer = MockWebServer() - mockWebServer.enqueue(MockResponse().setResponseCode(200) - .setBody("CrashID=bp-924121d3-4de3-4b32-ab12-026fc0190928")) - mockWebServer.start() - val serverUrl = mockWebServer.url("/") - val service = spy(MozillaSocorroService( - testContext, - "Test App", - "{1234-1234-1234}", - "0.1", - "1.0", - "Mozilla Test", - serverUrl = serverUrl.toString(), - versionName = "1.0.0" - )) - val throwable = RuntimeException("Test") - service.report(throwable) - - val fileInputStream = ByteArrayInputStream(mockWebServer.takeRequest().body.inputStream().readBytes()) - val inputStream = GZIPInputStream(fileInputStream) - val reader = InputStreamReader(inputStream) - val bufferedReader = BufferedReader(reader) - val request = bufferedReader.readText() - - assert(request.contains("name=JavaStackTrace\r\n\r\n$INFO_PREFIX java.lang.RuntimeException: Test")) - assert(request.contains("name=Android_ProcessName\r\n\r\nmozilla.components.lib.crash.test")) - assert(request.contains("name=ProductID\r\n\r\n{1234-1234-1234}")) - assert(request.contains("name=Version\r\n\r\n1.0.0")) - assert(request.contains("name=GeckoViewVersion\r\n\r\n0.1")) - assert(request.contains("name=AndroidComponentVersion\r\n\r\n${Build.version}")) - assert(request.contains("name=GleanVersion\r\n\r\n${Build.gleanSdkVersion}")) - assert(request.contains("name=ApplicationServicesVersion\r\n\r\n${Build.applicationServicesVersion}")) - assert(request.contains("name=BuildID\r\n\r\n1.0")) - assert(request.contains("name=Vendor\r\n\r\nMozilla Test")) - assert(request.contains("name=ReleaseChannel\r\n\r\nnightly")) - assert(request.contains("name=Android_PackageName\r\n\r\nmozilla.components.lib.crash.test")) - assert(request.contains("name=Android_Device\r\n\r\nrobolectric")) - assert(request.contains("name=CrashType\r\n\r\n$CAUGHT_EXCEPTION_TYPE")) - - verify(service).report(throwable) - verify(service).sendReport(throwable, null, null, false, false) + try { + mockWebServer.enqueue( + MockResponse().setResponseCode(200) + .setBody("CrashID=bp-924121d3-4de3-4b32-ab12-026fc0190928") + ) + mockWebServer.start() + val serverUrl = mockWebServer.url("/") + val service = spy( + MozillaSocorroService( + testContext, + "Test App", + "{1234-1234-1234}", + "0.1", + "1.0", + "Mozilla Test", + serverUrl = serverUrl.toString(), + versionName = "1.0.0" + ) + ) + + val throwable = RuntimeException("Test") + service.report(throwable) + + val fileInputStream = + ByteArrayInputStream(mockWebServer.takeRequest().body.inputStream().readBytes()) + val inputStream = GZIPInputStream(fileInputStream) + val reader = InputStreamReader(inputStream) + val bufferedReader = BufferedReader(reader) + val request = bufferedReader.readText() + + assert(request.contains("name=JavaStackTrace\r\n\r\n$INFO_PREFIX java.lang.RuntimeException: Test")) + assert(request.contains("name=Android_ProcessName\r\n\r\nmozilla.components.lib.crash.test")) + assert(request.contains("name=ProductID\r\n\r\n{1234-1234-1234}")) + assert(request.contains("name=Version\r\n\r\n1.0.0")) + assert(request.contains("name=GeckoViewVersion\r\n\r\n0.1")) + assert(request.contains("name=AndroidComponentVersion\r\n\r\n${Build.version}")) + assert(request.contains("name=GleanVersion\r\n\r\n${Build.gleanSdkVersion}")) + assert(request.contains("name=ApplicationServicesVersion\r\n\r\n${Build.applicationServicesVersion}")) + assert(request.contains("name=BuildID\r\n\r\n1.0")) + assert(request.contains("name=Vendor\r\n\r\nMozilla Test")) + assert(request.contains("name=ReleaseChannel\r\n\r\nnightly")) + assert(request.contains("name=Android_PackageName\r\n\r\nmozilla.components.lib.crash.test")) + assert(request.contains("name=Android_Device\r\n\r\nrobolectric")) + assert(request.contains("name=CrashType\r\n\r\n$CAUGHT_EXCEPTION_TYPE")) + + verify(service).report(throwable) + verify(service).sendReport(throwable, null, null, false, false) + } finally { + mockWebServer.shutdown() + } } @Test fun `MozillaSocorroService caught exception request with no app version`() { val mockWebServer = MockWebServer() - mockWebServer.enqueue(MockResponse().setResponseCode(200) - .setBody("CrashID=bp-924121d3-4de3-4b32-ab12-026fc0190928")) - mockWebServer.start() - val serverUrl = mockWebServer.url("/") - val service = spy(MozillaSocorroService( - testContext, - "Test App", - "{1234-1234-1234}", - "0.1", - "1.0", - "Mozilla Test", - serverUrl = serverUrl.toString() - )) - - val throwable = RuntimeException("Test") - service.report(throwable) - val fileInputStream = ByteArrayInputStream(mockWebServer.takeRequest().body.inputStream().readBytes()) - val inputStream = GZIPInputStream(fileInputStream) - val reader = InputStreamReader(inputStream) - val bufferedReader = BufferedReader(reader) - val request = bufferedReader.readText() - - assert(request.contains("name=JavaStackTrace\r\n\r\n$INFO_PREFIX java.lang.RuntimeException: Test")) - assert(request.contains("name=Android_ProcessName\r\n\r\nmozilla.components.lib.crash.test")) - assert(request.contains("name=ProductID\r\n\r\n{1234-1234-1234}")) - assert(request.contains("name=Version\r\n\r\nN/A")) - - verify(service).report(throwable) - verify(service).sendReport(throwable, null, null, false, false) + try { + mockWebServer.enqueue( + MockResponse().setResponseCode(200) + .setBody("CrashID=bp-924121d3-4de3-4b32-ab12-026fc0190928") + ) + mockWebServer.start() + val serverUrl = mockWebServer.url("/") + val service = spy( + MozillaSocorroService( + testContext, + "Test App", + "{1234-1234-1234}", + "0.1", + "1.0", + "Mozilla Test", + serverUrl = serverUrl.toString() + ) + ) + + val throwable = RuntimeException("Test") + service.report(throwable) + + val fileInputStream = + ByteArrayInputStream(mockWebServer.takeRequest().body.inputStream().readBytes()) + val inputStream = GZIPInputStream(fileInputStream) + val reader = InputStreamReader(inputStream) + val bufferedReader = BufferedReader(reader) + val request = bufferedReader.readText() + + assert(request.contains("name=JavaStackTrace\r\n\r\n$INFO_PREFIX java.lang.RuntimeException: Test")) + assert(request.contains("name=Android_ProcessName\r\n\r\nmozilla.components.lib.crash.test")) + assert(request.contains("name=ProductID\r\n\r\n{1234-1234-1234}")) + assert(request.contains("name=Version\r\n\r\nN/A")) + + verify(service).report(throwable) + verify(service).sendReport(throwable, null, null, false, false) + } finally { + mockWebServer.shutdown() + } } @Test fun `MozillaSocorroService handles caught exception with no stacktrace correctly`() { val mockWebServer = MockWebServer() - mockWebServer.enqueue(MockResponse().setResponseCode(200) - .setBody("CrashID=bp-924121d3-4de3-4b32-ab12-026fc0190928")) - mockWebServer.start() - val serverUrl = mockWebServer.url("/") - val service = spy(MozillaSocorroService( - testContext, - "Test App", - "{1234-1234-1234}", - "0.1", - "1.0", - "Mozilla Test", - serverUrl = serverUrl.toString(), - versionName = "1.0.0" - )) - val throwable = RuntimeException("Test") - throwable.stackTrace = emptyArray() - service.report(throwable) - - val fileInputStream = ByteArrayInputStream(mockWebServer.takeRequest().body.inputStream().readBytes()) - val inputStream = GZIPInputStream(fileInputStream) - val reader = InputStreamReader(inputStream) - val bufferedReader = BufferedReader(reader) - val request = bufferedReader.readText() - - assertFalse(request.contains("name=JavaStackTrace")) - assert(request.contains("name=Android_ProcessName\r\n\r\nmozilla.components.lib.crash.test")) - assert(request.contains("name=ProductID\r\n\r\n{1234-1234-1234}")) - assert(request.contains("name=Version\r\n\r\n1.0.0")) - assert(request.contains("name=GeckoViewVersion\r\n\r\n0.1")) - assert(request.contains("name=AndroidComponentVersion\r\n\r\n${Build.version}")) - assert(request.contains("name=GleanVersion\r\n\r\n${Build.gleanSdkVersion}")) - assert(request.contains("name=ApplicationServicesVersion\r\n\r\n${Build.applicationServicesVersion}")) - assert(request.contains("name=BuildID\r\n\r\n1.0")) - assert(request.contains("name=Vendor\r\n\r\nMozilla Test")) - assert(request.contains("name=ReleaseChannel\r\n\r\nnightly")) - assert(request.contains("name=Android_PackageName\r\n\r\nmozilla.components.lib.crash.test")) - assert(request.contains("name=Android_Device\r\n\r\nrobolectric")) - assert(request.contains("name=CrashType\r\n\r\n$CAUGHT_EXCEPTION_TYPE")) - - verify(service).report(throwable) - verify(service).sendReport(throwable, null, null, false, false) + try { + mockWebServer.enqueue( + MockResponse().setResponseCode(200) + .setBody("CrashID=bp-924121d3-4de3-4b32-ab12-026fc0190928") + ) + mockWebServer.start() + val serverUrl = mockWebServer.url("/") + val service = spy( + MozillaSocorroService( + testContext, + "Test App", + "{1234-1234-1234}", + "0.1", + "1.0", + "Mozilla Test", + serverUrl = serverUrl.toString(), + versionName = "1.0.0" + ) + ) + + val throwable = RuntimeException("Test") + throwable.stackTrace = emptyArray() + service.report(throwable) + + val fileInputStream = + ByteArrayInputStream(mockWebServer.takeRequest().body.inputStream().readBytes()) + val inputStream = GZIPInputStream(fileInputStream) + val reader = InputStreamReader(inputStream) + val bufferedReader = BufferedReader(reader) + val request = bufferedReader.readText() + + assertFalse(request.contains("name=JavaStackTrace")) + assert(request.contains("name=Android_ProcessName\r\n\r\nmozilla.components.lib.crash.test")) + assert(request.contains("name=ProductID\r\n\r\n{1234-1234-1234}")) + assert(request.contains("name=Version\r\n\r\n1.0.0")) + assert(request.contains("name=GeckoViewVersion\r\n\r\n0.1")) + assert(request.contains("name=AndroidComponentVersion\r\n\r\n${Build.version}")) + assert(request.contains("name=GleanVersion\r\n\r\n${Build.gleanSdkVersion}")) + assert(request.contains("name=ApplicationServicesVersion\r\n\r\n${Build.applicationServicesVersion}")) + assert(request.contains("name=BuildID\r\n\r\n1.0")) + assert(request.contains("name=Vendor\r\n\r\nMozilla Test")) + assert(request.contains("name=ReleaseChannel\r\n\r\nnightly")) + assert(request.contains("name=Android_PackageName\r\n\r\nmozilla.components.lib.crash.test")) + assert(request.contains("name=Android_Device\r\n\r\nrobolectric")) + assert(request.contains("name=CrashType\r\n\r\n$CAUGHT_EXCEPTION_TYPE")) + + verify(service).report(throwable) + verify(service).sendReport(throwable, null, null, false, false) + } finally { + mockWebServer.shutdown() + } } @Test fun `MozillaSocorroService handles 200 response correctly`() { val mockWebServer = MockWebServer() - mockWebServer.enqueue(MockResponse().setResponseCode(200) - .setBody("CrashID=bp-924121d3-4de3-4b32-ab12-026fc0190928")) - mockWebServer.start() - val serverUrl = mockWebServer.url("/") - val service = spy(MozillaSocorroService( - testContext, - "Test App", - serverUrl = serverUrl.toString() - )) - val crash = Crash.UncaughtExceptionCrash(RuntimeException("Test"), arrayListOf()) - service.report(crash) - - mockWebServer.shutdown() - verify(service).report(crash) - verify(service).sendReport(crash.throwable, null, null, false, true) + try { + mockWebServer.enqueue( + MockResponse().setResponseCode(200) + .setBody("CrashID=bp-924121d3-4de3-4b32-ab12-026fc0190928") + ) + mockWebServer.start() + val serverUrl = mockWebServer.url("/") + val service = spy( + MozillaSocorroService( + testContext, + "Test App", + serverUrl = serverUrl.toString() + ) + ) + + val crash = Crash.UncaughtExceptionCrash(RuntimeException("Test"), arrayListOf()) + service.report(crash) + + mockWebServer.shutdown() + verify(service).report(crash) + verify(service).sendReport(crash.throwable, null, null, false, true) + } finally { + mockWebServer.shutdown() + } } @Test fun `MozillaSocorroService handles 404 response correctly`() { val mockWebServer = MockWebServer() - mockWebServer.enqueue(MockResponse().setResponseCode(404).setBody("error")) - mockWebServer.start() - val serverUrl = mockWebServer.url("/") - val service = spy(MozillaSocorroService( - testContext, - "Test App", - serverUrl = serverUrl.toString() - )) - - val crash = Crash.NativeCodeCrash(null, true, null, false, arrayListOf()) - service.report(crash) - mockWebServer.shutdown() - verify(service).report(crash) - verify(service).sendReport(null, crash.minidumpPath, crash.extrasPath, true, false) + try { + mockWebServer.enqueue(MockResponse().setResponseCode(404).setBody("error")) + mockWebServer.start() + val serverUrl = mockWebServer.url("/") + val service = spy( + MozillaSocorroService( + testContext, + "Test App", + serverUrl = serverUrl.toString() + ) + ) + + val crash = Crash.NativeCodeCrash(null, true, null, false, arrayListOf()) + service.report(crash) + mockWebServer.shutdown() + + verify(service).report(crash) + verify(service).sendReport(null, crash.minidumpPath, crash.extrasPath, true, false) + } finally { + mockWebServer.shutdown() + } } @Test @@ -508,82 +596,134 @@ class MozillaSocorroServiceTest { @Test fun `MozillaSocorroService reports specified parameter correctly`() { val mockWebServer = MockWebServer() - mockWebServer.enqueue(MockResponse().setResponseCode(200) - .setBody("CrashID=bp-924121d3-4de3-4b32-ab12-026fc0190928")) - mockWebServer.start() - val serverUrl = mockWebServer.url("/") - val service = spy(MozillaSocorroService( - applicationContext = testContext, - appName = "Test App", - appId = "{1234-1234-1234}", - version = "0.1", - buildId = "1.0", - vendor = "Mozilla Test", - serverUrl = serverUrl.toString(), - versionName = "0.0.1", - releaseChannel = "test channel" - )) - - val throwable = RuntimeException("Test") - throwable.stackTrace = emptyArray() - service.report(throwable) - val fileInputStream = ByteArrayInputStream(mockWebServer.takeRequest().body.inputStream().readBytes()) - val inputStream = GZIPInputStream(fileInputStream) - val reader = InputStreamReader(inputStream) - val bufferedReader = BufferedReader(reader) - val request = bufferedReader.readText() - - assert(request.contains("name=ProductName\r\n\r\nTest App")) - assert(request.contains("name=ProductID\r\n\r\n{1234-1234-1234}")) - assert(request.contains("name=GeckoViewVersion\r\n\r\n0.1")) - assert(request.contains("name=BuildID\r\n\r\n1.0")) - assert(request.contains("name=Vendor\r\n\r\nMozilla Test")) - assert(request.contains("name=Version\r\n\r\n0.0.1")) - assert(request.contains("name=ReleaseChannel\r\n\r\ntest channel")) - - verify(service).report(throwable) - verify(service).sendReport(throwable, null, null, false, false) + try { + mockWebServer.enqueue( + MockResponse().setResponseCode(200) + .setBody("CrashID=bp-924121d3-4de3-4b32-ab12-026fc0190928") + ) + mockWebServer.start() + val serverUrl = mockWebServer.url("/") + val service = spy( + MozillaSocorroService( + applicationContext = testContext, + appName = "Test App", + appId = "{1234-1234-1234}", + version = "0.1", + buildId = "1.0", + vendor = "Mozilla Test", + serverUrl = serverUrl.toString(), + versionName = "0.0.1", + releaseChannel = "test channel" + ) + ) + + val throwable = RuntimeException("Test") + throwable.stackTrace = emptyArray() + service.report(throwable) + + val fileInputStream = + ByteArrayInputStream(mockWebServer.takeRequest().body.inputStream().readBytes()) + val inputStream = GZIPInputStream(fileInputStream) + val reader = InputStreamReader(inputStream) + val bufferedReader = BufferedReader(reader) + val request = bufferedReader.readText() + + assert(request.contains("name=ProductName\r\n\r\nTest App")) + assert(request.contains("name=ProductID\r\n\r\n{1234-1234-1234}")) + assert(request.contains("name=GeckoViewVersion\r\n\r\n0.1")) + assert(request.contains("name=BuildID\r\n\r\n1.0")) + assert(request.contains("name=Vendor\r\n\r\nMozilla Test")) + assert(request.contains("name=Version\r\n\r\n0.0.1")) + assert(request.contains("name=ReleaseChannel\r\n\r\ntest channel")) + + verify(service).report(throwable) + verify(service).sendReport(throwable, null, null, false, false) + } finally { + mockWebServer.shutdown() + } } @Test fun `Confirm MozillaSocorroService parameter order is correct`() { val mockWebServer = MockWebServer() - mockWebServer.enqueue(MockResponse().setResponseCode(200) - .setBody("CrashID=bp-924121d3-4de3-4b32-ab12-026fc0190928")) - mockWebServer.start() - val serverUrl = mockWebServer.url("/") - val service = spy(MozillaSocorroService( - testContext, - "Test App", - "{1234-1234-1234}", - "0.1", - "1.0", - "Mozilla Test", - serverUrl.toString(), - "0.0.1", - "test channel" - )) - val throwable = RuntimeException("Test") - throwable.stackTrace = emptyArray() - service.report(throwable) + try { + mockWebServer.enqueue( + MockResponse().setResponseCode(200) + .setBody("CrashID=bp-924121d3-4de3-4b32-ab12-026fc0190928") + ) + mockWebServer.start() + val serverUrl = mockWebServer.url("/") + val service = spy( + MozillaSocorroService( + testContext, + "Test App", + "{1234-1234-1234}", + "0.1", + "1.0", + "Mozilla Test", + serverUrl.toString(), + "0.0.1", + "test channel" + ) + ) + + val throwable = RuntimeException("Test") + throwable.stackTrace = emptyArray() + service.report(throwable) + + val fileInputStream = + ByteArrayInputStream(mockWebServer.takeRequest().body.inputStream().readBytes()) + val inputStream = GZIPInputStream(fileInputStream) + val reader = InputStreamReader(inputStream) + val bufferedReader = BufferedReader(reader) + val request = bufferedReader.readText() + + assert(request.contains("name=ProductName\r\n\r\nTest App")) + assert(request.contains("name=ProductID\r\n\r\n{1234-1234-1234}")) + assert(request.contains("name=GeckoViewVersion\r\n\r\n0.1")) + assert(request.contains("name=BuildID\r\n\r\n1.0")) + assert(request.contains("name=Vendor\r\n\r\nMozilla Test")) + assert(request.contains("name=Version\r\n\r\n0.0.1")) + assert(request.contains("name=ReleaseChannel\r\n\r\ntest channel")) + + verify(service).report(throwable) + verify(service).sendReport(throwable, null, null, false, false) + } finally { + mockWebServer.shutdown() + } + } - val fileInputStream = ByteArrayInputStream(mockWebServer.takeRequest().body.inputStream().readBytes()) - val inputStream = GZIPInputStream(fileInputStream) - val reader = InputStreamReader(inputStream) - val bufferedReader = BufferedReader(reader) - val request = bufferedReader.readText() + @Test + fun `MozillaSocorroService returns crash id from Socorro`() { + val mockWebServer = MockWebServer() - assert(request.contains("name=ProductName\r\n\r\nTest App")) - assert(request.contains("name=ProductID\r\n\r\n{1234-1234-1234}")) - assert(request.contains("name=GeckoViewVersion\r\n\r\n0.1")) - assert(request.contains("name=BuildID\r\n\r\n1.0")) - assert(request.contains("name=Vendor\r\n\r\nMozilla Test")) - assert(request.contains("name=Version\r\n\r\n0.0.1")) - assert(request.contains("name=ReleaseChannel\r\n\r\ntest channel")) + try { + mockWebServer.enqueue( + MockResponse().setResponseCode(200) + .setBody("CrashID=bp-924121d3-4de3-4b32-ab12-026fc0190928") + ) + mockWebServer.start() - verify(service).report(throwable) - verify(service).sendReport(throwable, null, null, false, false) + val service = MozillaSocorroService( + testContext, + "Test App", + "{1234-1234-1234}", + "0.1", + "1.0", + "Mozilla Test", + mockWebServer.url("/").toString(), + "0.0.1", + "test channel" + ) + + val throwable = RuntimeException("Test") + val id = service.report(throwable) + + assertEquals("bp-924121d3-4de3-4b32-ab12-026fc0190928", id) + } finally { + mockWebServer.shutdown() + } } } diff --git a/components/lib/crash/src/test/java/mozilla/components/lib/crash/service/SendCrashReportServiceTest.kt b/components/lib/crash/src/test/java/mozilla/components/lib/crash/service/SendCrashReportServiceTest.kt index 9afcbce1c52..a1d4a1b5fae 100644 --- a/components/lib/crash/src/test/java/mozilla/components/lib/crash/service/SendCrashReportServiceTest.kt +++ b/components/lib/crash/src/test/java/mozilla/components/lib/crash/service/SendCrashReportServiceTest.kt @@ -57,16 +57,19 @@ class SendCrashReportServiceTest { val crashReporter = spy(CrashReporter( shouldPrompt = CrashReporter.Prompt.NEVER, services = listOf(object : CrashReporterService { - override fun report(crash: Crash.UncaughtExceptionCrash) { + override fun report(crash: Crash.UncaughtExceptionCrash): String? { fail("Didn't expect uncaught exception crash") + return null } - override fun report(crash: Crash.NativeCodeCrash) { + override fun report(crash: Crash.NativeCodeCrash): String? { caughtCrash = crash + return null } - override fun report(throwable: Throwable) { + override fun report(throwable: Throwable): String? { fail("Didn't expect caught exception") + return null } }), scope = scope diff --git a/components/lib/crash/src/test/java/mozilla/components/lib/crash/service/SendCrashTelemetryServiceTest.kt b/components/lib/crash/src/test/java/mozilla/components/lib/crash/service/SendCrashTelemetryServiceTest.kt index 84c10d8499d..bb727c609d3 100644 --- a/components/lib/crash/src/test/java/mozilla/components/lib/crash/service/SendCrashTelemetryServiceTest.kt +++ b/components/lib/crash/src/test/java/mozilla/components/lib/crash/service/SendCrashTelemetryServiceTest.kt @@ -55,16 +55,19 @@ class SendCrashTelemetryServiceTest { val crashReporter = spy(CrashReporter( shouldPrompt = CrashReporter.Prompt.NEVER, telemetryServices = listOf(object : CrashReporterService { - override fun report(crash: Crash.UncaughtExceptionCrash) { + override fun report(crash: Crash.UncaughtExceptionCrash): String? { fail("Didn't expect uncaught exception crash") + return null } - override fun report(crash: Crash.NativeCodeCrash) { + override fun report(crash: Crash.NativeCodeCrash): String? { caughtCrash = crash + return null } - override fun report(throwable: Throwable) { + override fun report(throwable: Throwable): String? { fail("Didn't expect caught exception") + return null } }), scope = scope diff --git a/components/lib/crash/src/test/java/mozilla/components/lib/crash/service/SentryServiceTest.kt b/components/lib/crash/src/test/java/mozilla/components/lib/crash/service/SentryServiceTest.kt index 7d6c8ec80a1..9d817c4b492 100644 --- a/components/lib/crash/src/test/java/mozilla/components/lib/crash/service/SentryServiceTest.kt +++ b/components/lib/crash/src/test/java/mozilla/components/lib/crash/service/SentryServiceTest.kt @@ -106,7 +106,7 @@ class SentryServiceTest { } @Test - fun `SentryService sends message for native code crashes`() { + fun `SentryService sends event for native code crashes`() { val client: SentryClient = mock() val service = SentryService( @@ -119,11 +119,11 @@ class SentryServiceTest { service.report(Crash.NativeCodeCrash("", true, "", false, arrayListOf())) - verify(client).sendMessage(any()) + verify(client).sendEvent(any()) } @Test - fun `SentryService does not send message for native code crashes by default`() { + fun `SentryService does not send event for native code crashes by default`() { val client: SentryClient = mock() val service = SentryService( @@ -135,7 +135,7 @@ class SentryServiceTest { service.report(Crash.NativeCodeCrash("", true, "", false, arrayListOf())) - verify(client, never()).sendMessage(any()) + verify(client, never()).sendEvent(any()) } @Test diff --git a/samples/crash/src/main/java/org/mozilla/samples/crash/CrashApplication.kt b/samples/crash/src/main/java/org/mozilla/samples/crash/CrashApplication.kt index e7c864f5658..b3f0892531f 100644 --- a/samples/crash/src/main/java/org/mozilla/samples/crash/CrashApplication.kt +++ b/samples/crash/src/main/java/org/mozilla/samples/crash/CrashApplication.kt @@ -55,22 +55,25 @@ private fun createDummyCrashService(context: Context): CrashReporterService { // For this sample we create a dummy service. In a real application this would be an instance of SentryCrashService // or SocorroCrashService. return object : CrashReporterService { - override fun report(crash: Crash.UncaughtExceptionCrash) { + override fun report(crash: Crash.UncaughtExceptionCrash): String? { GlobalScope.launch(Dispatchers.Main) { Toast.makeText(context, "Uploading uncaught exception crash...", Toast.LENGTH_SHORT).show() } + return null } - override fun report(crash: Crash.NativeCodeCrash) { + override fun report(crash: Crash.NativeCodeCrash): String? { GlobalScope.launch(Dispatchers.Main) { Toast.makeText(context, "Uploading native crash...", Toast.LENGTH_SHORT).show() } + return null } - override fun report(throwable: Throwable) { + override fun report(throwable: Throwable): String? { GlobalScope.launch(Dispatchers.Main) { Toast.makeText(context, "Uploading caught exception...", Toast.LENGTH_SHORT).show() } + return null } } }